[Checkins] SVN: lovely.tag/trunk/ Initial import from Lovely Systems repository

Jodok Batlogg jodok.batlogg at lovelysystems.com
Sat Aug 12 09:06:22 EDT 2006


Log message for revision 69423:
  Initial import from Lovely Systems repository

Changed:
  A   lovely.tag/trunk/
  A   lovely.tag/trunk/setup.py
  A   lovely.tag/trunk/src/
  A   lovely.tag/trunk/src/lovely/
  A   lovely.tag/trunk/src/lovely/__init__.py
  A   lovely.tag/trunk/src/lovely/tag/
  A   lovely.tag/trunk/src/lovely/tag/README.txt
  A   lovely.tag/trunk/src/lovely/tag/SETUP.cfg
  A   lovely.tag/trunk/src/lovely/tag/__init__.py
  A   lovely.tag/trunk/src/lovely/tag/browser/
  A   lovely.tag/trunk/src/lovely/tag/browser/README.txt
  A   lovely.tag/trunk/src/lovely/tag/browser/__init__.py
  A   lovely.tag/trunk/src/lovely/tag/browser/linkedtagcloud.pt
  A   lovely.tag/trunk/src/lovely/tag/browser/tag.py
  A   lovely.tag/trunk/src/lovely/tag/browser/tag.zcml
  A   lovely.tag/trunk/src/lovely/tag/browser/tagcloud.pt
  A   lovely.tag/trunk/src/lovely/tag/configure.zcml
  A   lovely.tag/trunk/src/lovely/tag/engine.py
  A   lovely.tag/trunk/src/lovely/tag/ftesting.zcml
  A   lovely.tag/trunk/src/lovely/tag/ftests.py
  A   lovely.tag/trunk/src/lovely/tag/interfaces.py
  A   lovely.tag/trunk/src/lovely/tag/lovely.tag-configure.zcml
  A   lovely.tag/trunk/src/lovely/tag/sampledata.py
  A   lovely.tag/trunk/src/lovely/tag/sampledata.zcml
  A   lovely.tag/trunk/src/lovely/tag/stresstest.txt
  A   lovely.tag/trunk/src/lovely/tag/tag.py
  A   lovely.tag/trunk/src/lovely/tag/tagging.py
  A   lovely.tag/trunk/src/lovely/tag/tags.data
  A   lovely.tag/trunk/src/lovely/tag/tests.py
  A   lovely.tag/trunk/src/lovely/tag/users.data

-=-
Added: lovely.tag/trunk/setup.py
===================================================================
--- lovely.tag/trunk/setup.py	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/setup.py	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,20 @@
+#!python
+from setuptools import setup, find_packages
+
+setup(name='lovely.tag',
+      version='0.1',
+      author = "Stephan Richter, Jodok Batlogg",
+      author_email = "srichter at cosmos.phy.tufts.edu, jodok.batlogg at lovelysystems.com",
+      description = "A tagging engine for zope 3",
+      license = "ZPL 2.1",
+      keywords = "zope3 web20 zope tagging",
+      url='svn://svn.zope.org/repos/main/lovely.tag',
+
+      packages=find_packages('src'),
+      include_package_data=True,
+      package_dir = {'':'src'},
+      namespace_packages=['lovely'],
+      install_requires = ['setuptools', ],
+      dependency_links = ['https://backstage.lovelysystems.com/software/eggs'],
+     )
+


Property changes on: lovely.tag/trunk/setup.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/__init__.py
===================================================================
--- lovely.tag/trunk/src/lovely/__init__.py	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/__init__.py	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,5 @@
+# this is a namespace package
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    pass


