root/trunk/djedna/store/models.py

Revision 409, 15.7 kB (checked in by thomas, 11 months ago)

First check in of initial store functionality

Line 
1 # (c) Copyright 2008 Thomas Bohmbach, Jr.
2 #
3 # This file is part of DJ Edna.
4 #
5 # DJ Edna is free software: you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option)
8 # any later version.
9 #
10 # DJ Edna is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13 # more details.
14 #
15 # You should have received a copy of the GNU General Public License along with
16 # DJ Edna.  If not, see <http://www.gnu.org/licenses/>.
17
18 import datetime
19 import types
20 import logging as log
21
22 from django.contrib.auth.models import User
23 from django.contrib.contenttypes import generic
24 from django.contrib.contenttypes.models import ContentType
25 from django.db import models
26
27 from djedna.permit.models import Action, UserAccessPermit
28
29
30 class FixedPrice(models.Model):
31     price = models.DecimalField(core=True, default=0.0, max_digits=7, decimal_places=2)
32    
33     def __unicode__(self):
34         return u'%s' % self.price
35    
36     def _get_minimum(self):
37         return self.price
38     minimum = property(_get_minimum)
39    
40     def _get_recommended(self):
41         return self.price
42     recommended = property(_get_recommended)
43    
44     def is_valid_price(self, price):
45         return price == self.price
46    
47     class Admin:
48         list_display = ('price',)
49         ordering = ('-price',)
50    
51
52 class FloatingPrice(models.Model):
53     minimum = models.DecimalField(core=True, default=0.0, max_digits=7, decimal_places=2)
54     recommended = models.DecimalField(null=True, blank=True, max_digits=7, decimal_places=2)
55    
56     def __unicode__(self):
57         return u'Min: %s, Recommend: %s' % (self.minimum, self.recommended)
58    
59     def _get_price(self):
60         return self.recommended
61     price = property(_get_price)
62    
63     def is_valid_price(self, price):
64         if price >= self.minimum:
65             return True
66         return False
67    
68     class Admin:
69         list_display = ('minimum', 'recommended')
70         ordering = ('-recommended',)
71    
72
73 class UserAccessPermitProductManager(models.Manager):
74     def get_products(self, user, content_object):
75         products = set()
76         try:
77             # This import is done here to avoid circular import importing this module
78             from djedna.catalog.models import Artist, Album, Track, TrackFile
79            
80             content_object_type = ContentType.objects.get_for_model(content_object)
81             track_file_model_type = ContentType.objects.get_for_model(TrackFile)
82             if content_object_type == track_file_model_type:
83                 products |= set(self.get_track_file_products(content_object))
84                 products |= set(self.get_track_products(content_object.track))
85                 products |= set(self.get_album_products(content_object.track.album))
86                 products |= set(self.get_artist_products(content_object.track.artist))
87             track_model_type = ContentType.objects.get_for_model(Track)
88             if content_object_type == track_model_type:
89                 products |= set(self.get_track_products(content_object))
90                 products |= set(self.get_album_products(content_object.album))
91                 products |= set(self.get_artist_products(content_object.artist))
92             album_model_type = ContentType.objects.get_for_model(Album)
93             if content_object_type == album_model_type:
94                 products |= set(self.get_album_products(content_object))
95                 products |= set(self.get_artist_products(content_object.artist))
96             artist_model_type = ContentType.objects.get_for_model(Artist)
97             if content_object_type == artist_model_type:
98                 products |= set(self.get_artist_products(content_object))
99         except Exception, e:
100             log.error("UserAccessPermitProductManager:get_products(): %s" % e)
101         return list(products)
102    
103     def get_artist_products(self, artist):
104         artist_products = []
105         if artist:
106             # This import is done here to avoid circular import importing this module
107             from djedna.catalog.models import Artist
108             artist_model_type = ContentType.objects.get_for_model(Artist)
109             artist_type = ContentType.objects.get_for_model(artist)
110             if artist_type == artist_model_type:
111                 products = self.filter(content_type__pk=artist_model_type.id, content_id=artist.id)
112                 for product in products:
113                     if product.is_valid():
114                         artist_products.append(product)
115         return artist_products
116    
117     def get_album_products(self, album):
118         album_products = []
119         if album:
120             # This import is done here to avoid circular import importing this module
121             from djedna.catalog.models import Album
122             album_model_type = ContentType.objects.get_for_model(Album)
123             album_type = ContentType.objects.get_for_model(album)
124             if album_type == album_model_type:
125                 products = self.filter(content_type__pk=album_model_type.id, content_id=album.id)
126                 for product in products:
127                     if product.is_valid():
128                         album_products.append(product)
129         return album_products
130    
131     def get_track_products(self, track):
132         track_products = []
133         if track:
134             # This import is done here to avoid circular import importing this module
135             from djedna.catalog.models import Track
136             track_model_type = ContentType.objects.get_for_model(Track)
137             track_type = ContentType.objects.get_for_model(track)
138             if track_type == track_model_type:
139                 products = self.filter(content_type__pk=track_model_type.id, content_id=track.id)
140                 for product in products:
141                     if product.is_valid():
142                         track_products.append(product)
143         return track_products
144    
145     def get_track_file_products(self, track_file):
146         track_file_products = []
147         if track_file:
148             # This import is done here to avoid circular import importing this module
149             from djedna.catalog.models import TrackFile
150             track_file_model_type = ContentType.objects.get_for_model(TrackFile)
151             track_file_type = ContentType.objects.get_for_model(track_file)
152             if track_file_type == track_file_model_type:
153                 products = self.filter(content_type__pk=track_file_model_type.id, content_id=track_file.id)
154                 for product in products:
155                     if product.is_valid():
156                         track_file_products.append(product)
157         return track_file_products
158    
159 # class Product(models.Model):
160 #     name = models.CharField(max_length=1024, core=True, unique=True)
161 #     slug = models.SlugField(prepopulate_from=['name'])
162 #     description = models.CharField(max_length=2048)
163 #     is_active = models.BooleanField(default=True)
164 #     product_begins = models.DateTimeField(null=True, blank=True)
165 #     product_expires = models.DateTimeField(null=True, blank=True)
166 #     pricing_type = models.ForeignKey(ContentType, limit_choices_to={'name__in' : ['fixed price', 'floating price']}, core=True, radio_admin=True, related_name='product_pricings')
167 #     pricing_id = models.PositiveIntegerField(core=True)
168 #     pricing_object = generic.GenericForeignKey(ct_field="pricing_type", fk_field="pricing_id")
169 #     cart_items = generic.GenericRelation('CartItem', object_id_field='product_id', content_type_field='product_type')
170 #     added = models.DateTimeField(auto_now_add=True)
171 #     deliverable_type = models.ForeignKey(ContentType, limit_choices_to={'name__in' : ['user access permit product']}, core=True, radio_admin=True, related_name='product_contents')
172 #     deliverable_id = models.PositiveIntegerField(core=True)
173 #     deliverable = generic.GenericForeignKey(ct_field='deliverable_type', fk_field='deliverable_id')
174
175 class UserAccessPermitProduct(models.Model):
176     name = models.CharField(max_length=1024, core=True, unique=True)
177     slug = models.SlugField(prepopulate_from=['name'])
178     actions = models.ManyToManyField(Action, filter_interface=True, related_name='permit_products')
179     content_type = models.ForeignKey(ContentType, limit_choices_to={'name__in' : ['artist', 'album', 'track']}, core=True, radio_admin=True, related_name='product_contents')
180     content_id = models.PositiveIntegerField(core=True)
181     content_object = generic.GenericForeignKey(ct_field='content_type', fk_field='content_id')
182     permit_length = models.IntegerField(null=True, blank=True)
183     max_access = models.IntegerField(null=True, blank=True)
184     is_active = models.BooleanField(default=True)
185     product_begins = models.DateTimeField(null=True, blank=True)
186     product_expires = models.DateTimeField(null=True, blank=True)
187     pricing_type = models.ForeignKey(ContentType, limit_choices_to={'name__in' : ['fixed price', 'floating price']}, core=True, radio_admin=True, related_name='product_pricings')
188     pricing_id = models.PositiveIntegerField(core=True)
189     pricing_object = generic.GenericForeignKey(ct_field="pricing_type", fk_field="pricing_id")
190     cart_items = generic.GenericRelation('CartItem', object_id_field='product_id', content_type_field='product_type')
191     added = models.DateTimeField(auto_now_add=True)
192     objects = UserAccessPermitProductManager()
193    
194     def __unicode__(self):
195         return u'%s-[%s: %s]-%s' % (self.action_list(), self.content_type.name, self.content_object, self.pricing_object)
196    
197     def _get_artist(self):
198         if self.content_type.name == 'artist':
199             return self.content_object
200         else:
201             return None
202     artist = property(_get_artist)
203    
204     def _get_album(self):
205         if self.content_type.name == 'album':
206             return self.content_object
207         else:
208             return None
209     album = property(_get_album)
210    
211     def _get_track(self):
212         if self.content_type.name == 'track':
213             return self.content_object
214         else:
215             return None
216     track = property(_get_track)
217    
218     def _get_price(self):
219         return self.pricing_object.price
220     price = property(_get_price)
221    
222     def is_valid(self, as_of=None):
223         if not self.is_active:
224             return False
225         if not as_of:
226             as_of = datetime.datetime.now()
227         if self.product_begins and (self.product_begins >= as_of):
228             return False
229         if self.product_expires and (self.product_expires <= as_of):
230             return False
231         return True
232     is_valid.boolean = True
233    
234     def purchase(self, user, price):
235         if self.is_valid() and self.pricing_object.is_valid_price(price):
236             #Create the permit
237             now = datetime.datetime.now()
238             expiration = None
239             if self.permit_length:
240                 expiration = now + datetime.timedelta(days=self.permit_length)
241             user_action_permit = UserAccessPermit.objects.create(
242                                         user=user,
243                                         content_object=self.content_object,
244                                         expires=expiration,
245                                         max_access=self.max_access)
246             for action in self.actions.all():
247                 user_action_permit.actions.add(action)
248             #Create the purchase record
249             UserAccessPermitPurchase.objects.create(user=user, price=price, product=self, permit=user_action_permit, date=now)
250             return True
251         return False
252    
253     class Admin:
254         list_display = ('action_list', 'content_object_name', 'permit_length',
255                         'max_access', 'pricing_object_name', 'product_begins',
256                         'product_expires', 'added', 'is_active', 'is_valid')
257         date_hierarchy = 'added'
258         ordering = ('-added',)
259    
260     def content_object_name(self):
261         if self.content_object:
262             return u'%s: %s' % (self.content_type.name, self.content_object)
263         else:
264             return None
265     content_object_name.short_description = 'Permit To'
266    
267     def pricing_object_name(self):
268         if self.pricing_object:
269             return u'%s' % self.pricing_object
270         else:
271             return None
272     pricing_object_name.short_description = 'Price'
273    
274     def action_list(self):
275         if self.actions.count() > 0:
276             return u', '.join([action.name for action in self.actions.all()])
277         else:
278             return None
279     action_list.short_description = 'Permitted Actions'
280    
281
282 def get_products(user, content_object):
283         products = set()
284         try:
285             products = set(UserAccessPermitProduct.objects.get_products(user, content_object))
286             #products |= set(GroupProduct.objects.get_products(user, group))
287         except Exception, e:
288             log.error("Error getting products: %s" % e)
289         return list(products)
290
291 class UserAccessPermitPurchase(models.Model):
292     user = models.ForeignKey(User, core=True, null=True, blank=True)
293     price = models.DecimalField(core=True, default=0.0, max_digits=7, decimal_places=2)
294     product = models.ForeignKey('UserAccessPermitProduct', core=True, related_name='purchases')
295     permit = models.ForeignKey(UserAccessPermit, null=True, blank=True, related_name='purchases')
296     date = models.DateTimeField(core=True, null=True, blank=True)
297    
298     def __unicode__(self):
299         return u'%s purchased (%s) on %s for %.2f' % (self.user, self.product, self.date, self.price)
300    
301     class Admin:
302         list_display = ('product', 'user', 'user_email', 'price', 'date')
303         list_filter = ('user',)
304         date_hierarchy = 'date'
305         ordering = ('-date',)
306         search_fields = ['user__username', 'user__email']
307    
308     def user_email(self):
309         return self.user.email
310     user_email.short_description = 'User e-mail'
311    
312
313 class CartItem(models.Model):
314     product_type = models.ForeignKey(ContentType, limit_choices_to={'name__in' : ['user access permit purchase']}, core=True, radio_admin=True)
315     product_id = models.PositiveIntegerField(core=True)
316     product = generic.GenericForeignKey(ct_field='product_type', fk_field='product_id')
317     price = models.DecimalField(core=True, default=0.0, max_digits=7, decimal_places=2)
318     cart = models.ForeignKey('Cart', core=True, related_name='items')
319     added = models.DateTimeField(auto_now_add=True)
320    
321     def __unicode__(self):
322         return u'%s for %.2f' % (self.product, self.price)
323    
324     def purchase(self):
325         self.product.purchase(self.cart.user, self.price)
326    
327     class Meta:
328         ordering = ['-added']
329    
330     class Admin:
331         pass
332    
333
334 class Cart(models.Model):
335     user = models.ForeignKey(User, core=True, unique=True)
336     added = models.DateTimeField(auto_now_add=True)
337     modified = models.DateTimeField(auto_now=True)
338     #items = From CartItems ForeignKey
339     
340     def __unicode__(self):
341         return u'Cart for %s contains %s' % (self.user, self.items.all())
342    
343     def add(self, product, price):
344         CartItem.objects.create(product=product, price=price, cart=self)
345    
346     def remove(self, product):
347         product_type = ContentType.objects.get_for_model(product)
348         CartItem.objects.filter(product_type=product_type, product_id=product.id, cart=self).delete()
349    
350     def contains_product(self, product):
351         product_type = ContentType.objects.get_for_model(product)
352         return CartItem.objects.filter(product_type=product_type, product_id=product.id, cart=self).count() > 0
353    
354     def clear(self):
355         CartItem.objects.filter(cart=self).delete()
356    
357     def purchase(self):
358         for item in self.items:
359             item.purchase()
360             item.delete()
361    
362     class Admin:
363         pass
364    
365
Note: See TracBrowser for help on using the browser.