[Checkins] SVN: lovely.tag/trunk/src/lovely/tag/ added normalize method to engine

Bernd Dorn bernd.dorn at lovelysystems.com
Mon Jan 8 12:49:30 EST 2007


Log message for revision 71822:
  added normalize method to engine

Changed:
  U   lovely.tag/trunk/src/lovely/tag/README.txt
  U   lovely.tag/trunk/src/lovely/tag/browser/README.txt
  U   lovely.tag/trunk/src/lovely/tag/browser/configure.zcml
  U   lovely.tag/trunk/src/lovely/tag/browser/engine.py
  U   lovely.tag/trunk/src/lovely/tag/browser/tag.py
  U   lovely.tag/trunk/src/lovely/tag/configure.zcml
  U   lovely.tag/trunk/src/lovely/tag/engine.py
  U   lovely.tag/trunk/src/lovely/tag/ftesting.zcml
  U   lovely.tag/trunk/src/lovely/tag/interfaces.py

-=-
Modified: lovely.tag/trunk/src/lovely/tag/README.txt
===================================================================
--- lovely.tag/trunk/src/lovely/tag/README.txt	2007-01-08 17:34:19 UTC (rev 71821)
+++ lovely.tag/trunk/src/lovely/tag/README.txt	2007-01-08 17:49:28 UTC (rev 71822)
@@ -619,3 +619,34 @@
    0
    >>> len(sorted(engine.getItems(tags=[u'renamedtag'])))
    2
+
+Normalizing Tags
+----------------
+
+It is also possible to normalize tags with a callable ojbect which
+returns a new name for any given name.
+lower case.
+
+   >>> engine.update(123, 'jukart', [u'RenamedTag', u'USA'])
+   >>> sorted(engine.getTags())
+   [u'RenamedTag', u'USA', u'renamedtag', u'someothertag', u'usa']
+
+Let us normalize all tags to lowercase by using the lower function
+from the string module.
+
+   >>> import string
+
+The normalize method returns the number of tag objects affected.
+
+   >>> engine.normalize(string.lower)
+   2
+   >>> sorted(engine.getTags())
+   [u'renamedtag', u'someothertag', u'usa']
+
+The normalize method also accepts a python dotted name, which will be
+resolved to a global object.
+
+   >>> engine.normalize('string.upper')
+   7
+   >>> sorted(engine.getTags())
+   [u'RENAMEDTAG', u'SOMEOTHERTAG', u'USA']

Modified: lovely.tag/trunk/src/lovely/tag/browser/README.txt
===================================================================
--- lovely.tag/trunk/src/lovely/tag/browser/README.txt	2007-01-08 17:34:19 UTC (rev 71821)
+++ lovely.tag/trunk/src/lovely/tag/browser/README.txt	2007-01-08 17:49:28 UTC (rev 71822)
@@ -38,3 +38,52 @@
    <span class="tag1">mysteries(1)</span>
   </div>
 
+
+Engine specific Views
+---------------------
+
+CSV export of tag database:
+
+  >>> engine = 'http://localhost/tags/++etc++site/default/tagging-engine/'
+  >>> browser.open(engine + '@@tags.csv')
+  >>> print browser.contents
+  Name,User,Item,Timestamp
+  ...
+
+Normalize Tags:
+
+  >>> browser.open(engine + 'manage.html')
+  >>> browser.getControl(u'Normalize').click()
+  >>> u'No normalizer function defined' in browser.contents
+  True
+
+All tags are lowercase so it should not affect any tags.
+
+  >>> browser.getControl(u'Normalizer').value="string.lower"
+  >>> browser.getControl(u'Normalize').click()
+  >>> print browser.contents
+  <...<div class="summary">Normalized 0 tag objects</div>...
+
+Let us convert it to upper case.
+
+  >>> browser.getControl(u'Normalizer').value="string.upper"
+  >>> browser.getControl(u'Normalize').click()
+  >>> print browser.contents
+  <...<div class="summary">Normalized 111 tag objects</div>...
+
+  >>> browser.open('http://localhost/tags/@@tagCloud')
+  >>> print browser.contents
+  <div class="tagCloud">
+   <span class="tag1">ADAM(1)</span>
+   <span class="tag6">ALEX(2)</span>
+   <span class="tag1">ALTERNATIVE(1)</span>
+   <span class="tag1">AMSTERDAM(1)</span>
+   <span class="tag1">ANIMALS(1)</span>
+  ...
+
+Cleaning of stale items:
+
+  >>> browser.open(engine + 'manage.html')
+  >>> browser.getControl(u'Clean Stale Items').click()
+  >>> print browser.contents
+  <...<div class="summary">Cleaned out 1 items</div>...

