Added for:/of: tagging capability.
authorEevee <git@veekun.com>
Sat, 5 Dec 2009 22:24:43 +0000 (14:24 -0800)
committerEevee <git@veekun.com>
Sat, 5 Dec 2009 22:24:43 +0000 (14:24 -0800)
TagMixin is no more.

floof/controllers/tag.py
floof/model/art.py
floof/model/tags.py

index e5ce85b..e57d0c5 100644 (file)
@@ -1,16 +1,18 @@
 import logging
+import re
 
-from pylons import request, response, session, tmpl_context as c, h
+from pylons import request, response, session, tmpl_context as c, url
 from pylons.controllers.util import abort, redirect
 
+import elixir
+from floof.model import Art, ArtUser, ArtUserType, Tag, TagText, User
 from floof.lib.base import BaseController, render
-from pylons import url
+from floof.lib.dbhelpers import find_or_create
+from floof.lib import helpers as h
+from sqlalchemy import func
 
 log = logging.getLogger(__name__)
 
-import elixir
-from floof.model import Art, Tag
-
 class TagController(BaseController):
 
     # TODO: login required
@@ -23,7 +25,77 @@ class TagController(BaseController):
     # TODO: login required
     def create(self, art_id):
         c.art = h.get_object_or_404(Art, id=art_id)
-        c.art.add_tags(request.params.get("tags",""), c.user)
+
+        tag_string = request.params.get('tags', '')
+
+        # Add or remove tags
+        bad_tags = []
+        for tag_text in tag_string.split():
+            original_tag_text = tag_text
+            tag_text = tag_text.lower()
+
+            # Adding or removing a tag?
+            if tag_text[0] == '-':
+                add = False
+                tag_text = tag_text[1:]
+            else:
+                # Allow "+foo" to mean "add foo"
+                if tag_text[0] == '+':
+                    tag_text = tag_text[1:]
+                add = True
+
+            # Check for special namespaces
+            prefix = None
+            if ':' in tag_text:
+                prefix, tag_text = tag_text.split(':', 1)
+                if prefix not in ['by', 'for', 'of']:
+                    # This is bogus.  Skip it.
+                    bad_tags.append(original_tag_text)
+                    continue
+
+                if prefix == 'by':
+                    # XXX this needs supporting.  silently ignore for now
+                    continue
+
+            # Must be 3-50 alphanumeric characters
+            if not re.match('^[a-z0-9]{3,50}$', tag_text):
+                bad_tags.append(original_tag_text)
+                continue
+
+            # Do work!
+            if prefix:
+                target_user = User.query.filter(func.lower(User.name) == tag_text) \
+                                  .one()
+
+                # Special tag; at the moment, just a relationship
+                if prefix == 'by':
+                    rel = ArtUserType.BY
+                elif prefix == 'for':
+                    rel = ArtUserType.FOR
+                elif prefix == 'of':
+                    rel = ArtUserType.OF
+
+                user_assoc_data = dict(art=c.art, user=target_user, type=rel)
+                if add:
+                    find_or_create(ArtUser, **user_assoc_data)
+
+                else:
+                    # XXX this will die for nonassociations
+                    user_assoc = ArtUser.get_by(art=c.art, **user_assoc_data)
+                    user_assoc.delete()
+
+            else:
+                # Regular tag
+                if add:
+                    tag = find_or_create(TagText, text=tag_text)
+                    find_or_create(Tag, art=c.art, tagger=c.user, tagtext=tag)
+
+                else:
+                    tag = TagText.get_by(text=tag_text)
+                    if tag:
+                        # XXX this will die
+                        tag_assoc = Tag.get_by(art=c.art, tagger=c.user, tagtext=tag)
+                        tag_assoc.delete()
+
         elixir.session.commit()
         redirect(url('show_art', id=c.art.id))
-
index ea6d6ee..0452e32 100644 (file)
@@ -44,7 +44,6 @@ class Art(Entity):
     def participants(self):
         return (au.user for au in self.art_users if au.type == ArtUserType.OF)
 
-
 class ArtUserType(object):
     BY = 1
     FOR = 2
index 1a41278..b0a7072 100644 (file)
@@ -1,7 +1,5 @@
 from elixir import *
 from art import Art
-from floof.lib.dbhelpers import find_or_create, update_or_create
-
 
 class Tag(Entity):
     # look into how ondelete works.  This just sets a database property.
@@ -14,33 +12,9 @@ class Tag(Entity):
             return "(broken)"
         return unicode(self.tagtext)
 
-
 class TagText(Entity):
     text = Field(Unicode(50)) # gotta enforce this somehow
     tags = OneToMany('Tag')
 
     def __unicode__(self):
         return self.text
-
-
-class TagMixin(object):
-    def add_tags(self, tags, user):
-        for text in tags.split():
-            if text[0] == '-':
-                # Nega-tags
-                tagtext = TagText.get_by(text=text[1:])
-                if tagtext:
-                    tag = Tag.get_by(art=self, tagger=user, tagtext=tagtext)
-                    if tag:
-                        elixir.session.delete(tag)
-
-            else:
-                if len(text) > 50:
-                    raise "Long Tag!" # can we handle this more gracefully?
-                # sqlite seems happy to store strings much longer than the supplied limit...
-
-                # elixir should really have its own find_or_create.
-                tagtext = find_or_create(TagText, text=text)
-                tag     = find_or_create(Tag, art=self, tagger=user, tagtext=tagtext)
-                
-Art.__bases__ += (TagMixin, )