41298c829dd87e8e4a46e665308b716ada7cd3d1
[zzz-floof.git] / floof / controllers / art.py
1 import logging
2
3 from pylons import request, response, session, tmpl_context as c, h
4 from pylons.controllers.util import abort, redirect
5 from pylons import url
6 from floof.lib.base import BaseController, render
7
8 log = logging.getLogger(__name__)
9
10 import elixir
11 from floof.model.users import User
12 from floof.model import Art, Rating, UserRelation
13 from floof.model.comments import Discussion
14 from floof.model.users import User, UserRelationship
15
16 from sqlalchemy import func
17 from sqlalchemy.exceptions import IntegrityError
18 from sqlalchemy.orm.exc import NoResultFound
19
20 from wtforms.validators import ValidationError
21 from wtforms import *
22
23
24 class ArtUploadForm(Form):
25 by = TextField('Artists')
26 file = FileField('Upload')
27 url = TextField('Link')
28
29 # TODO: make this general purpose
30 def validate_file(self, field):
31 if field.data == u'':
32 raise ValidationError('File is required')
33
34 # Also make this into a general User List field validator
35 """ PLEASE NOTE! I just realized that I need to have a __str__ method on User
36 to get it to write the usernames back in the form when it redisplays them, since
37 this validator turns them into user objects instead. This fact actually sounds dangerous
38 to me in the future, since it means I proably shouldn't be changing the data input
39 by the user right here in the validator, or the user will see the post-mangled data instead
40 of what they actually typed. Hm.
41
42 One solution to this could be to only look up the users after normal validation is over,
43 and then manually add validation errors to the form if that fails. But I think that kind of
44 sucks. Perhaps the ideology in Formish, where they keep Validation and Conversion as
45 separate tasks, is a better way of doing it? That way there is less risk of changing the user's
46 input -- you do that at the conversiot stage -- yet it is still encapsulated in the form workflow.
47 Hm. But that means I'd have to query for the users in the validation step and throw them away,
48 or something equally stupid. Guess there's no perfect solution here, but I thought it was
49 worth discussing.
50
51 Btw, this is meant to be used by a field with multi user autocompletion on it (like on stackoverflow tags),
52 so the user should never actually submit anything invalid unless they disable javascript and force it.
53 """
54 def validate_by(self, field):
55 if not field.data:
56 raise ValidationError("Needs at least one creator")
57 user_names = field.data.split()
58 users = []
59 # TODO: Could totally do a filter__in here instead of picking them out individually
60 for user_name in user_names:
61 user = User.get_by(name=user_name)
62 if not user:
63 raise ValidationError("Couldn't find user %s" % user_name)
64 users.append(user)
65 field.data = users
66
67 class ArtController(BaseController):
68 def __before__(self, id=None):
69 super(ArtController, self).__before__()
70 # Awesome refactoring!
71 if id:
72 c.art = h.get_object_or_404(Art, id=id)
73
74 def new(self):
75 """ New Art! """
76 c.form = ArtUploadForm()
77 return render("/art/new.mako")
78
79 # TODO: login required
80 def create(self):
81 c.form = ArtUploadForm(request.params)
82 if c.form.validate():
83
84 c.art = Art(uploader=c.user, **request.params)
85 c.art.discussion = Discussion(count=0)
86
87 for artist in c.form.by.data:
88 UserRelation(user=artist, kind="by", creator=c.user, art=c.art)
89
90 file = request.params['file']
91
92 try:
93 elixir.session.commit()
94 redirect(url('show_art', id=c.art.id))
95 except IntegrityError:
96 # hurr, there must be a better way to do this but I am lazy right now
97 hash = c.art.hash
98 elixir.session.rollback()
99 duplicate_art = Art.get_by(hash=hash)
100 h.flash("We already have that one.")
101 redirect(url('show_art', id=duplicate_art.id))
102
103 else:
104 ## TODO: JavaScript should be added to the upload form so that it is
105 ## impossible to submit the form when it contains any invalid users,
106 ## so this never happens. Only autocompled usernames should be allowed.
107 return render("/art/new.mako")
108
109
110 def show(self, id):
111 # c.art = h.get_object_or_404(Art, id=id)
112 if c.user:
113 c.your_score = c.art.user_score(c.user)
114 return render("/art/show.mako")
115
116
117 # TODO: login required
118 def rate(self, id):
119 # c.art = h.get_object_or_404(Art, id=id)
120 score = request.params.get("score")
121 if score and score.isnumeric():
122 score = int(score)
123 else:
124 score = Rating.reverse_options.get(score)
125
126 c.art.rate(score, c.user)
127 elixir.session.commit()
128
129 redirect(url('show_art', id=c.art.id))
130
131
132 def watchstream(self, name):
133 """Watchstream for a certain user."""
134 try:
135 c.watching_user = User.query.filter(func.lower(User.name) == name) \
136 .one()
137 except NoResultFound:
138 abort(404)
139
140 # This user has watches which are users which have art
141 # XXX use artist, not uploader
142 c.artwork = Art.query.join(Art.uploader,
143 User.target_of_relationships) \
144 .filter(UserRelationship.user_id == c.watching_user.id)
145
146 return render('/index.mako')