Property changes on: lovely.tag/trunk/src/lovely/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/README.txt
===================================================================
--- lovely.tag/trunk/src/lovely/tag/README.txt	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/README.txt	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,373 @@
+=======
+Tagging
+=======
+
+A tagging engine allows you to assign tags to any type of object by an user. A
+tag is a simple string.
+
+  >>> from lovely import tag
+
+Tagging Engine
+--------------
+
+The tagging engine provides the capabilities to manipulate and and query
+tagged items.
+
+  >>> engine = tag.TaggingEngine()
+  >>> engine
+  <TaggingEngine entries=0>
+
+The first step is to associate tags with an item for a user. Items are
+referenced by id, the user is a system-wide unique string and the tags is a
+simple list of strings.
+
+Before updating the engine we need to ensure that persistent objects can be
+adapted to key references:
+
+  >>> import zope.component
+  >>> from zope.app.keyreference import testing
+
+  >>> zope.component.provideAdapter(testing.SimpleKeyReference)
+
+
+Instead providing a separate API for adding and updating tags, both actions
+are done via the ``update()`` method. Think of it as updating the tagging
+engine.
+
+  >>> engine.update(1, u'srichter', [u'USA', u'personal'])
+  >>> engine.update(2, u'srichter', [u'austria', u'lovely'])
+  >>> engine.update(3, u'jodok', [u'Austria', u'personal'])
+  >>> engine.update(2, u'jodok', [u'austria', u'lovely', u'work'])
+
+Next you can ask the engine several questions.
+
+Querying for Tags
+~~~~~~~~~~~~~~~~~
+
+A common request is to ask for tags based on items and users. First, you can
+ask for all tags for a particular item:
+
+  >>> sorted(engine.getTags(items=(1,)))
+  [u'USA', u'personal']
+
+Note: The query methods return sets.
+
+  >>> type(engine.getTags())
+  <type 'set'>
+
+The method always returns the normalized tag strings. You can also specify
+several items:
+
+  >>> sorted(engine.getTags(items=(1, 2)))
+  [u'USA', u'austria', u'lovely', u'personal', u'work']
+
+You can also ask for tags of a user:
+
+  >>> sorted(engine.getTags(users=(u'srichter',)))
+  [u'USA', u'austria', u'lovely', u'personal']
+
+Again, you can specify multiple users:
+
+  >>> sorted(engine.getTags(users=(u'srichter', u'jodok')))
+  [u'Austria', u'USA', u'austria', u'lovely', u'personal', u'work']
+
+Finally, you can also specify a combination of both:
+
+  >>> sorted(engine.getTags(items=(1,), users=(u'srichter',)))
+  [u'USA', u'personal']
+  >>> sorted(engine.getTags(items=(3,), users=(u'srichter',)))
+  []
+
+You can also query all tags by not specifying items or users:
+
+  >>> sorted(engine.getTags())
+  [u'Austria', u'USA', u'austria', u'lovely', u'personal', u'work']
+
+
+Querying for Items
+~~~~~~~~~~~~~~~~~~
+
+This method allows to look for items. For example, we would like to find all
+items that have the "personal" tag:
+
+  >>> sorted(engine.getItems(tags=(u'personal',)))
+  [1, 3]
+
+Note: The query methods return sets.
+
+  >>> type(engine.getItems())
+  <type 'set'>
+
+Furthermore, you can query for all items of a particular user:
+
+  >>> sorted(engine.getItems(users=(u'srichter',)))
+  [1, 2]
+  >>> sorted(engine.getItems(users=(u'srichter', u'jodok')))
+  [1, 2, 3]
+
+Finally, you can combine tag and user specifications:
+
+  >>> sorted(engine.getItems(
+  ...     tags=(u'personal',), users=(u'srichter', u'jodok')))
+  [1, 3]
+
+You can also query all items by not specifying tags or users:
+
+  >>> sorted(engine.getItems())
+  [1, 2, 3]
+
+
+Querying for Users
+~~~~~~~~~~~~~~~~~~
+
+Similar to the two methods above, you can query for users. First we are
+looking for all users specifying a particular tag.
+
+  >>> sorted(engine.getUsers(tags=(u'personal',)))
+  [u'jodok', u'srichter']
+  >>> sorted(engine.getUsers(tags=(u'Austria',)))
+  [u'jodok']
+
+Note: The query methods return sets.
+
+  >>> type(engine.getUsers())
+  <type 'set'>
+
+Next you can also find all items that that have been tagged by a user:
+
+  >>> sorted(engine.getUsers(items=(1,)))
+  [u'srichter']
+  >>> sorted(engine.getUsers(items=(2,)))
+  [u'jodok', u'srichter']
+
+As before you can combine the two criteria as well:
+
+  >>> sorted(engine.getUsers(tags=(u'USA',), items=(1,)))
+  [u'srichter']
+  >>> sorted(engine.getUsers(tags=(u'personal',), items=(1, 3)))
+  [u'jodok', u'srichter']
+
+You can also query all users by not specifying tags or items:
+
+  >>> sorted(engine.getUsers())
+  [u'jodok', u'srichter']
+
+
+Combining Queries
+-----------------
+
+Since those query methods return sets, you can easily combine them:
+
+  >>> users1 = engine.getUsers(items=(1,))
+  >>> users2 = engine.getUsers(items=(2,))
+  >>> sorted(users1.intersection(users2))
+  [u'srichter']
+
+
+Changing and deleting Entries
+-----------------------------
+
+"srichter" moved from USA to Germany:
+
+  >>> engine.update(1, u'srichter', [u'Germany', u'personal'])
+  >>> sorted(engine.getTags(items=(1,), users=(u'srichter',)))
+  [u'Germany', u'personal']
+
+
+We delete entries by passing an empty list to the update method:
+
+  >>> engine.update(1, u'srichter', [])
+  >>> sorted(engine.getTags(items=(1,)))
+  []
+  >>> sorted(engine.getTags())
+  [u'Austria', u'austria', u'lovely', u'personal', u'work']
+  >>> sorted(engine.getItems())
+  [2, 3]
+
+Now let's delete the tags of the second item. We want to be sure that
+"srichter" can't be found anymore:
+
+  >>> engine.update(2, u'srichter', [])
+  >>> sorted(engine.getUsers())
+  [u'jodok']
+
+
+Tag Object
+----------
+
+Internally, the tagging engine uses the ``Tag`` class to store all data about
+one particular item, user and tag names pair.
+
+  >>> from lovely.tag.tag import Tag
+
+The ``Tag`` object is initialized with the three pieces information mentioned
+above.
+
+  >>> sample = Tag(1, u'user', u'tag1')
+  >>> sample
+  <Tag u'tag1' for 1 by u'user'>
+
+You can also think of those three items as the unique key of the
+tag. Additionally to those three attributes, a creation date is also
+specified:
+
+  >>> sample.item
+  1
+  >>> sample.user
+  u'user'
+  >>> sample.name
+  u'tag1'
+  >>> sample.timestamp
+  datetime.datetime(...)
+
+
+Taggable Objects
+----------------
+
+Theoretically all objects are taggable. But this might not be desirable. Thus
+objects must provide the ``ITaggable`` interface to be taggable.
+
+  >>> import zope.interface
+
+  >>> class Image(object):
+  ...     zope.interface.implements(tag.interfaces.ITaggable)
+  >>> image = Image()
+
+  >>> class File(object):
+  ...     pass
+  >>> file = File()
+
+Taggable objects can then be adapted to the ``ITagging`` interface. For this
+to work we have to register the adapter:
+
+  >>> zope.component.provideAdapter(tag.Tagging)
+
+Before we can now use the tagging object, we need to register our tagging
+engine as well as the integer id generator as a utility:
+
+  >>> zope.component.provideUtility(engine, tag.interfaces.ITaggingEngine)
+
+  >>> from zope.app import intid
+  >>> zope.component.provideUtility(intid.IntIds(), intid.interfaces.IIntIds)
+
+Adapting the file to be tagged should fail:
+
+  >>> tag.interfaces.ITagging(file)
+  Traceback (most recent call last):
+  ...
+  TypeError: ('Could not adapt', <File ...>, <InterfaceClass ...ITagging>)
+
+But images can be tagged:
+
+  >>> tagging = tag.interfaces.ITagging(image)
+
+At first there are no tags for the image:
+
+  >>> sorted(tagging.getTags())
+  []
+
+Let's now have "srichter" and "jodok" add a few tags:
+
+  >>> tagging.update(u'srichter', [u'home', u'USA'])
+  >>> tagging.update(u'jodok', [u'vacation', u'USA'])
+
+  >>> sorted(tagging.getTags())
+  [u'USA', u'home', u'vacation']
+
+Of course, you can also ask just for the tags by "srichter":
+
+  >>> sorted(tagging.getTags(users=[u'srichter']))
+  [u'USA', u'home']
+
+Further you can request to see all users that have tagged the image:
+
+  >>> sorted(tagging.getUsers())
+  [u'jodok', u'srichter']
+
+or all users that have specified a particular tag:
+
+  >>> sorted(tagging.getUsers(tags=(u'home',)))
+  [u'srichter']
+  >>> sorted(tagging.getUsers(tags=(u'USA',)))
+  [u'jodok', u'srichter']
+
+
+Tag Clouds
+----------
+
+All portals like Flickr, del.icio.us use tagging and generate tag clouds.
+Tag clouds contain tags and their frequency.
+
+The ``getCloud`` method returns a set of tuples in the form of 
+('tag', frequency). 
+
+  >>> type(engine.getCloud())
+  <type 'set'>
+
+Now let's add some tags to generate clouds later:
+
+  >>> engine.update(3, u'michael', [u'Austria', u'Bizau'])
+  >>> engine.update(2, u'michael', [u'lovely', u'USA'])
+  >>> engine.update(1, u'jodok', [u'USA',])
+
+
+The most common use-case is to generate a global tag cloud.
+
+  >>> sorted(engine.getCloud())
+  [(u'Austria', 2), (u'Bizau', 1), (u'USA', 4), (u'austria', 1), (u'home', 1), 
+   (u'lovely', 2), (u'personal', 1), (u'vacation', 1), (u'work', 1)]
+
+
+Of course you can generate clouds on item basis. You can't pass a tuple of
+items, only a single one is allowed:
+
+  >>> sorted(engine.getCloud(item=1))
+  [(u'USA', 1)]
+  
+The same applies to queries by user:
+
+  >>> sorted(engine.getCloud(user=u'srichter'))
+  [(u'USA', 1), (u'home', 1)]
+  
+It makes no sense to combine user and item. This will never be a cloud.
+
+  >>> engine.getCloud(item=1, user=u'srichter')
+  Traceback (most recent call last):
+  ...
+  ValueError: You cannot specify both, an item and an user.
+
+
+Related Tags
+------------
+
+An advanced feature of the tagging engine is to find all tags that are related
+to a given tag.
+
+  >>> sorted(engine.getRelatedTags(u'austria'))
+  [u'lovely', u'work']
+
+By default the method only searches for the first degree related tags. You can
+also search for other degrees:
+
+  >>> engine.update(4, u'jodok', [u'lovely', u'dornbirn', u'personal'])
+  >>> sorted(engine.getRelatedTags(u'austria', degree=2))
+  [u'USA', u'dornbirn', u'lovely', u'personal', u'work']
+
+  >>> engine.update(4, u'jodok', [u'lovely', u'dornbirn', u'personal'])
+  >>> sorted(engine.getRelatedTags(u'austria', degree=3))
+  [u'Austria', u'USA', u'dornbirn', u'home', u'lovely', u'personal', 
+   u'vacation', u'work']
+
+Frequency Of Tags
+-----------------
+
+If we have a list of tags we can ask for the frequencies of the tags.
+
+  >>> sorted(engine.getFrequency([u'Austria', u'USA']))
+  [(u'Austria', 2), (u'USA', 4)] 
+
+We get a frequency of 0 if we ask for a tag which is not in the engine.
+
+  >>> sorted(engine.getFrequency([u'Austria', u'jukart', u'USA']))
+  [(u'Austria', 2), (u'USA', 4), (u'jukart', 0)] 
+


