class Day: def __init__( self, cal, day ): self._calendar = cal self._day = day self._confs = OOBTree() self._categs = [] def _getCalendar( self ): return self._calendar def addConference(self,conf,categList,tz): for categ in categList: if categ not in self._categs: self._categs.append(categ) t = conf.getStartDate().astimezone(tz).time() if not self._confs.has_key(t): self._confs[t]=set() self._confs[t].add(conf) #sorting functions that caches calculated start times for every conf def _sortFunc(self, x,y): return cmp(self._cache[x], self._cache[y]) def _calculateCache(self, confs): self._cache = {} for conf in confs: self._cache[conf] = conf.calculateDayStartTime(self._day).time() def getConferences(self): return [conf for confs in self._confs.values() for conf in confs] def getConferencesWithStartTime(self): res= [conf for confs in self._confs.values() for conf in confs] self._calculateCache(res) if res!=[]: res.sort(self._sortFunc) return [(event, self._cache[event]) for event in res] def getCategories(self): return self._categs def getWeekDay( self ): return calendar.weekday( self._day.year, \ self._day.month, \ self._day.day ) def getDayNumber( self ): return self._day.day def getDate( self ): return self._day def __str__( self ): return "CalendarDay at '%s': %s --> %s"%(self._day, self._confs, self._categs)
class PersitentOOBTree(Persistent): """A persitent wrapper around a OOBTree""" def __init__(self): self._data = OOBTree() Persistent.__init__(self) self.__len = Length() @Lazy def _PersitentOOBTree__len(self): l = Length() ol = len(self._data) if ol > 0: l.change(ol) self._p_changed = True return l def __len__(self): return self.__len() def __setitem__(self, key, value): # make sure our lazy property gets set l = self.__len self._data[key] = value l.change(1) def __delitem__(self, key): # make sure our lazy property gets set l = self.__len del self._data[key] l.change(-1) def __iter__(self): return iter(self._data) def __getitem__(self, key): """See interface `IReadContainer`. """ return self._data[key] def get(self, key, default=None): """See interface `IReadContainer`. """ return self._data.get(key, default) def __contains__(self, key): """See interface `IReadContainer`. """ return key in self._data has_key = __contains__ def items(self, key=None): return self._data.items(key) def keys(self, key=None): return self._data.keys(key) def values(self, key=None): return self._data.values(key)
class TopicIndex(Persistent): implements(IInjection, ITopicQuerying) def __init__(self): self.clear() def clear(self): # mapping filter id -> filter self._filters = OOBTree() def addFilter(self, f ): """ Add filter 'f' with ID 'id' """ self._filters[f.getId()] = f def delFilter(self, id): """ remove a filter given by its ID 'id' """ del self._filters[id] def index_doc(self, docid, obj): """index an object""" for f in self._filters.values(): f.index_doc(docid, obj) def unindex_doc(self, docid): """unindex an object""" for f in self._filters.values(): f.unindex_doc(docid) def search(self, query, operator='and'): if isinstance(query, StringTypes): query = [query] if not isinstance(query, (TupleType, ListType)): raise TypeError('query argument must be a list/tuple of filter ids') f = {'and' : intersection, 'or' : union}[operator] rs = None for id in self._filters.keys(): if id in query: docids = self._filters[id].getIds() rs = f(rs, docids) if rs: return rs else: return IISet()
class Package(Persistent): pypi_url = 'http://pypi.python.org/pypi/{}/json' def __init__(self, name): self.__name__ = name self.name = name self.releases = OOBTree() def __getitem__(self, release_name): return self.releases[release_name] def __setitem__(self, key, value): key = format_key(key) self.releases[key] = value self.releases[key].__parent__ = self @classmethod @cache_region('pypi', 'get_last_remote_filename') def get_last_remote_version(cls, proxy, package_name): logger.debug('Not in cache') if not proxy: return None try: result = requests.get('http://pypi.python.org/pypi/{}/json'.format(package_name)) if not result.status_code == 200: return None result = json.loads(result.content.decode('utf-8')) return result['info']['version'] except ConnectionError: pass return None def repository_is_up_to_date(self, last_remote_release): if not last_remote_release: return True remote_version = parse_version(last_remote_release) local_versions = [release.version for release in self.releases.values()] for version in local_versions: if parse_version(version) >= remote_version: return True return False @classmethod def by_name(cls, name, request): root = repository_root_factory(request) return root[name] if name in root else None def get_last_release(self): max_version = max([parse_version(version) for version in self.releases.keys()]) for version, release in self.releases.items(): if parse_version(version) == max_version: return release
class Day: def __init__( self, cal, day ): self._calendar = cal self._day = day self._confs = OOBTree() self._categs = [] def _getCalendar( self ): return self._calendar def addConference(self,conf,categList,tz): for categ in categList: if categ not in self._categs: self._categs.append(categ) t = conf.getStartDate().astimezone(tz).time() if not self._confs.has_key(t): self._confs[t]=sets.Set() self._confs[t].add(conf.getId()) def getConferenceIds(self): res=[] for confs in self._confs.values(): for conf in confs: res.append(conf) return res def getConferences(self): res=[] ch=conference.ConferenceHolder() for confId in self.getConferenceIds(): res.append(ch.getById(confId)) if res!=[]: res.sort(lambda x,y: cmp(x.calculateDayStartTime(self._day).time(),y.calculateDayStartTime(self._day).time())) return res def getCategories(self): return self._categs def getWeekDay( self ): return calendar.weekday( self._day.year, \ self._day.month, \ self._day.day ) def getDayNumber( self ): return self._day.day def getDate( self ): return self._day def __str__( self ): return "CalendarDay at '%s': %s --> %s"%(self._day, self._confs, self._categs)
class ActivityUtil(object): """ See IActivityUtil """ implements(IActivityUtil) def __init__(self): self._storage = OOBTree() def mark_activity_for(self, userid, meeting_uid, dt = None, **kw): assert isinstance(meeting_uid, basestring) kw['dt'] = dt and dt or utcnow() kw['userid'] = userid kw['m_uid'] = meeting_uid try: us = self._storage[meeting_uid][userid] except KeyError: if meeting_uid not in self._storage: self._storage[meeting_uid] = OOBTree() us = self._storage[meeting_uid][userid] = OOBTree() for (k, v) in kw.items(): us[k] = v return kw.keys() def maybe_mark(self, context, request, dt = None, **kw): userid = authenticated_userid(request) if not userid: return if not has_permission(VIEW, context, request): return meeting = find_interface(context, IMeeting) if meeting: self.mark_activity_for(userid, meeting.uid, dt = dt, **kw) def latest_activity(self, meeting_uid, userid = None, limit = 5): if meeting_uid not in self._storage: return () res = [v for (k, v) in self._storage[meeting_uid].items() if k != userid] res = sorted(res, key = lambda x: x['dt'], reverse = True) return limit and tuple(res[:limit]) or tuple(res) def latest_user_activity(self, userid, meeting_uid = None, limit = 5): if meeting_uid is not None: try: return tuple([self._storage[meeting_uid][userid]]) except KeyError: return () res = [] for m in self._storage.values(): if userid in m: res.append(m[userid]) res = sorted(res, key = lambda x: x['dt'], reverse = True) return limit and tuple(res[:limit]) or tuple(res)
class Inbox(BTreeDictBase): username = None new_messages_count = 0 def __init__(self, username): self.data = OOBTree() self.username = username def add_conversation(self, conversation): self[conversation.username] = conversation return conversation def __setitem__(self, key, conversation): if key != conversation.username: msg = 'conversation.username and key differ ({0}, {1})' raise KeyError(msg.format(conversation.username, key)) if conversation.username == self.username: raise ValueError("You can't speak to yourself") verifyObject(IConversation, conversation) if key in self: raise KeyError('Conversation exists already') super(Inbox, self).__setitem__(conversation.username, conversation) self.update_new_messages_count(conversation.new_messages_count) return conversation def __delitem__(self, key): conversation = self[key] self.update_new_messages_count(conversation.new_messages_count * -1) super(Inbox, self).__delitem__(key) def get_conversations(self): return self.data.values() def is_blocked(self, username): # FIXME: not implemented return False def update_new_messages_count(self, difference): count = self.new_messages_count count = count + difference if count < 0: # FIXME: Error. Log? count = 0 self.new_messages_count = count
class AttachmentStorage(Traversable, Persistent, Explicit): """ The attachment storage is a container for all attachments on content objects (that provide IAttachmentStoragable). All objects including non-folderish ones, can be adapted to store attachments, since attached objects are not stored inside the object, but inside this AttachmentStorage container (which is stored as an annotation on the object being attached to). """ interface.implements(IAttachmentStorage, IHideFromBreadcrumbs) __allow_access_to_unprotected_subobjects__ = True def __init__(self, id="++attachments++default"): self.id = id if not shasattr(self, '_attachments'): self.init_storage() def __getitem__(self, id): """ """ return self.get(id) def getId(self): """ Get the id of the storage. This is used to construct a URL. """ return self.id def init_storage(self): self._attachments = OOBTree() def keys(self): return self._attachments.keys() def values(self): return [att.__of__(self) for att in self._attachments.values()] def get(self, id): return self._attachments.get(id).__of__(self) def add(self, attachment): if attachment.getId() in self._attachments: raise DuplicateIDError self._attachments[attachment.getId()] = attachment def remove(self, id): del self._attachments[id]
class Survey(Content, ContextACLMixin, LocalRolesMixin, TranslationMixin): type_title = _("Survey") type_name = "Survey" add_permission = ADD_SURVEY allow_anonymous_to_invite_themselves = False # allow_anonymous_to_start = False def __init__(self, **kw): self.tokens = OOBTree() super(Survey, self).__init__(**kw) def create_token(self, email, size = 15, hours = 0, overwrite = False): """ Create a survey invitation token.""" if email not in self.tokens or (email in self.tokens and overwrite == True): token = None while token is None or token in self.tokens.values(): token = Token(size = size, hours = hours) self.tokens[email] = token return self.tokens[email] def get_participants_data(self): """Returns the participants with statistics on the survey """ participants = [] for (email, uid) in self.tokens.items(): participant = {} participant['uid'] = uid participant['email'] = email response = 0 questions = 0 sections = [x for x in self.values() if ISurveySection.providedBy(x)] for section in sections: response += len(section.responses.get(uid, {})) questions += len(section.question_ids) if response != 0: participant['finished'] = Decimal(response) / Decimal(questions) * 100 else: participant['finished'] = 0 participants.append(participant) return participants
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'
def test_range_seach(self): """ """ array = list(range(self.complexity)) random.shuffle(array) d = {i: -i for i in array} t = OOBTree() t.update(d) # === dict === st = time.clock() result1 = list() for k, v in d.items(): if (self.lower <= k <= self.upper): result1.append(v) elapse1 = time.clock() - st # === bisearch === sorted_keys = list(d.keys()) # sort the list of keys sorted_keys.sort() st = time.clock() lower_ind = bisect.bisect_left(sorted_keys, self.lower) # find the min index upper_ind = bisect.bisect_right(sorted_keys, self.upper) - 1 # find the max index result2 = list() # fetch item for ind in range(lower_ind, upper_ind+1): result2.append(d[sorted_keys[ind]]) elapse2 = time.clock() - st # === tree === st = time.clock() result3 = t.values(min=self.lower, max=self.upper, excludemin=False, excludemax=False) elapse3 = time.clock() - st print("results are:", result1, result2, list(result3)) print("dict method = %.6f, bisearch = %.6f, tree = %.6f" % (elapse1, elapse2, elapse3)) self.assertGreater(elapse1, elapse2) self.assertGreater(elapse2, elapse3)
class SinTool(UniqueObject, ActionProviderBase, SimpleItem): """ CMF Syndication Client """ id = 'sin_tool' meta_type = 'CMFSin Syndication Tool' security = ClassSecurityInfo() _actions = [ActionInformation( id='newfeeds' , title='NewsFeeds' , action=Expression(text='string: ${portal_url}/sin_tool/sincfg') , condition=Expression(text='member') , permissions=(ManagePortal,) , category='portal_tabs' , visible=0 )] manage_options=( ({ 'label' : 'Config', 'action' : 'manage_configForm', }, { 'label' : 'Debug', 'action' : 'manage_debugForm', }, ) + SimpleItem.manage_options + ActionProviderBase.manage_options ) manage_debugForm = PageTemplateFile('www/debug', globals()) manage_configForm = PageTemplateFile('www/config', globals()) def __init__(self): self._reset() def _reset(self, config=None): self.maps = OOBTree() self._v_data = OOBTree() self.channels = OOBTree() self.config = config security.declarePublic('Maps') def Maps(self): """ Available Maps """ return self.maps.values() security.declarePublic('Channels') def Channels(self): """ Available Channels """ return self.channels.values() security.declarePublic('Policies') def Policies(self): """ Available Policies """ return listPolicies() def encode(self, parsed_data): # Get site encoding enc = 'iso8859-1' try: pp = getToolByName(self, 'portal_properties') enc = getattr(pp.site_properties, 'default_charset', 'iso8859-1') except (AttributeError, KeyError): pass info = parsed_data['channel'] data = parsed_data['items'] # encoding = parsed_data.get('encoding', 'ascii') # for key in ('description', 'tagline', 'title'): # if isinstance(info[key], basestring) and not isinstance(info[key], unicode): # info[key] = udecode(info[key], encoding) # for r in data: # for key in ('description', 'source', 'summary', 'title'): # if isinstance(r[key], basestring) and not isinstance(r[key], unicode): # r[key] = udecode(r[key], encoding) # return info, data if info.has_key('title'): if type(info['title']) not in (UnicodeType, ): info['title'] = udecode(info['title']).encode(enc) if info.has_key('description'): if type(info['description']) not in (UnicodeType, ): info['description'] = udecode(info['description']).encode(enc) for r in data: if r.has_key('title'): if type(r['title']) not in (UnicodeType, ): r['title'] = udecode(r['title']).encode(enc) return info, data def _update(self, channel, force=None): # Hard update of a channels feed -> data try: parser = lookupParser(channel.id) data = self.encode(parser(channel.uri)) channel.update(data, force) # Lastly, we update the existing data # if everything worked if not hasattr(aq_base(self), '_v_data'): self._v_data = OOBTree() self._v_data[channel.id] = data except (IOError, OSError): channel.failed() security.declareProtected(View, 'updateChannel') def updateChannel(self, channel, force=None): if not isinstance(channel, Channel): channel = self.channels[channel] if force or channel.requireUpdate(): self._update(channel, force) security.declareProtected(View, 'sin') def sin(self, map_name, force=0, max_size=None): """ Returns the syndication info for a given mapping force -- force a channel update max_size -- max size of result set (may be used in policy to calc pri) """ if not hasattr(aq_base(self), '_v_data'): self._v_data = OOBTree() # With a fallback to channel for development map = self.maps.get(map_name) if not map: map = self.channels.get(map_name, None) if map is None: LOG('SinTool.sin', INFO, 'channel %s not found' % map_name) return [] else: channels = (map, ) else: channels = map.Channels() for ci in channels: enabled = ci['enabled'] if not enabled: continue channel = ci['channel'] self.updateChannel(channel, force) # How safe is it to use threading inside Zope? # import threading # thread_name = "SinTool.Channel.%s" % channel.id # if not [t for t in threading.enumerate() if \ # t.getName() == thread_name]: # threading.Thread(target=self.updateChannel, name=thread_name, \ # args=(channel, force)).start() #Collect all the data for all the enabled channels now results = [] links = {} for ci in channels: enabled = ci['enabled'] if not enabled: continue channel = ci['channel'] priority = ci['priority'] data = self._v_data.get(channel.id, None) final = [] if not data: continue # We remove dup links inline for link in data[1]: if not links.has_key(link['link']): links[link['link']] = 1 final.append(link) #We don't cache skips now because the Map #doesn't keep any data, just the channel #relationship, if this is an issue #its easy enough to change data[0]['priority'] = priority results.append(data) policy = map.policy results = policy.order(results, max_size=max_size) return results security.declareProtected(ModifyPortalContent, 'addChannel') def addChannel(self, name, url, **kwargs): c = Channel(name, url, **kwargs) self.channels[name] = c security.declareProtected(ModifyPortalContent, 'addMap') def addMap(self, map, channels=[]): self.maps[map] = m = Map(map) for c in channels: c = self.channels[c] m.addChannel(c) return m security.declarePrivate('parse') def parse(self, file): if type(file) == type(''): file = StringIO(file) config = ConfigParser() config.readfp(file) s = 'channels' options = config.options(s) args = {} for o in options: uri = config.get(s, o, raw=1) match = schedRe.match(uri) if match: uri = uri[match.end():] args['period'] = match.group('period') args['frequency'] = int(match.group('freq')) self.addChannel(o, uri, **args) args.clear() s = 'maps' options = config.options(s) for o in options: channels = config.get(s, o, raw=1) #We look for policy_name:x,y,z #if no policy is specified default is used idx = channels.find(':') policy = None if idx != -1: policy, channels = channels[:idx], channels[idx+1:] m = self.addMap(o) if policy: m.setPolicy(policy) channels = channels.split(',') for c in channels: c = c.strip() # remove whitespace around delimitters if c.endswith(')'): #look for channel(pri) format idx = c.rfind('(') if idx != -1: pri = int(c[idx+1:-1]) c = c[:idx] else: pri = 0 c = self.channels[c] m.addChannel(c, priority=pri) file.seek(0) self.config = file.read() security.declarePrivate('load') def load(self, filename): """ Load a file into config """ name = os.path.join(package_home(globals()), \ os.path.basename(filename)) if not name.endswith('.cfg'): name += '.cfg' fp = open(name) self.parse(fp) security.declarePrivate('save') def save(self, filename): """ Write config into a file """ name = os.path.join(package_home(globals()), \ os.path.basename(filename)) if not name.endswith('.cfg'): name += '.cfg' fp = open(name, 'w') fp.write(self.config) fp.close() security.declareProtected(ModifyPortalContent, 'updateConfig') def updateConfig(self, config=None, REQUEST=None, *args): """Update the config using new info""" self.parse(config) if REQUEST: return REQUEST.RESPONSE.redirect(self.absolute_url() + \ "/sincfg?portal_status_message=Config+Updated") security.declareProtected(ManagePortal, 'manage_configSin') def manage_configSin(self, submit, config='', filename='', \ REQUEST=None, **kwargs): """config this puppy""" if submit == "Set Config": self.parse(config) elif submit == "Import": self.load(filename) elif submit == "Export": self.save(filename) if REQUEST: return REQUEST.RESPONSE.redirect(self.absolute_url() + \ "/manage_workspace") security.declareProtected(ManagePortal, 'manage_debug') def manage_debug(self, submit, maps=(), REQUEST=None, *args, **kwargs): """update maps for testing""" if submit == "Purge": self._reset(self.config) self.parse(self.config) elif submit == "Update Maps": for id in maps: self.sin(id, force=1) if REQUEST: return REQUEST.RESPONSE.redirect(self.absolute_url() + \ "/manage_workspace") security.declarePrivate('setCurrentFeed') def setCurrentFeed(self, name): self._v_current_feed = name security.declareProtected(View, 'getCurrentFeed') def getCurrentFeed(self): """ Get the current configured feed """ fd = self._v_current_feed if fd not in self.maps.keys(): raise ValueError, "'%s' is not a valid map" % fd return fd security.declareProtected(View, 'getChannelUri') def getChannelUri(self, map_name): """ return the uri of a given channel """ map = self.sin_tool.maps.get(map_name) return map.Channels()[0]['channel'].uri # slightly improved hack-o-rama to allow # using a simpler path expression: here/sintool/macros/slashdot security.declareProtected(View, 'macros') def macros(self): """ Allow traversing to map macro via ZPT """ return SinMacro(self) macros = ComputedAttribute(macros, 1)
class ElementItem: """ ElementItem is the "term", it contains information imported from SKOS/rdf, it also contains all results from google search saved as GoogleItems. >>> from Products.ALiSS.managers.element_item import ElementItem >>> eItem = ElementItem( id= 'eItem', ... name= 'eName', ... definition= 'eDefinition', ... translations= {'en':'eEN'}, ... center_parent= 'cParent', ... url= 'eURL') Let's check if our initiation of ElementItem worked. >>> eItem.id 'eItem' >>> eItem.name 'eName' >>> eItem.definition 'eDefinition' >>> eItem.translations {'en': 'eEN'} >>> eItem.center_parent 'cParent' >>> eItem.url 'eURL' >>> eItem.google_disabled [] >>> len(eItem.google_collection) 0 We have a clean ElementItem with no indexed content groups associated (GoogleItem) stored. Lets add some content groups. >>> eItem.add_google_item(id= 'itemID', ... elem_parent= 'eItem', ... center_parent= 'cParent', ... group_id= 'grpId', ... match_level= 'exact') We have added a google item with id=gItem, let's try to retrieve it. >>> from Products.ALiSS.managers.google_item import GoogleItem >>> ourItem = eItem.get_google_collection_item('grpId') >>> isinstance(ourItem, GoogleItem) True Worked fine, there is not much more we can do here except delete the google item :) >>> eItem.delete_google_item('grpId') >>> ourItem = eItem.get_google_collection_item('grpId') >>> ourItem == None True """ security = ClassSecurityInfo() meta_type = METATYPE_ALISSELEMENT def __init__(self, id, name, definition, translations, center_parent, url): #identifiers self.id = id self.center_parent = center_parent #element data self.url = url self.name = name self.definition = definition self.translations = translations #google data self.google_collection = OOBTree() self.google_disabled = [] def __getattr__(self, name): """ """ if name.startswith('objecttrans_'): parts = name.split('_') func, lang = parts[0], parts[1] return self.objecttrans(lang) if name.startswith('objectname_'): parts = name.split('_') func, lang = parts[0], parts[1] return self.objectname(lang) raise AttributeError, name ################################# # GOOGLE_COLLECTION BASICS # ################################# def __add_google_page(self, group_id, id, google_parent, elem_parent, center_parent, g_server, page_title, page_url, page_snippet, page_directoryTitle, page_cachedSize, page_hostName): #add a page google_item = self.get_google_collection_item(group_id) if google_item: google_item.add_google_page(id, google_parent, elem_parent, center_parent, g_server, page_title, page_url, page_snippet, page_directoryTitle, page_cachedSize, page_hostName) ################# # BASIC API # ################# def add_google_page(self, group_id, id, google_parent, elem_parent, center_parent, g_server, page_title, page_url, page_snippet, page_directoryTitle, page_cachedSize, page_hostName): #create a new page self.__add_google_page(group_id, id, google_parent, elem_parent, center_parent, g_server, page_title, page_url, page_snippet, page_directoryTitle, page_cachedSize, page_hostName) def add_google_item(self, id, elem_parent, center_parent, group_id, match_level): #create a new item item = GoogleItem(id, elem_parent, center_parent, group_id, match_level) self.google_collection[group_id] = item def delete_google_item(self, group_id): #delete an item if self.google_collection.get(group_id): del self.google_collection[group_id] ############### # GETTERS # ############### def get_google_collection(self): """ get the collection """ return self.google_collection def get_google_ids(self): """ get the google ids """ return self.google_collection.keys() def get_google_collection_item(self, group_id): """ get an item """ return self.google_collection.get(group_id, None) def get_google_objs(self): """ get a list with all items """ return self.google_collection.values() def getCollectionStatistics(self): """ """ return self.get_google_ids() def getDefinition(self): """ return the definition in UTF8 for this item. Use this method when you want to display on UTF8/XHTML webpages. """ return content_filter.safeXHTMLUnicode(self.definition) def getUrl(self): """ return the escaped URL for this item. Use this method when you want to display valid excaped URL in <a>-tag. """ return utUrlEncode(self.url) #TODO: DEPRECATED # def getName(self): # """ return lower name (for catalog use) """ # return self.name.lower() ##################### # TRANSLATIONS # ##################### def hasTranslations(self): """ test if the element contains any translation """ return len(self.translations.keys()) def getTranslations(self): """ return all translations """ return self.translations def getTranslation(self, lang): """ return a translation for a given language code """ if lang == 'en': return self.name try: return self.translations[lang] except: return '' def getTranslationsList(self): """ """ trans = self.translations.values() trans.append(self.name) return trans def getTranslationsSuggest(self): """ """ return ' '.join(self.getTranslationsList()) def objecttrans(self, lang): """ used to catalog translations """ return self.getTranslation(lang) def objectname(self, lang): """ used to catalog translations """ return self.getTranslation(lang).lower() #################################### # GOOGLE ENABLE/DISABLE RELATED # #################################### def mark_google_error(self, group_id): #mark the object if an error occur on Google querying my_group = self.get_google_collection_item(group_id) my_group.google_error = 1 self.google_collection[group_id] = my_group def set_google_enable(self, state, group_id): #set enable/disable state my_group = self.get_google_collection_item(group_id) my_group.google_enable = state self.google_collection[group_id] = my_group def __add_google_disabled(self, content_id): #add a disabled content group if content_id not in self.google_disabled: self.google_disabled.append(content_id) def addGoogleDisabled(self, content_ids): #add a disabled content group if not utIsListType(content_ids): content_ids = [ content_ids ] for content_id in content_ids: self.__add_google_disabled(content_id) def __del_google_disabled(self, content_id): #remove a disabled content group if content_id in self.google_disabled: self.google_disabled.remove(content_id) def delGoogleDisabled(self, content_ids): #remove a disabled content group if not utIsListType(content_ids): content_ids = [ content_ids ] for content_id in content_ids: self.__del_google_disabled(content_id) def isGroupDisabled(self, group_id): return group_id in self.google_disabled security.setDefaultAccess("allow")
class ZODBRoleManager( BasePlugin ): """ PAS plugin for managing roles in the ZODB. """ meta_type = 'ZODB Role Manager' security = ClassSecurityInfo() def __init__(self, id, title=None): self._id = self.id = id self.title = title self._roles = OOBTree() self._principal_roles = OOBTree() def manage_afterAdd( self, item, container ): self.addRole( 'Manager' ) if item is self: role_holder = aq_parent( aq_inner( container ) ) for role in getattr( role_holder, '__ac_roles__', () ): try: if role not in ('Anonymous', 'Authenticated'): self.addRole( role ) except KeyError: pass # # IRolesPlugin implementation # security.declarePrivate( 'getRolesForPrincipal' ) def getRolesForPrincipal( self, principal, request=None ): """ See IRolesPlugin. """ result = list( self._principal_roles.get( principal.getId(), () ) ) getGroups = getattr( principal, 'getGroups', lambda x: () ) for group_id in getGroups(): result.extend( self._principal_roles.get( group_id, () ) ) return tuple( result ) # # IRoleEnumerationPlugin implementation # def enumerateRoles( self , id=None , exact_match=False , sort_by=None , max_results=None , **kw ): """ See IRoleEnumerationPlugin. """ role_info = [] role_ids = [] plugin_id = self.getId() if isinstance( id, str ): id = [ id ] if exact_match and ( id ): role_ids.extend( id ) if role_ids: role_filter = None else: # Searching role_ids = self.listRoleIds() role_filter = _ZODBRoleFilter( id, **kw ) for role_id in role_ids: if self._roles.get( role_id ): e_url = '%s/manage_roles' % self.getId() p_qs = 'role_id=%s' % role_id m_qs = 'role_id=%s&assign=1' % role_id info = {} info.update( self._roles[ role_id ] ) info[ 'pluginid' ] = plugin_id info[ 'properties_url' ] = '%s?%s' % (e_url, p_qs) info[ 'members_url' ] = '%s?%s' % (e_url, m_qs) if not role_filter or role_filter( info ): role_info.append( info ) return tuple( role_info ) # # IRoleAssignerPlugin implementation # security.declarePrivate( 'doAssignRoleToPrincipal' ) def doAssignRoleToPrincipal( self, principal_id, role ): return self.assignRoleToPrincipal( role, principal_id ) # # Role management API # security.declareProtected( ManageUsers, 'listRoleIds' ) def listRoleIds( self ): """ Return a list of the role IDs managed by this object. """ return self._roles.keys() security.declareProtected( ManageUsers, 'listRoleInfo' ) def listRoleInfo( self ): """ Return a list of the role mappings. """ return self._roles.values() security.declareProtected( ManageUsers, 'getRoleInfo' ) def getRoleInfo( self, role_id ): """ Return a role mapping. """ return self._roles[ role_id ] security.declareProtected( ManageUsers, 'addRole' ) def addRole( self, role_id, title='', description='' ): """ Add 'role_id' to the list of roles managed by this object. o Raise KeyError on duplicate. """ if self._roles.get( role_id ) is not None: raise KeyError, 'Duplicate role: %s' % role_id self._roles[ role_id ] = { 'id' : role_id , 'title' : title , 'description' : description } security.declareProtected( ManageUsers, 'updateRole' ) def updateRole( self, role_id, title, description ): """ Update title and description for the role. o Raise KeyError if not found. """ self._roles[ role_id ].update( { 'title' : title , 'description' : description } ) security.declareProtected( ManageUsers, 'removeRole' ) def removeRole( self, role_id ): """ Remove 'role_id' from the list of roles managed by this object. o Raise KeyError if not found. """ for principal_id in self._principal_roles.keys(): self.removeRoleFromPrincipal( role_id, principal_id ) del self._roles[ role_id ] # # Role assignment API # security.declareProtected( ManageUsers, 'listAvailablePrincipals' ) def listAvailablePrincipals( self, role_id, search_id ): """ Return a list of principal IDs to whom a role can be assigned. o If supplied, 'search_id' constrains the principal IDs; if not, return empty list. o Omit principals with existing assignments. """ result = [] if search_id: # don't bother searching if no criteria parent = aq_parent( self ) for info in parent.searchPrincipals( max_results=20 , sort_by='id' , id=search_id , exact_match=False ): id = info[ 'id' ] title = info.get( 'title', id ) if ( role_id not in self._principal_roles.get( id, () ) and role_id != id ): result.append( ( id, title ) ) return result security.declareProtected( ManageUsers, 'listAssignedPrincipals' ) def listAssignedPrincipals( self, role_id ): """ Return a list of principal IDs to whom a role is assigned. """ result = [] for k, v in self._principal_roles.items(): if role_id in v: # should be at most one and only one mapping to 'k' parent = aq_parent( self ) info = parent.searchPrincipals( id=k, exact_match=True ) assert( len( info ) in ( 0, 1 ) ) if len( info ) == 0: title = '<%s: not found>' % k else: title = info[0].get( 'title', k ) result.append( ( k, title ) ) return result security.declareProtected( ManageUsers, 'assignRoleToPrincipal' ) def assignRoleToPrincipal( self, role_id, principal_id ): """ Assign a role to a principal (user or group). o Return a boolean indicating whether a new assignment was created. o Raise KeyError if 'role_id' is unknown. """ role_info = self._roles[ role_id ] # raise KeyError if unknown! current = self._principal_roles.get( principal_id, () ) already = role_id in current if not already: new = current + ( role_id, ) self._principal_roles[ principal_id ] = new return not already security.declareProtected( ManageUsers, 'removeRoleFromPrincipal' ) def removeRoleFromPrincipal( self, role_id, principal_id ): """ Remove a role from a principal (user or group). o Return a boolean indicating whether the role was already present. o Raise KeyError if 'role_id' is unknown. o Ignore requests to remove a role not already assigned to the principal. """ role_info = self._roles[ role_id ] # raise KeyError if unknown! current = self._principal_roles.get( principal_id, () ) new = tuple( [ x for x in current if x != role_id ] ) already = current != new if already: self._principal_roles[ principal_id ] = new return already # # ZMI # manage_options = ( ( { 'label': 'Roles', 'action': 'manage_roles', } , ) + BasePlugin.manage_options ) security.declareProtected( ManageUsers, 'manage_roles' ) manage_roles = PageTemplateFile( 'www/zrRoles' , globals() , __name__='manage_roles' ) security.declareProtected( ManageUsers, 'manage_twoLists' ) manage_twoLists = PageTemplateFile( '../www/two_lists' , globals() , __name__='manage_twoLists' ) security.declareProtected( ManageUsers, 'manage_addRole' ) def manage_addRole( self , role_id , title , description , RESPONSE ): """ Add a role via the ZMI. """ self.addRole( role_id, title, description ) message = 'Role+added' RESPONSE.redirect( '%s/manage_roles?manage_tabs_message=%s' % ( self.absolute_url(), message ) ) security.declareProtected( ManageUsers, 'manage_updateRole' ) def manage_updateRole( self , role_id , title , description , RESPONSE ): """ Update a role via the ZMI. """ self.updateRole( role_id, title, description ) message = 'Role+updated' RESPONSE.redirect( '%s/manage_roles?role_id=%s&manage_tabs_message=%s' % ( self.absolute_url(), role_id, message ) ) security.declareProtected( ManageUsers, 'manage_removeRoles' ) def manage_removeRoles( self , role_ids , RESPONSE ): """ Remove one or more roles via the ZMI. """ role_ids = filter( None, role_ids ) if not role_ids: message = 'no+roles+selected' else: for role_id in role_ids: self.removeRole( role_id ) message = 'Roles+removed' RESPONSE.redirect( '%s/manage_roles?manage_tabs_message=%s' % ( self.absolute_url(), message ) ) security.declareProtected( ManageUsers, 'manage_assignRoleToPrincipals' ) def manage_assignRoleToPrincipals( self , role_id , principal_ids , RESPONSE ): """ Assign a role to one or more principals via the ZMI. """ assigned = [] for principal_id in principal_ids: if self.assignRoleToPrincipal( role_id, principal_id ): assigned.append( principal_id ) if not assigned: message = 'Role+%s+already+assigned+to+all+principals' % role_id else: message = 'Role+%s+assigned+to+%s' % ( role_id , '+'.join( assigned ) ) RESPONSE.redirect( ( '%s/manage_roles?role_id=%s&assign=1' + '&manage_tabs_message=%s' ) % ( self.absolute_url(), role_id, message ) ) security.declareProtected( ManageUsers, 'manage_removeRoleFromPrincipals' ) def manage_removeRoleFromPrincipals( self , role_id , principal_ids , RESPONSE ): """ Remove a role from one or more principals via the ZMI. """ removed = [] for principal_id in principal_ids: if self.removeRoleFromPrincipal( role_id, principal_id ): removed.append( principal_id ) if not removed: message = 'Role+%s+alread+removed+from+all+principals' % role_id else: message = 'Role+%s+removed+from+%s' % ( role_id , '+'.join( removed ) ) RESPONSE.redirect( ( '%s/manage_roles?role_id=%s&assign=1' + '&manage_tabs_message=%s' ) % ( self.absolute_url(), role_id, message ) )
class ZODBRoleManager(BasePlugin): """ PAS plugin for managing roles in the ZODB. """ meta_type = 'ZODB Role Manager' security = ClassSecurityInfo() def __init__(self, id, title=None): self._id = self.id = id self.title = title self._roles = OOBTree() self._principal_roles = OOBTree() def manage_afterAdd(self, item, container): if item is self: role_holder = aq_parent(aq_inner(container)) for role in getattr(role_holder, '__ac_roles__', ()): try: if role not in ('Anonymous', 'Authenticated'): self.addRole(role) except KeyError: pass if 'Manager' not in self._roles: self.addRole('Manager') # # IRolesPlugin implementation # @security.private def getRolesForPrincipal(self, principal, request=None): """ See IRolesPlugin. """ result = list(self._principal_roles.get(principal.getId(), ())) getGroups = getattr(principal, 'getGroups', lambda x: ()) for group_id in getGroups(): result.extend(self._principal_roles.get(group_id, ())) return tuple(result) # # IRoleEnumerationPlugin implementation # def enumerateRoles(self, id=None, exact_match=False, sort_by=None, max_results=None, **kw): """ See IRoleEnumerationPlugin. """ role_info = [] role_ids = [] plugin_id = self.getId() if isinstance(id, str): id = [id] if exact_match and (id): role_ids.extend(id) if role_ids: role_filter = None else: # Searching role_ids = self.listRoleIds() role_filter = _ZODBRoleFilter(id, **kw) for role_id in role_ids: if self._roles.get(role_id): e_url = '%s/manage_roles' % self.getId() p_qs = 'role_id=%s' % role_id m_qs = 'role_id=%s&assign=1' % role_id info = {} info.update(self._roles[role_id]) info['pluginid'] = plugin_id info['properties_url'] = '%s?%s' % (e_url, p_qs) info['members_url'] = '%s?%s' % (e_url, m_qs) if not role_filter or role_filter(info): role_info.append(info) return tuple(role_info) # # IRoleAssignerPlugin implementation # @security.private def doAssignRoleToPrincipal(self, principal_id, role): return self.assignRoleToPrincipal(role, principal_id) @security.private def doRemoveRoleFromPrincipal(self, principal_id, role): return self.removeRoleFromPrincipal(role, principal_id) # # Role management API # @security.protected(ManageUsers) def listRoleIds(self): """ Return a list of the role IDs managed by this object. """ return self._roles.keys() @security.protected(ManageUsers) def listRoleInfo(self): """ Return a list of the role mappings. """ return self._roles.values() @security.protected(ManageUsers) def getRoleInfo(self, role_id): """ Return a role mapping. """ return self._roles[role_id] @security.private def addRole(self, role_id, title='', description=''): """ Add 'role_id' to the list of roles managed by this object. o Raise KeyError on duplicate. """ if self._roles.get(role_id) is not None: raise KeyError('Duplicate role: %s' % role_id) self._roles[role_id] = { 'id': role_id, 'title': title, 'description': description } @security.private def updateRole(self, role_id, title, description): """ Update title and description for the role. o Raise KeyError if not found. """ self._roles[role_id].update({ 'title': title, 'description': description }) @security.private def removeRole(self, role_id, REQUEST=None): """ Remove 'role_id' from the list of roles managed by this object. o Raise KeyError if not found. Note that if you really want to remove a role you should first remove it from the roles in the root of the site (at the bottom of the Security tab at manage_access). """ for principal_id in self._principal_roles.keys(): self.removeRoleFromPrincipal(role_id, principal_id) del self._roles[role_id] # # Role assignment API # @security.protected(ManageUsers) def listAvailablePrincipals(self, role_id, search_id): """ Return a list of principal IDs to whom a role can be assigned. o If supplied, 'search_id' constrains the principal IDs; if not, return empty list. o Omit principals with existing assignments. """ result = [] if search_id: # don't bother searching if no criteria parent = aq_parent(self) for info in parent.searchPrincipals(max_results=20, sort_by='id', id=search_id, exact_match=False): id = info['id'] title = info.get('title', id) if role_id not in self._principal_roles.get(id, ()) and \ role_id != id: result.append((id, title)) return result @security.protected(ManageUsers) def listAssignedPrincipals(self, role_id): """ Return a list of principal IDs to whom a role is assigned. """ result = [] for k, v in self._principal_roles.items(): if role_id in v: # should be at most one and only one mapping to 'k' parent = aq_parent(self) info = parent.searchPrincipals(id=k, exact_match=True) if len(info) > 1: message = ("Multiple groups or users exist with the " "name '%s'. Remove one of the duplicate groups " "or users." % (k)) LOG.error(message) raise MultiplePrincipalError(message) if len(info) == 0: title = '<%s: not found>' % k else: title = info[0].get('title', k) result.append((k, title)) return result @security.private def assignRoleToPrincipal(self, role_id, principal_id): """ Assign a role to a principal (user or group). o Return a boolean indicating whether a new assignment was created. o Raise KeyError if 'role_id' is unknown. """ # raise KeyError if unknown! role_info = self._roles[role_id] # noqa current = self._principal_roles.get(principal_id, ()) already = role_id in current if not already: new = current + (role_id, ) self._principal_roles[principal_id] = new self._invalidatePrincipalCache(principal_id) return not already @security.private def removeRoleFromPrincipal(self, role_id, principal_id): """ Remove a role from a principal (user or group). o Return a boolean indicating whether the role was already present. o Raise KeyError if 'role_id' is unknown. o Ignore requests to remove a role not already assigned to the principal. """ # raise KeyError if unknown! role_info = self._roles[role_id] # noqa current = self._principal_roles.get(principal_id, ()) new = tuple([x for x in current if x != role_id]) already = current != new if already: self._principal_roles[principal_id] = new self._invalidatePrincipalCache(principal_id) return already # # ZMI # manage_options = (({ 'label': 'Roles', 'action': 'manage_roles' }, ) + BasePlugin.manage_options) security.declareProtected(ManageUsers, 'manage_roles') manage_roles = PageTemplateFile('www/zrRoles', globals(), __name__='manage_roles') security.declareProtected(ManageUsers, 'manage_twoLists') manage_twoLists = PageTemplateFile('../www/two_lists', globals(), __name__='manage_twoLists') @security.protected(ManageUsers) @csrf_only @postonly def manage_addRole(self, role_id, title, description, RESPONSE=None, REQUEST=None): """ Add a role via the ZMI. """ self.addRole(role_id, title, description) message = 'Role+added' if RESPONSE is not None: RESPONSE.redirect('%s/manage_roles?manage_tabs_message=%s' % (self.absolute_url(), message)) @security.protected(ManageUsers) @csrf_only @postonly def manage_updateRole(self, role_id, title, description, RESPONSE=None, REQUEST=None): """ Update a role via the ZMI. """ self.updateRole(role_id, title, description) message = 'Role+updated' if RESPONSE is not None: RESPONSE.redirect('%s/manage_roles?role_id=%s&' 'manage_tabs_message=%s' % (self.absolute_url(), role_id, message)) @security.protected(ManageUsers) @csrf_only @postonly def manage_removeRoles(self, role_ids, RESPONSE=None, REQUEST=None): """ Remove one or more role assignments via the ZMI. Note that if you really want to remove a role you should first remove it from the roles in the root of the site (at the bottom of the Security tab at manage_access). """ role_ids = filter(None, role_ids) if not role_ids: message = 'no+roles+selected' else: for role_id in role_ids: self.removeRole(role_id) message = 'Role+assignments+removed' if RESPONSE is not None: RESPONSE.redirect('%s/manage_roles?manage_tabs_message=%s' % (self.absolute_url(), message)) @security.protected(ManageUsers) @csrf_only @postonly def manage_assignRoleToPrincipals(self, role_id, principal_ids, RESPONSE, REQUEST=None): """ Assign a role to one or more principals via the ZMI. """ assigned = [] for principal_id in principal_ids: if self.assignRoleToPrincipal(role_id, principal_id): assigned.append(principal_id) if not assigned: message = 'Role+%s+already+assigned+to+all+principals' % role_id else: message = 'Role+%s+assigned+to+%s' % (role_id, '+'.join(assigned)) if RESPONSE is not None: RESPONSE.redirect('%s/manage_roles?role_id=%s&assign=1' '&manage_tabs_message=%s' % (self.absolute_url(), role_id, message)) @security.protected(ManageUsers) @csrf_only @postonly def manage_removeRoleFromPrincipals(self, role_id, principal_ids, RESPONSE=None, REQUEST=None): """ Remove a role from one or more principals via the ZMI. """ removed = [] for principal_id in principal_ids: if self.removeRoleFromPrincipal(role_id, principal_id): removed.append(principal_id) if not removed: message = 'Role+%s+alread+removed+from+all+principals' % role_id else: message = 'Role+%s+removed+from+%s' % (role_id, '+'.join(removed)) if RESPONSE is not None: RESPONSE.redirect('%s/manage_roles?role_id=%s&assign=1' '&manage_tabs_message=%s' % (self.absolute_url(), role_id, message))
class Calendar: """This class represents a calendar which is a set of days which contain information about which conferences whithin certain categories are happening for each of these days and for a certain access. This class allows to configure the date interval and the category set to be considered and provides operations which allow to know about what's happening on each of those days. Attributes: _aw - (accessControl.AccessWrapper) Information about the access for which the calendar will be built. _sDate - (datetime) Starting date for the calendar. _eDate - (datetime) Ending date for the calendar. _categList - (List) List of categories to be considered. _days - (OOBTree) Index of days which build up the calendar. """ def __init__( self, aw, sDate, eDate, categList=[] ): self._aw = aw self._tz = sDate.tzinfo self._sDate = sDate.replace(hour=0, minute=0, second=0, microsecond=0) self._eDate = eDate.replace(hour=23, minute=59, second=59, microsecond=0) self._categList = categList self._icons = {} self._days = None def getIcons( self ): try: return self._icons except: self._icons = {} return {} def setIcons(self, categ): """Retrieves the list of icons in a given category tree """ if categ.getIcon() != None: return [categ.getId()] res = [] for subcat in categ.getSubCategoryList(): res += self.setIcons(subcat) return res def getStartDate( self ): return self._sDate def getEndDate( self ): return self._eDate def getCategoryList( self ): return self._categList def getLocator( self ): """Returns the generic locator for the current object. This locator contains the folloing entries corresponding to values for which the calendar is configured: selCateg -> List of category ids. sDate -> Starting date. eDate -> Ending date. """ l = Locators.Locator() ids = [] for c in self.getCategoryList(): ids.append( c.getId() ) l["selCateg"] = ids l["sDate"] = self.getStartDate().strftime("%Y-%m-%d") l["eDate"] = self.getStartDate().strftime("%Y-%m-%d") return l def _mapConferenceToDays(self,conf,categList): """Registers a given conference for the days on which it is taking place whithin the calendar date interval. Parameters: conf - (conference.Conference) Conference to be mapped. categList - (List) List of calendar categories in which the specified conference is found. """ inc = timedelta(1) d = max(conf.getStartDate().astimezone(self._tz).replace(hour=0,minute=0,second=0), self.getStartDate().astimezone(self._tz).replace(hour=0,minute=0,second=0)) ed = min(conf.getEndDate().astimezone(self._tz), self.getEndDate().astimezone(self._tz)) if ed > self.getEndDate(): ed = self.getEndDate() while d <= ed: #norm_date=d.tzinfo.normalize(d) #norm_date=norm_date.replace(hour=0) norm_date=self.getNormDate(d) if not self._days.has_key( norm_date ): self._days[norm_date] = Day( self, d ) self._days[norm_date].addConference( conf, categList, self._tz ) d += inc def _initialiseDays( self ): """ """ self._days = OOBTree() res = set() self._categIdx = {} self._icons={} catDayIdx = indexes.IndexesHolder().getIndex("categoryDate") for categ in self.getCategoryList(): confs = catDayIdx.getObjectsInDays(categ.getId(), self.getStartDate(), self.getEndDate()) for conf in confs: confId = conf.getId() if not self._categIdx.has_key(confId): self._categIdx[confId]=[] self._categIdx[confId].append(categ) res.update(confs) for conf in res: #getting icon from the nearest owner category owner = conf.getOwner() while owner != None and owner.getId() != "0": if owner.getIcon(): if self._icons.has_key(owner.getId()): self._icons[owner.getId()].append(conf.getId()) else: self._icons[owner.getId()] = [conf.getId()] break owner = owner.getOwner() #mapping conf to days self._mapConferenceToDays(conf ,self._categIdx[conf.getId()]) def getNormDate(self,date): # we have to normalize, but as we are going over all the days, we have to keep the date # and just normalize the tzinfo. norm_date=date.tzinfo.normalize(date) norm_date=norm_date.replace(year=date.year, month=date.month,day=date.day, hour=0) return norm_date def getDay( self, date ): if not self._days: self._initialiseDays() norm_date=self.getNormDate(date) if not self._days.has_key( norm_date ): self._days[norm_date] = Day( self, date ) return self._days[norm_date] def getDayList( self ): inc = timedelta( 1 ) d = self.getStartDate() l = [] while d<self.getEndDate(): l.append( self.getDay( d ) ) d += inc return l def getConferenceCategories( self, conf ): return self._categIdx[conf.getId()] def __str__(self): l = [] if self._days: for day in self._days.values(): l.append("%s"%day) str = _("Calendar between %s and %s: %s")%(\ self.getStartDate().strftime("%Y-%m-%d"), \ self.getEndDate().strftime("%Y-%m-%d"), \ "\n\t\t".join(l) ) return str
class TopicIndex(Persistent, SimpleItem): """A TopicIndex maintains a set of FilteredSet objects. Every FilteredSet object consists of an expression and and IISet with all Ids of indexed objects that eval with this expression to 1. """ implements(ITopicIndex, IPluggableIndex) meta_type="TopicIndex" query_options = ('query', 'operator') manage_options= ( {'label': 'FilteredSets', 'action': 'manage_main'}, ) def __init__(self,id,caller=None): self.id = id self.filteredSets = OOBTree() self.operators = ('or','and') self.defaultOperator = 'or' def getId(self): return self.id def clear(self): for fs in self.filteredSets.values(): fs.clear() def index_object(self, docid, obj ,threshold=100): """ hook for (Z)Catalog """ for fid, filteredSet in self.filteredSets.items(): filteredSet.index_object(docid,obj) return 1 def unindex_object(self,docid): """ hook for (Z)Catalog """ for fs in self.filteredSets.values(): try: fs.unindex_object(docid) except KeyError: LOG.debug('Attempt to unindex document' ' with id %s failed' % docid) return 1 def numObjects(self): """Return the number of indexed objects.""" return "n/a" def indexSize(self): """Return the size of the index in terms of distinct values.""" return "n/a" def search(self,filter_id): if self.filteredSets.has_key(filter_id): return self.filteredSets[filter_id].getIds() def _apply_index(self, request): """ hook for (Z)Catalog 'request' -- mapping type (usually {"topic": "..." } """ record = parseIndexRequest(request, self.id, self.query_options) if record.keys is None: return None operator = record.get('operator', self.defaultOperator).lower() if operator == 'or': set_func = union else: set_func = intersection res = None for filter_id in record.keys: rows = self.search(filter_id) res = set_func(res,rows) if res: return res, (self.id,) else: return IITreeSet(), (self.id,) def uniqueValues(self,name=None, withLength=0): """ needed to be consistent with the interface """ return self.filteredSets.keys() def getEntryForObject(self,docid, default=_marker): """ Takes a document ID and returns all the information we have on that specific object. """ return self.filteredSets.keys() def addFilteredSet(self, filter_id, typeFilteredSet, expr): # Add a FilteredSet object. if self.filteredSets.has_key(filter_id): raise KeyError,\ 'A FilteredSet with this name already exists: %s' % filter_id self.filteredSets[filter_id] = factory(filter_id, typeFilteredSet, expr, ) def delFilteredSet(self, filter_id): # Delete the FilteredSet object specified by 'filter_id'. if not self.filteredSets.has_key(filter_id): raise KeyError,\ 'no such FilteredSet: %s' % filter_id del self.filteredSets[filter_id] def clearFilteredSet(self, filter_id): # Clear the FilteredSet object specified by 'filter_id'. if not self.filteredSets.has_key(filter_id): raise KeyError,\ 'no such FilteredSet: %s' % filter_id self.filteredSets[filter_id].clear() def manage_addFilteredSet(self, filter_id, typeFilteredSet, expr, URL1, \ REQUEST=None,RESPONSE=None): """ add a new filtered set """ if len(filter_id) == 0: raise RuntimeError,'Length of ID too short' if len(expr) == 0: raise RuntimeError,'Length of expression too short' self.addFilteredSet(filter_id, typeFilteredSet, expr) if RESPONSE: RESPONSE.redirect(URL1+'/manage_workspace?' 'manage_tabs_message=FilteredSet%20added') def manage_delFilteredSet(self, filter_ids=[], URL1=None, \ REQUEST=None,RESPONSE=None): """ delete a list of FilteredSets""" for filter_id in filter_ids: self.delFilteredSet(filter_id) if RESPONSE: RESPONSE.redirect(URL1+'/manage_workspace?' 'manage_tabs_message=FilteredSet(s)%20deleted') def manage_saveFilteredSet(self,filter_id, expr, URL1=None,\ REQUEST=None,RESPONSE=None): """ save expression for a FilteredSet """ self.filteredSets[filter_id].setExpression(expr) if RESPONSE: RESPONSE.redirect(URL1+'/manage_workspace?' 'manage_tabs_message=FilteredSet(s)%20updated') def getIndexSourceNames(self): """ return names of indexed attributes """ return ('n/a',) def getIndexQueryNames(self): return (self.id,) def manage_clearFilteredSet(self, filter_ids=[], URL1=None, \ REQUEST=None,RESPONSE=None): """ clear a list of FilteredSets""" for filter_id in filter_ids: self.clearFilteredSet(filter_id) if RESPONSE: RESPONSE.redirect(URL1+'/manage_workspace?' 'manage_tabs_message=FilteredSet(s)%20cleared') manage = manage_main = DTMLFile('dtml/manageTopicIndex',globals()) manage_main._setName('manage_main') editFilteredSet = DTMLFile('dtml/editFilteredSet',globals())
class Folder(Persistent, Contained): """The standard Zope Folder implementation.""" implements(IContentContainer) def __init__(self): self.data = OOBTree() def keys(self): """Return a sequence-like object containing the names associated with the objects that appear in the folder """ return self.data.keys() def __iter__(self): return iter(self.data.keys()) def values(self): """Return a sequence-like object containing the objects that appear in the folder. """ return self.data.values() def items(self): """Return a sequence-like object containing tuples of the form (name, object) for the objects that appear in the folder. """ return self.data.items() def __getitem__(self, name): """Return the named object, or raise ``KeyError`` if the object is not found. """ return self.data[name] def get(self, name, default=None): """Return the named object, or the value of the `default` argument if the object is not found. """ return self.data.get(name, default) def __contains__(self, name): """Return true if the named object appears in the folder.""" return self.data.has_key(name) def __len__(self): """Return the number of objects in the folder.""" return len(self.data) def __setitem__(self, name, object): """Add the given object to the folder under the given name.""" if not (isinstance(name, str) or isinstance(name, unicode)): raise TypeError("Name must be a string rather than a %s" % name.__class__.__name__) try: unicode(name) except UnicodeError: raise TypeError("Non-unicode names must be 7-bit-ascii only") if not name: raise TypeError("Name must not be empty") if name in self.data: raise KeyError("name, %s, is already in use" % name) setitem(self, self.data.__setitem__, name, object) def __delitem__(self, name): """Delete the named object from the folder. Raises a KeyError if the object is not found.""" uncontained(self.data[name], self, name) del self.data[name]
import pandas from BTrees.OOBTree import OOBTree import pickle t = OOBTree() class content: def __init__(self): self.row_num = [] self.business_id = [] def insert(self,row,b_id): self.row_num.append(row) self.business_id.append(b_id) file2 = open(r'../test/funny.pkl', 'rb') t = pickle.load(file2) file2.close() #print(list(t.values(8))) for item in list(t.values(8,8)): print(item.row_num,item.business_id)
class PathIndex(Persistent, Implicit, SimpleItem): """ A path index stores all path components of the physical path of an object: Internal datastructure: - a physical path of an object is split into its components - every component is kept as a key of a OOBTree in self._indexes - the value is a mapping 'level of the path component' to 'all documentIds with this path component on this level' """ __implements__ = (PluggableIndex.PluggableIndexInterface,) meta_type="PathIndex" manage_options= ( {'label': 'Settings', 'action': 'manage_main', 'help': ('PathIndex','PathIndex_Settings.stx')}, ) query_options = ["query","level","operator"] def __init__(self,id,caller=None): self.id = id # experimental code for specifing the operator self.operators = ['or','and'] self.useOperator = 'or' self.clear() def getId(self): return self.id def clear(self): """ clear everything """ self._depth = 0 self._index = OOBTree() self._unindex = IOBTree() def insertEntry(self,comp,id,level): """ k is a path component (generated by splitPath() ) v is the documentId level is the level of the component inside the path """ if self._index.has_key(comp)==0: self._index[comp] = IOBTree() if self._index[comp].has_key(level)==0: self._index[comp][level] = IISet() self._index[comp][level].insert(id) if level > self._depth: self._depth = level def index_object(self, documentId, obj ,threshold=100): """ hook for (Z)Catalog """ # first we check if the object provide an attribute or # method to be used as hook for the PathIndex if hasattr(obj,self.id): f = getattr(obj,self.id) try: if callable(f): path = f() else: path = f except: return 0 if not (isinstance(path,StringType) or isinstance(path,TupleType)): raise TypeError, "attribute/method must be/return string or tuple" else: try: path = obj.getPhysicalPath() except: return 0 if type(path) in (ListType,TupleType): path = '/'+ '/'.join(path[1:]) comps = self.splitPath(path,obj) # if obj.meta_type != 'Folder': # comps = comps[:-1] for i in range(len(comps)): self.insertEntry( comps[i],documentId,i) self._unindex[documentId] = path return 1 def unindex_object(self,documentId): """ hook for (Z)Catalog """ if not self._unindex.has_key(documentId): LOG(self.__class__.__name__, ERROR, 'Attempt to unindex nonexistent document' ' with id %s' % documentId) return path = self._unindex[documentId] comps = path.split('/') for level in range(len(comps[1:])): comp = comps[level+1] try: self._index[comp][level].remove(documentId) if len(self._index[comp][level])==0: del self._index[comp][level] if len(self._index[comp])==0: del self._index[comp] except KeyError: LOG(self.__class__.__name__, ERROR, 'Attempt to unindex document' ' with id %s failed' % documentId) del self._unindex[documentId] def printIndex(self): for k,v in self._index.items(): print "-"*78 print k for k1,v1 in v.items(): print k1,v1, print def splitPath(self,path,obj=None): """ split physical path of object. If the object has as function splitPath() we use this user-defined function to split the path """ if hasattr(obj,"splitPath"): comps = obj.splitPath(path) else: comps = filter(lambda x: x , re.split("/",path)) return comps def search(self,path,default_level=0): """ path is either a string representing a relative URL or a part of a relative URL or a tuple (path,level). level>=0 starts searching at the given level level<0 not implemented yet """ if isinstance(path,StringType): level = default_level else: level = int(path[1]) path = path[0] comps = self.splitPath(path) if len(comps) == 0: return IISet(self._unindex.keys()) if level >=0: results = [] for i in range(len(comps)): comp = comps[i] if not self._index.has_key(comp): return IISet() if not self._index[comp].has_key(level+i): return IISet() results.append( self._index[comp][level+i] ) res = results[0] for i in range(1,len(results)): res = intersection(res,results[i]) return res else: results = IISet() for level in range(0,self._depth + 1): ids = None error = 0 for cn in range(0,len(comps)): comp = comps[cn] try: ids = intersection(ids,self._index[comp][level+cn]) except KeyError: error = 1 if error==0: results = union(results,ids) return results def __len__(self): """ len """ return len(self._index) def numObjects(self): """ return the number of indexed objects""" return len(self._unindex) def keys(self): """ return list of all path components """ keys = [] for k in self._index.keys(): keys.append(k) return keys def values(self): values = [] for k in self._index.values(): values.append(k) return values def items(self): """ mapping path components : documentIds """ items = [] for k in self._index.items(): items.append(k) return items def _apply_index(self, request, cid=''): """ hook for (Z)Catalog request mapping type (usually {"path": "..." } additionaly a parameter "path_level" might be passed to specify the level (see search()) cid ??? """ record = parseIndexRequest(request,self.id,self.query_options) if record.keys==None: return None if request.has_key('%s_level' % cid): warnings.warn("The usage of the '%s_level' " "is no longer recommended.\n" "Please use a mapping object and the " "'level' key to specify the operator." % cid) # get the level parameter level = record.get("level",0) # experimental code for specifing the operator operator = record.get('operator',self.useOperator).lower() # depending on the operator we use intersection of union if operator=="or": set_func = union else: set_func = intersection res = None for k in record.keys: rows = self.search(k,level) res = set_func(res,rows) if res: return res, (self.id,) else: return IISet(), (self.id,) def uniqueValues(self,name=None,withLength=0): """ needed to be consistent with the interface """ return self._index.keys() def getEntryForObject(self,documentId,default=_marker): """ Takes a document ID and returns all the information we have on that specific object. """ try: return self._unindex[documentId] except: return None index_html = DTMLFile('dtml/index', globals()) manage_workspace = DTMLFile('dtml/managePathIndex', globals())
class MemberDataTool (UniqueObject, SimpleItem, PropertyManager, ActionProviderBase): '''This tool wraps user objects, making them act as Member objects. ''' id = 'portal_memberdata' meta_type = 'CMF Member Data Tool' _actions = [] _v_temps = None _properties = () security = ClassSecurityInfo() manage_options=( ActionProviderBase.manage_options + ({ 'label' : 'Overview' , 'action' : 'manage_overview' } , { 'label' : 'Contents' , 'action' : 'manage_showContents' } ) + PropertyManager.manage_options + SimpleItem.manage_options ) # # ZMI methods # security.declareProtected( CMFCorePermissions.ManagePortal , 'manage_overview' ) manage_overview = DTMLFile( 'explainMemberDataTool', _dtmldir ) security.declareProtected( CMFCorePermissions.ViewManagementScreens , 'manage_showContents') manage_showContents = DTMLFile('memberdataContents', _dtmldir ) security.declareProtected( CMFCorePermissions.ViewManagementScreens , 'getContentsInformation',) def __init__(self): self._members = OOBTree() # Create the default properties. self._setProperty('email', '', 'string') self._setProperty('portal_skin', '', 'string') self._setProperty('listed', '', 'boolean') self._setProperty('login_time', '2000/01/01', 'date') self._setProperty('last_login_time', '2000/01/01', 'date') # # 'portal_memberdata' interface methods # security.declarePrivate('listActions') def listActions(self, info=None): """ Return actions provided via tool. """ return self._actions security.declarePrivate('getMemberDataContents') def getMemberDataContents(self): ''' Return the number of members stored in the _members BTree and some other useful info ''' membertool = getToolByName(self, 'portal_membership') members = self._members user_list = membertool.listMemberIds() member_list = members.keys() member_count = len(members) orphan_count = 0 for member in member_list: if member not in user_list: orphan_count = orphan_count + 1 return [{ 'member_count' : member_count, 'orphan_count' : orphan_count }] security.declarePrivate( 'searchMemberDataContents' ) def searchMemberDataContents( self, search_param, search_term ): """ Search members """ res = [] if search_param == 'username': search_param = 'id' for user_wrapper in self._members.values(): searched = getattr( user_wrapper, search_param, None ) if searched is not None and string.find( searched, search_term ) != -1: res.append( { 'username' : getattr( user_wrapper, 'id' ) , 'email' : getattr( user_wrapper, 'email', '' ) } ) return res security.declarePrivate('pruneMemberDataContents') def pruneMemberDataContents(self): ''' Compare the user IDs stored in the member data tool with the list in the actual underlying acl_users and delete anything not in acl_users ''' membertool= getToolByName(self, 'portal_membership') members = self._members user_list = membertool.listMemberIds() for tuple in members.items(): member_name = tuple[0] member_obj = tuple[1] if member_name not in user_list: del members[member_name] security.declarePrivate('wrapUser') def wrapUser(self, u): ''' If possible, returns the Member object that corresponds to the given User object. ''' id = u.getUserName() members = self._members if not members.has_key(id): # Get a temporary member that might be # registered later via registerMemberData(). temps = self._v_temps if temps is not None and temps.has_key(id): m = temps[id] else: base = aq_base(self) m = MemberData(base, id) if temps is None: self._v_temps = {id:m} else: temps[id] = m else: m = members[id] # Return a wrapper with self as containment and # the user as context. return m.__of__(self).__of__(u) security.declarePrivate('registerMemberData') def registerMemberData(self, m, id): ''' Adds the given member data to the _members dict. This is done as late as possible to avoid side effect transactions and to reduce the necessary number of entries. ''' self._members[id] = m
class FieldIndex(persistent.Persistent): zope.interface.implements( interfaces.IInjection, interfaces.IStatistics, interfaces.IIndexSearch, ) def __init__(self): self.clear() def clear(self): """Initialize forward and reverse mappings.""" # The forward index maps indexed values to a sequence of docids self._fwd_index = OOBTree() # The reverse index maps a docid to its index value self._rev_index = IOBTree() self._num_docs = Length(0) def documentCount(self): """See interface IStatistics""" return self._num_docs() def wordCount(self): """See interface IStatistics""" return len(self._fwd_index) def index_doc(self, docid, value): """See interface IInjection""" rev_index = self._rev_index if docid in rev_index: # unindex doc if present self.unindex_doc(docid) # Insert into forward index. set = self._fwd_index.get(value) if set is None: set = IFTreeSet() self._fwd_index[value] = set set.insert(docid) # increment doc count self._num_docs.change(1) # Insert into reverse index. rev_index[docid] = value def unindex_doc(self, docid): """See interface IInjection""" rev_index = self._rev_index value = rev_index.get(docid) if value is None: return # not in index del rev_index[docid] try: set = self._fwd_index[value] set.remove(docid) except KeyError: # This is fishy, but we don't want to raise an error. # We should probably log something. # but keep it from throwing a dirty exception set = 1 if not set: del self._fwd_index[value] self._num_docs.change(-1) def apply(self, query): if len(query) != 2 or not isinstance(query, tuple): raise TypeError("two-length tuple expected", query) return multiunion(self._fwd_index.values(*query))
class JobCatalogTool(CatalogTool): """ A specific catalog for indexing Jobs. """ title = 'Job Catalog' id = 'job_catalog' portal_type = meta_type = 'JobCatalog' plone_tool = 1 security = ClassSecurityInfo() _properties = ( {'id': 'title', 'type': 'string', 'mode': 'w'}, ) def __init__(self): super(JobCatalogTool, self).__init__() self._catalog = JobCatalog() self.jobs = OOBTree() security.declareProtected(SearchZCatalog, 'resolve_path') def resolve_path(self, path): # Attempt to resolve a job id (path) within this catalog. # The path is meant to be a job id. # If no object is found, None is returned. # No exceptions are raised. try: return self.jobs[path] except Exception: pass security.declareProtected(ManageZCatalogEntries, 'clearFindAndRebuild') def clearFindAndRebuild(self): """ Empties catalog, then finds all contentish objects (i.e. objects with an indexObject method), and reindexes them. This may take a long time. """ self.manage_catalogClear() for job in self.jobs.values(): self.reindexObject(job, uid=job.id) security.declareProtected(ManageZCatalogEntries, 'reindexIndex') def reindexIndex(self, name, REQUEST, pghandler=None): if isinstance(name, str): name = (name, ) paths = self._catalog.uids.keys() i = 0 if pghandler: pghandler.init('reindexing %s' % name, len(paths)) for p in paths: i += 1 if pghandler: pghandler.report(i) obj = self.resolve_path(p) if obj is None: LOG.error('reindexIndex could not resolve ' 'an object from the uid %r.' % p) else: # don't update metadata when only reindexing a single # index via the UI self.catalog_object(obj, p, idxs=name, update_metadata=0, pghandler=pghandler) if pghandler: pghandler.finish()
class TagNode(persistent.Persistent): """ Node of TagTree. """ def __init__(self, name, parent=None): """ :param name: Name of this TaggedNode. (Not a full name.) None means that this node is a root of tree. :type name: unicode or None :param parent: Parent node of this. :type parent: TagNode """ self._name = name self._parent = parent self._children = OOBTree() self._objects = IOBTree() def destroy(self): """ Untags all objects, tagged by this TagNode, and also destroys all children recursively. """ for node in self._children.values(): node.destroy() tag = self.get_tag() for obj in list(self._objects.values()): obj.remove_tag(tag) def get_parent(self): """ Returns parent node. """ return self._parent def get_name(self): """ Returns name (tag level) of this node. """ return self._name def create_child_node(self, name): """ Creates and returns child node. """ if self._children.has_key(name): raise KeyError( u'TagNode already has child with name {0}.'.format( name)) else: self._children[name] = TagNode(name, self) return self._children[name] def delete_child_node(self, name): """ Deletes child node. """ if not self._children.has_key(name): raise KeyError( u'TagNode does not have child with name {0}.'.format( name)) else: self._children[name].destroy() del self._children[name] def get_child_node(self, name): """ Returns child node. """ try: return self._children[name] except KeyError: raise KeyError( u'TagNode does not have child with name {0}.'.format( name)) def get_tag(self): """ Returns :py:class:`Tag`, which this node represents. """ levels = [] node = self while node.get_parent(): levels.append(node.get_name()) node = node.get_parent() return Tag(reversed(levels)) def add_object(self, obj): """ Adds object to tagged objects list. """ if self._objects.has_key(obj.get_id()): raise KeyError(u'Object already tagged.') else: self._objects[obj.get_id()] = obj def remove_object(self, obj): """ Removes object from tagged objects list. :param obj: TaggedObject id or TaggedObject itself. """ if isinstance(obj, int): del self._objects[obj] else: del self._objects[obj.get_id()] def get_object(self, object_id): """ Returns object by its id. """ return self._objects[object_id] def get_object_dict(self, tag, filter): """ Returns dict (id->object) of all children nodes objects tagged with tag for which filter returns True. If ``tag`` is None, returns all objects (nodes and its children) which pass filter. :type tag: list of unicode strings or None """ if tag is None or len(tag) == 0: # Collect all objects. objects = dict([ (obj.get_id(), obj) for obj in self._objects.values() if filter(obj)]) for node in self._children.values(): objects.update(node.get_object_dict(None, filter)) return objects else: # Recursively call child node. name = tag.pop() return self._children[name].get_object_dict(tag, filter) def get_object_list(self, tag=None, filter=lambda x: True): """ Returns list of all objects which passes tag and filter. + If ``tag`` is None, returns all objects, which passes filter. + Otherwise returns objects, which belongs to child TagNode with full name ``tag``, and which passes filter. :type tag: Tag or None """ if tag is None: return self.get_object_dict(tag, filter).values() else: return self.get_object_dict( list(reversed(tag.as_tuple())), filter).values()
class LookupTable(base.Base): "LookupTable class" meta_type = "LookupTable" security = ClassSecurityInfo() records = None recordsLength = None drawDict = base.Base.drawDict.copy() drawDict['drawTable'] = 'drawTable' security.declareProtected('View management screens', 'edit') def edit(self, *args, **kw): "Inline edit short object" format = "<p>Currently there are %s records</p><div>%s</div>" if self.records is None: self.records = OOBTree() lenRecords = self.recordsLength( ) if self.recordsLength is not None else 0 return format % (lenRecords, self.create_button('clear', "Clear")) security.declarePrivate('processRecorderChanges') def processRecorderChanges(self, form): "process the recorder changes" clear = form.pop('clear', None) if clear is not None: self.clear() security.declarePrivate('after_manage_edit') def before_manage_edit(self, form): "process the edits" self.processRecorderChanges(form) security.declareProtected('View management screens', "drawTable") def drawTable(self): "Render page" temp = [] format = '<p>%s:%s</p>' if self.records is not None: for key, value in self.records.items(): temp.append(format % (repr(key), repr(value))) return ''.join(temp) security.declareProtected('Python Record Modification', 'insert') def insert(self, key, value): "modify this key and value into the OOBTree" if self.records is None: self.records = OOBTree() if self.recordsLength is None: self.setObject('recordsLength', BTrees.Length.Length()) if key not in self.records: self.recordsLength.change(1) self.records.insert(key, value) security.declareProtected('Python Record Modification', 'add') def add(self, key, value): "this this key and value into the OOBTree" if self.records is None: self.records = OOBTree() if self.recordsLength is None: self.setObject('recordsLength', BTrees.Length.Length()) if key not in self.records: self.recordsLength.change(1) self.records[key] = value security.declareProtected('Python Record Access', 'items') def items(self, min=None, max=None): "return the items in this OOBTree" if self.records is None: return [] return self.records.items(min, max) security.declareProtected('Python Record Access', 'values') def values(self, min=None, max=None): "return the values of this OOBTree" if self.records is None: return [] return self.records.values(min, max) security.declareProtected('Python Record Modification', 'update') def update(self, collection): "update our OOBTree with the data in collection" if self.records is None: self.records = OOBTree() if self.recordsLength is None: self.setObject('recordsLength', BTrees.Length.Length()) records = self.records change = self.recordsLength.change for key, value in collection.items(): if key not in records: change(1) records[key] = value security.declareProtected('Python Record Access', 'keys') def keys(self, min=None, max=None): "return the keys of this OOBTree" if self.records is None: return [] return self.records.keys(min, max) security.declareProtected('Python Record Modification', '__delitem__') def __delitem__(self, key): "delete this key from the OOBTree" if self.records is not None: del self.records[key] self.recordsLength.change(-1) security.declareProtected('Python Record Modification', 'remove') def remove(self, key): "delete this key from the OOBTree" if self.records is not None: del self.records[key] self.recordsLength.change(-1) security.declareProtected('Python Record Modification', '__setitem__') def __setitem__(self, key, value): "set this key and value in the OOBTree" if self.records is None: self.records = OOBTree() self.records[key] = value security.declareProtected('Python Record Access', '__getitem__') def __getitem__(self, index): "get this item from the OOBTree" if self.records is not None: return self.records[index] raise KeyError, index security.declareProtected('Python Record Access', 'get') def get(self, key, default=None): "get this item from the OOBTree" if self.records is not None: return self.records.get(key, default) return default security.declareProtected('Python Record Access', 'has_key') def has_key(self, key): "see if we have this key in the OOBTree" if self.records is not None: return self.records.has_key(key) return False security.declareProtected('Python Record Modification', 'clear') def clear(self): "clear the OOBTree" self.setObject('records', None) self.setObject('recordsLength', None) security.declarePrivate("PrincipiaSearchSource") def PrincipiaSearchSource(self): "This is the basic search function" return '' security.declarePrivate('classUpgrader') def classUpgrader(self): "upgrade this class" self.createBTreeLength() security.declarePrivate('createBTreeLength') def createBTreeLength(self): "remove Filters that are not being used" if self.records is not None: length = BTrees.Length.Length() length.set(len(self.records)) self.setObject('recordsLength', length) createBTreeLength = utility.upgradeLimit(createBTreeLength, 165)
class LookupTable(base.Base): "LookupTable class" meta_type = "LookupTable" security = ClassSecurityInfo() records = None recordsLength = None drawDict = base.Base.drawDict.copy() drawDict['drawTable'] = 'drawTable' security.declareProtected('View management screens', 'edit') def edit(self, *args, **kw): "Inline edit short object" format = "<p>Currently there are %s records</p><div>%s</div>" if self.records is None: self.records = OOBTree() lenRecords = self.recordsLength() if self.recordsLength is not None else 0 return format % (lenRecords, self.create_button('clear', "Clear")) security.declarePrivate('processRecorderChanges') def processRecorderChanges(self, form): "process the recorder changes" clear = form.pop('clear', None) if clear is not None: self.clear() security.declarePrivate('after_manage_edit') def before_manage_edit(self, form): "process the edits" self.processRecorderChanges(form) security.declareProtected('View management screens', "drawTable") def drawTable(self): "Render page" temp = [] format = '<p>%s:%s</p>' if self.records is not None: for key,value in self.records.items(): temp.append(format % (repr(key), repr(value))) return ''.join(temp) security.declareProtected('Python Record Modification', 'insert') def insert(self, key, value): "modify this key and value into the OOBTree" if self.records is None: self.records = OOBTree() if self.recordsLength is None: self.setObject('recordsLength' ,BTrees.Length.Length()) if key not in self.records: self.recordsLength.change(1) self.records.insert(key,value) security.declareProtected('Python Record Modification', 'add') def add(self, key, value): "this this key and value into the OOBTree" if self.records is None: self.records = OOBTree() if self.recordsLength is None: self.setObject('recordsLength' ,BTrees.Length.Length()) if key not in self.records: self.recordsLength.change(1) self.records[key] = value security.declareProtected('Python Record Access', 'items') def items(self, min=None, max=None): "return the items in this OOBTree" if self.records is None: return [] return self.records.items(min, max) security.declareProtected('Python Record Access', 'values') def values(self, min=None, max=None): "return the values of this OOBTree" if self.records is None: return [] return self.records.values(min, max) security.declareProtected('Python Record Modification', 'update') def update(self, collection): "update our OOBTree with the data in collection" if self.records is None: self.records = OOBTree() if self.recordsLength is None: self.setObject('recordsLength' ,BTrees.Length.Length()) records = self.records change = self.recordsLength.change for key,value in collection.items(): if key not in records: change(1) records[key] = value security.declareProtected('Python Record Access', 'keys') def keys(self, min=None, max=None): "return the keys of this OOBTree" if self.records is None: return [] return self.records.keys(min,max) security.declareProtected('Python Record Modification', '__delitem__') def __delitem__(self, key): "delete this key from the OOBTree" if self.records is not None: del self.records[key] self.recordsLength.change(-1) security.declareProtected('Python Record Modification', 'remove') def remove(self, key): "delete this key from the OOBTree" if self.records is not None: del self.records[key] self.recordsLength.change(-1) security.declareProtected('Python Record Modification', '__setitem__') def __setitem__(self, key, value): "set this key and value in the OOBTree" if self.records is None: self.records = OOBTree() self.records[key] = value security.declareProtected('Python Record Access', '__getitem__') def __getitem__(self, index): "get this item from the OOBTree" if self.records is not None: return self.records[index] raise KeyError, index security.declareProtected('Python Record Access', 'get') def get(self, key, default=None): "get this item from the OOBTree" if self.records is not None: return self.records.get(key,default) return default security.declareProtected('Python Record Access', 'has_key') def has_key(self, key): "see if we have this key in the OOBTree" if self.records is not None: return self.records.has_key(key) return False security.declareProtected('Python Record Modification', 'clear') def clear(self): "clear the OOBTree" self.setObject('records', None) self.setObject('recordsLength', None) security.declarePrivate("PrincipiaSearchSource") def PrincipiaSearchSource(self): "This is the basic search function" return '' security.declarePrivate('classUpgrader') def classUpgrader(self): "upgrade this class" self.createBTreeLength() security.declarePrivate('createBTreeLength') def createBTreeLength(self): "remove Filters that are not being used" if self.records is not None: length = BTrees.Length.Length() length.set(len(self.records)) self.setObject('recordsLength', length) createBTreeLength = utility.upgradeLimit(createBTreeLength, 165)
class LinkSet(Persistent, Contained): """Set of links. This class is used internally to represent relationships. Initially it is empty >>> linkset = LinkSet() >>> list(linkset) [] You can add new links to it >>> from schooltool.relationship.tests import URIStub >>> link1 = Link('example:Group', object(), URIStub('example:Member'), ... URIStub('example:Membership')) >>> link2 = Link('example:Friend', object(), URIStub('example:Friend'), ... URIStub('example:Friendship')) >>> linkset.add(link1) >>> linkset.add(link2) The links have landed in the cache too: >>> expected = { ... 'example:Member': [link1], ... 'example:Friend': [link2]} >>> dict(linkset._byrole.items()) == expected True Let's zap the cache and call getCachedLinksByRole(), which should restore it: >>> del linkset._byrole >>> linkset.getCachedLinksByRole(URIStub('something')) [] >>> dict(linkset._byrole.items()) == expected True Links should get named: >>> link1.__name__ '1' >>> link2.__name__ '2' We can access our links through their names: >>> linkset['1'] is link1 True >>> linkset['2'] is link2 True And get parents set: >>> link1.__parent__ is linkset True We got them in the container now: >>> set(linkset) == set([link1, link2]) # order is not preserved True You can look for links for a specific relationship >>> linkset.find('example:Group', ... link1.target, ... URIStub('example:Member'), ... URIStub('example:Membership')) is link1 True We can't add same link into the container twice: >>> linkset.add(link1) # doctest: +ELLIPSIS Traceback (most recent call last): ... ValueError: ... If find fails, it raises ValueError, just like list.index. >>> linkset.find('example:Member', link1.target, ... URIStub('example:Group'), ... URIStub('example:Membership')) # doctest: +ELLIPSIS Traceback (most recent call last): ... ValueError: ... You can remove links >>> linkset.remove(link2) >>> set(linkset) == set([link1]) True The links are removed from the cache too: >>> list(linkset._byrole.keys()) ['example:Member'] If you try to remove a link that is not in the set, you will get a ValueError. >>> linkset.remove(link2) # doctest: +ELLIPSIS Traceback (most recent call last): ... ValueError: ... You can remove all links >>> linkset.clear() >>> set(linkset) == set([]) True The cache has been cleared too: >>> len(linkset._byrole) 0 The class is documented in IRelationshipLinks >>> from zope.interface.verify import verifyObject >>> verifyObject(IRelationshipLinks, linkset) True """ implements(IRelationshipLinks) _lids = None def __init__(self): self._lids = IFBTree.TreeSet() self._links = OOBTree() @property def catalog(self): return getLinkCatalog() def getCachedLinksByRole(self, role, catalog=None): """Get a set of links by role.""" if catalog is None: catalog = self.catalog lids = self.query(role=role, catalog=catalog) return [CLink(catalog, lid) for lid in lids] def getCachedLinksByTarget(self, target, catalog=None): if catalog is None: catalog = self.catalog lids = self.query(target=target, catalog=catalog) return [CLink(catalog, lid) for lid in lids] def add(self, link): if link.__parent__ == self: raise ValueError("You are adding same link twice.") i = 1 while "%s" % i in self._links: i += 1 link.__name__ = "%s" % i self._links[link.__name__] = link link.__parent__ = self notify(ObjectAddedEvent(link, self._links, link.__name__)) def remove(self, link): if link is self._links.get(link.__name__): link_name = link.__name__ self._lids.remove(getUtility(IIntIds).getId(link)) del self._links[link.__name__] notify(ObjectRemovedEvent(link, self._links, link_name)) else: raise ValueError("This link does not belong to this container!") def clear(self): deleted = list(self._links.items()) self._links.clear() self._byrole.clear() for name, link in deleted: notify(ObjectRemovedEvent(link, self._links, name)) def __iter__(self): return iter(self._links.values()) def find(self, my_role, target, role, rel_type): for link in self._links.values(): if (link.role_hash == hash(role) and link.target is target and link.rel_type_hash == hash(rel_type) and link.my_role_hash == hash(my_role)): return link else: raise ValueError(my_role, target, role, rel_type) def __getitem__(self, id): return self._links[id] def get(self, key, default=None): return self._links.get(key, default) def query(self, my_role=None, target=None, role=None, rel_type=None, catalog=None): if catalog is None: catalog = self.catalog empty = IFBTree.TreeSet() this_hash = hash_persistent(self.__parent__) result = None if my_role is not None: ids = catalog['my_role_hash'].values_to_documents.get( (hash(my_role), this_hash), empty) if result is None: result = ids else: result = IFBTree.intersection(result, ids) if not result: return result if target is not None: ids = catalog['target'].values_to_documents.get( (IKeyReference(target), this_hash), empty) if result is None: result = ids else: result = IFBTree.intersection(result, ids) if not result: return result if role is not None: ids = catalog['role_hash'].values_to_documents.get( (hash(role), this_hash), empty) if result is None: result = ids else: result = IFBTree.intersection(result, ids) if not result: return result if rel_type is not None: ids = catalog['rel_type_hash'].values_to_documents.get( (hash(rel_type), this_hash), empty) if result is None: result = ids else: result = IFBTree.intersection(result, ids) return result def iterLinksByRole(self, role, rel_type=None, catalog=None): if catalog is None: catalog = self.catalog lids = self.query(role=role, rel_type=rel_type, catalog=catalog) if rel_type is None: filters = {} for lid in lids: link = CLink(catalog, lid) if link.rel_type_hash not in filters: filters[link.rel_type_hash] = link.rel_type.filter if filters[link.rel_type_hash](link): yield link else: filter = rel_type.filter for lid in lids: link = CLink(catalog, lid) if filter(link): yield link def getTargetsByRole(self, role, rel_type=None, catalog=None): links = self.iterLinksByRole(role, rel_type=rel_type, catalog=catalog) return [link.target for link in links] def iterTargetsByRole(self, role, rel_type=None, catalog=None): for link in self.iterLinksByRole(role, rel_type=rel_type, catalog=catalog): yield link.target
class ZODBGroupManager( BasePlugin ): """ PAS plugin for managing groups, and groups of groups in the ZODB """ __implements__ = ( IGroupEnumerationPlugin , IGroupsPlugin ) meta_type = 'ZODB Group Manager' security = ClassSecurityInfo() def __init__(self, id, title=None): self._id = self.id = id self.title = title self._groups = OOBTree() self._principal_groups = OOBTree() # # IGroupEnumerationPlugin implementation # security.declarePrivate( 'enumerateGroups' ) def enumerateGroups( self , id=None , title=None , exact_match=False , sort_by=None , max_results=None , **kw ): """ See IGroupEnumerationPlugin. """ group_info = [] group_ids = [] plugin_id = self.getId() if isinstance( id, str ): id = [ id ] if isinstance( title, str ): title = [ title ] if exact_match and ( id or title ): if id: group_ids.extend( id ) elif title: group_ids.extend( title ) if group_ids: group_filter = None else: # Searching group_ids = self.listGroupIds() group_filter = _ZODBGroupFilter( id, title, **kw ) for group_id in group_ids: if self._groups.get( group_id, None ): e_url = '%s/manage_groups' % self.getId() p_qs = 'group_id=%s' % group_id m_qs = 'group_id=%s&assign=1' % group_id info = {} info.update( self._groups[ group_id ] ) info[ 'pluginid' ] = plugin_id info[ 'properties_url' ] = '%s?%s' % ( e_url, p_qs ) info[ 'members_url' ] = '%s?%s' % ( e_url, m_qs ) if not group_filter or group_filter( info ): group_info.append( info ) return tuple( group_info ) # # IGroupsPlugin implementation # security.declarePrivate( 'getGroupsForPrincipal' ) def getGroupsForPrincipal( self, principal, request=None ): """ See IGroupsPlugin. """ return tuple( self._principal_groups.get( principal.getId(), () ) ) # # (notional)IZODBGroupManager interface # security.declareProtected( ManageGroups, 'listGroupIds' ) def listGroupIds( self ): """ -> ( group_id_1, ... group_id_n ) """ return self._groups.keys() security.declareProtected( ManageGroups, 'listGroupInfo' ) def listGroupInfo( self ): """ -> ( {}, ...{} ) o Return one mapping per group, with the following keys: - 'id' """ return self._groups.values() security.declareProtected( ManageGroups, 'getGroupInfo' ) def getGroupInfo( self, group_id ): """ group_id -> {} """ return self._groups[ group_id ] security.declarePrivate( 'addGroup' ) def addGroup( self, group_id, title=None, description=None ): """ Add 'group_id' to the list of groups managed by this object. o Raise KeyError on duplicate. """ if self._groups.get( group_id ) is not None: raise KeyError, 'Duplicate group ID: %s' % group_id self._groups[ group_id ] = { 'id' : group_id , 'title' : title , 'description' : description } security.declarePrivate( 'updateGroup' ) def updateGroup( self, group_id, title, description ): """ Update properties for 'group_id' o Raise KeyError if group_id doesn't already exist. """ self._groups[ group_id ].update({ 'title' : title , 'description' : description }) self._groups[ group_id ] = self._groups[ group_id ] security.declarePrivate( 'removeGroup' ) def removeGroup( self, group_id ): """ Remove 'role_id' from the list of roles managed by this object, removing assigned members from it before doing so. o Raise KeyError if 'group_id' doesn't already exist. """ for principal_id in self._principal_groups.keys(): self.removePrincipalFromGroup( principal_id, group_id ) del self._groups[ group_id ] # # Group assignment API # security.declareProtected( ManageGroups, 'listAvailablePrincipals' ) def listAvailablePrincipals( self, group_id, search_name ): """ Return a list of principal IDs to that can belong to the group. o If supplied, 'search_name' constrains the principal IDs; if not, return empty list. o Omit principals with existing assignments. """ result = [] if search_name: # don't bother searching if no criteria parent = aq_parent( self ) for info in parent.searchPrincipals( max_results=20 , sort_by='id' , name=search_name , exact_match=False ): id = info[ 'id' ] title = info.get( 'title', id ) if ( group_id not in self._principal_groups.get( id, () ) and group_id != id ): result.append( ( id, title ) ) return result security.declareProtected( ManageGroups, 'listAssignedPrincipals' ) def listAssignedPrincipals( self, group_id ): """ Return a list of principal IDs belonging to a group. """ result = [] for k, v in self._principal_groups.items(): if group_id in v: # should be one and only one mapping to 'k' parent = aq_parent( self ) info = parent.searchPrincipals( id=k, exact_match=True ) assert( len( info ) == 1 ) result.append( ( k, info[0].get( 'title', k ) ) ) return result security.declareProtected( ManageGroups, 'addPrincipalToGroup' ) def addPrincipalToGroup( self, principal_id, group_id ): """ Add a principal to a group. o Return a boolean indicating whether a new assignment was created. o Raise KeyError if 'group_id' is unknown. """ group_info = self._groups[ group_id ] # raise KeyError if unknown! current = self._principal_groups.get( principal_id, () ) already = group_id in current if not already: new = current + ( group_id, ) self._principal_groups[ principal_id ] = new return not already security.declareProtected( ManageGroups, 'removePrincipalFromGroup' ) def removePrincipalFromGroup( self, principal_id, group_id ): """ Remove a prinicpal from from a group. o Return a boolean indicating whether the principal was already a member of the group. o Raise KeyError if 'group_id' is unknown. o Ignore requests to remove a principal if not already a member of the group. """ group_info = self._groups[ group_id ] # raise KeyError if unknown! current = self._principal_groups.get( principal_id, () ) new = tuple( [ x for x in current if x != group_id ] ) already = current != new if already: self._principal_groups[ principal_id ] = new return already # # ZMI # manage_options = ( ( { 'label': 'Groups', 'action': 'manage_groups', } , ) + BasePlugin.manage_options ) security.declarePublic( 'manage_widgets' ) manage_widgets = PageTemplateFile( 'www/zuWidgets' , globals() , __name__='manage_widgets' ) security.declareProtected( ManageGroups, 'manage_groups' ) manage_groups = PageTemplateFile( 'www/zgGroups' , globals() , __name__='manage_groups' ) security.declareProtected( ManageGroups, 'manage_twoLists' ) manage_twoLists = PageTemplateFile( '../www/two_lists' , globals() , __name__='manage_twoLists' ) security.declareProtected( ManageGroups, 'manage_addGroup' ) def manage_addGroup( self , group_id , title=None , description=None , RESPONSE=None ): """ Add a group via the ZMI. """ self.addGroup( group_id, title, description ) message = 'Group+added' if RESPONSE is not None: RESPONSE.redirect( '%s/manage_groups?manage_tabs_message=%s' % ( self.absolute_url(), message ) ) security.declareProtected( ManageGroups, 'manage_updateGroup' ) def manage_updateGroup( self , group_id , title , description , RESPONSE=None ): """ Update a group via the ZMI. """ self.updateGroup( group_id, title, description ) message = 'Group+updated' if RESPONSE is not None: RESPONSE.redirect( '%s/manage_groups?manage_tabs_message=%s' % ( self.absolute_url(), message ) ) security.declareProtected( ManageGroups, 'manage_removeGroups' ) def manage_removeGroups( self , group_ids , RESPONSE=None ): """ Remove one or more groups via the ZMI. """ group_ids = filter( None, group_ids ) if not group_ids: message = 'no+groups+selected' else: for group_id in group_ids: self.removeGroup( group_id ) message = 'Groups+removed' if RESPONSE is not None: RESPONSE.redirect( '%s/manage_groups?manage_tabs_message=%s' % ( self.absolute_url(), message ) ) security.declareProtected( ManageGroups, 'manage_addPrincipalsToGroup' ) def manage_addPrincipalsToGroup( self , group_id , principal_ids , RESPONSE=None ): """ Add one or more principals to a group via the ZMI. """ assigned = [] for principal_id in principal_ids: if self.addPrincipalToGroup( principal_id, group_id ): assigned.append( principal_id ) if not assigned: message = 'Principals+already+members+of+%s' % group_id else: message = '%s+added+to+%s' % ( '+'.join( assigned ) , group_id ) if RESPONSE is not None: RESPONSE.redirect( ( '%s/manage_groups?group_id=%s&assign=1' + '&manage_tabs_message=%s' ) % ( self.absolute_url(), group_id, message ) ) security.declareProtected( ManageGroups , 'manage_removePrincipalsFromGroup' ) def manage_removePrincipalsFromGroup( self , group_id , principal_ids , RESPONSE=None ): """ Remove one or more principals from a group via the ZMI. """ removed = [] for principal_id in principal_ids: if self.removePrincipalFromGroup( principal_id, group_id ): removed.append( principal_id ) if not removed: message = 'Principals+not+in+group+%s' % group_id else: message = 'Principals+%s+removed+from+%s' % ( '+'.join( removed ) , group_id ) if RESPONSE is not None: RESPONSE.redirect( ( '%s/manage_groups?group_id=%s&assign=1' + '&manage_tabs_message=%s' ) % ( self.absolute_url(), group_id, message ) )
class TokenStorage(UniqueObject, SimpleItem, persistent.Persistent): isPrincipiaFolderish = True # Show up in the ZMI security = ClassSecurityInfo() meta_type = "TokenStorage" id = "onetimetoken_storage" _timedelta = 504 # three weeks def __init__(self, request): self._tokens = OOBTree() def getTokens(self): """ Return all usernames and dates without tokens, read only """ return self._tokens.values() security.declareProtected(ManageUsers, "setToken") def setToken(self, userId=None, generate_username_callback=None, generate_username_kwargs=None): """ Generate token for user or create one-time-user + token """ token = "" m_tool = getToolByName(self, "portal_membership") if not userId: if generate_username_callback: userId = generate_username_callback(**(generate_username_kwargs or {})) else: userId = self.uniqueString() done = m_tool.acl_users.source_users.doAddUser(userId, self.uniqueString()) assert done, "User could not be created for OneTimeToken!" expiry = str(self.expirationDate()) token = self.uniqueString() self._tokens[token] = (userId, expiry) login = "******" % (userId, token) # encode the login string to make it url safe token = encodestring(login) return token security.declarePublic("verifyToken") def verifyToken(self, loginCode): """ """ try: userId, token = decodestring(loginCode).split(":") except: raise TokenError("InvalidLoginCodeError") try: u, expiry = self._tokens[token] except KeyError: raise TokenError("InvalidTokenError") if self.expired(expiry): raise TokenError("ExpiredExpiryError") if not u == userId: raise TokenError("InvalidUserError") del self._tokens[token] return u security.declarePublic("deleteTemporaryUser") def deleteTemporaryUser(self, userId): """ """ m_tool = getToolByName(self, "portal_membership") return m_tool.acl_users.source_users.doDeleteUser(userId) security.declarePrivate("uniqueString") def uniqueString(self): """Returns a string that is random and unguessable, or at least as close as possible.""" # this is the informal UUID algorithm of # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/213761 # by Carl Free Jr t = long(time.time() * 1000) r = long(random.random() * 100000000000000000L) try: a = socket.gethostbyname(socket.gethostname()) except: # if we can't get a network address, just imagine one a = random.random() * 100000000000000000L data = str(t) + " " + str(r) + " " + str(a) # +' '+str(args) data = md5.md5(data).hexdigest() return str(data) security.declarePrivate("expirationDate") def expirationDate(self): """Returns a DateTime for exipiry of a request from the current time. This is used by housekeeping methods (like clearEpired) and stored in reset request records.""" if not hasattr(self, "_timedelta"): self._timedelta = 168 try: if isinstance(self._timedelta, datetime.timedelta): expire = datetime.datetime.utcnow() + self._timedelta return DateTime(expire.year, expire.month, expire.day, expire.hour, expire.minute, expire.second, "UTC") except NameError: pass # that's okay, it must be a number of hours... expire = time.time() + self._timedelta * 3600 # 60 min/hr * 60 sec/min return DateTime(expire) security.declarePrivate("expired") def expired(self, datetime, now=None): """Tells whether a DateTime or timestamp 'datetime' is expired with regards to either 'now', if provided, or the current time.""" if not now: now = DateTime() return now.greaterThanEqualTo(datetime) security.declarePrivate("clearExpired") def clearExpired(self, days=0): """Destroys all expired reset request records. Parameter 'days' controls how many days past expired it must be to clear token. """ for token, record in self._tokens.items(): stored_user, expiry = record if self.expired(DateTime(expiry), DateTime() - days): del self._tokens[token] self.deleteTemporaryUser(stored_user)
class ZODBGroupManager(BasePlugin): """ PAS plugin for managing groups, and groups of groups in the ZODB """ meta_type = 'ZODB Group Manager' security = ClassSecurityInfo() def __init__(self, id, title=None): self._id = self.id = id self.title = title self._groups = OOBTree() self._principal_groups = OOBTree() # # IGroupEnumerationPlugin implementation # security.declarePrivate('enumerateGroups') def enumerateGroups(self, id=None, title=None, exact_match=False, sort_by=None, max_results=None, **kw): """ See IGroupEnumerationPlugin. """ group_info = [] group_ids = [] plugin_id = self.getId() if isinstance(id, str): id = [id] if isinstance(title, str): title = [title] if exact_match and (id or title): if id: group_ids.extend(id) elif title: group_ids.extend(title) if group_ids: group_filter = None else: # Searching group_ids = self.listGroupIds() group_filter = _ZODBGroupFilter(id, title, **kw) for group_id in group_ids: if self._groups.get(group_id, None): e_url = '%s/manage_groups' % self.getId() p_qs = 'group_id=%s' % group_id m_qs = 'group_id=%s&assign=1' % group_id info = {} info.update(self._groups[group_id]) info['pluginid'] = plugin_id info['properties_url'] = '%s?%s' % (e_url, p_qs) info['members_url'] = '%s?%s' % (e_url, m_qs) info['id'] = '%s%s' % (self.prefix, info['id']) if not group_filter or group_filter(info): group_info.append(info) return tuple(group_info) # # IGroupsPlugin implementation # security.declarePrivate('getGroupsForPrincipal') def getGroupsForPrincipal(self, principal, request=None): """ See IGroupsPlugin. """ unadorned = self._principal_groups.get(principal.getId(), ()) return tuple(['%s%s' % (self.prefix, x) for x in unadorned]) # # (notional)IZODBGroupManager interface # security.declareProtected(ManageGroups, 'listGroupIds') def listGroupIds(self): """ -> ( group_id_1, ... group_id_n ) """ return self._groups.keys() security.declareProtected(ManageGroups, 'listGroupInfo') def listGroupInfo(self): """ -> ( {}, ...{} ) o Return one mapping per group, with the following keys: - 'id' """ return self._groups.values() security.declareProtected(ManageGroups, 'getGroupInfo') def getGroupInfo(self, group_id): """ group_id -> {} """ return self._groups[group_id] security.declarePrivate('addGroup') def addGroup(self, group_id, title=None, description=None): """ Add 'group_id' to the list of groups managed by this object. o Raise KeyError on duplicate. """ if self._groups.get(group_id) is not None: raise KeyError, 'Duplicate group ID: %s' % group_id self._groups[group_id] = { 'id': group_id, 'title': title, 'description': description } security.declarePrivate('updateGroup') def updateGroup(self, group_id, title, description): """ Update properties for 'group_id' o Raise KeyError if group_id doesn't already exist. """ self._groups[group_id].update({ 'title': title, 'description': description }) self._groups[group_id] = self._groups[group_id] security.declarePrivate('removeGroup') def removeGroup(self, group_id): """ Remove 'role_id' from the list of roles managed by this object, removing assigned members from it before doing so. o Raise KeyError if 'group_id' doesn't already exist. """ for principal_id in self._principal_groups.keys(): self.removePrincipalFromGroup(principal_id, group_id) del self._groups[group_id] # # Group assignment API # security.declareProtected(ManageGroups, 'listAvailablePrincipals') def listAvailablePrincipals(self, group_id, search_id): """ Return a list of principal IDs to that can belong to the group. o If supplied, 'search_id' constrains the principal IDs; if not, return empty list. o Omit principals with existing assignments. """ result = [] if search_id: # don't bother searching if no criteria parent = aq_parent(self) for info in parent.searchPrincipals(max_results=20, sort_by='id', id=search_id, exact_match=False): id = info['id'] title = info.get('title', id) if (group_id not in self._principal_groups.get(id, ()) and group_id != id): result.append((id, title)) return result security.declareProtected(ManageGroups, 'listAssignedPrincipals') def listAssignedPrincipals(self, group_id): """ Return a list of principal IDs belonging to a group. """ result = [] for k, v in self._principal_groups.items(): if group_id in v: # should be one and only one mapping to 'k' parent = aq_parent(self) info = parent.searchPrincipals(id=k, exact_match=True) assert (len(info) in (0, 1)) if len(info) == 0: title = '<%s: not found>' % k else: title = info[0].get('title', k) result.append((k, title)) return result security.declareProtected(ManageGroups, 'addPrincipalToGroup') def addPrincipalToGroup(self, principal_id, group_id, REQUEST=None): """ Add a principal to a group. o Return a boolean indicating whether a new assignment was created. o Raise KeyError if 'group_id' is unknown. """ group_info = self._groups[group_id] # raise KeyError if unknown! current = self._principal_groups.get(principal_id, ()) already = group_id in current if not already: new = current + (group_id, ) self._principal_groups[principal_id] = new return not already addPrincipalToGroup = postonly(addPrincipalToGroup) security.declareProtected(ManageGroups, 'removePrincipalFromGroup') def removePrincipalFromGroup(self, principal_id, group_id, REQUEST=None): """ Remove a prinicpal from from a group. o Return a boolean indicating whether the principal was already a member of the group. o Raise KeyError if 'group_id' is unknown. o Ignore requests to remove a principal if not already a member of the group. """ group_info = self._groups[group_id] # raise KeyError if unknown! current = self._principal_groups.get(principal_id, ()) new = tuple([x for x in current if x != group_id]) already = current != new if already: self._principal_groups[principal_id] = new return already removePrincipalFromGroup = postonly(removePrincipalFromGroup) # # ZMI # manage_options = (({ 'label': 'Groups', 'action': 'manage_groups', }, ) + BasePlugin.manage_options) security.declarePublic('manage_widgets') manage_widgets = PageTemplateFile('www/zuWidgets', globals(), __name__='manage_widgets') security.declareProtected(ManageGroups, 'manage_groups') manage_groups = PageTemplateFile('www/zgGroups', globals(), __name__='manage_groups') security.declareProtected(ManageGroups, 'manage_twoLists') manage_twoLists = PageTemplateFile('../www/two_lists', globals(), __name__='manage_twoLists') security.declareProtected(ManageGroups, 'manage_addGroup') def manage_addGroup(self, group_id, title=None, description=None, RESPONSE=None): """ Add a group via the ZMI. """ self.addGroup(group_id, title, description) message = 'Group+added' if RESPONSE is not None: RESPONSE.redirect('%s/manage_groups?manage_tabs_message=%s' % (self.absolute_url(), message)) security.declareProtected(ManageGroups, 'manage_updateGroup') def manage_updateGroup(self, group_id, title, description, RESPONSE=None): """ Update a group via the ZMI. """ self.updateGroup(group_id, title, description) message = 'Group+updated' if RESPONSE is not None: RESPONSE.redirect('%s/manage_groups?manage_tabs_message=%s' % (self.absolute_url(), message)) security.declareProtected(ManageGroups, 'manage_removeGroups') def manage_removeGroups(self, group_ids, RESPONSE=None, REQUEST=None): """ Remove one or more groups via the ZMI. """ group_ids = filter(None, group_ids) if not group_ids: message = 'no+groups+selected' else: for group_id in group_ids: self.removeGroup(group_id) message = 'Groups+removed' if RESPONSE is not None: RESPONSE.redirect('%s/manage_groups?manage_tabs_message=%s' % (self.absolute_url(), message)) manage_removeGroups = postonly(manage_removeGroups) security.declareProtected(ManageGroups, 'manage_addPrincipalsToGroup') def manage_addPrincipalsToGroup(self, group_id, principal_ids, RESPONSE=None, REQUEST=None): """ Add one or more principals to a group via the ZMI. """ assigned = [] for principal_id in principal_ids: if self.addPrincipalToGroup(principal_id, group_id): assigned.append(principal_id) if not assigned: message = 'Principals+already+members+of+%s' % group_id else: message = '%s+added+to+%s' % ('+'.join(assigned), group_id) if RESPONSE is not None: RESPONSE.redirect(('%s/manage_groups?group_id=%s&assign=1' + '&manage_tabs_message=%s') % (self.absolute_url(), group_id, message)) manage_addPrincipalsToGroup = postonly(manage_addPrincipalsToGroup) security.declareProtected(ManageGroups, 'manage_removePrincipalsFromGroup') def manage_removePrincipalsFromGroup(self, group_id, principal_ids, RESPONSE=None, REQUEST=None): """ Remove one or more principals from a group via the ZMI. """ removed = [] for principal_id in principal_ids: if self.removePrincipalFromGroup(principal_id, group_id): removed.append(principal_id) if not removed: message = 'Principals+not+in+group+%s' % group_id else: message = 'Principals+%s+removed+from+%s' % ('+'.join(removed), group_id) if RESPONSE is not None: RESPONSE.redirect(('%s/manage_groups?group_id=%s&assign=1' + '&manage_tabs_message=%s') % (self.absolute_url(), group_id, message)) manage_removePrincipalsFromGroup = postonly( manage_removePrincipalsFromGroup)
class Calendar: """This class represents a calendar which is a set of days which contain information about which conferences whithin certain categories are happening for each of these days and for a certain access. This class allows to configure the date interval and the category set to be considered and provides operations which allow to know about what's happening on each of those days. Attributes: _aw - (accessControl.AccessWrapper) Information about the access for which the calendar will be built. _sDate - (datetime) Starting date for the calendar. _eDate - (datetime) Ending date for the calendar. _categList - (List) List of categories to be considered. _days - (OOBTree) Index of days which build up the calendar. """ def __init__(self, aw, sDate, eDate, categList=[]): self._aw = aw self._tz = sDate.tzinfo self._sDate = sDate.replace(hour=0, minute=0, second=0, microsecond=0) self._eDate = eDate.replace(hour=23, minute=59, second=59, microsecond=0) self._categList = categList self._icons = {} self._days = None def getIcons(self): try: return self._icons except: self._icons = {} return {} def setIcons(self, categ): """Retrieves the list of icons in a given category tree """ if categ.getIcon() != None: return [categ.getId()] res = [] for subcat in categ.getSubCategoryList(): res += self.setIcons(subcat) return res def getStartDate(self): return self._sDate def getEndDate(self): return self._eDate def getCategoryList(self): return self._categList def getLocator(self): """Returns the generic locator for the current object. This locator contains the folloing entries corresponding to values for which the calendar is configured: selCateg -> List of category ids. sDate -> Starting date. eDate -> Ending date. """ l = Locators.Locator() ids = [] for c in self.getCategoryList(): ids.append(c.getId()) l["selCateg"] = ids l["sDate"] = self.getStartDate().strftime("%Y-%m-%d") l["eDate"] = self.getStartDate().strftime("%Y-%m-%d") return l def _mapConferenceToDays(self, conf, categList): """Registers a given conference for the days on which it is taking place whithin the calendar date interval. Parameters: conf - (conference.Conference) Conference to be mapped. categList - (List) List of calendar categories in which the specified conference is found. """ inc = timedelta(1) d = max( conf.getStartDate().astimezone(self._tz).replace(hour=0, minute=0, second=0), self.getStartDate().astimezone(self._tz).replace(hour=0, minute=0, second=0)) ed = min(conf.getEndDate().astimezone(self._tz), self.getEndDate().astimezone(self._tz)) if ed > self.getEndDate(): ed = self.getEndDate() while d <= ed: #norm_date=d.tzinfo.normalize(d) #norm_date=norm_date.replace(hour=0) norm_date = self.getNormDate(d) if not self._days.has_key(norm_date): self._days[norm_date] = Day(self, d) self._days[norm_date].addConference(conf, categList, self._tz) d += inc def _initialiseDays(self): """ """ self._days = OOBTree() res = set() self._categIdx = {} self._icons = {} catDayIdx = indexes.IndexesHolder().getIndex("categoryDate") for categ in self.getCategoryList(): confs = catDayIdx.getObjectsInDays(categ.getId(), self.getStartDate(), self.getEndDate()) for conf in confs: confId = conf.getId() if not self._categIdx.has_key(confId): self._categIdx[confId] = [] self._categIdx[confId].append(categ) res.update(confs) for conf in res: #getting icon from the nearest owner category owner = conf.getOwner() while owner != None and owner.getId() != "0": if owner.getIcon(): if self._icons.has_key(owner.getId()): self._icons[owner.getId()].append(conf.getId()) else: self._icons[owner.getId()] = [conf.getId()] break owner = owner.getOwner() #mapping conf to days self._mapConferenceToDays(conf, self._categIdx[conf.getId()]) def getNormDate(self, date): # we have to normalize, but as we are going over all the days, we have to keep the date # and just normalize the tzinfo. norm_date = date.tzinfo.normalize(date) norm_date = norm_date.replace(year=date.year, month=date.month, day=date.day, hour=0) return norm_date def getDay(self, date): if not self._days: self._initialiseDays() norm_date = self.getNormDate(date) if not self._days.has_key(norm_date): self._days[norm_date] = Day(self, date) return self._days[norm_date] def getDayList(self): inc = timedelta(1) d = self.getStartDate() l = [] while d < self.getEndDate(): l.append(self.getDay(d)) d += inc return l def getConferenceCategories(self, conf): return self._categIdx[conf.getId()] def __str__(self): l = [] if self._days: for day in self._days.values(): l.append("%s" % day) str = _("Calendar between %s and %s: %s")%(\ self.getStartDate().strftime("%Y-%m-%d"), \ self.getEndDate().strftime("%Y-%m-%d"), \ "\n\t\t".join(l) ) return str
class ToManyContRelationship(ToManyRelationshipBase): """ ToManyContRelationship is the ToMany side of a realtionship that contains its related objects (like the normal Zope ObjectManager) """ meta_type = "ToManyContRelationship" security = ClassSecurityInfo() def __init__(self, id): """set our instance values""" self.id = id self._objects = OOBTree() def _safeOfObjects(self): """ Try to safely return ZenPack objects rather than causing imports to fail. """ objs = [] for ob in self._objects.values(): try: objs.append(ob.__of__(self)) except AttributeError: log.info("Ignoring unresolvable object '%s'", str(ob)) return objs def __call__(self): """when we are called return our related object in our aq context""" return self._safeOfObjects() def __getattr__(self, name): """look in the two object stores for related objects""" if '_objects' in self.__dict__: objects = self._objects if objects.has_key(name): return objects[name] raise AttributeError( "Unable to find the attribute '%s'" % name ) def __hasattr__(self, name): """check to see if we have an object by an id this will fail if passed a short id and object is stored with fullid (ie: it is related not contained) use hasobject to get around this issue""" return self._objects.has_key(name) def hasobject(self, obj): "check to see if we have this object" return self._objects.get(obj.id) == obj def addRelation(self, obj): """Override base to run manage_afterAdd like ObjectManager""" if self._objects.has_key(obj.getId()): log.debug("obj %s already exists on %s", obj.getPrimaryId(), self.getPrimaryId()) notify(ObjectWillBeAddedEvent(obj, self, obj.getId())) ToManyRelationshipBase.addRelation(self, obj) obj = obj.__of__(self) o = self._getOb(obj.id) notify(ObjectAddedEvent(o, self, obj.getId())) def _setObject(self,id,object,roles=None,user=None,set_owner=1): """ObjectManager interface to add contained object.""" unused(user, roles, set_owner) object.__primary_parent__ = aq_base(self) self.addRelation(object) return object.getId() def manage_afterAdd(self, item, container): # Don't do recursion anymore, a subscriber does that. pass manage_afterAdd.__five_method__ = True def manage_afterClone(self, item): # Don't do recursion anymore, a subscriber does that. pass manage_afterClone.__five_method__ = True def manage_beforeDelete(self, item, container): # Don't do recursion anymore, a subscriber does that. pass manage_beforeDelete.__five_method__ = True def _add(self,obj): """add an object to one side of a ToManyContRelationship. """ id = obj.id if self._objects.has_key(id): raise RelationshipExistsError v=checkValidId(self, id) if v is not None: id=v self._objects[id] = aq_base(obj) obj = aq_base(obj).__of__(self) def _remove(self, obj=None, suppress_events=False): """remove object from our side of a relationship""" if obj: objs = [obj] else: objs = self.objectValuesAll() if not suppress_events: for robj in objs: notify(ObjectWillBeRemovedEvent(robj, self, robj.getId())) if obj: id = obj.id if not self._objects.has_key(id): raise ObjectNotFound( "object %s not found on %s" % ( obj.getPrimaryId(), self.getPrimaryId())) del self._objects[id] else: self._objects = OOBTree() self.__primary_parent__._p_changed = True if not suppress_events: for robj in objs: notify(ObjectRemovedEvent(robj, self, robj.getId())) def _remoteRemove(self, obj=None): """remove an object from the far side of this relationship if no object is passed in remove all objects""" if obj: if not self._objects.has_key(obj.id): raise ObjectNotFound("object %s not found on %s" % ( obj.getPrimaryId(), self.getPrimaryId())) objs = [obj] else: objs = self.objectValuesAll() remoteName = self.remoteName() for obj in objs: rel = getattr(obj, remoteName) try: rel._remove(self.__primary_parent__) except ObjectNotFound: message = log_tb(sys.exc_info()) log.error('Remote remove failed. Run "zenchkrels -r -x1". ' + message) continue def _getOb(self, id, default=zenmarker): """look up in our local store and wrap in our aq_chain""" if self._objects.has_key(id): return self._objects[id].__of__(self) elif default == zenmarker: raise AttributeError( "Unable to find %s" % id ) return default security.declareProtected('View', 'objectIds') def objectIds(self, spec=None): """only return contained objects""" if spec: if isinstance(spec,basestring): spec=[spec] return [obj.id for obj in self._objects.values() \ if obj.meta_type in spec] return [ k for k in self._objects.keys() ] objectIdsAll = objectIds security.declareProtected('View', 'objectValues') def objectValues(self, spec=None): """override to only return owned objects for many to many rel""" if spec: if isinstance(spec,basestring): spec=[spec] return [ob.__of__(self) for ob in self._objects.values() \ if ob.meta_type in spec] return self._safeOfObjects() security.declareProtected('View', 'objectValuesAll') objectValuesAll = objectValues def objectValuesGen(self): """Generator that returns all related objects.""" return (obj.__of__(self) for obj in self._objects.values()) def objectItems(self, spec=None): """over ride to only return owned objects for many to many rel""" if spec: if isinstance(spec,basestring): spec=[spec] return [(key,value.__of__(self)) \ for (key,value) in self._objects.items() \ if value.meta_type in spec] return [(key,value.__of__(self)) \ for (key,value) in self._objects.items()] objectItemsAll = objectItems #FIXME - need to make this work # def all_meta_types(self, interfaces=None): # mts = [] # for mt in ToManyRelationshipBase.all_meta_types(self, interfaces): # if (mt.has_key('instance') and mt['instance']): # for cl in self.sub_classes: # if checkClass(mt['instance'], cl): # mts.append(mt) # return mts def _getCopy(self, container): """ make new relation add copies of contained objs and refs if the relation is a many to many """ rel = self.__class__(self.id) rel.__primary_parent__ = container rel = rel.__of__(container) norelcopy = getattr(self, 'zNoRelationshipCopy', []) if self.id in norelcopy: return rel for oobj in self.objectValuesAll(): cobj = oobj._getCopy(rel) rel._setObject(cobj.id, cobj) return rel def checkValidId(self, id): """ Is this a valid id for this container? """ try: checkValidId(self, id) except: raise else: return True def exportXml(self, ofile, ignorerels=[]): """Return an xml representation of a ToManyContRelationship <tomanycont id='interfaces'> <object id='hme0' module='Products.Confmon.IpInterface' class='IpInterface'> <property></property> etc.... </object> </tomanycont> """ if self.countObjects() == 0: return ofile.write("<tomanycont id='%s'>\n" % self.id) for obj in self.objectValues(): obj.exportXml(ofile, ignorerels) ofile.write("</tomanycont>\n") def checkRelation(self, repair=False): """Check to make sure that relationship bidirectionality is ok. """ if len(self._objects): log.debug("checking relation: %s", self.id) else: return # look for objects that don't point back to us # or who should no longer exist in the database remoteName = self.remoteName() parentObject = self.getPrimaryParent() for obj in self._objects.values(): if not hasattr(obj, remoteName): path = parentObject.getPrimaryUrlPath() if repair: log.warn("Deleting %s object '%s' relation '%s' (missing remote relation '%s')", path, obj, self.id, remoteName) self._remove(obj, True) continue else: msg = "%s object '%s' relation '%s' missing remote relation '%s'" % ( path, obj, self.id, remoteName) raise AttributeError(msg) rrel = getattr(obj, remoteName) if not rrel.hasobject(parentObject): log.error("remote relation %s doesn't point back to %s", rrel.getPrimaryId(), self.getPrimaryId()) if repair: log.warn("reconnecting relation %s to relation %s", rrel.getPrimaryId(),self.getPrimaryId()) rrel._add(parentObject)
class Index(Persistent, object): implements(IIndex) ranking_method = defaults['ranking_method'] def __init__(self, **kw): # perform argument check first illegal_args = [k for k in kw.keys() if not k in defaults.keys()] if illegal_args: raise ValueError('Unknown parameters: %s' % ', '.join(illegal_args)) # setup preferences using default args (preferences are stored as # attributes of the index instance for k, v in defaults.items(): v = kw.get(k, v) setattr(self, k, v) self.clear() def clear(self): # lexicon & storage self._lexicon = createObject(self.lexicon, self.languages) # build either a mapping of storages when using dedicated storages # otherwise use a single storage self._feature_ranking = False # this index supports ranking? if self.dedicated_storage: self._storage = OOBTree() for f in self.fields: self._storage[f] = createObject(self.storage) self._feature_ranking = IStorageWithTermFrequency.providedBy( self._storage[f]) else: self._storage = createObject(self.storage) self._feature_ranking = IStorageWithTermFrequency.providedBy( self._storage) def getLexicon(self): """ return the lexicon """ return self._lexicon def getStorage(self, field): """ return the storage """ if self.dedicated_storage: try: return self._storage[field] except KeyError: raise ValueError("No such storage for field '%s'" % field) else: return self._storage def getSettings(self): """ returns a mapping contains the indexes preferences """ from copy import copy d = {} for k in defaults.keys(): d[k] = copy(getattr(self, k)) return d def index_object(self, obj, docid): """ index a given object under a given document id """ # Call the content extractor which is responsible for # the extraction of all related content parts from obj # based on the object type and the index settings. # The extractor should return a dictionary that maps # all fields of the index to a dictionary the following # key-value pairs: # 'language' -> language # 'content' -> content of particular field in obj as unicode # string try: indexable_content = extract_content( self.fields, obj, default_encoding=self.default_encoding, default_language=self.languages[0]) except: handle_exc('extract_content failed', obj, sys.exc_info()) return False if indexable_content is None or not indexable_content: return False # now iterate over all fields and pass all field related content # through the indexer pipeline all_wordids = [] # we need this only if dedicated_storage == False for field in [ f for f in self.fields if f in indexable_content.getFields() ]: wordids = [] for info in indexable_content.getFieldData(field): content = info['content'] if not isinstance(content, unicode): raise ValueError('Content must be unicode: %s' % repr(content)) # If a document has an unknown language (other than the ones configured # for the index), an exception will be raised or the content is # indexed under the first configured language language = info['language'] if not language in self.languages: if self.index_unknown_languages: language = self.languages[0] else: raise ValueError( 'Unsupported language: %s (allowed: %s)' % (language, ', '.join(self.languages))) # run content through the pipline (splitter, stopword remover, normalizer etc) words = self._process_words(content, language) # now obtain wordids from the lexicon for all words wordids.extend(self._lexicon.insertWords(words, language)) # If we have dedicated storages for every field then we must insert the # wordids here otherwise we collect all wordids and insert them into # the default storage. This implies that one should use dedicated storages # when indexing multiple fields. If you index multiple fields without # dedicated storage you will not be able to search by-field. if self.dedicated_storage: try: old_ids = self._storage[field].getWordIdsForDocId(docid) except StorageException: old_ids = [] if old_ids != wordids: self._storage[field].insertDocument(docid, wordids) else: all_wordids.extend(wordids) # Insert everything into the default storage when not using dedicated # storages if not self.dedicated_storage: try: old_ids = self._storage.getWordIdsForDocId(docid) except StorageException: old_ids = [] if old_ids != all_wordids: self._storage.insertDocument(docid, all_wordids) return True def _process_words(self, content, language): """ implements the processing pipeline """ # first normalize content string if self.use_normalizer: normalizer = getUtility(INormalizer) content = normalizer.process(content, language) # now create a new splitter splitter = createObject( self.splitter, casefolding=self.splitter_casefolding, separator=self.splitter_additional_chars, maxlen=self.splitter_max_length, ) # and split unicode content into list of unicode strings words = splitter.split(content) # now filter out all stopwords if self.use_stopwords: sw_utility = getUtility(IStopwords) words = sw_utility.process(words, language) # Stem words if required. If no stemmer for 'language' is available # then do not stem if self.use_stemmer: S = getStemmer(language) if S: words = S.stem(words) return words def unindex_object(self, docid): """ remove a document given its document id from the index """ if self.dedicated_storage: for field in self.fields: self._storage[field].removeDocument(docid) else: self._storage.removeDocument(docid) def _prepare_query(self, query, language): """ performs similar transformations as _process_words() but only for a query. So we don't need the splitter etc. """ # to lowercase if necessary if self.splitter_casefolding: query = query.lower() # normalize query string if self.use_normalizer: normalizer = getUtility(INormalizer) query = normalizer.process(query, language) return query query_options = ('parser', 'language', 'field', 'search_all_fields', 'autoexpand', 'similarity_ratio', 'thesaurus', 'ranking', 'ranking_maxhits') def search(self, query, **kw): """ Perform a query against the index. Valid query options are: 'parser' -- named utility implementing IParser 'language' -- language to be used to lookup words from the lexicon 'field' -- perform searches against a configured index field 'autoexpand' -- off|always|on_miss (see below) """ # queries must be unicode if not isinstance(query, unicode): raise ValueError('Query must be unicode string') # First check query options for k in kw.keys(): if not k in self.query_options: raise ValueError( 'Unknown option: %s (supported query options: %s)' % (k, ', '.join(self.query_options))) # obtain parser ID (which is the name of named utility implementing IParser) parser_id = kw.get('parser', self.query_parser) # determine query language language = kw.get('language', self.languages[0]) if not language in self.languages: raise ValueError( 'Unsupported language: %s (supported languages: %s)' % (language, ', '.join(self.languages))) # check if field is known to the index field = kw.get('field') search_all_fields = kw.get('search_all_fields') if field and search_all_fields: raise ValueError('Cannot specify field and search_all_fields') if search_all_fields: if not self.dedicated_storage: raise ValueError( 'search_all_fields cannot be used without dedicated ' 'storage.') search_fields = self.fields else: if not field: field = self.fields[0] if field not in self.fields: raise ValueError('Unknown field: %s (known fields: %s)' % (field, ', '.join(self.fields))) search_fields = [field] # perform optional cosine ranking after searching ranking = bool(kw.get('ranking', self.ranking)) if ranking and not self._feature_ranking: raise ValueError( "The storage used for this index does not support relevance ranking" ) # Limit *ranked* result set to at most XXX hits ranking_maxhits = kw.get('ranking_maxhits', 50) if not isinstance(ranking_maxhits, int): raise ValueError('"ranking_maxhits" must be an integer') if kw.has_key('ranking_maxhits') and not ranking: raise ValueError( 'Specify "ranking_maxhits" only with having set ranking=True') # autoexpansion of query terms # 'off' -- expand never # 'always' -- expand always # 'on_miss' -- expand only for not-found terms in the query string autoexpand = kw.get('autoexpand', self.autoexpand) if not autoexpand in ('off', 'always', 'on_miss'): raise ValueError( '"autoexpand" must either be "off", "always" or "on_miss"') # Use a sequence of configured thesauri (identified by their configured name) # for additional lookup of terms thesaurus = kw.get('thesaurus', []) if isinstance(thesaurus, str): thesaurus = (thesaurus, ) if not isinstance(thesaurus, (list, tuple)): raise ValueError( '"thesaurus" must be list or tuple of configured thesaurus ids' ) # Similarity ratio (measured as Levenshtein distance) similarity_ratio = float(kw.get('similarity_ratio', 0.75)) if similarity_ratio < 0.0 or similarity_ratio > 1.0: raise ValueError( 'similarity_ratio must been 0.0 and 1.0 (value %f)' % similarity_ratio) # obtain a parser (registered as named utility) parser = getUtility(IParser, parser_id) # run query string through normalizer, case normalizer etc. query = self._prepare_query(query, language) # create a tree of nodes parsed_query = parser.parse(query) if not parsed_query: raise ValueError('No query specified') # Post-filter for stopwords. We need to perform this # outside the query parser because the lex/yacc-based query # parser implementation can't be used in a reasonable way # to deal with such additional functionality. if self.use_stopwords: sw_utility = getUtility(IStopwords) stopwords = sw_utility.stopwordsForLanguage(language) if stopwords: # The stopword remover removes WordNodes representing # a stopword *in-place* stopword_remover(parsed_query, stopwords) # Split word nodes with the splitter splitter = createObject( self.splitter, casefolding=self.splitter_casefolding, separator=self.splitter_additional_chars, maxlen=self.splitter_max_length, ) parsed_query = node_splitter(parsed_query, splitter) # build an instance for the search resultsets = [] for field in search_fields: sr = SearchRequest(self, query=query, parsetree=parsed_query, field=field, autoexpand=autoexpand, similarity_ratio=similarity_ratio, thesaurus=thesaurus, language=language) # call the evaluator and produce a ResultSet instance resultsets.append(Evaluator(sr).run()) resultset = unionResultSets(resultsets) # optional ranking using the cosine measure or another configure # ranking method if ranking: ranking_method = getUtility(IRanking, name=self.ranking_method) resultset.ranking(ranking_method, index=self, language=language, nbest=ranking_maxhits) return resultset ############################################################ # index attributes defined as properties ############################################################ def _setUse_stemmer(self, value): if not value in (True, False, 0, 1): raise ValueError('"use_stemmer" must be either True or False') self._use_stemmer = bool(value) def _getUse_stemmer(self): return self._use_stemmer use_stemmer = property(_getUse_stemmer, _setUse_stemmer) def _setSplitter(self, value): self._splitter = value def _getSplitter(self): return self._splitter splitter = property(_getSplitter, _setSplitter) def _setLexicon(self, value): self.__lexicon = value # using __lexicon instead of __lexicon to avoid a name clash # (_lexicon is the lexicon object) def _getLexicon(self): return self.__lexicon lexicon = property(_getLexicon, _setLexicon) def _setDedicated_storage(self, value): if not value in (True, False): raise ValueError('"dedicated_storage" must be True or False') self._dedicated_storage = value def _getDedicated_storage(self): return self._dedicated_storage dedicated_storage = property(_getDedicated_storage, _setDedicated_storage) def _setSplitter_max_length(self, value): self._splitter_max_length = value def _getSplitter_max_length(self): return self._splitter_max_length splitter_max_length = property(_getSplitter_max_length, _setSplitter_max_length) def _setFields(self, value): self._fields = value def _getFields(self): return self._fields fields = property(_getFields, _setFields) def _setUse_normalizer(self, value): if not value in (True, False): raise ValueError('"use_normalizer" must be True or False') self._use_normalizer = value def _getUse_normalizer(self): return self._use_normalizer use_normalizer = property(_getUse_normalizer, _setUse_normalizer) def _setstorage(self, value): self.__storage = value def _getstorage(self): return self.__storage storage = property(_getstorage, _setstorage) def _setDefault_encoding(self, value): self._default_encoding = value def _getDefault_encoding(self): return self._default_encoding default_encoding = property(_getDefault_encoding, _setDefault_encoding) def _setLanguages(self, value): if not isinstance(value, (list, tuple)): raise ValueError( '"languages" must be list or tuple of country codes') if not value: raise ValueError('No languages given') self._languages = value def _getLanguages(self): return self._languages languages = property(_getLanguages, _setLanguages) def _setSplitter_additional_chars(self, value): self._splitter_additional_chars = value def _getSplitter_additional_chars(self): value = getattr(self, '_splitter_additional_chars', None) if value is None: return self._splitter_separators return value splitter_additional_chars = property(_getSplitter_additional_chars, _setSplitter_additional_chars) def _setQuery_parser(self, value): self._query_parser = value def _getQuery_parser(self): return self._query_parser query_parser = property(_getQuery_parser, _setQuery_parser) def _setSplitter_casefolding(self, value): if not value in (True, False): raise ValueError('"splitter_casefolding" must be True or False') self._splitter_casefolding = value def _getSplitter_casefolding(self): return self._splitter_casefolding splitter_casefolding = property(_getSplitter_casefolding, _setSplitter_casefolding) def _setIndex_unknown_languages(self, value): self._index_unknown_languages = value def _getIndex_unknown_languages(self): return self._index_unknown_languages index_unknown_languages = property(_getIndex_unknown_languages, _setIndex_unknown_languages) def _setAutoexpand(self, value): self._autoexpand = value def _getAutoexpand(self): return getattr(self, '_autoexpand', 'off') autoexpand = property(_getAutoexpand, _setAutoexpand) def _setAutoexpand_limit(self, value): self._autoexpand_limit = value def _getAutoexpand_limit(self): return self._autoexpand_limit autoexpand_limit = property(_getAutoexpand_limit, _setAutoexpand_limit) def _setRanking(self, value): self._ranking = value def _getRanking(self): return self._ranking ranking = property(_getRanking, _setRanking) def _setUse_stopwords(self, value): if not value in (True, False): raise ValueError('"use_stopwords" must be True or False') self._use_stopwords = value def _getUse_stopwords(self): return self._use_stopwords use_stopwords = property(_getUse_stopwords, _setUse_stopwords) ############################################################ # Some helper methods ############################################################ def _dump(self): """ perform low-level dump of the index """ print 'Lexicon' for lang in self.getLexicon().getLanguages(): print lang for k, v in self.getLexicon()._words[lang].items(): print repr(k), v print print '-' * 80 print 'Storage' for field in self.fields: S = self.getStorage(field) for k, v in S._wid2doc.items(): print k, list(v) def __repr__(self): return '%s[%s]' % (self.__class__.__name__, ', '.join([ '%s=%s' % (k, repr(getattr(self, k, None))) for k in defaults.keys() ])) def __len__(self): if self.dedicated_storage: return sum([len(s) for s in self._storage.values()]) else: return len(self._storage)
class TopicIndex(Persistent, SimpleItem): """A TopicIndex maintains a set of FilteredSet objects. Every FilteredSet object consists of an expression and and IISet with all Ids of indexed objects that eval with this expression to 1. """ meta_type = 'TopicIndex' zmi_icon = 'fas fa-info-circle' operators = ('or', 'and') useOperator = 'or' query_options = ('query', 'operator') manage_options = ( {'label': 'FilteredSets', 'action': 'manage_main'}, ) def __init__(self, id, caller=None): self.id = id self.filteredSets = OOBTree() def getId(self): return self.id def clear(self): for fs in self.filteredSets.values(): fs.clear() def index_object(self, docid, obj, threshold=100): """ hook for (Z)Catalog """ for fid, filteredSet in self.filteredSets.items(): filteredSet.index_object(docid, obj) return 1 def unindex_object(self, docid): """ hook for (Z)Catalog """ for fs in self.filteredSets.values(): try: fs.unindex_object(docid) except KeyError: LOG.debug('Attempt to unindex document' ' with id %s failed', docid) return 1 def numObjects(self): """Return the number of indexed objects.""" setlist = [] for fs in self.filteredSets.values(): setlist.append(fs.getIds()) return len(multiunion(setlist)) def indexSize(self): """Return the size of the index in terms of distinct values.""" return len(self.filteredSets) def search(self, filter_id): f = self.filteredSets.get(filter_id, None) if f is not None: return f.getIds() def _apply_index(self, request): record = IndexQuery(request, self.id, self.query_options, self.operators, self.useOperator) if record.keys is None: return None return (self.query_index(record), (self.id, )) def query_index(self, record, resultset=None): """Hook for (Z)Catalog 'record' -- mapping type (usually {"topic": "..." } """ operator = record.operator if operator == 'or': set_func = union else: set_func = intersection res = None for filter_id in record.keys: rows = self.search(filter_id) res = set_func(res, rows) if res: return res return IITreeSet() def hasUniqueValuesFor(self, name): """has unique values for column name""" if name == self.id: return 1 return 0 def uniqueValues(self, name=None, withLength=0): """Return an iterable/sequence of unique values for name. If 'withLengths' is true, returns a iterable/sequence of tuples of (value, length). """ if name is None: name = self.id elif name != self.id: return if not withLength: for key in self.filteredSets.keys(): yield key else: for key, value in self.filteredSets.items(): yield (key, len(value.getIds())) def getEntryForObject(self, docid, default=None): """ Takes a document ID and returns all the information we have on that specific object. """ res = [] for fs in self.filteredSets.values(): ids = fs.getIds() if docid in ids: res.append(fs.getId()) return res or default def addFilteredSet(self, filter_id, typeFilteredSet, expr): # Add a FilteredSet object. if filter_id in self.filteredSets: raise KeyError(('A FilteredSet with this name already ' 'exists: {0}'.format(filter_id))) self.filteredSets[filter_id] = factory( filter_id, typeFilteredSet, expr) def delFilteredSet(self, filter_id): # Delete the FilteredSet object specified by 'filter_id'. if filter_id not in self.filteredSets: raise KeyError( 'no such FilteredSet: {0}'.format(filter_id)) del self.filteredSets[filter_id] def clearFilteredSet(self, filter_id): # Clear the FilteredSet object specified by 'filter_id'. f = self.filteredSets.get(filter_id, None) if f is None: raise KeyError('no such FilteredSet: {0}'.format(filter_id)) f.clear() def manage_addFilteredSet(self, filter_id, typeFilteredSet, expr, URL1, REQUEST=None, RESPONSE=None): """ add a new filtered set """ if len(filter_id) == 0: raise RuntimeError('Length of ID too short') if len(expr) == 0: raise RuntimeError('Length of expression too short') self.addFilteredSet(filter_id, typeFilteredSet, expr) if RESPONSE: RESPONSE.redirect(URL1 + ( '/manage_workspace?' 'manage_tabs_message=FilteredSet%20added')) def manage_delFilteredSet(self, filter_ids=[], URL1=None, REQUEST=None, RESPONSE=None): """ delete a list of FilteredSets""" for filter_id in filter_ids: self.delFilteredSet(filter_id) if RESPONSE: RESPONSE.redirect(URL1 + ( '/manage_workspace?' 'manage_tabs_message=FilteredSet(s)%20deleted')) def manage_saveFilteredSet(self, filter_id, expr, URL1=None, REQUEST=None, RESPONSE=None): """ save expression for a FilteredSet """ self.filteredSets[filter_id].setExpression(expr) if RESPONSE: RESPONSE.redirect(URL1 + ( '/manage_workspace?' 'manage_tabs_message=FilteredSet(s)%20updated')) def getIndexSourceNames(self): """ return names of indexed attributes """ return ('n/a', ) def getIndexQueryNames(self): return (self.id,) def manage_clearFilteredSet(self, filter_ids=[], URL1=None, REQUEST=None, RESPONSE=None): """ clear a list of FilteredSets""" for filter_id in filter_ids: self.clearFilteredSet(filter_id) if RESPONSE: RESPONSE.redirect(URL1 + ( '/manage_workspace?' 'manage_tabs_message=FilteredSet(s)%20cleared')) manage = manage_main = DTMLFile('dtml/manageTopicIndex', globals()) manage_main._setName('manage_main') editFilteredSet = DTMLFile('dtml/editFilteredSet', globals())
class BTreeFolder2Base(Persistent): """Base for BTree-based folders. """ security = ClassSecurityInfo() manage_options=( ({'label': 'Contents', 'action': 'manage_main'}, ) + Folder.manage_options[1:] ) security.declareProtected(view_management_screens, 'manage_main') manage_main = DTMLFile('contents', globals()) _tree = None # OOBTree: { id -> object } _count = None # A BTrees.Length _v_nextid = 0 # The integer component of the next generated ID _mt_index = None # OOBTree: { meta_type -> OIBTree: { id -> 1 } } title = '' # superValues() looks for the _objects attribute, but the implementation # would be inefficient, so superValues() support is disabled. _objects = () def __init__(self, id=None): if id is not None: self.id = id self._initBTrees() def _initBTrees(self): self._tree = OOBTree() self._count = Length() self._mt_index = OOBTree() def _populateFromFolder(self, source): """Fill this folder with the contents of another folder. """ for name in source.objectIds(): value = source._getOb(name, None) if value is not None: self._setOb(name, aq_base(value)) security.declareProtected(view_management_screens, 'manage_fixCount') def manage_fixCount(self): """Calls self._fixCount() and reports the result as text. """ old, new = self._fixCount() path = '/'.join(self.getPhysicalPath()) if old == new: return "No count mismatch detected in BTreeFolder2 at %s." % path else: return ("Fixed count mismatch in BTreeFolder2 at %s. " "Count was %d; corrected to %d" % (path, old, new)) def _fixCount(self): """Checks if the value of self._count disagrees with len(self.objectIds()). If so, corrects self._count. Returns the old and new count values. If old==new, no correction was performed. """ old = self._count() new = len(self.objectIds()) if old != new: self._count.set(new) return old, new security.declareProtected(view_management_screens, 'manage_cleanup') def manage_cleanup(self): """Calls self._cleanup() and reports the result as text. """ v = self._cleanup() path = '/'.join(self.getPhysicalPath()) if v: return "No damage detected in BTreeFolder2 at %s." % path else: return ("Fixed BTreeFolder2 at %s. " "See the log for more details." % path) def _cleanup(self): """Cleans up errors in the BTrees. Certain ZODB bugs have caused BTrees to become slightly insane. Fortunately, there is a way to clean up damaged BTrees that always seems to work: make a new BTree containing the items() of the old one. Returns 1 if no damage was detected, or 0 if damage was detected and fixed. """ from BTrees.check import check path = '/'.join(self.getPhysicalPath()) try: check(self._tree) for key in self._tree.keys(): if key not in self._tree: raise AssertionError( "Missing value for key: %s" % repr(key)) check(self._mt_index) for key, value in self._mt_index.items(): if (key not in self._mt_index or self._mt_index[key] is not value): raise AssertionError( "Missing or incorrect meta_type index: %s" % repr(key)) check(value) for k in value.keys(): if k not in value: raise AssertionError( "Missing values for meta_type index: %s" % repr(key)) return 1 except AssertionError: LOG.warn('Detected damage to %s. Fixing now.' % path, exc_info=sys.exc_info()) try: self._tree = OOBTree(self._tree) mt_index = OOBTree() for key, value in self._mt_index.items(): mt_index[key] = OIBTree(value) self._mt_index = mt_index except: LOG.error('Failed to fix %s.' % path, exc_info=sys.exc_info()) raise else: LOG.info('Fixed %s.' % path) return 0 def _getOb(self, id, default=_marker): """Return the named object from the folder. """ tree = self._tree if default is _marker: ob = tree[id] return ob.__of__(self) else: ob = tree.get(id, _marker) if ob is _marker: return default else: return ob.__of__(self) security.declareProtected(access_contents_information, 'get') def get(self, name, default=None): return self._getOb(name, default) def __getitem__(self, name): return self._getOb(name) def __getattr__(self, name): # Boo hoo hoo! Zope 2 prefers implicit acquisition over traversal # to subitems, and __bobo_traverse__ hooks don't work with # restrictedTraverse() unless __getattr__() is also present. # Oh well. res = self._tree.get(name) if res is None: raise AttributeError(name) return res def _setOb(self, id, object): """Store the named object in the folder. """ tree = self._tree if id in tree: raise KeyError('There is already an item named "%s".' % id) tree[id] = object self._count.change(1) # Update the meta type index. mti = self._mt_index meta_type = getattr(object, 'meta_type', None) if meta_type is not None: ids = mti.get(meta_type, None) if ids is None: ids = OIBTree() mti[meta_type] = ids ids[id] = 1 def _delOb(self, id): """Remove the named object from the folder. """ tree = self._tree meta_type = getattr(tree[id], 'meta_type', None) del tree[id] self._count.change(-1) # Update the meta type index. if meta_type is not None: mti = self._mt_index ids = mti.get(meta_type, None) if ids is not None and id in ids: del ids[id] if not ids: # Removed the last object of this meta_type. # Prune the index. del mti[meta_type] security.declareProtected(view_management_screens, 'getBatchObjectListing') def getBatchObjectListing(self, REQUEST=None): """Return a structure for a page template to show the list of objects. """ if REQUEST is None: REQUEST = {} pref_rows = int(REQUEST.get('dtpref_rows', 20)) b_start = int(REQUEST.get('b_start', 1)) b_count = int(REQUEST.get('b_count', 1000)) b_end = b_start + b_count - 1 url = self.absolute_url() + '/manage_main' idlist = self.objectIds() # Pre-sorted. count = self.objectCount() if b_end < count: next_url = url + '?b_start=%d' % (b_start + b_count) else: b_end = count next_url = '' if b_start > 1: prev_url = url + '?b_start=%d' % max(b_start - b_count, 1) else: prev_url = '' formatted = [] formatted.append(listtext0 % pref_rows) for i in range(b_start - 1, b_end): optID = escape(idlist[i]) formatted.append(listtext1 % (escape(optID, quote=1), optID)) formatted.append(listtext2) return {'b_start': b_start, 'b_end': b_end, 'prev_batch_url': prev_url, 'next_batch_url': next_url, 'formatted_list': ''.join(formatted)} security.declareProtected(view_management_screens, 'manage_object_workspace') def manage_object_workspace(self, ids=(), REQUEST=None): '''Redirects to the workspace of the first object in the list.''' if ids and REQUEST is not None: REQUEST.RESPONSE.redirect( '%s/%s/manage_workspace' % ( self.absolute_url(), quote(ids[0]))) else: return self.manage_main(self, REQUEST) security.declareProtected(access_contents_information, 'tpValues') def tpValues(self): """Ensures the items don't show up in the left pane. """ return () security.declareProtected(access_contents_information, 'objectCount') def objectCount(self): """Returns the number of items in the folder.""" return self._count() def __len__(self): return self.objectCount() def __nonzero__(self): return True security.declareProtected(access_contents_information, 'has_key') def has_key(self, id): """Indicates whether the folder has an item by ID. """ return id in self._tree # backward compatibility hasObject = has_key security.declareProtected(access_contents_information, 'objectIds') def objectIds(self, spec=None): # Returns a list of subobject ids of the current object. # If 'spec' is specified, returns objects whose meta_type # matches 'spec'. if spec is None: return self.keys() if isinstance(spec, str): spec = [spec] set = None mti = self._mt_index for meta_type in spec: ids = mti.get(meta_type, None) if ids is not None: set = union(set, ids) if set is None: return () else: return set.keys() security.declareProtected(access_contents_information, 'keys') def keys(self): return self._tree.keys() def __contains__(self, name): return name in self._tree def __iter__(self): return iter(self.objectIds()) security.declareProtected(access_contents_information, 'objectValues') def objectValues(self, spec=None): # Returns a list of actual subobjects of the current object. # If 'spec' is specified, returns only objects whose meta_type # match 'spec'. if spec is None: return self.values() return LazyMap(self._getOb, self.objectIds(spec)) security.declareProtected(access_contents_information, 'values') def values(self): return self._tree.values() security.declareProtected(access_contents_information, 'objectItems') def objectItems(self, spec=None): # Returns a list of (id, subobject) tuples of the current object. # If 'spec' is specified, returns only objects whose meta_type match # 'spec' if spec is None: return self.items() return LazyMap(lambda id, _getOb=self._getOb: (id, _getOb(id)), self.objectIds(spec)) security.declareProtected(access_contents_information, 'items') def items(self): return self._tree.items() security.declareProtected(access_contents_information, 'objectMap') def objectMap(self): # Returns a tuple of mappings containing subobject meta-data. return LazyMap(lambda (k, v): {'id': k, 'meta_type': getattr(v, 'meta_type', None)}, self._tree.items(), self._count()) security.declareProtected(access_contents_information, 'objectIds_d') def objectIds_d(self, t=None): ids = self.objectIds(t) res = {} for id in ids: res[id] = 1 return res security.declareProtected(access_contents_information, 'objectMap_d') def objectMap_d(self, t=None): return self.objectMap() def _checkId(self, id, allow_dup=0): if not allow_dup and id in self: raise BadRequestException('The id "%s" is invalid--' 'it is already in use.' % id) def _setObject(self, id, object, roles=None, user=None, set_owner=1, suppress_events=False): ob = object # better name, keep original function signature v = self._checkId(id) if v is not None: id = v # If an object by the given id already exists, remove it. if id in self: self._delObject(id) if not suppress_events: notify(ObjectWillBeAddedEvent(ob, self, id)) self._setOb(id, ob) ob = self._getOb(id) if set_owner: # TODO: eventify manage_fixupOwnershipAfterAdd # This will be called for a copy/clone, or a normal _setObject. ob.manage_fixupOwnershipAfterAdd() # Try to give user the local role "Owner", but only if # no local roles have been set on the object yet. if getattr(ob, '__ac_local_roles__', _marker) is None: user = getSecurityManager().getUser() if user is not None: userid = user.getId() if userid is not None: ob.manage_setLocalRoles(userid, ['Owner']) if not suppress_events: notify(ObjectAddedEvent(ob, self, id)) notifyContainerModified(self) compatibilityCall('manage_afterAdd', ob, ob, self) return id def __setitem__(self, key, value): return self._setObject(key, value) def _delObject(self, id, dp=1, suppress_events=False): ob = self._getOb(id) compatibilityCall('manage_beforeDelete', ob, ob, self) if not suppress_events: notify(ObjectWillBeRemovedEvent(ob, self, id)) self._delOb(id) if not suppress_events: notify(ObjectRemovedEvent(ob, self, id)) notifyContainerModified(self) def __delitem__(self, name): return self._delObject(id=name) # Utility for generating unique IDs. security.declareProtected(access_contents_information, 'generateId') def generateId(self, prefix='item', suffix='', rand_ceiling=999999999): """Returns an ID not used yet by this folder. The ID is unlikely to collide with other threads and clients. The IDs are sequential to optimize access to objects that are likely to have some relation. """ tree = self._tree n = self._v_nextid attempt = 0 while 1: if n % 4000 != 0 and n <= rand_ceiling: id = '%s%d%s' % (prefix, n, suffix) if id not in tree: break n = randint(1, rand_ceiling) attempt = attempt + 1 if attempt > MAX_UNIQUEID_ATTEMPTS: # Prevent denial of service raise ExhaustedUniqueIdsError self._v_nextid = n + 1 return id
class UserRatingStorage(Contained, Persistent): """BTree-based storage for user ratings, keeps a running statistics tally for efficiency.""" implements(IUserRating, IRatingStorage) _average = 0.0 _anon_average = 0.0 _most_recent = None _penultimate = None _length = None _anon_length = None # BBB scale = 5 def __init__(self): """Setup our data structures""" self._anon_ratings = IOBTree() self._ratings = OOBTree() self._sessions = OOBTree() self._length = Length() self._anon_length = Length() def rate(self, rating, username=None, session_key=None): """Set a rating for a particular user""" # We keep a running average for efficiency, we need to # have the current statistics to do so orig_total = self._average * self.numberOfRatings orig_rating = self._ratings.get(username, 0.0) rating = Rating(rating, username) if username: self._ratings[username] = rating if not orig_rating: # If the user hadn't set a rating yet, the number of # ratings grew self._length.change(1) else: # For anonymous users, we use a sequential key, which # may lead to conflicts. There's probably a better way anon_total = self._anon_average * self._anon_count # Update the corresponding BTree length to get the key self._anon_length.change(1) self._anon_ratings[self._anon_count] = rating self._anon_average = (anon_total + rating)/self._anon_count # If a session key was passed in for an anonymous user # store it with a datestamp if session_key: self._sessions[session_key] = datetime.utcnow() # Calculate the updated average self._average = (orig_total + rating - orig_rating)/self.numberOfRatings # If this isn't just a change in the last rating update the # most recent rating if self._most_recent and username != self._most_recent.userid: self._penultimate = self._most_recent # Mark this new rating as the most recent self._most_recent = rating return rating def userRating(self, username=None): """Retreive the rating for the specified user, or the average anonymous rating if no user is specified""" if username is not None: return self._ratings.get(username, None) else: if self._anon_count: return Rating(self._anon_average) else: return None def remove_rating(self, username): """Remove the rating for a given user""" orig_total = self._average * self.numberOfRatings rating = self._ratings[username] del self._ratings[username] self._length.change(-1) # Since we want to keep track of the most recent rating, we # need to replace it with the second most recent if the most # recent was deleted if rating is self.most_recent: self._most_recent = self._penultimate self._penultimate = None if self._most_recent is None: ordered = sorted(self.all_user_ratings(True), key=lambda x: x.timestamp) if ordered: self._most_recent = ordered[-1] if len(ordered > 1): self._penultimate = ordered[-2] else: self._penultimate = None else: self._most_recent = None self._penultimate = None # Update the average self._average = float(self.numberOfRatings and (orig_total - rating)/self.numberOfRatings) @property def _anon_count(self): # Dynamic Migration if self._anon_length is None: self._anon_length = Length(len(self._anon_ratings)) return self._anon_length() @property def most_recent(self): return self._most_recent def all_user_ratings(self, include_anon=False): ratings = self._ratings.values() if include_anon: ratings = chain(ratings, self._anon_ratings.values()) return ratings def all_raters(self): return self._ratings.keys() @property def numberOfRatings(self): # Dynamic Migration if self._length is None: self._length = Length(len(self._ratings)) return self._length() + self._anon_count @property def averageRating(self): return self._average def last_anon_rating(self, session_key): """Returns a timestamp indicating the last time the anonymous user with the given session_key rated the object.""" return self._sessions.get(session_key, None)
class ToManyContRelationship(ToManyRelationshipBase): """ ToManyContRelationship is the ToMany side of a realtionship that contains its related objects (like the normal Zope ObjectManager) """ meta_type = "ToManyContRelationship" security = ClassSecurityInfo() def __init__(self, id): """set our instance values""" self.id = id self._objects = OOBTree() def _safeOfObjects(self): """ Try to safely return ZenPack objects rather than causing imports to fail. """ objs = [] for ob in self._objects.values(): try: objs.append(ob.__of__(self)) except AttributeError: log.info("Ignoring unresolvable object '%s'", str(ob)) return objs def __call__(self): """when we are called return our related object in our aq context""" return self._safeOfObjects() def __getattr__(self, name): """look in the two object stores for related objects""" if '_objects' in self.__dict__: objects = self._objects if objects.has_key(name): return objects[name] raise AttributeError("Unable to find the attribute '%s'" % name) def __hasattr__(self, name): """check to see if we have an object by an id this will fail if passed a short id and object is stored with fullid (ie: it is related not contained) use hasobject to get around this issue""" return self._objects.has_key(name) def hasobject(self, obj): "check to see if we have this object" return self._objects.get(obj.id) == obj def addRelation(self, obj): """Override base to run manage_afterAdd like ObjectManager""" if self._objects.has_key(obj.getId()): log.debug("obj %s already exists on %s", obj.getPrimaryId(), self.getPrimaryId()) notify(ObjectWillBeAddedEvent(obj, self, obj.getId())) ToManyRelationshipBase.addRelation(self, obj) obj = obj.__of__(self) o = self._getOb(obj.id) notify(ObjectAddedEvent(o, self, obj.getId())) def _setObject(self, id, object, roles=None, user=None, set_owner=1): """ObjectManager interface to add contained object.""" unused(user, roles, set_owner) object.__primary_parent__ = aq_base(self) self.addRelation(object) return object.getId() def manage_afterAdd(self, item, container): # Don't do recursion anymore, a subscriber does that. pass manage_afterAdd.__five_method__ = True def manage_afterClone(self, item): # Don't do recursion anymore, a subscriber does that. pass manage_afterClone.__five_method__ = True def manage_beforeDelete(self, item, container): # Don't do recursion anymore, a subscriber does that. pass manage_beforeDelete.__five_method__ = True def _add(self, obj): """add an object to one side of a ToManyContRelationship. """ id = obj.id if self._objects.has_key(id): raise RelationshipExistsError v = checkValidId(self, id) if v is not None: id = v self._objects[id] = aq_base(obj) obj = aq_base(obj).__of__(self) def _remove(self, obj=None, suppress_events=False): """remove object from our side of a relationship""" if obj: objs = [obj] else: objs = self.objectValuesAll() if not suppress_events: for robj in objs: notify(ObjectWillBeRemovedEvent(robj, self, robj.getId())) if obj: id = obj.id if not self._objects.has_key(id): raise ObjectNotFound("object %s not found on %s" % (obj.getPrimaryId(), self.getPrimaryId())) del self._objects[id] else: self._objects = OOBTree() self.__primary_parent__._p_changed = True if not suppress_events: for robj in objs: notify(ObjectRemovedEvent(robj, self, robj.getId())) def _remoteRemove(self, obj=None): """remove an object from the far side of this relationship if no object is passed in remove all objects""" if obj: if not self._objects.has_key(obj.id): raise ObjectNotFound("object %s not found on %s" % (obj.getPrimaryId(), self.getPrimaryId())) objs = [obj] else: objs = self.objectValuesAll() remoteName = self.remoteName() for obj in objs: rel = getattr(obj, remoteName) try: rel._remove(self.__primary_parent__) except ObjectNotFound: message = log_tb(sys.exc_info()) log.error('Remote remove failed. Run "zenchkrels -r -x1". ' + message) continue def _getOb(self, id, default=zenmarker): """look up in our local store and wrap in our aq_chain""" if self._objects.has_key(id): return self._objects[id].__of__(self) elif default == zenmarker: raise AttributeError("Unable to find %s" % id) return default security.declareProtected('View', 'objectIds') def objectIds(self, spec=None): """only return contained objects""" if spec: if isinstance(spec, basestring): spec = [spec] return [obj.id for obj in self._objects.values() \ if obj.meta_type in spec] return [k for k in self._objects.keys()] objectIdsAll = objectIds security.declareProtected('View', 'objectValues') def objectValues(self, spec=None): """override to only return owned objects for many to many rel""" if spec: if isinstance(spec, basestring): spec = [spec] return [ob.__of__(self) for ob in self._objects.values() \ if ob.meta_type in spec] return self._safeOfObjects() security.declareProtected('View', 'objectValuesAll') objectValuesAll = objectValues def objectValuesGen(self): """Generator that returns all related objects.""" return (obj.__of__(self) for obj in self._objects.values()) def objectItems(self, spec=None): """over ride to only return owned objects for many to many rel""" if spec: if isinstance(spec, basestring): spec = [spec] return [(key,value.__of__(self)) \ for (key,value) in self._objects.items() \ if value.meta_type in spec] return [(key,value.__of__(self)) \ for (key,value) in self._objects.items()] objectItemsAll = objectItems #FIXME - need to make this work # def all_meta_types(self, interfaces=None): # mts = [] # for mt in ToManyRelationshipBase.all_meta_types(self, interfaces): # if (mt.has_key('instance') and mt['instance']): # for cl in self.sub_classes: # if checkClass(mt['instance'], cl): # mts.append(mt) # return mts def _getCopy(self, container): """ make new relation add copies of contained objs and refs if the relation is a many to many """ rel = self.__class__(self.id) rel.__primary_parent__ = container rel = rel.__of__(container) norelcopy = getattr(self, 'zNoRelationshipCopy', []) if self.id in norelcopy: return rel for oobj in self.objectValuesAll(): cobj = oobj._getCopy(rel) rel._setObject(cobj.id, cobj) return rel def checkValidId(self, id): """ Is this a valid id for this container? """ try: checkValidId(self, id) except: raise else: return True def exportXml(self, ofile, ignorerels=[]): """Return an xml representation of a ToManyContRelationship <tomanycont id='interfaces'> <object id='hme0' module='Products.Confmon.IpInterface' class='IpInterface'> <property></property> etc.... </object> </tomanycont> """ if self.countObjects() == 0: return ofile.write("<tomanycont id='%s'>\n" % self.id) for obj in self.objectValues(): obj.exportXml(ofile, ignorerels) ofile.write("</tomanycont>\n") def checkRelation(self, repair=False): """Check to make sure that relationship bidirectionality is ok. """ if len(self._objects): log.debug("checking relation: %s", self.id) else: return # look for objects that don't point back to us # or who should no longer exist in the database remoteName = self.remoteName() parentObject = self.getPrimaryParent() for obj in self._objects.values(): if not hasattr(obj, remoteName): path = parentObject.getPrimaryUrlPath() if repair: log.warn( "Deleting %s object '%s' relation '%s' (missing remote relation '%s')", path, obj, self.id, remoteName) self._remove(obj, True) continue else: msg = "%s object '%s' relation '%s' missing remote relation '%s'" % ( path, obj, self.id, remoteName) raise AttributeError(msg) rrel = getattr(obj, remoteName) if not rrel.hasobject(parentObject): log.error("remote relation %s doesn't point back to %s", rrel.getPrimaryId(), self.getPrimaryId()) if repair: log.warn("reconnecting relation %s to relation %s", rrel.getPrimaryId(), self.getPrimaryId()) rrel._add(parentObject)