From: Nick Retallack Date: Mon, 7 Dec 2009 02:05:00 +0000 (-0800) Subject: merged suff, commented out some of my own X-Git-Url: http://git.veekun.com/zzz-floof.git/commitdiff_plain/490b45cdc311a09db0266a3f974289e3979bf2ea?ds=sidebyside;hp=-c merged suff, commented out some of my own --- 490b45cdc311a09db0266a3f974289e3979bf2ea diff --combined floof/config/routing.py index f6906f4,f6906f4..7a0227a --- a/floof/config/routing.py +++ b/floof/config/routing.py @@@ -22,8 -22,8 +22,8 @@@ def make_map() require_POST = dict(conditions={'method': ['POST']}) # get rid of trailing slashes -- map.redirect('/*(url)/', '/{url}', -- _redirect_code='301 Moved Permanently') ++ # map.redirect('/*(url)/', '/{url}', ++ # _redirect_code='301 Moved Permanently') # The ErrorController route (handles 404/500 error pages); it should diff --combined floof/controllers/art.py index 9a2f1ba,7c371ed..0b89d98 --- a/floof/controllers/art.py +++ b/floof/controllers/art.py @@@ -1,15 -1,15 +1,15 @@@ import logging - from pylons import config, request, response, session, tmpl_context as c, h + from pylons import config, request, response, session, tmpl_context as c from pylons.controllers.util import abort, redirect from pylons import url from floof.lib.base import BaseController, render log = logging.getLogger(__name__) - from floof.lib import file_storage as storage - from floof.model.users import User - from floof.model import Art, Rating, UserRelation + from floof.lib import file_storage as storage, helpers as h + from floof.model import Art, Rating, ArtUser + from floof.model.art import ArtUserType from floof.model.comments import Discussion from floof.model.users import User, UserRelationship @@@ -18,7 -18,6 +18,6 @@@ import elixi import os.path import PIL import PIL.Image - from sqlalchemy import func from sqlalchemy.exceptions import IntegrityError from sqlalchemy.orm.exc import NoResultFound from wtforms.validators import ValidationError @@@ -27,7 -26,6 +26,7 @@@ from wtforms import class ArtUploadForm(Form): by = TextField('Artists') + by_me = BooleanField('me') file = FileField('Upload') url = TextField('Link') @@@ -36,38 -34,6 +35,6 @@@ if field.data == u'': raise ValidationError('File is required') - # Also make this into a general User List field validator - """ PLEASE NOTE! I just realized that I need to have a __str__ method on User - to get it to write the usernames back in the form when it redisplays them, since - this validator turns them into user objects instead. This fact actually sounds dangerous - to me in the future, since it means I proably shouldn't be changing the data input - by the user right here in the validator, or the user will see the post-mangled data instead - of what they actually typed. Hm. - - One solution to this could be to only look up the users after normal validation is over, - and then manually add validation errors to the form if that fails. But I think that kind of - sucks. Perhaps the ideology in Formish, where they keep Validation and Conversion as - separate tasks, is a better way of doing it? That way there is less risk of changing the user's - input -- you do that at the conversiot stage -- yet it is still encapsulated in the form workflow. - Hm. But that means I'd have to query for the users in the validation step and throw them away, - or something equally stupid. Guess there's no perfect solution here, but I thought it was - worth discussing. - - Btw, this is meant to be used by a field with multi user autocompletion on it (like on stackoverflow tags), - so the user should never actually submit anything invalid unless they disable javascript and force it. - """ - def validate_by(self, field): - if not field.data: - raise ValidationError("Needs at least one creator") - user_names = field.data.split() - users = [] - # TODO: Could totally do a filter__in here instead of picking them out individually - for user_name in user_names: - user = User.get_by(name=user_name) - if not user: - raise ValidationError("Couldn't find user %s" % user_name) - users.append(user) - field.data = users class ArtController(BaseController): def __before__(self, id=None): @@@ -128,11 -94,9 +95,17 @@@ ) c.art.discussion = Discussion(count=0) - if c.form.by_me and c.user not in c.form.by.data: - UserRelation(user=c.user, creator=c.user, kind="by", art=c.art) - - for artist in c.form.by.data: - UserRelation(user=artist, creator=c.user, kind="by", art=c.art) ++ # <<<<<<< HEAD ++ # if c.form.by_me and c.user not in c.form.by.data: ++ # UserRelation(user=c.user, creator=c.user, kind="by", art=c.art) ++ # ++ # for artist in c.form.by.data: ++ # UserRelation(user=artist, creator=c.user, kind="by", art=c.art) ++ # ======= + # For the moment, cheerfully assume that people are uploading their own + # art + ArtUser(art=c.art, user=c.user, type=ArtUserType.BY) ++ # >>>>>>> origin/master try: @@@ -174,8 -138,7 +147,7 @@@ def watchstream(self, name): """Watchstream for a certain user.""" try: - c.watching_user = User.query.filter(func.lower(User.name) == name) \ - .one() + c.watching_user = User.get_by(name=name) except NoResultFound: abort(404) diff --combined floof/lib/tags.py index 0000000,104fe65..6186048 mode 000000,100644..100644 --- a/floof/lib/tags.py +++ b/floof/lib/tags.py @@@ -1,0 -1,126 +1,128 @@@ + from floof.model import Art, ArtUser, ArtUserType, Tag, TagText, User + ++from dbhelpers import find_or_create ++import re + def parse(search_string): + """Parses a search query, and returns a query object on Art. + + Queries can contain: + - Regular tags: foo + - User relations: by:kalu, of:eevee, for:ootachi + + Later: + - Negative versions of anything above: -by:eevee, -dongs + """ + + # XXX doesn't do negative querying yet. + # XXX could use some sane limits. + + # We'll be building this as we go. + q = Art.query + + terms = search_string.split() + for tag in terms: + if ':' in tag: + # This is a special tag; at the moment, by/for/of to indicate + # related users + prefix, tag = tag.split(':', 1) + + # XXX what to do if this fails? abort? return empty query? + target_user = User.get_by(name=tag) + + if prefix == 'by': + rel = ArtUserType.BY + elif prefix == 'for': + rel = ArtUserType.FOR + elif prefix == 'of': + rel = ArtUserType.OF + else: + # Bogus tag. Not sure what to do here, so for the moment, + # ignore it + continue + + # Inner join to the ArtUser table + q = q.join(ArtUser, aliased=True) \ + .filter(ArtUser.user == target_user) \ + .filter(ArtUser.type == rel) + + else: + # Regular ol' tag + q = q.join(Tag, TagText, aliased=True) \ + .filter(TagText.text == tag) + + return q + + def add_tags(art, tag_string, user): + """Takes a string that looks like a tag query, and effectively modifies the + art's tags to match it. + """ + + # XXX what to do with invalid tags? just return them and let caller fix? + 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.get_by(name=tag_text) + + # 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=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=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=art, tagger=user, tagtext=tag) + + else: + tag = TagText.get_by(text=tag_text) + if tag: + # XXX this will die + tag_assoc = Tag.get_by(art=art, tagger=user, tagtext=tag) + tag_assoc.delete() + + elixir.session.commit() diff --combined floof/templates/art/new.mako index 684c004,2b18b42..0adc916 --- a/floof/templates/art/new.mako +++ b/floof/templates/art/new.mako @@@ -1,44 -1,26 +1,42 @@@ <%inherit file="/base.mako" />