Property changes on: lovely.tag/trunk/src/lovely/tag/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/SETUP.cfg
===================================================================
--- lovely.tag/trunk/src/lovely/tag/SETUP.cfg	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/SETUP.cfg	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,3 @@
+<data-files zopeskel/etc/package-includes>
+  lovely.tag-*.zcml
+</data-files>

Added: lovely.tag/trunk/src/lovely/tag/__init__.py
===================================================================
--- lovely.tag/trunk/src/lovely/tag/__init__.py	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/__init__.py	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,9 @@
+# Make a package.
+
+import zope.i18nmessageid
+
+from engine import TaggingEngine
+from tagging import Tagging
+
+_ = zope.i18nmessageid.MessageFactory('feed')
+


Property changes on: lovely.tag/trunk/src/lovely/tag/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/browser/README.txt
===================================================================
--- lovely.tag/trunk/src/lovely/tag/browser/README.txt	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/browser/README.txt	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,41 @@
+=============
+Tagging Views
+=============
+
+We provide default views which are relevant for tagging.
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.addHeader('Authorization','Basic mgr:mgrpw')
+  >>> browser.handleErrors = False
+
+  >>> browser.open('http://localhost/@@managesamples.html')
+  >>> browser.getLink(text='tagbrowsertest').click()
+  >>> browser.getControl(name='lovely.sampledata.site.sitename').value = 'tags'
+  >>> browser.getControl('Generate').click()
+
+Tag cloud
+---------
+
+Shows a tag cloud.
+
+  >>> browser.open('http://localhost/tags/@@tagCloud')
+  >>> print browser.contents
+  <div class="tagCloud">
+   <span class="tag1">1970s(1)</span>
+   ...
+  </div>
+
+
+Related tag cloud
+-----------------
+
+Shows a tag cloud of all related tags of a tag.
+
+  >>> browser.open('http://localhost/tags/@@relatedTagCloud?tagname=1970s')
+  >>> print browser.contents
+  <div class="tagCloud">
+   <span class="tag1">city(1)</span>
+   ...
+  </div>
+


Property changes on: lovely.tag/trunk/src/lovely/tag/browser/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/browser/__init__.py
===================================================================


Property changes on: lovely.tag/trunk/src/lovely/tag/browser/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/browser/linkedtagcloud.pt
===================================================================
--- lovely.tag/trunk/src/lovely/tag/browser/linkedtagcloud.pt	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/browser/linkedtagcloud.pt	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,8 @@
+<div class="tagCloud">
+    <a class="tag" href=""
+       tal:repeat="tag view/getCloud"
+       tal:attributes="class string:tag${tag/normalized};
+                       href string:?tagname=${tag/name}
+                      "
+       tal:content="string:${tag/name}(${tag/frequency})"/>
+</div>


Property changes on: lovely.tag/trunk/src/lovely/tag/browser/linkedtagcloud.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/browser/tag.py
===================================================================
--- lovely.tag/trunk/src/lovely/tag/browser/tag.py	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/browser/tag.py	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,146 @@
+##############################################################################
+#
+# Copyright (c) 2006 Lovely Systems and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tagging Views
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+from zope.publisher.browser import BrowserView
+from zope import component, schema
+from lovely.tag.interfaces import ITaggingEngine, ITagging
+from zope.cachedescriptors.property import Lazy
+from zope.app.pagetemplate import ViewPageTemplateFile
+from zope.formlib import form
+from lovely.tag import _
+import pytz
+import datetime
+from zope.interface.common import idatetime
+
+
+class TaggingMixin(object):
+
+    @Lazy
+    def engine(self):
+        return component.getUtility(ITaggingEngine)
+
+    @Lazy
+    def tagging(self):
+        return ITagging(self.context)
+
+    
+class TaggingView(BrowserView, TaggingMixin):
+    """Show the tags of the context as a cloud"""
+
+    cloud       = ViewPageTemplateFile('tagcloud.pt')
+    linkedcloud = ViewPageTemplateFile('linkedtagcloud.pt')
+    
+    def __init__(self, context, request):
+        super(TaggingView, self).__init__(context, request)
+
+    def getCloud(self, maxTags=100):
+        """returns a tag cloud"""
+        cloud = self.engine.getCloud()
+        return normalize(cloud, maxTags)
+
+    
+class RelatedView(BrowserView, TaggingMixin):
+    """Show related tags as a cloud"""
+
+    cloud       = ViewPageTemplateFile('tagcloud.pt')
+    linkedcloud = ViewPageTemplateFile('linkedtagcloud.pt')
+    
+    def __init__(self, context, request):
+        super(RelatedView, self).__init__(context, request)
+
+    def getCloud(self, tag=None, maxTags=100):
+        """returns a tag cloud"""
+        if tag is None:
+            tag = self.request.get('tagname',None)
+        if tag is None:
+            return []
+        cloud = self.engine.getFrequency(self.engine.getRelatedTags(tag))
+        return normalize(cloud, maxTags)
+
+
+def normalize(cloud, maxTags=100):
+    if len(cloud) == 0:
+        return []
+    minmax = sorted(cloud, key=lambda i: i[1],reverse=True)
+
+    end = min(maxTags,len(minmax))
+    if end == 0:
+        return []
+    minmax = minmax[:end]
+    minFreq = minmax[-1][1]
+    maxFreq = minmax[0][1]
+    freqRange = maxFreq-minFreq
+    if freqRange>0:
+        ratio = 6.0/freqRange
+    else:
+        ratio = None
+    res = []
+    for tag, frequency in sorted(cloud):
+        if ratio is None:
+            normalized=1
+        else:
+            normalized = int(float(frequency-minFreq)*ratio) +1
+        res.append(dict(name=tag,
+                        normalized=normalized,
+                        frequency=frequency,))
+    return res
+
+
+class UserTagForm(form.EditForm, TaggingMixin):
+
+    form_fields = form.Fields(
+        schema.TextLine(__name__='tags', title=_("Tags")))
+
+    def setUpWidgets(self, ignore_request=False):
+        # override to add existing tags to the field
+        tags = u' '.join(self.tagging.getTags(
+            [self.request.principal.id]))
+        self.widgets = form.setUpWidgets(
+            self.form_fields, self.prefix, self.context, self.request,
+            data=dict(tags=tags),
+            ignore_request=ignore_request
+            )
+
+    @form.action(_("Apply"))
+    def handle_edit_action(self, action, data):
+        tags = data.get('tags','')
+        tags = set(tags.split())
+        
+        user = self.request.principal.id
+        oldTags = self.tagging.getTags(user)
+        if oldTags != tags:
+            self.tagging.update(user, tags)
+            formatter = self.request.locale.dates.getFormatter(
+                            'dateTime', 'medium')
+            try:
+                time_zone = idatetime.ITZInfo(self.request)
+            except TypeError:
+                time_zone = pytz.UTC
+            status = _("Updated on ${date_time}",
+                       mapping={'date_time':
+                                formatter.format(
+                                   time_zone.normalize(datetime.datetime.now())
+                                )
+                               }
+                      )
+            self.status = status
+        else:
+            self.status = _('No changes')
+
+