Modified: lovely.tag/trunk/src/lovely/tag/browser/configure.zcml
===================================================================
--- lovely.tag/trunk/src/lovely/tag/browser/configure.zcml	2007-01-08 17:34:19 UTC (rev 71821)
+++ lovely.tag/trunk/src/lovely/tag/browser/configure.zcml	2007-01-08 17:49:28 UTC (rev 71822)
@@ -53,5 +53,15 @@
      name="manage.html"
      />
 
+ <browser:page
+     menu="zmi_views"
+     title="Manage"
+     for="lovely.tag.interfaces.ITaggingEngine"
+     permission="lovely.tag.ManageEngine"
+     class=".engine.CSVExportView"
+     name="tags.csv"
+     />
+
  
+ 
 </configure>

Modified: lovely.tag/trunk/src/lovely/tag/browser/engine.py
===================================================================
--- lovely.tag/trunk/src/lovely/tag/browser/engine.py	2007-01-08 17:34:19 UTC (rev 71821)
+++ lovely.tag/trunk/src/lovely/tag/browser/engine.py	2007-01-08 17:49:28 UTC (rev 71822)
@@ -18,21 +18,57 @@
 __docformat__ = "reStructuredText"
 
 from zope.publisher.browser import BrowserView
-from zope import component, schema
+from zope import schema
 from zope.cachedescriptors.property import Lazy
 from zope.app.pagetemplate import ViewPageTemplateFile
 from zope.formlib import form
 from lovely.tag import _
-
 from zope.formlib import form
+import csv
+import cStringIO
+from zope.cachedescriptors.property import Lazy
 
 class ManageView(form.PageForm):
 
     label=_(u"Manage Tagging Engine")
     
-    form_fields = form.Fields()
+    form_fields = form.Fields(
+        schema.DottedName(__name__=u'normalizer',
+                          title=_(u'Normalizer Function'),
+                          required=False)
+                          )
     
     @form.action(label=_(u'Clean Stale Items'))
     def cleanStale(self, action, data):
         cleaned = self.context.cleanStaleItems()
         self.status = u'Cleaned out %s items' % len(cleaned)
+        
+
+    @form.action(label=_(u'Normalize'))
+    def normalize(self, action, data):
+        normalizer = data.get('normalizer')
+        if not normalizer:
+            self.status=_(u'No normalizer function defined')
+            return
+        count = self.context.normalize(normalizer)
+        self.status = u'Normalized %s tag objects' % count
+    
+
+class CSVExportView(BrowserView):
+
+    def __call__(self):
+        self.request.response.setHeader('Content-Type', 'text/csv')
+        res = cStringIO.StringIO()
+        writer = csv.writer(res, dialect=csv.excel)
+        encoding = 'utf-8'
+        writer.writerow(('Name', 'User', 'Item', 'Timestamp'))
+        for tag in self.context.getTagObjects():
+            row = [tag.name.encode(encoding)]
+            row.append(tag.user.encode(encoding))
+            row.append(tag.item)
+            row.append(tag.timestamp)
+            writer.writerow(row)
+        return res.getvalue()
+
+        
+        

Modified: lovely.tag/trunk/src/lovely/tag/browser/tag.py
===================================================================
--- lovely.tag/trunk/src/lovely/tag/browser/tag.py	2007-01-08 17:34:19 UTC (rev 71821)
+++ lovely.tag/trunk/src/lovely/tag/browser/tag.py	2007-01-08 17:49:28 UTC (rev 71822)
@@ -28,7 +28,6 @@
 import datetime
 from zope.interface.common import idatetime
 