Add New Art

-

Now: Upload a file. Later: Supply a link? Not exclusive to uploading.

- - ## Todo: write some macros to make outputting form fields easier. - ${h.form(h.url('create_art'), multipart=True)} -
- ${normal_field(c.form.by)} - ${checkbox_field(c.form.by_me)} -
- -
${normal_field(c.form.file)}
- ++##<<<<<<< HEAD ++##
++## ${normal_field(c.form.by)} ++## ${checkbox_field(c.form.by_me)} ++##
++## ++##
${normal_field(c.form.file)}
++##======= + ## Todo: write some macros to make outputting form fields easier. + ${normal_field(c.form.file)} ++##>>>>>>> origin/master - ##Artist: ${h.text('artist')} - ##${h.file('file')} ${h.submit(None, 'Upload!')} ${h.end_form()} +<%def name="field_errors(errors)"> + %if errors: + + %endif + + <%def name="normal_field(field)"> -
-${field.label()|n} -${field()|n} -%if field.errors: - -%endif -
+ ${field.label()|n} + ${field()|n} + ${field_errors(field.errors)} + +<%def name="checkbox_field(field)"> + ${field()|n} ${field.label()|n} + ${field_errors(field.errors)} + diff --combined floof/templates/art/show.mako index a38806d,bf14dff..9356447 --- a/floof/templates/art/show.mako +++ b/floof/templates/art/show.mako @@@ -34,17 -34,21 +34,15 @@@ ${h.end_form()

Relations

--

Add Relations

--${h.form (h.url("create_relation", kind="by", art_id=c.art.id))} --By: ${h.text('username')} --${h.submit('add','Add')} --${h.end_form()} -- ${comments.comment_block(c.art.discussion.comments)}