Property changes on: lovely.tag/trunk/src/lovely/tag/browser/tag.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/browser/tag.zcml
===================================================================
--- lovely.tag/trunk/src/lovely/tag/browser/tag.zcml	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/browser/tag.zcml	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,47 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:browser="http://namespaces.zope.org/browser"
+           xmlns:i18n="http://namespaces.zope.org/i18n"
+           i18n_domain="lovely.mediaportal">
+
+ <browser:page
+  for="*"
+  permission="zope.Public"
+  class=".tag.TaggingView"
+  name="tagCloud"
+  attribute="cloud"
+  />
+
+ <browser:page
+  for="*"
+  permission="zope.Public"
+  class=".tag.TaggingView"
+  name="linkedTagCloud"
+  attribute="linkedcloud"
+  />
+
+ <browser:page
+  for="*"
+  permission="zope.Public"
+  class=".tag.RelatedView"
+  name="relatedTagCloud"
+  attribute="cloud"
+  />
+
+ <browser:page
+  for="*"
+  permission="zope.Public"
+  class=".tag.RelatedView"
+  name="linkedRelatedTagCloud"
+  attribute="linkedcloud"
+  />
+
+
+ <!-- XXX fix permission lovely.tag.UpdateTag -->
+ <browser:page
+  for="lovely.tag.interfaces.ITaggable"
+  permission="zope.Public"
+  class=".tag.UserTagForm"
+  name="editTags.html"
+  />
+ 
+</configure>


Property changes on: lovely.tag/trunk/src/lovely/tag/browser/tag.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/browser/tagcloud.pt
===================================================================
--- lovely.tag/trunk/src/lovely/tag/browser/tagcloud.pt	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/browser/tagcloud.pt	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,5 @@
+<div class="tagCloud">
+ <span class="tag" tal:repeat="tag view/getCloud"
+       tal:attributes="class string:tag${tag/normalized}"
+       tal:content="string:${tag/name}(${tag/frequency})"/>
+</div>


Property changes on: lovely.tag/trunk/src/lovely/tag/browser/tagcloud.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/configure.zcml
===================================================================
--- lovely.tag/trunk/src/lovely/tag/configure.zcml	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/configure.zcml	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,54 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain="lovely.tag">
+
+  <!-- Permission declarations -->
+
+  <permission
+      id="lovely.tag.UpdateTag"
+      title="Update Tag"
+      description="Allow updating of any tags."
+      />
+
+  <permission
+      id="lovely.tag.AccessTag"
+      title="Access Tag"
+      description="Allow accessing any tags related statistics."
+      />
+
+  <!-- Tagging Engine Setup -->
+
+  <class class=".engine.TaggingEngine">
+    <implements
+        interface="zope.annotation.interfaces.IAttributeAnnotatable"
+        />
+    <require
+        permission="lovely.tag.UpdateTag"
+        attributes="update"
+        />
+    <require
+        permission="lovely.tag.AccessTag"
+        attributes="getTags getItems getUsers getCloud getRelatedTags"
+        />
+  </class>
+
+  <!-- Tagging adapter for taggable objects. -->
+
+  <class class=".tagging.Tagging">
+    <require
+        permission="lovely.tag.UpdateTag"
+        attributes="update"
+        />
+    <require
+        permission="lovely.tag.AccessTag"
+        attributes="getTags getUsers"
+        />
+  </class>
+
+  <adapter
+      factory=".tagging.Tagging"
+      trusted="True"
+      locate="True"
+      />
+
+</configure>