-
 class TaggingMixin(object):
 
     @Lazy
@@ -39,10 +38,10 @@
     def tagging(self):
         return ITagging(self.context)
 
-    def getCloud(self, maxTags=100):
+    def getCloud(self, maxTags=100, calc=None):
         """returns a tag cloud"""
         cloud = self.engine.getCloud()
-        return normalize(cloud, maxTags)
+        return normalize(cloud, maxTags, 6, calc)
 
 
 class TaggingView(BrowserView, TaggingMixin):
@@ -64,17 +63,19 @@
     def __init__(self, context, request):
         super(RelatedView, self).__init__(context, request)
 
-    def getCloud(self, tag=None, maxTags=100):
+    def getCloud(self, tag=None, maxTags=100, calc=None):
         """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)
+        return normalize(cloud, maxTags, 6, calc)
 
 
-def normalize(cloud, maxTags=100, maxValue=6):
+def normalize(cloud, maxTags=100, maxValue=6, calc=None):
+    if calc is None:
+        calc = lambda x: x
     if len(cloud) == 0:
         return []
     minmax = sorted(cloud, key=lambda i: i[1],reverse=True)
@@ -86,8 +87,8 @@
     if end == 0:
         return []
     minmax = minmax[:end]
-    minFreq = minmax[-1][1]
-    maxFreq = minmax[0][1]
+    minFreq = calc(minmax[-1][1])
+    maxFreq = calc(minmax[0][1])
     freqRange = maxFreq-minFreq
     if freqRange>0:
         ratio = float(maxValue-1)/freqRange
@@ -98,7 +99,7 @@
         if ratio is None:
             normalized=1
         else:
-            normalized = int((frequency-minFreq)*ratio) +1
+            normalized = int((calc(frequency)-minFreq)*ratio) +1
         res.append(dict(name=tag,
                         normalized=normalized,
                         frequency=frequency,))
@@ -124,7 +125,6 @@
     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:

Modified: lovely.tag/trunk/src/lovely/tag/configure.zcml
===================================================================
--- lovely.tag/trunk/src/lovely/tag/configure.zcml	2007-01-08 17:34:19 UTC (rev 71821)
+++ lovely.tag/trunk/src/lovely/tag/configure.zcml	2007-01-08 17:49:28 UTC (rev 71822)
@@ -34,7 +34,7 @@
         />
     <require
         permission="lovely.tag.ManageEngine"
-        attributes="cleanStaleItems delete rename"
+        attributes="cleanStaleItems delete rename getTagObjects normalize"
         />
     <require
         permission="lovely.tag.AccessTag"
@@ -74,7 +74,20 @@
         interface=".interfaces.IUserTagging"
         />
   </class>
+  
+  <class class=".tag.Tag">
+    <require
+        permission="lovely.tag.UpdateTag"
+        set_schema=".interfaces.ITag"
+        />
+    <require
+        permission="lovely.tag.AccessTag"
+        interface=".interfaces.ITag"
+        />
+  </class>
 
+  
+  
   <adapter
       factory=".tagging.UserTagging"
       trusted="True"

Modified: lovely.tag/trunk/src/lovely/tag/engine.py
===================================================================
--- lovely.tag/trunk/src/lovely/tag/engine.py	2007-01-08 17:34:19 UTC (rev 71821)
+++ lovely.tag/trunk/src/lovely/tag/engine.py	2007-01-08 17:49:28 UTC (rev 71822)
@@ -26,6 +26,8 @@
 from zope.app import intid
 from zope.app.intid.interfaces import IIntIdRemovedEvent, IIntIds
 from lovely.tag import interfaces, tag
+from zope.dottedname.resolve import resolve
+import types
 
 class TaggingEngine(persistent.Persistent, contained.Contained):
     zope.interface.implements(interfaces.ITaggingEngine,
@@ -146,10 +148,10 @@
             # shortcut
             return set(self._name_to_tagids.keys())
 
-        result = self._getTagObjects(items, users)
+        result = self.getTagObjects(items, users)
         return set([tag.name for tag in result])
 
-    def _getTagObjects(self, items, users):
+    def getTagObjects(self, items=None, users=None):
 
         if items is None and users is None:
             users_result = set()
@@ -251,13 +253,12 @@
 
     def getCloud(self, items=None, users=None):
         """See interfaces.ITaggingEngine"""
-        import types
         if type(items) == types.IntType:
             items = [items]
         if type(users) in types.StringTypes:
             users = [users]
 
-        tags = self._getTagObjects(items=items, users=users)
+        tags = self.getTagObjects(items=items, users=users)
         d = {}
         for tag in tags:
             if d.has_key(tag.name):
@@ -305,8 +306,18 @@
         self._name_to_tagids[new] = newTagIds
         del self._name_to_tagids[old]
         return len(tagIds)
-        
 
+    def normalize(self, normalizer):
+        if type(normalizer) == types.StringType:
+            normalizer = resolve(normalizer)
+        names = list(self._name_to_tagids.keys())
+        count = 0
+        for name in names:
+            newName = normalizer(name)
+            if newName != name:
+                count += self.rename(name, newName)
+        return count
+
 @component.adapter(IIntIdRemovedEvent)
 def removeItemSubscriber(event):
     

Modified: lovely.tag/trunk/src/lovely/tag/ftesting.zcml
===================================================================
--- lovely.tag/trunk/src/lovely/tag/ftesting.zcml	2007-01-08 17:34:19 UTC (rev 71821)
+++ lovely.tag/trunk/src/lovely/tag/ftesting.zcml	2007-01-08 17:49:28 UTC (rev 71822)
@@ -7,7 +7,8 @@
   <meta:provides feature="devmode" />
 
   <include package="zope.app" />
-
+  <include package="zope.app" file="ftesting.zcml"/>
+  
   <include package="zope.viewlet" file="meta.zcml"/>
   <include package="zope.app.securitypolicy" file="meta.zcml" />
 
@@ -15,8 +16,9 @@
   
   <include package="zope.app.server" />
   <include package="zope.app.authentication" />
+  
   <securityPolicy
-    component="zope.app.securitypolicy.zopepolicy.ZopeSecurityPolicy" />
+      component="zope.app.securitypolicy.zopepolicy.ZopeSecurityPolicy" />
 
   <include package="zope.app.securitypolicy" />
 
@@ -24,7 +26,7 @@
         description="All users have this role implicitly" />
 
   <role id="zope.Manager" title="Site Manager" />
-  <grantAll role="zope.Manager" />
+
   
   <principal
    id="zope.manager"
@@ -75,4 +77,5 @@
   <grant permission="zope.app.dublincore.view"
          role="zope.Anonymous" />
   
+  <grantAll role="zope.Manager" />
 </configure>

Modified: lovely.tag/trunk/src/lovely/tag/interfaces.py
===================================================================
--- lovely.tag/trunk/src/lovely/tag/interfaces.py	2007-01-08 17:34:19 UTC (rev 71821)
+++ lovely.tag/trunk/src/lovely/tag/interfaces.py	2007-01-08 17:49:28 UTC (rev 71822)
@@ -69,6 +69,11 @@
         The method returns a set of *normalized* tag names.
         """
 
+    def getTagObjects(self, items, users):
+        """same as getTags but returns tag objects implementing
+        ITag"""
+        
+
     def getCloud(items=None, users=None):
         """Get a set of tuples in the form of ('tag',
         frequency). Arguments are the same as getTags."""
@@ -115,6 +120,13 @@
     def rename(old, new):
         """rename tags from @old to @new, this method joins the tags
         if tags with the new name do exist"""
+
+    def normalize(normalizer):
+        """Normalize tagnames with the given normalizer
+        function. @normalizer can also be a dotted name.
+
+        The function provided should return a new name for an existing
+        name."""
         
     
 class ITaggingStatistics(zope.interface.Interface):



More information about the Checkins mailing list