From: Nick Retallack Date: Sat, 7 Nov 2009 10:11:14 +0000 (-0800) Subject: some nice mixins for tags, ratings, relations X-Git-Url: http://git.veekun.com/zzz-floof.git/commitdiff_plain/da9dc9c1a060be8de3c13fa308a047b4b57eaaeb?ds=inline;hp=5a8f3bd86e5fbcdd1ae077b15c17a66de14c091e some nice mixins for tags, ratings, relations --- diff --git a/floof/controllers/art.py b/floof/controllers/art.py index 5216da7..bb3bc19 100644 --- a/floof/controllers/art.py +++ b/floof/controllers/art.py @@ -9,7 +9,7 @@ log = logging.getLogger(__name__) import elixir from floof.model.users import User -from floof.model.art import Art, Rating, UserRelation +from floof.model import Art, Rating, UserRelation from floof.model.comments import Discussion from sqlalchemy.exceptions import IntegrityError diff --git a/floof/controllers/comments.py b/floof/controllers/comments.py index b096ba2..342b009 100644 --- a/floof/controllers/comments.py +++ b/floof/controllers/comments.py @@ -6,7 +6,7 @@ from pylons.controllers.util import abort, redirect, redirect_to from sqlalchemy import and_ from floof.lib.base import BaseController, render -from floof.model.art import Art +from floof.model import Art from floof.model.comments import Comment log = logging.getLogger(__name__) diff --git a/floof/controllers/relation.py b/floof/controllers/relation.py index 4139da8..b1d415d 100644 --- a/floof/controllers/relation.py +++ b/floof/controllers/relation.py @@ -7,7 +7,7 @@ from floof.lib.base import BaseController, render log = logging.getLogger(__name__) -from floof.model.art import Art, UserRelation +from floof.model import Art, UserRelation from floof.model.users import User import elixir diff --git a/floof/controllers/search.py b/floof/controllers/search.py index 4e92a6d..f87ddae 100644 --- a/floof/controllers/search.py +++ b/floof/controllers/search.py @@ -9,8 +9,8 @@ from floof.lib.search import do_search log = logging.getLogger(__name__) -from floof.model.art import Art, Tag, TagText -from floof.model.search import SavedSearch, GalleryWidget +from floof.model import Art, Tag, TagText +from floof.model import SavedSearch, GalleryWidget import elixir class SearchController(BaseController): diff --git a/floof/controllers/tag.py b/floof/controllers/tag.py index 4f98994..e5ce85b 100644 --- a/floof/controllers/tag.py +++ b/floof/controllers/tag.py @@ -9,7 +9,7 @@ from pylons import url log = logging.getLogger(__name__) import elixir -from floof.model.art import Art, Tag +from floof.model import Art, Tag class TagController(BaseController): diff --git a/floof/lib/search.py b/floof/lib/search.py index f7ce65b..37d762d 100644 --- a/floof/lib/search.py +++ b/floof/lib/search.py @@ -1,4 +1,4 @@ -from floof.model.art import Art, Tag, TagText +from floof.model import Art, Tag, TagText def do_search(query): tags = query.split() diff --git a/floof/model/__init__.py b/floof/model/__init__.py index 1a81770..8368357 100644 --- a/floof/model/__init__.py +++ b/floof/model/__init__.py @@ -22,7 +22,14 @@ if elixir.options_defaults.get('autoload', False) \ # # import other entities here, e.g. # from floof.model.blog import BlogEntry, BlogComment -from floof.model import art, users, search +from floof.model.art import * +from floof.model.ratings import * +from floof.model.comments import * +from floof.model.search import * +from floof.model.tags import * +from floof.model.users import * +from floof.model.relations import * + # Finally, call elixir to set up the tables. # but not if using reflected tables diff --git a/floof/model/art.py b/floof/model/art.py index ff8035a..bb68c9f 100644 --- a/floof/model/art.py +++ b/floof/model/art.py @@ -14,6 +14,10 @@ from floof.lib.file_storage import get_path, save_file from floof.lib.dbhelpers import find_or_create, update_or_create import floof.model.comments + +# Note: Art is the most important class. To keep its size down, and to better organize the source code, +# other modules will mix into it automatically by adding to its __bases__. + class Art(Entity): title = Field(Unicode(120)) original_filename = Field(Unicode(120)) @@ -36,99 +40,5 @@ class Art(Entity): if self.hash: return get_path("art", self.hash) - - 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) - - - - - def rate(self, score, user): - return update_or_create(Rating, {"rater":user, "art":self}, {"score":score}) - - def user_score(self, user): - rating = Rating.get_by(rater=user, art=self) - if rating: - return rating.score - return Rating.default - def __unicode__(self): return self.get_path() - - -class Tag(Entity): - # look into how ondelete works. This just sets a database property. - art = ManyToOne('Art', ondelete='cascade') - tagger = ManyToOne('User', ondelete='cascade') - tagtext = ManyToOne('TagText') - - def __unicode__(self): - if not self.tagtext: - 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 Rating(Entity): - art = ManyToOne('Art', ondelete='cascade') - rater = ManyToOne('User', ondelete='cascade') - score = Field(Integer) - - options = {-1:"sucks", 0:"undecided", 1:"good", 2:"great"} - default = 0 - -Rating.reverse_options = dict (zip(Rating.options.values(), Rating.options.keys())) - - - -class UserRelation(Entity): - user = ManyToOne("User") - art = ManyToOne("Art") - kind = Field(String) # by for of - creator = ManyToOne("User") - confirmed_by_related_user = Field(Boolean) - - # it is useful to record which authority figure on a given artwork - # confirmed the validity of this relation. - confirmed_by_authority = ManyToOne("User") - - def __init__(self, **kwargs): - super(UserRelation, self).__init__(**kwargs) - assert self.user and self.art and self.kind and self.creator - - if self.creator == self.user: - self.confirmed_by_related_user = True - # TODO: implement authorities - # if self.creator in self.art.authorities - # self.confirmed_by_authority = self.creator - - def __unicode__(self): - return "%s: %s" % (self.kind, self.related_user) - - - -# class CharacterRelation(Entity): -# pass diff --git a/floof/model/ratings.py b/floof/model/ratings.py new file mode 100644 index 0000000..6b5850a --- /dev/null +++ b/floof/model/ratings.py @@ -0,0 +1,26 @@ +from elixir import * +from art import Art +from floof.lib.dbhelpers import find_or_create, update_or_create + +class Rating(Entity): + art = ManyToOne('Art', ondelete='cascade') + rater = ManyToOne('User', ondelete='cascade') + score = Field(Integer) + + options = {-1:"sucks", 0:"undecided", 1:"good", 2:"great"} + default = 0 + +Rating.reverse_options = dict (zip(Rating.options.values(), Rating.options.keys())) + + +class RatingMixin(object): + def rate(self, score, user): + return update_or_create(Rating, {"rater":user, "art":self}, {"score":score}) + + def user_score(self, user): + rating = Rating.get_by(rater=user, art=self) + if rating: + return rating.score + return Rating.default + +Art.__bases__ += (RatingMixin,) \ No newline at end of file diff --git a/floof/model/relations.py b/floof/model/relations.py new file mode 100644 index 0000000..a6fe020 --- /dev/null +++ b/floof/model/relations.py @@ -0,0 +1,34 @@ +from elixir import * +from art import Art + + +class UserRelation(Entity): + user = ManyToOne("User") + art = ManyToOne("Art") + kind = Field(String) # by for of + creator = ManyToOne("User") + confirmed_by_related_user = Field(Boolean) + + # it is useful to record which authority figure on a given artwork + # confirmed the validity of this relation. + confirmed_by_authority = ManyToOne("User") + + def __init__(self, **kwargs): + super(UserRelation, self).__init__(**kwargs) + assert self.user and self.art and self.kind and self.creator + + if self.creator == self.user: + self.confirmed_by_related_user = True + # TODO: implement authorities + # if self.creator in self.art.authorities + # self.confirmed_by_authority = self.creator + + def __unicode__(self): + return "%s: %s" % (self.kind, self.related_user) + + +class RelationMixin(object): + def add_relation(creator, kind, user): + return UserRelation(art=self, creator=creator, kind=kind, user=user) + +Art.__bases__ += (RelationMixin,) diff --git a/floof/model/search.py b/floof/model/search.py index 2296491..fb86f35 100644 --- a/floof/model/search.py +++ b/floof/model/search.py @@ -1,7 +1,6 @@ from elixir import * # from users import User -from floof.lib.search import do_search class SavedSearch(Entity): string = Field(Unicode) # I tried calling this query, but it broke elixir @@ -13,6 +12,9 @@ class SavedSearch(Entity): @property def results(self): + # This caused some cyclic dependencies when I tried importing it + # at the module level. I wonder why that is... + from floof.lib.search import do_search return do_search(self.string) diff --git a/floof/model/tags.py b/floof/model/tags.py new file mode 100644 index 0000000..1a41278 --- /dev/null +++ b/floof/model/tags.py @@ -0,0 +1,46 @@ +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. + art = ManyToOne('Art', ondelete='cascade') + tagger = ManyToOne('User', ondelete='cascade') + tagtext = ManyToOne('TagText') + + def __unicode__(self): + if not self.tagtext: + 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, ) diff --git a/floof/templates/art/show.mako b/floof/templates/art/show.mako index 45ebcb9..05d1e42 100644 --- a/floof/templates/art/show.mako +++ b/floof/templates/art/show.mako @@ -1,7 +1,7 @@ <%inherit file="/base.mako" /> <%namespace name="comments" file="/comments/lib.mako" /> -<%! from floof.model.art import Rating %> +<%! from floof.model import Rating %>

Viewing Art