Property changes on: lovely.tag/trunk/src/lovely/tag/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/engine.py
===================================================================
--- lovely.tag/trunk/src/lovely/tag/engine.py	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/engine.py	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,237 @@
+##############################################################################
+#
+# Copyright (c) 2006 Lovely Systems and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tagging Engine Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import persistent
+import persistent.list
+import zope.interface
+from BTrees import IOBTree, OOBTree
+from zope.app.container import contained
+from zope.app import intid
+
+from lovely.tag import interfaces, tag
+
+class TaggingEngine(persistent.Persistent, contained.Contained):
+    zope.interface.implements(interfaces.ITaggingEngine)
+
+    def __init__(self):
+        super(TaggingEngine, self).__init__()
+        self._reset()
+
+    def _reset(self):
+        # Used purely to provide a persistence reference for the tag objects
+        self._tags = persistent.list.PersistentList()
+
+        # Used to generate ids for tag objects
+        self._tag_ids = intid.IntIds()
+        # Our indeces for efficient querying
+        self._user_to_tagids = OOBTree.OOBTree()
+        self._item_to_tagids = IOBTree.IOBTree()
+        self._name_to_tagids = OOBTree.OOBTree()
+
+    def update(self, item, user, tags):
+        """See interfaces.ITaggingEngine"""
+        tags_item = set(self._item_to_tagids.get(item, ()))
+        tags_user = set(self._user_to_tagids.get(user, ()))
+
+        old_tag_ids = tags_item.intersection(tags_user)
+        old_tags = set([self._tag_ids.getObject(id)
+                        for id in old_tag_ids])
+
+        new_tags = set([tag.Tag(item, user, tagName)
+                        for tagName in tags])
+
+        add_tags = new_tags.difference(old_tags)
+        del_tags = old_tags.difference(new_tags)
+
+        for tagObj in add_tags:
+            self._tags.append(tagObj)
+            # set the __parent__ in order to get a _p_oid for the object
+            tagObj.__parent__ = self
+            id = self._tag_ids.register(tagObj)
+
+            ids = self._user_to_tagids.get(user)
+            if ids is None:
+                self._user_to_tagids[user] = IOBTree.IOSet((id,))
+            else:
+                ids.insert(id)
+
+            ids = self._item_to_tagids.get(item)
+            if ids is None:
+                self._item_to_tagids[item] = IOBTree.IOSet((id,))
+            else:
+                ids.insert(id)
+
+            ids = self._name_to_tagids.get(tagObj.name)
+            if ids is None:
+                self._name_to_tagids[tagObj.name] = IOBTree.IOSet((id,))
+            else:
+                ids.insert(id)
+
+        for tagObj in del_tags:
+            id = self._tag_ids.getId(tagObj)
+            self._tag_ids.unregister(tagObj)
+            self._tags.remove(tagObj)
+
+            self._user_to_tagids[tagObj.user].remove(id)
+            if not len(self._user_to_tagids[tagObj.user]):
+                del self._user_to_tagids[tagObj.user]
+
+            self._item_to_tagids[tagObj.item].remove(id)
+            if not len(self._item_to_tagids[tagObj.item]):
+                del self._item_to_tagids[tagObj.item]
+
+            self._name_to_tagids[tagObj.name].remove(id)
+            if not len(self._name_to_tagids[tagObj.name]):
+                del self._name_to_tagids[tagObj.name]
+
+
+    def getTags(self, items=None, users=None):
+        """See interfaces.ITaggingEngine"""
+        if items is None and users is None:
+            return set(self._name_to_tagids.keys())
+
+        if items is not None:
+            items_result = set()
+            for item in items:
+                items_result.update(self._item_to_tagids.get(item, set()))
+
+        if users is not None:
+            users_result = set()
+            for user in users:
+                users_result.update(self._user_to_tagids.get(user, set()))
+
+        if items is None:
+            result = users_result
+        elif users is None:
+            result = items_result
+        else:
+            result = items_result.intersection(users_result)
+
+        return set([self._tag_ids.getObject(id).name for id in result])
+
+
+    def getItems(self, tags=None, users=None):
+        """See interfaces.ITaggingEngine"""
+        if tags is None and users is None:
+            return set(self._item_to_tagids.keys())
+
+        if tags is not None:
+            tags_result = set()
+            for name in tags:
+                tags_result.update(self._name_to_tagids.get(name, set()))
+
+        if users is not None:
+            users_result = set()
+            for user in users:
+                users_result.update(self._user_to_tagids.get(user, set()))
+
+        if tags is None:
+            result = users_result
+        elif users is None:
+            result = tags_result
+        else:
+            result = tags_result.intersection(users_result)
+
+        return set([self._tag_ids.getObject(id).item for id in result])
+
+
+    def getUsers(self, tags=None, items=None):
+        """See interfaces.ITaggingEngine"""
+        if tags is None and items is None:
+            return set(self._user_to_tagids.keys())
+
+        if tags is not None:
+            tags_result = set()
+            for name in tags:
+                tags_result.update(self._name_to_tagids.get(name, set()))
+
+        if items is not None:
+            items_result = set()
+            for item in items:
+                items_result.update(self._item_to_tagids.get(item, set()))
+
+        if tags is None:
+            result = items_result
+        elif items is None:
+            result = tags_result
+        else:
+            result = tags_result.intersection(items_result)
+
+        return set([self._tag_ids.getObject(id).user for id in result])
+
+
+    def getRelatedTags(self, tag, degree=1):
+        """See interfaces.ITaggingEngine"""
+        result = set()
+        degree_counter = 1
+        previous_degree_tags = set([tag])
+        degree_tags = set()
+        while degree_counter <= degree:
+            for cur_name in previous_degree_tags:
+                tagids = self._name_to_tagids.get(cur_name, ())
+                for tagid in tagids:
+                    tag_obj = self._tag_ids.getObject(tagid)
+                    degree_tags.update(self.getTags(
+                        items=(tag_obj.item,), users=(tag_obj.user,) ))
+            # After all the related tags of this degree were found, update the
+            # result set and clean up the variables for the next round.
+            result.update(degree_tags)
+            previous_degree_tags = degree_tags
+            degree_tags = set()
+            degree_counter += 1
+        # Make sure the original is not included
+        if tag in result:
+            result.remove(tag)
+        return result
+
+    def getCloud(self, item=None, user=None):
+        """See interfaces.ITaggingEngine"""
+        if item is not None and user is not None:
+            raise ValueError('You cannot specify both, an item and an user.')
+
+        if item is None and user is None:
+            return set([(name, len(tags))
+                        for name, tags in self._name_to_tagids.items()])
+
+        if item is not None:
+            ids = self._item_to_tagids.get(item, [])
+
+        if user is not None:
+            ids = self._user_to_tagids.get(user, [])
+
+        result = {}
+        for id in ids:
+            tagObj = self._tag_ids.getObject(id)
+            result.setdefault(tagObj.name, 0)
+            result[tagObj.name] += 1
+
+        return set(result.items())
+
+    def getFrequency(self, tags):
+        """See interfaces.ITaggingEngine"""
+        result = {}
+        for tag in tags:
+            frequency = 0
+            if tag in self._name_to_tagids:
+                frequency = len(self._name_to_tagids[tag])
+            result[tag] = frequency
+        return set(result.items())
+
+    def __repr__(self):
+        return '<%s entries=%i>' %(self.__class__.__name__, len(self._tags))


Property changes on: lovely.tag/trunk/src/lovely/tag/engine.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/ftesting.zcml
===================================================================
--- lovely.tag/trunk/src/lovely/tag/ftesting.zcml	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/ftesting.zcml	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,80 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:browser="http://namespaces.zope.org/browser"
+           xmlns:meta="http://namespaces.zope.org/meta"
+           i18n_domain="zope">
+
+  <!-- Turn on the devmode which is needed for sample data generation -->
+  <meta:provides feature="devmode" />
+
+  <include package="zope.app" />
+
+  <include package="zope.viewlet" file="meta.zcml"/>
+  <include package="zope.app.securitypolicy" file="meta.zcml" />
+
+  <include package="lovely.sampledata" file="meta.zcml"/>
+  
+  <include package="zope.app.server" />
+  <include package="zope.app.authentication" />
+  <securityPolicy
+    component="zope.app.securitypolicy.zopepolicy.ZopeSecurityPolicy" />
+
+  <include package="zope.app.securitypolicy" />
+
+  <role id="zope.Anonymous" title="Everybody"
+        description="All users have this role implicitly" />
+
+  <role id="zope.Manager" title="Site Manager" />
+  <grantAll role="zope.Manager" />
+  
+  <principal
+   id="zope.manager"
+   title="Administrator"
+   login="mgr"
+   password="mgrpw" />
+  <grant
+   role="zope.Manager"
+   principal="zope.manager"
+   />
+  
+  <unauthenticatedPrincipal
+    id="zope.anybody"
+    title="Unauthenticated User" />
+
+  <unauthenticatedGroup
+    id="zope.Anybody"
+    title="Unauthenticated Users" 
+    />
+
+  <authenticatedGroup
+    id="zope.Authenticated"
+    title="Authenticated Users" 
+    />
+
+  <everybodyGroup
+    id="zope.Everybody"
+    title="All Users" 
+    />
+  
+  <include package="zope.app.session" />
+  <include package="zope.app.catalog" />
+  <include package="zope.app.intid" />
+  <include package="zope.app.keyreference" />
+  <include package="zope.contentprovider"/>
+  <include package="zope.viewlet"/>
+  <include package="zope.formlib"/>
+  
+  <include package="lovely.sampledata"/>
+  <include package="lovely.sampledata" file="site.zcml"/>
+
+  <include package="lovely.tag"/>
+  <include package="lovely.tag.browser" file="tag.zcml"/>
+  <include package="lovely.tag" file="sampledata.zcml"/>
+
+  <grant permission="zope.View"
+         role="zope.Anonymous" />
+  <grant permission="lovely.tag.AccessTag"
+         role="zope.Anonymous" />
+  <grant permission="zope.app.dublincore.view"
+         role="zope.Anonymous" />
+  
+</configure>


