<s> import hashlib <EOL> import os <EOL> import re <EOL> import time <EOL> import uuid <EOL> from datetime import datetime <EOL> from django . conf import settings <EOL> from django . core . cache import cache <EOL> from django . db import connection , models <EOL> import caching . base as caching <EOL> from olympia import amo <EOL> from olympia . amo . models import ManagerBase , ModelBase <EOL> from olympia . access import acl <EOL> from olympia . addons . models import Addon <EOL> from olympia . amo . helpers import absolutify , user_media_path , user_media_url <EOL> from olympia . amo . urlresolvers import reverse <EOL> from olympia . amo . utils import sorted_groupby <EOL> from olympia . translations . fields import ( <EOL> LinkifiedField , save_signal , NoLinksNoMarkupField , TranslatedField ) <EOL> from olympia . users . models import UserProfile <EOL> SPECIAL_SLUGS = amo . COLLECTION_SPECIAL_SLUGS <EOL> class TopTags ( object ) : <EOL> """<STR_LIT>""" <EOL> def key ( self , obj ) : <EOL> return '<STR_LIT>' % ( settings . CACHE_PREFIX , obj . id ) <EOL> def __get__ ( self , obj , type = None ) : <EOL> if obj is None : <EOL> return self <EOL> return cache . get ( self . key ( obj ) , [ ] ) <EOL> def __set__ ( self , obj , value ) : <EOL> two_days = <NUM_LIT> * <NUM_LIT> * <NUM_LIT> * <NUM_LIT:2> <EOL> cache . set ( self . key ( obj ) , value , two_days ) <EOL> class CollectionQuerySet ( caching . CachingQuerySet ) : <EOL> def with_has_addon ( self , addon_id ) : <EOL> """<STR_LIT>""" <EOL> has_addon = """<STR_LIT>""" <EOL> return self . extra ( <EOL> select = { '<STR_LIT>' : has_addon } , <EOL> select_params = ( addon_id , ) ) <EOL> class CollectionManager ( ManagerBase ) : <EOL> def get_queryset ( self ) : <EOL> qs = super ( CollectionManager , self ) . get_queryset ( ) <EOL> qs = qs . _clone ( klass = CollectionQuerySet ) <EOL> return qs . transform ( Collection . transformer ) <EOL> def manual ( self ) : <EOL> """<STR_LIT>""" <EOL> types = ( amo . COLLECTION_NORMAL , amo . COLLECTION_FAVORITES , <EOL> amo . COLLECTION_FEATURED , ) <EOL> return self . filter ( type__in = types ) <EOL> def listed ( self ) : <EOL> """<STR_LIT>""" <EOL> return self . filter ( listed = True ) <EOL> def publishable_by ( self , user ) : <EOL> """<STR_LIT>""" <EOL> owned_by = models . Q ( author = user . id ) <EOL> publishable_by = models . Q ( users = user . id ) <EOL> collections = self . filter ( owned_by | publishable_by ) <EOL> return collections . distinct ( ) . order_by ( '<STR_LIT>' ) <EOL> class Collection ( ModelBase ) : <EOL> TYPE_CHOICES = amo . COLLECTION_CHOICES . items ( ) <EOL> uuid = models . CharField ( max_length = <NUM_LIT> , blank = True , unique = True ) <EOL> name = TranslatedField ( require_locale = False ) <EOL> nickname = models . CharField ( max_length = <NUM_LIT:30> , blank = True , unique = True , <EOL> null = True ) <EOL> slug = models . CharField ( max_length = <NUM_LIT:30> , blank = True , null = True ) <EOL> description = NoLinksNoMarkupField ( require_locale = False ) <EOL> default_locale = models . CharField ( max_length = <NUM_LIT:10> , default = '<STR_LIT>' , <EOL> db_column = '<STR_LIT>' ) <EOL> type = models . PositiveIntegerField ( db_column = '<STR_LIT>' , <EOL> choices = TYPE_CHOICES , default = <NUM_LIT:0> ) <EOL> icontype = models . CharField ( max_length = <NUM_LIT> , blank = True ) <EOL> listed = models . BooleanField ( <EOL> default = True , help_text = '<STR_LIT>' ) <EOL> subscribers = models . PositiveIntegerField ( default = <NUM_LIT:0> ) <EOL> downloads = models . PositiveIntegerField ( default = <NUM_LIT:0> ) <EOL> weekly_subscribers = models . PositiveIntegerField ( default = <NUM_LIT:0> ) <EOL> monthly_subscribers = models . PositiveIntegerField ( default = <NUM_LIT:0> ) <EOL> application = models . PositiveIntegerField ( choices = amo . APPS_CHOICES , <EOL> db_column = '<STR_LIT>' , <EOL> null = True ) <EOL> addon_count = models . PositiveIntegerField ( default = <NUM_LIT:0> , <EOL> db_column = '<STR_LIT>' ) <EOL> upvotes = models . PositiveIntegerField ( default = <NUM_LIT:0> ) <EOL> downvotes = models . PositiveIntegerField ( default = <NUM_LIT:0> ) <EOL> rating = models . FloatField ( default = <NUM_LIT:0> ) <EOL> all_personas = models . BooleanField ( <EOL> default = False , <EOL> help_text = '<STR_LIT>' ) <EOL> addons = models . ManyToManyField ( Addon , through = '<STR_LIT>' , <EOL> related_name = '<STR_LIT>' ) <EOL> author = models . ForeignKey ( UserProfile , null = True , <EOL> related_name = '<STR_LIT>' ) <EOL> users = models . ManyToManyField ( UserProfile , through = '<STR_LIT>' , <EOL> related_name = '<STR_LIT>' ) <EOL> addon_index = models . CharField ( <EOL> max_length = <NUM_LIT> , null = True , db_index = True , <EOL> help_text = '<STR_LIT>' ) <EOL> objects = CollectionManager ( ) <EOL> top_tags = TopTags ( ) <EOL> class Meta ( ModelBase . Meta ) : <EOL> db_table = '<STR_LIT>' <EOL> unique_together = ( ( '<STR_LIT>' , '<STR_LIT>' ) , ) <EOL> def __unicode__ ( self ) : <EOL> return u'<STR_LIT>' % ( self . name , self . addon_count ) <EOL> @ classmethod <EOL> def make_index ( cls , addon_ids ) : <EOL> ids = '<STR_LIT::>' . join ( map ( str , sorted ( addon_ids ) ) ) <EOL> return hashlib . md5 ( ids ) . hexdigest ( ) <EOL> def flush_urls ( self ) : <EOL> urls = [ '<STR_LIT>' % self . get_url_path ( ) , <EOL> self . icon_url ] <EOL> return urls <EOL> def save ( self , ** kw ) : <EOL> if not self . uuid : <EOL> self . uuid = unicode ( uuid . uuid4 ( ) ) <EOL> if not self . slug : <EOL> self . slug = self . uuid [ : <NUM_LIT:30> ] <EOL> self . clean_slug ( ) <EOL> if self . id : <EOL> ids = self . addons . values_list ( '<STR_LIT:id>' , flat = True ) <EOL> self . addon_index = self . make_index ( ids ) <EOL> super ( Collection , self ) . save ( ** kw ) <EOL> def clean_slug ( self ) : <EOL> if self . type in SPECIAL_SLUGS : <EOL> self . slug = SPECIAL_SLUGS [ self . type ] <EOL> return <EOL> if self . slug in SPECIAL_SLUGS . values ( ) : <EOL> self . slug += '<STR_LIT>' <EOL> if not self . author : <EOL> return <EOL> qs = self . author . collections . using ( '<STR_LIT:default>' ) <EOL> slugs = dict ( ( slug , id ) for slug , id in qs . values_list ( '<STR_LIT>' , '<STR_LIT:id>' ) ) <EOL> if self . slug in slugs and slugs [ self . slug ] != self . id : <EOL> for idx in range ( len ( slugs ) ) : <EOL> new = '<STR_LIT>' % ( self . slug , idx + <NUM_LIT:1> ) <EOL> if new not in slugs : <EOL> self . slug = new <EOL> return <EOL> def get_url_path ( self ) : <EOL> return reverse ( '<STR_LIT>' , <EOL> args = [ self . author_username , self . slug ] ) <EOL> def get_abs_url ( self ) : <EOL> return absolutify ( self . get_url_path ( ) ) <EOL> def get_img_dir ( self ) : <EOL> return os . path . join ( user_media_path ( '<STR_LIT>' ) , <EOL> str ( self . id / <NUM_LIT:1000> ) ) <EOL> def upvote_url ( self ) : <EOL> return reverse ( '<STR_LIT>' , <EOL> args = [ self . author_username , self . slug , '<STR_LIT>' ] ) <EOL> def downvote_url ( self ) : <EOL> return reverse ( '<STR_LIT>' , <EOL> args = [ self . author_username , self . slug , '<STR_LIT>' ] ) <EOL> def edit_url ( self ) : <EOL> return reverse ( '<STR_LIT>' , <EOL> args = [ self . author_username , self . slug ] ) <EOL> def watch_url ( self ) : <EOL> return reverse ( '<STR_LIT>' , <EOL> args = [ self . author_username , self . slug ] ) <EOL> def delete_url ( self ) : <EOL> return reverse ( '<STR_LIT>' , <EOL> args = [ self . author_username , self . slug ] ) <EOL> def delete_icon_url ( self ) : <EOL> return reverse ( '<STR_LIT>' , <EOL> args = [ self . author_username , self . slug ] ) <EOL> def share_url ( self ) : <EOL> return reverse ( '<STR_LIT>' , <EOL> args = [ self . author_username , self . slug ] ) <EOL> def feed_url ( self ) : <EOL> return reverse ( '<STR_LIT>' , <EOL> args = [ self . author_username , self . slug ] ) <EOL> def stats_url ( self ) : <EOL> return reverse ( '<STR_LIT>' , <EOL> args = [ self . author_username , self . slug ] ) <EOL> @ property <EOL> def author_username ( self ) : <EOL> return self . author . username if self . author else '<STR_LIT>' <EOL> @ classmethod <EOL> def get_fallback ( cls ) : <EOL> return cls . _meta . get_field ( '<STR_LIT>' ) <EOL> @ property <EOL> def url_slug ( self ) : <EOL> """<STR_LIT>""" <EOL> return self . nickname or self . uuid <EOL> @ property <EOL> def icon_url ( self ) : <EOL> modified = int ( time . mktime ( self . modified . timetuple ( ) ) ) <EOL> if self . icontype : <EOL> split_id = re . match ( r'<STR_LIT>' , str ( self . id ) ) <EOL> path = "<STR_LIT:/>" . join ( [ <EOL> split_id . group ( <NUM_LIT:2> ) or '<STR_LIT:0>' , <EOL> "<STR_LIT>" % ( self . id , modified ) <EOL> ] ) <EOL> return user_media_url ( '<STR_LIT>' ) + path <EOL> elif self . type == amo . COLLECTION_FAVORITES : <EOL> return settings . STATIC_URL + '<STR_LIT>' <EOL> else : <EOL> return settings . STATIC_URL + '<STR_LIT>' <EOL> def set_addons ( self , addon_ids , comments = { } ) : <EOL> """<STR_LIT>""" <EOL> order = dict ( ( a , idx ) for idx , a in enumerate ( addon_ids ) ) <EOL> existing = set ( self . addons . using ( '<STR_LIT:default>' ) <EOL> . values_list ( '<STR_LIT:id>' , flat = True ) ) <EOL> add , update = [ ] , [ ] <EOL> for addon in addon_ids : <EOL> bucket = update if addon in existing else add <EOL> bucket . append ( ( addon , order [ addon ] ) ) <EOL> remove = existing . difference ( addon_ids ) <EOL> cursor = connection . cursor ( ) <EOL> now = datetime . now ( ) <EOL> if remove : <EOL> cursor . execute ( "<STR_LIT>" <EOL> "<STR_LIT>" % <EOL> ( self . id , '<STR_LIT:U+002C>' . join ( map ( str , remove ) ) ) ) <EOL> if self . listed : <EOL> for addon in remove : <EOL> amo . log ( amo . LOG . REMOVE_FROM_COLLECTION , <EOL> ( Addon , addon ) , self ) <EOL> if add : <EOL> insert = '<STR_LIT>' <EOL> values = [ insert % ( a , self . id , idx ) for a , idx in add ] <EOL> cursor . execute ( """<STR_LIT>""" % '<STR_LIT:U+002C>' . join ( values ) ) <EOL> if self . listed : <EOL> for addon_id , idx in add : <EOL> amo . log ( amo . LOG . ADD_TO_COLLECTION , <EOL> ( Addon , addon_id ) , self ) <EOL> for addon , ordering in update : <EOL> ( CollectionAddon . objects . filter ( collection = self . id , addon = addon ) <EOL> . update ( ordering = ordering , modified = now ) ) <EOL> for addon , comment in comments . iteritems ( ) : <EOL> try : <EOL> c = ( CollectionAddon . objects . using ( '<STR_LIT:default>' ) <EOL> . get ( collection = self . id , addon = addon ) ) <EOL> except CollectionAddon . DoesNotExist : <EOL> pass <EOL> else : <EOL> c . comments = comment <EOL> c . save ( force_update = True ) <EOL> self . save ( ) <EOL> def is_subscribed ( self , user ) : <EOL> """<STR_LIT>""" <EOL> return self . following . filter ( user = user ) . exists ( ) <EOL> def add_addon ( self , addon ) : <EOL> "<STR_LIT>" <EOL> CollectionAddon . objects . get_or_create ( addon = addon , collection = self ) <EOL> if self . listed : <EOL> amo . log ( amo . LOG . ADD_TO_COLLECTION , addon , self ) <EOL> self . save ( ) <EOL> def remove_addon ( self , addon ) : <EOL> CollectionAddon . objects . filter ( addon = addon , collection = self ) . delete ( ) <EOL> if self . listed : <EOL> amo . log ( amo . LOG . REMOVE_FROM_COLLECTION , addon , self ) <EOL> self . save ( ) <EOL> def owned_by ( self , user ) : <EOL> return ( user . id == self . author_id ) <EOL> def can_view_stats ( self , request ) : <EOL> if request and request . user : <EOL> return ( self . publishable_by ( request . user ) or <EOL> acl . action_allowed ( request , '<STR_LIT>' , '<STR_LIT>' ) ) <EOL> return False <EOL> @ caching . cached_method <EOL> def publishable_by ( self , user ) : <EOL> return bool ( self . owned_by ( user ) or self . users . filter ( pk = user . id ) ) <EOL> @ staticmethod <EOL> def transformer ( collections ) : <EOL> if not collections : <EOL> return <EOL> author_ids = set ( c . author_id for c in collections ) <EOL> authors = dict ( ( u . id , u ) for u in <EOL> UserProfile . objects . filter ( id__in = author_ids ) ) <EOL> for c in collections : <EOL> c . author = authors . get ( c . author_id ) <EOL> @ staticmethod <EOL> def post_save ( sender , instance , ** kwargs ) : <EOL> from . import tasks <EOL> if kwargs . get ( '<STR_LIT>' ) : <EOL> return <EOL> tasks . collection_meta . delay ( instance . id , using = '<STR_LIT:default>' ) <EOL> tasks . index_collections . delay ( [ instance . id ] ) <EOL> @ staticmethod <EOL> def post_delete ( sender , instance , ** kwargs ) : <EOL> from . import tasks <EOL> if kwargs . get ( '<STR_LIT>' ) : <EOL> return <EOL> tasks . unindex_collections . delay ( [ instance . id ] ) <EOL> def check_ownership ( self , request , require_owner , require_author , <EOL> ignore_disabled , admin ) : <EOL> """<STR_LIT>""" <EOL> from olympia . access import acl <EOL> return acl . check_collection_ownership ( request , self , require_owner ) <EOL> models . signals . post_save . connect ( Collection . post_save , sender = Collection , <EOL> dispatch_uid = '<STR_LIT>' ) <EOL> models . signals . pre_save . connect ( save_signal , sender = Collection , <EOL> dispatch_uid = '<STR_LIT>' ) <EOL> models . signals . post_delete . connect ( Collection . post_delete , sender = Collection , <EOL> dispatch_uid = '<STR_LIT>' ) <EOL> class CollectionAddon ( ModelBase ) : <EOL> addon = models . ForeignKey ( Addon ) <EOL> collection = models . ForeignKey ( Collection ) <EOL> comments = LinkifiedField ( null = True ) <EOL> downloads = models . PositiveIntegerField ( default = <NUM_LIT:0> ) <EOL> user = models . ForeignKey ( UserProfile , null = True ) <EOL> ordering = models . PositiveIntegerField ( <EOL> default = <NUM_LIT:0> , <EOL> help_text = '<STR_LIT>' <EOL> '<STR_LIT>' ) <EOL> class Meta ( ModelBase . Meta ) : <EOL> db_table = '<STR_LIT>' <EOL> unique_together = ( ( '<STR_LIT>' , '<STR_LIT>' ) , ) <EOL> @ staticmethod <EOL> def post_save_or_delete ( sender , instance , ** kwargs ) : <EOL> """<STR_LIT>""" <EOL> from . import tasks <EOL> tasks . collection_meta . delay ( instance . collection_id , using = '<STR_LIT:default>' ) <EOL> models . signals . pre_save . connect ( save_signal , sender = CollectionAddon , <EOL> dispatch_uid = '<STR_LIT>' ) <EOL> models . signals . post_save . connect ( CollectionAddon . post_save_or_delete , <EOL> sender = CollectionAddon , <EOL> dispatch_uid = '<STR_LIT>' ) <EOL> models . signals . post_delete . connect ( CollectionAddon . post_save_or_delete , <EOL> sender = CollectionAddon , <EOL> dispatch_uid = '<STR_LIT>' ) <EOL> class CollectionFeature ( ModelBase ) : <EOL> title = TranslatedField ( ) <EOL> tagline = TranslatedField ( ) <EOL> class Meta ( ModelBase . Meta ) : <EOL> db_table = '<STR_LIT>' <EOL> models . signals . pre_save . connect ( save_signal , sender = CollectionFeature , <EOL> dispatch_uid = '<STR_LIT>' ) <EOL> class CollectionPromo ( ModelBase ) : <EOL> collection = models . ForeignKey ( Collection , null = True ) <EOL> locale = models . CharField ( max_length = <NUM_LIT:10> , null = True ) <EOL> collection_feature = models . ForeignKey ( CollectionFeature ) <EOL> class Meta ( ModelBase . Meta ) : <EOL> db_table = '<STR_LIT>' <EOL> unique_together = ( '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ) <EOL> @ staticmethod <EOL> def transformer ( promos ) : <EOL> if not promos : <EOL> return <EOL> promo_dict = dict ( ( p . id , p ) for p in promos ) <EOL> q = ( Collection . objects . no_cache ( ) <EOL> . filter ( collectionpromo__in = promos ) <EOL> . extra ( select = { '<STR_LIT>' : '<STR_LIT>' } ) ) <EOL> for promo_id , collection in ( sorted_groupby ( q , '<STR_LIT>' ) ) : <EOL> promo_dict [ promo_id ] . collection = collection . next ( ) <EOL> class CollectionWatcher ( ModelBase ) : <EOL> collection = models . ForeignKey ( Collection , related_name = '<STR_LIT>' ) <EOL> user = models . ForeignKey ( UserProfile ) <EOL> class Meta ( ModelBase . Meta ) : <EOL> db_table = '<STR_LIT>' <EOL> def flush_urls ( self ) : <EOL> urls = [ '<STR_LIT>' % self . user_id ] <EOL> return urls <EOL> @ staticmethod <EOL> def post_save_or_delete ( sender , instance , ** kw ) : <EOL> from . import tasks <EOL> tasks . collection_watchers ( instance . collection_id , using = '<STR_LIT:default>' ) <EOL> models . signals . post_save . connect ( CollectionWatcher . post_save_or_delete , <EOL> sender = CollectionWatcher ) <EOL> models . signals . post_delete . connect ( CollectionWatcher . post_save_or_delete , <EOL> sender = CollectionWatcher ) <EOL> class CollectionUser ( models . Model ) : <EOL> collection = models . ForeignKey ( Collection ) <EOL> user = models . ForeignKey ( UserProfile ) <EOL> role = models . SmallIntegerField ( <EOL> default = <NUM_LIT:1> , <EOL> choices = amo . COLLECTION_AUTHOR_CHOICES . items ( ) ) <EOL> class Meta : <EOL> db_table = '<STR_LIT>' <EOL> class CollectionVote ( models . Model ) : <EOL> collection = models . ForeignKey ( Collection , related_name = '<STR_LIT>' ) <EOL> user = models . ForeignKey ( UserProfile , related_name = '<STR_LIT>' ) <EOL> vote = models . SmallIntegerField ( default = <NUM_LIT:0> ) <EOL> created = models . DateTimeField ( null = True , auto_now_add = True ) <EOL> class Meta : <EOL> db_table = '<STR_LIT>' <EOL> def flush_urls ( self ) : <EOL> urls = [ '<STR_LIT>' % self . collection . get_url_path ( ) ] <EOL> return urls <EOL> @ staticmethod <EOL> def post_save_or_delete ( sender , instance , ** kwargs ) : <EOL> if Collection . objects . filter ( id = instance . collection_id ) . exists ( ) : <EOL> from . import tasks <EOL> tasks . collection_votes ( instance . collection_id , using = '<STR_LIT:default>' ) <EOL> models . signals . post_save . connect ( CollectionVote . post_save_or_delete , <EOL> sender = CollectionVote ) <EOL> models . signals . post_delete . connect ( CollectionVote . post_save_or_delete , <EOL> sender = CollectionVote ) <EOL> class FeaturedCollection ( ModelBase ) : <EOL> application = models . PositiveIntegerField ( choices = amo . APPS_CHOICES , <EOL> db_column = '<STR_LIT>' ) <EOL> collection = models . ForeignKey ( Collection ) <EOL> locale = models . CharField ( max_length = <NUM_LIT:10> , null = True ) <EOL> class Meta : <EOL> db_table = '<STR_LIT>' <EOL> def __unicode__ ( self ) : <EOL> return u'<STR_LIT>' % ( self . collection , self . application , <EOL> self . locale ) <EOL> class MonthlyPick ( ModelBase ) : <EOL> addon = models . ForeignKey ( Addon ) <EOL> blurb = models . TextField ( ) <EOL> image = models . URLField ( ) <EOL> locale = models . CharField ( max_length = <NUM_LIT:10> , unique = True , null = True , <EOL> blank = True ) <EOL> class Meta : <EOL> db_table = '<STR_LIT>' </s>