Property changes on: lovely.tag/trunk/src/lovely/tag/ftesting.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/ftests.py
===================================================================
--- lovely.tag/trunk/src/lovely/tag/ftests.py	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/ftests.py	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,29 @@
+import unittest
+from zope.app.testing import functional
+
+functional.defineLayer('TestLayer', 'ftesting.zcml')
+
+
+def setUp(test):
+    """Setup a reasonable environment for the tag tests"""
+    pass
+
+
+def tearDown(test):
+    pass
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suites = (
+        functional.FunctionalDocFileSuite('browser/README.txt',
+                                          setUp=setUp, tearDown=tearDown,
+                                         ),
+        )
+    for s in suites:
+        s.layer=TestLayer
+        suite.addTest(s)
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')


Property changes on: lovely.tag/trunk/src/lovely/tag/ftests.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/interfaces.py
===================================================================
--- lovely.tag/trunk/src/lovely/tag/interfaces.py	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/interfaces.py	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,128 @@
+##############################################################################
+#
+# Copyright (c) 2006 Lovely Systems and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Interfaces
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.schema
+
+class ITag(zope.interface.Interface):
+    """A single tag."""
+
+    item = zope.schema.Int(
+        title=u'Item',
+        description=u'The item that is tagged.',
+        required=True)
+
+    user = zope.schema.TextLine(
+        title=u'User',
+        description=u'The user id that created the tag.',
+        required=True)
+
+    name = zope.schema.TextLine(
+        title=u'Tag Name',
+        description=u'The tag name the user provided for the item.',
+        required=True)
+
+    timestamp = zope.schema.Datetime(
+        title=u'Timestamp',
+        description=u"The timestamp of the tag's creation date",
+        required=True)
+
+
+class ITaggingEngine(zope.interface.Interface):
+    """Manangement and Querying of Tags.
+
+    Tags are small stickers that are specific to an object to be tageed and
+    the user tagging the object.
+    """
+
+    def update(item, user, tags):
+        """Update the tagging engine for an item and user and its tags.
+
+        The item must be an integer, the user can be a string and the tags
+        argument must be an iterable of tag names (strings).
+
+        This method always overwrites the old tag settings. However, existing
+        tags will not be readded and are just skipped.
+        """
+
+    def getTags(items=None, users=None):
+        """Get all tags matching the specified items and users.
+
+        If no items are specified, match all items. Ditto for users.
+
+        The method returns a set of *normalized* tag names.
+        """
+
+    def getItems(tags=None, users=None):
+        """Get all items matching the specified items and users.
+
+        If no tags are specified, match all tags. Ditto for users.
+
+        The method returns a set of item ids.
+        """
+
+    def getUsers(tags=None, items=None):
+        """Get all items matching the specified tags and items.
+
+        If no tags are specified, match all tags. Ditto for items.
+
+        The method returns a set of user strings.
+        """
+
+    def getRelatedTags(tag, degree=1):
+        """Get a set of all related tags."""
+
+    def getCloud(item=None, user=None):
+        """Get a set of tuples in the form of ('tag', frequency)."""
+
+    def getFrequency(tags):
+        """Get the frequency of all tags
+
+        Returns tuples in the form of ('tag', frequency)
+        """
+
+
+class ITaggable(zope.interface.Interface):
+    """A marker interface to declare an object as being able to be tagged."""
+
+
+class ITagging(zope.interface.Interface):
+    """Provides a tagging API for a single item.
+
+    Effectively, this interface provides a simplification (special case) of
+    the tagging engine API.
+    """
+
+    def update(user, tags):
+        """Update the tags of an item.
+
+        See ``ITaggingEngine.update()`` for more information.
+        """
+
+    def getTags(users=None):
+        """Get all tags matching the specified users.
+
+        See ``ITaggingEngine.getTags()`` for more information.
+        """
+
+    def getUsers(tags=None):
+        """Get all items matching the specified tags.
+
+        See ``ITaggingEngine.getUsers()`` for more information.
+        """


Property changes on: lovely.tag/trunk/src/lovely/tag/interfaces.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/lovely.tag-configure.zcml
===================================================================
--- lovely.tag/trunk/src/lovely/tag/lovely.tag-configure.zcml	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/lovely.tag-configure.zcml	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1 @@
+<include package="lovely.tag" />


Property changes on: lovely.tag/trunk/src/lovely/tag/lovely.tag-configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/sampledata.py
===================================================================
--- lovely.tag/trunk/src/lovely/tag/sampledata.py	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/sampledata.py	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,87 @@
+##############################################################################
+#
+# Copyright (c) 2006 Lovely Systems and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Sample Data for Tagging
+
+$Id$
+"""
+import os
+import random
+
+from zope import interface
+from zope import component
+from zope.app.component import hooks
+
+from lovely.sampledata.interfaces import ISampleDataPlugin
+from lovely import tag
+
+LOCAL_DIR = os.path.dirname(__file__)
+
+from lovely import tag
+
+
+def generate(max_tags=100000, engine=None, seed=0):
+
+    rand = random.Random(seed)
+
+    tag_data = file(os.path.join(LOCAL_DIR, 'tags.data'), 'r').read().split()
+    tag_data = [unicode(name) for name in tag_data]
+
+    user_data = file(os.path.join(LOCAL_DIR, 'users.data'), 'r').read().split()
+    user_data = [unicode(user) for user in user_data]
+
+    if engine is None:
+        engine = tag.TaggingEngine()
+
+    cur_tags = 0
+    obj_num = 0
+    while cur_tags < max_tags:
+        obj_num += 1
+        user_num = rand.randint(20, 30)
+        for user in rand.sample(user_data, user_num):
+            tags_num = rand.randint(1, 7)
+            cur_tags += tags_num
+            tags = rand.sample(tag_data, tags_num)
+            engine.update(obj_num, user, tags)
+
+    return engine
+
+
+class SampleEngine(object):
+    interface.implements(ISampleDataPlugin)
+    name = 'lovely.tags.sampleengine'
+    dependencies = []
+    schema = None
+
+    def generate(self, context, param={}, seed=None):
+        hooks.setSite(context)
+        sm = context.getSiteManager()
+
+        if 'tagging-engine' not in sm['default']:
+            # Add the tagging engine
+            engine = tag.TaggingEngine()
+            sm['default']['tagging-engine'] = engine
+            sm.registerUtility(engine, tag.interfaces.ITaggingEngine)
+        return engine
+
+
+class SampleTags(object):
+    interface.implements(ISampleDataPlugin)
+    name = 'lovely.tags.sampledata'
+    dependencies = []
+    schema = None
+
+    def generate(self, context, param={}, seed=None):
+        engine = component.getUtility(tag.interfaces.ITaggingEngine)
+        return generate(20, engine, seed)
+


Property changes on: lovely.tag/trunk/src/lovely/tag/sampledata.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/sampledata.zcml
===================================================================
--- lovely.tag/trunk/src/lovely/tag/sampledata.zcml	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/sampledata.zcml	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,41 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+  <configure
+      xmlns:zcml="http://namespaces.zope.org/zcml"
+      zcml:condition="have devmode">
+
+    <!-- This is a configuration for the testing environment -->
+
+    <utility
+        factory=".sampledata.SampleTags"
+        provides="lovely.sampledata.interfaces.ISampleDataPlugin"
+        name="lovely.tag.sampletags"
+        />
+
+    <utility
+        factory=".sampledata.SampleEngine"
+        provides="lovely.sampledata.interfaces.ISampleDataPlugin"
+        name="lovely.tag.sampleengine"
+        />
+
+    <SampleManager
+      name="tagbrowsertest"
+      >
+      <generator name="lovely.sampledata.site" />
+      <generator
+        name="lovely.sampledata.intids"
+        dependsOn="lovely.sampledata.site"
+        contextFrom="lovely.sampledata.site" />
+      <generator
+        name="lovely.tag.sampleengine"
+        contextFrom="lovely.sampledata.site" />
+      <generator
+        name="lovely.tag.sampletags"
+        dependsOn="lovely.tag.sampleengine"
+        contextFrom="lovely.sampledata.site" />
+    </SampleManager>
+
+  </configure>
+
+</configure>
+


Property changes on: lovely.tag/trunk/src/lovely/tag/sampledata.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/stresstest.txt
===================================================================
--- lovely.tag/trunk/src/lovely/tag/stresstest.txt	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/stresstest.txt	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,19 @@
+=============================
+Tagging Engine Stress Testing
+=============================
+
+Before the test, we need to make sure the key reference adapter is registered:
+
+  >>> import zope.component
+  >>> from zope.app.keyreference import testing
+  >>> zope.component.provideAdapter(testing.SimpleKeyReference)
+
+Let's start by creating a tagging engine with at least 1 million tag entries:
+
+  >>> from lovely.tag import sampledata
+  >>> engine = sampledata.generate(1e5)
+
+Let's now query for all tags specified by user srichter:
+
+  >>> len(engine.getTags(users=(u'srichter',)))
+  712


Property changes on: lovely.tag/trunk/src/lovely/tag/stresstest.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/tag.py
===================================================================
--- lovely.tag/trunk/src/lovely/tag/tag.py	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/tag.py	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,51 @@
+##############################################################################
+#
+# Copyright (c) 2006 Lovely Systems and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tag Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import datetime
+import persistent
+import zope.interface
+from zope.schema.fieldproperty import FieldProperty
+
+from lovely.tag import interfaces
+
+def normalizeTagName(name):
+    return name.lower()
+
+class Tag(persistent.Persistent):
+    """Simple implementation of a tag."""
+    zope.interface.implements(interfaces.ITag)
+
+    item = FieldProperty(interfaces.ITag['item'])
+    user = FieldProperty(interfaces.ITag['user'])
+    tag = FieldProperty(interfaces.ITag['name'])
+    timestamp = FieldProperty(interfaces.ITag['timestamp'])
+
+    def __init__(self, item, user, tag):
+        self.item = item
+        self.user = user
+        self.tag = tag
+        self.timestamp = datetime.datetime.now()
+
+    def __cmp__(self, other):
+        return cmp((self.item, self.user, self.tag),
+                   (other.item, other.user, other.tag))
+
+    def __repr__(self):
+        return '<%s %r for %i by %r>' %(
+            self.__class__.__name__, self.tag, self.item, self.user)


Property changes on: lovely.tag/trunk/src/lovely/tag/tag.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/tagging.py
===================================================================
--- lovely.tag/trunk/src/lovely/tag/tagging.py	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/tagging.py	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,50 @@
+##############################################################################
+#
+# Copyright (c) 2006 Lovely Systems and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tag Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.component
+import zope.interface
+from zope.app import intid
+
+from lovely.tag import interfaces
+
+class Tagging(object):
+    zope.interface.implements(interfaces.ITagging)
+    zope.component.adapts(interfaces.ITaggable)
+
+    def __init__(self, context):
+        self.context = context
+
+        ids = zope.component.getUtility(intid.interfaces.IIntIds)
+        self._id = ids.queryId(self.context)
+        if self._id is None:
+            ids.register(self.context)
+            self._id = ids.getId(self.context)
+
+        self._engine = zope.component.getUtility(interfaces.ITaggingEngine)
+
+    def update(self, user, tags):
+        """See interfaces.ITagging"""
+        return self._engine.update(self._id, user, tags)
+
+    def getTags(self, users=None):
+        """See interfaces.ITagging"""
+        return self._engine.getTags(items=(self._id,), users=users)
+
+    def getUsers(self, tags=None):
+        """See interfaces.ITagging"""
+        return self._engine.getUsers(items=(self._id,), tags=tags)


Property changes on: lovely.tag/trunk/src/lovely/tag/tagging.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/tags.data
===================================================================
--- lovely.tag/trunk/src/lovely/tag/tags.data	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/tags.data	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,715 @@
+.net
+1-must
+1106
+1950s
+1960s
+1970s
+1980s
+1990s
+19th-century
+2000s
+2005
+2006
+20th-century
+80s
+adam
+adult
+adventure
+africa
+agatha
+ails
+ajax
+alex
+aliens
+alleys
+alternative
+amazing
+america
+american
+amsterdam
+andrew
+animal
+animals
+animation
+anime
+anthology
+apple
+april
+architecture
+art
+article
+articles
+artists
+arts
+audio
+austen
+australia
+author
+authors
+autumn
+awesome
+babes
+baby
+barcelona
+bards
+baseball
+beach
+beatles
+beautiful
+beauty
+belinda
+bellaonline
+berlin
+beth
+bible
+biography
+bird
+birds
+birthday
+bk
+bl
+black
+blackandwhite
+blog
+blogging
+blogs
+blue
+bluegrass
+blues
+boat
+book
+bookie
+books
+boston
+boulevard
+brandon
+brasil
+brian
+bridge
+british
+britlit-classics-2
+buddhism
+buffy
+building
+business
+buy
+bw
+bwwm
+california
+calls
+camera
+cameraphone
+camping
+canada
+candy
+canon
+car
+carpet
+cat
+catholicism
+cats
+cd
+cds
+celebrity
+century
+chess
+chicago
+chick
+child
+children
+childrens
+chile
+china
+chris
+christian
+christianity
+christie
+christmas
+church
+city
+classic
+classical
+classics
+clouds
+coffee
+collection
+color
+comedy
+comics
+community
+computer
+computers
+concert
+connection
+contemporary
+cookbook
+cookbooks
+cooking
+cool
+country
+cover
+crafts
+crap
+crime
+crossdress
+css
+culture
+cyberpunk
+d20
+dad
+daily
+dance
+danged
+daniel
+dark
+darkness
+david
+day
+dc
+dead
+demons
+design
+development
+diet
+digital
+dimensions
+discomusic
+discworld
+disney
+diy
+dog
+dogs
+download
+dragonlance
+dragons
+drama
+dungeons
+dvd
+dvd-own
+dvds
+east
+easton
+eat
+economics
+education
+electronic
+electronics
+email
+emily
+england
+entertainment
+epic
+europe
+evolution
+excellent
+exercise
+eye
+faith
+family
+fantasy
+faves
+favorite
+favorites
+female
+festival
+fi
+fiction
+fighter
+film
+films
+finance
+firefox
+fitness
+flash
+flickr
+florida
+flower
+flowers
+folk
+food
+forgotten
+france
+free
+freeware
+friends
+fun
+funny
+gaiman
+game
+games
+garden
+gardening
+gay
+geotagged
+germany
+gift
+gifts
+girl
+girls
+gnome
+god
+goddess
+good
+google
+gps
+graduation
+graffiti
+grand
+graphic
+graphics
+greasemonkey
+great
+green
+grocery
+groups
+gtk
+guitar
+halloween
+hardware
+harry
+hawaii
+health
+heavy
+hercule
+hiking
+historical
+history
+hockey
+holiday
+home
+honeymoon
+hongkong
+horror
+horses
+house
+howto
+html
+humor
+idea
+illustration
+images
+in
+india
+indie
+industrial
+insect
+inspiration
+instro
+interesting
+internet
+interracial
+investing
+ipod
+ireland
+irish
+island
+it
+italy
+jack
+jacob
+jane
+japan
+jason
+java
+javascript
+jay
+jazz
+jeff
+jewelry
+jmk
+joe
+joecashin
+john
+jordan
+josh
+journeys
+july
+june
+justin
+katie
+kevin
+kexp
+kid
+kids
+king
+kitchen
+knitting
+kyle
+lake
+landscape
+language
+last
+latin
+leadership
+learning
+library
+lies
+lifehacks
+lifestyle
+light
+linguistics
+links
+linux
+lisa
+lit
+literature
+little
+london
+looks
+losangeles
+lotr
+love
+lover
+luxembourg
+mac
+macos
+macro
+madeira
+magic
+magical
+male
+man
+manga
+maps
+march
+mark
+markdown
+marketing
+martial
+math
+mathematics
+matt
+may
+maybe
+me
+media
+medical
+medieval
+meditation
+mediterranean
+meets
+megan
+meghan
+memoir
+men
+mesa
+meta
+metal
+metalmike
+mexico
+micampe
+michael
+mike
+mine
+minstrels
+missouri
+moblog
+mom
+money
+moon
+mountain
+mountains
+movie
+movies
+mp3
+murder
+museum
+music
+musical
+my
+mysteries
+mystery
+mythology
+nature
+neil
+networking
+new
+newbery
+news
+newyork
+newyorkcity
+newzealand
+ngagi
+nice
+nick
+night
+nikon
+nm
+non-fiction
+nonfiction
+nora
+novel
+novels
+nutrition
+nyc
+objects
+ocean
+of
+office
+older
+online
+opensource
+opera
+operatingsystems
+oreilly
+oriental
+osx
+paperbacks
+paranormal
+parenting
+paris
+park
+party
+pc
+peace
+people
+perfumes
+period
+personal
+perspectives
+peru
+philippines
+philosophy
+phone
+photo
+photography
+photos
+photoshop
+php
+picture
+pink
+plant
+plugin
+podcast
+poetry
+poirot
+poker
+politics
+pop
+portfolio
+portrait
+portugal
+potter
+pretty
+productivity
+programming
+progressive
+ps2
+ps3
+psychology
+punishment
+punk
+python
+r&b
+radeon
+rails
+rakeback
+rant
+rap
+read
+realms
+recipes
+recommended
+recordings
+red
+reference
+reflection
+regency
+relaxation
+religion
+research
+resources
+ride
+river
+roadtrip
+roberts
+rock
+romance
+romantic
+rome
+ross
+rpg
+ruby
+rubyonrails
+rumiko
+ryan
+sam
+samurai
+san
+sanfrancisco
+sarah
+sbagli
+scary
+scenery
+school
+sci
+sci-fi
+science
+scifi
+scotland
+scott
+scottish
+script
+sea
+search
+seattle
+security
+self-help
+series
+sf
+sga
+shakespeare
+shoes
+shopping
+short
+show
+shows
+singers
+sky
+snow
+social
+software
+sold
+songs
+souls
+sound
+soundtrack
+soundtracks
+spa
+space
+spain
+spanish
+sparkledoll
+spiritual
+spirituality
+sports
+spring
+springbook
+star
+statistics
+stephen
+stlouis
+stories
+story
+street
+study
+summer
+sun
+sunset
+superhero
+suspense
+swimsuit
+switch
+sydney
+taiwan
+takahashi
+tea
+tech
+technology
+technorati
+teen
+teens
+television
+texas
+textbook
+textpattern
+thailand
+the
+theology
+thinkpad
+thriller
+thrillers
+time
+tips
+to_listen
+to_read
+toddlers
+tokyo
+tools
+top
+toread
+toronto
+townscapes
+toy
+toys
+travel
+tree
+trees
+trek
+trip
+trips
+tshirt
+turkey
+tutorial
+tutorials
+tv
+tweak
+tyler
+type
+ubuntu
+uk
+ultimate
+uploadr
+urban
+us
+usa
+userinterface
+vacation
+vampire
+vampires
+vancouver
+various
+vhs
+video
+vintage
+wake
+wales
+want
+war
+wars
+washington
+watch
+watches
+water
+web
+web2.0
+webdesign
+webdev
+wedding
+werewolf
+west
+western
+white
+wicca
+wiki
+window
+windows
+winter
+wishes
+wishlist
+woman
+women
+wordpress
+work
+world
+writers
+writing
+ww2bl
+wysiswyg
+xbox
+xgl
+xmas
+ya
+yaoi
+yellow
+yes
+yoga
+york
+young
+youtube
+yup
+zebra
+zoo
+zope
+zope3
+zwiki

Added: lovely.tag/trunk/src/lovely/tag/tests.py
===================================================================
--- lovely.tag/trunk/src/lovely/tag/tests.py	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/tests.py	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,44 @@
+##############################################################################
+#
+# Copyright (c) 2006 Lovely Systems and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tag test setup
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import doctest
+import unittest
+from zope.app.testing import placelesssetup
+from zope.testing.doctestunit import DocFileSuite
+
+def test_suite():
+
+    stressSuite = DocFileSuite(
+        'stresstest.txt',
+        setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
+        optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS)
+    stressSuite.level = 2
+
+    return unittest.TestSuite(
+        (
+        DocFileSuite('README.txt',
+                     setUp=placelesssetup.setUp,
+                     tearDown=placelesssetup.tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        stressSuite
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')


Property changes on: lovely.tag/trunk/src/lovely/tag/tests.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.tag/trunk/src/lovely/tag/users.data
===================================================================
--- lovely.tag/trunk/src/lovely/tag/users.data	2006-08-12 12:04:26 UTC (rev 69422)
+++ lovely.tag/trunk/src/lovely/tag/users.data	2006-08-12 13:06:22 UTC (rev 69423)
@@ -0,0 +1,30 @@
+alex
+anne
+barry
+bdorn
+benji
+christine
+claudia
+dan
+danche
+dominik
+fred
+frisi
+gary
+gil
+hannes
+ineichen
+jim
+jodok
+kartnaller
+katy
+marie
+marion
+martha
+martin
+michael
+mirko
+projekt01
+srichter
+stefan
+wolfgang



More information about the Checkins mailing list