2011-12-03 04:07:55 +00:00
|
|
|
import re
|
2011-10-10 16:57:10 +00:00
|
|
|
import random
|
2012-03-07 19:42:16 +00:00
|
|
|
from regluit.utils.localdatetime import now, date_today
|
|
|
|
from datetime import timedelta
|
2011-10-10 16:57:10 +00:00
|
|
|
from decimal import Decimal
|
2012-04-03 14:45:12 +00:00
|
|
|
from notification import models as notification
|
2011-12-03 04:07:55 +00:00
|
|
|
|
2011-08-31 03:46:55 +00:00
|
|
|
from django.db import models
|
2012-03-26 22:46:34 +00:00
|
|
|
from django.db.models import Q, get_model
|
2011-09-02 04:10:54 +00:00
|
|
|
from django.contrib.auth.models import User
|
2011-11-16 19:45:37 +00:00
|
|
|
from django.conf import settings
|
2011-11-24 02:41:06 +00:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
2011-10-10 16:57:10 +00:00
|
|
|
|
2011-11-10 17:33:22 +00:00
|
|
|
import regluit
|
2011-12-20 04:26:55 +00:00
|
|
|
import regluit.core.isbn
|
2011-11-10 17:33:22 +00:00
|
|
|
|
2011-10-08 03:11:57 +00:00
|
|
|
class UnglueitError(RuntimeError):
|
|
|
|
pass
|
2011-09-06 03:50:38 +00:00
|
|
|
|
2011-11-10 01:31:31 +00:00
|
|
|
class CeleryTask(models.Model):
|
2012-03-07 19:42:16 +00:00
|
|
|
created = models.DateTimeField(auto_now_add=True, default=now())
|
2011-11-19 16:55:35 +00:00
|
|
|
task_id = models.CharField(max_length=255)
|
2011-11-10 17:33:22 +00:00
|
|
|
user = models.ForeignKey(User, related_name="tasks", null=True)
|
|
|
|
description = models.CharField(max_length=2048, null=True) # a description of what the task is
|
|
|
|
function_name = models.CharField(max_length=1024) # used to reconstitute the AsyncTask with which to get status
|
2011-11-15 21:35:50 +00:00
|
|
|
function_args = models.IntegerField(null=True) # not full generalized here -- takes only a single arg for now.
|
2011-11-10 17:33:22 +00:00
|
|
|
active = models.NullBooleanField(default=True)
|
2011-11-10 01:31:31 +00:00
|
|
|
|
|
|
|
def __unicode__(self):
|
2012-03-30 19:27:35 +00:00
|
|
|
return "Task %s arg:%s ID# %s %s: State %s " % (self.function_name, self.function_args, self.task_id, self.description, self.state)
|
2011-11-10 17:33:22 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def AsyncResult(self):
|
|
|
|
f = getattr(regluit.core.tasks,self.function_name)
|
|
|
|
return f.AsyncResult(self.task_id)
|
|
|
|
@property
|
|
|
|
def state(self):
|
|
|
|
f = getattr(regluit.core.tasks,self.function_name)
|
|
|
|
return f.AsyncResult(self.task_id).state
|
|
|
|
@property
|
|
|
|
def result(self):
|
|
|
|
f = getattr(regluit.core.tasks,self.function_name)
|
|
|
|
return f.AsyncResult(self.task_id).result
|
2011-11-10 01:31:31 +00:00
|
|
|
@property
|
2011-11-10 17:33:22 +00:00
|
|
|
def info(self):
|
|
|
|
f = getattr(regluit.core.tasks,self.function_name)
|
|
|
|
return f.AsyncResult(self.task_id).info
|
|
|
|
|
2011-11-07 20:41:51 +00:00
|
|
|
class Claim(models.Model):
|
2011-11-16 19:45:37 +00:00
|
|
|
STATUSES = ((
|
2012-04-05 15:58:26 +00:00
|
|
|
u'active', u'Claim has been accepted.'),
|
|
|
|
(u'pending', u'Claim is pending acceptance.'),
|
2012-03-28 18:12:10 +00:00
|
|
|
(u'release', u'Claim has not been accepted.'),
|
2011-11-16 19:45:37 +00:00
|
|
|
)
|
2011-11-19 16:55:35 +00:00
|
|
|
created = models.DateTimeField(auto_now_add=True)
|
2011-11-07 20:41:51 +00:00
|
|
|
rights_holder = models.ForeignKey("RightsHolder", related_name="claim", null=False )
|
|
|
|
work = models.ForeignKey("Work", related_name="claim", null=False )
|
2011-11-16 17:20:19 +00:00
|
|
|
user = models.ForeignKey(User, related_name="claim", null=False )
|
2011-11-16 19:45:37 +00:00
|
|
|
status = models.CharField(max_length=7, choices= STATUSES, default='pending')
|
2011-11-20 02:12:18 +00:00
|
|
|
|
2011-11-07 20:41:51 +00:00
|
|
|
class RightsHolder(models.Model):
|
2011-11-19 16:55:35 +00:00
|
|
|
created = models.DateTimeField(auto_now_add=True)
|
2011-11-07 20:41:51 +00:00
|
|
|
email = models.CharField(max_length=100, blank=True)
|
2012-03-28 18:12:10 +00:00
|
|
|
rights_holder_name = models.CharField(max_length=100, blank=False)
|
2011-11-07 20:41:51 +00:00
|
|
|
owner = models.ForeignKey(User, related_name="rights_holder", null=False )
|
2011-11-15 23:20:29 +00:00
|
|
|
def __unicode__(self):
|
|
|
|
return self.rights_holder_name
|
2011-11-07 20:41:51 +00:00
|
|
|
|
2011-11-06 19:41:14 +00:00
|
|
|
class Premium(models.Model):
|
2012-03-26 19:31:41 +00:00
|
|
|
PREMIUM_TYPES = ((u'00', u'Default'),(u'CU', u'Custom'),(u'XX', u'Inactive'))
|
2011-11-19 16:55:35 +00:00
|
|
|
created = models.DateTimeField(auto_now_add=True)
|
2011-11-06 19:43:10 +00:00
|
|
|
type = models.CharField(max_length=2, choices=PREMIUM_TYPES)
|
2011-11-06 19:58:51 +00:00
|
|
|
campaign = models.ForeignKey("Campaign", related_name="premiums", null=True)
|
2011-11-06 19:04:31 +00:00
|
|
|
amount = models.DecimalField(max_digits=10, decimal_places=0, blank=False)
|
|
|
|
description = models.TextField(null=True, blank=False)
|
2012-03-26 19:31:41 +00:00
|
|
|
limit = models.IntegerField(default = 0)
|
2011-11-06 19:04:31 +00:00
|
|
|
|
2012-03-26 22:46:34 +00:00
|
|
|
@property
|
|
|
|
def premium_count(self):
|
|
|
|
t_model=get_model('payment','Transaction')
|
|
|
|
return t_model.objects.filter(premium=self).count()
|
|
|
|
@property
|
|
|
|
def premium_remaining(self):
|
|
|
|
t_model=get_model('payment','Transaction')
|
|
|
|
return self.limit - t_model.objects.filter(premium=self).count()
|
|
|
|
|
2011-12-13 21:24:39 +00:00
|
|
|
class CampaignAction(models.Model):
|
|
|
|
timestamp = models.DateTimeField(auto_now_add=True)
|
|
|
|
# anticipated types: activated, withdrawn, suspended, restarted, succeeded, failed, unglued
|
|
|
|
type = models.CharField(max_length=15)
|
|
|
|
comment = models.TextField(null=True, blank=True)
|
|
|
|
campaign = models.ForeignKey("Campaign", related_name="actions", null=False)
|
|
|
|
|
2011-08-31 03:46:55 +00:00
|
|
|
class Campaign(models.Model):
|
2012-03-23 16:30:49 +00:00
|
|
|
LICENSE_CHOICES = (('CC BY-NC-ND','CC BY-NC-ND'),
|
|
|
|
('CC BY-ND','CC BY-ND'),
|
|
|
|
('CC BY','CC BY'),
|
|
|
|
('CC BY-NC','CC BY-NC'),
|
|
|
|
( 'CC BY-NC-SA','CC BY-NC-SA'),
|
|
|
|
( 'CC BY-SA','CC BY-SA'),
|
|
|
|
( 'CC0','CC0'),
|
|
|
|
)
|
2011-09-02 04:10:54 +00:00
|
|
|
created = models.DateTimeField(auto_now_add=True)
|
2011-10-14 19:37:22 +00:00
|
|
|
name = models.CharField(max_length=500, null=True, blank=False)
|
|
|
|
description = models.TextField(null=True, blank=False)
|
2011-11-21 03:23:51 +00:00
|
|
|
details = models.TextField(null=True, blank=False)
|
2011-10-14 19:37:22 +00:00
|
|
|
target = models.DecimalField(max_digits=14, decimal_places=2, null=True, blank=False)
|
2012-03-23 16:30:49 +00:00
|
|
|
license = models.CharField(max_length=255, choices = LICENSE_CHOICES, default='CC BY-NC-ND')
|
2011-12-14 05:52:42 +00:00
|
|
|
left = models.DecimalField(max_digits=14, decimal_places=2, null=True, blank=False)
|
2011-10-14 19:37:22 +00:00
|
|
|
deadline = models.DateTimeField()
|
2011-10-07 21:17:54 +00:00
|
|
|
activated = models.DateTimeField(null=True)
|
2011-10-14 19:37:22 +00:00
|
|
|
paypal_receiver = models.CharField(max_length=100, blank=True)
|
|
|
|
amazon_receiver = models.CharField(max_length=100, blank=True)
|
2011-10-09 18:27:27 +00:00
|
|
|
work = models.ForeignKey("Work", related_name="campaigns", null=False)
|
2011-11-21 03:23:51 +00:00
|
|
|
managers = models.ManyToManyField(User, related_name="campaigns", null=False)
|
2012-01-03 15:20:17 +00:00
|
|
|
# status: INITIALIZED, ACTIVE, SUSPENDED, WITHDRAWN, SUCCESSFUL, UNSUCCESSFUL
|
2011-12-06 15:35:05 +00:00
|
|
|
status = models.CharField(max_length=15, null=True, blank=False, default="INITIALIZED")
|
2011-11-24 02:41:06 +00:00
|
|
|
problems = []
|
|
|
|
|
2011-09-10 11:36:38 +00:00
|
|
|
def __unicode__(self):
|
2011-10-08 03:11:57 +00:00
|
|
|
try:
|
|
|
|
return u"Campaign for %s" % self.work.title
|
|
|
|
except:
|
|
|
|
return u"Campaign %s (no associated work)" % self.name
|
2011-11-24 02:41:06 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def launchable(self):
|
|
|
|
may_launch=True
|
|
|
|
if self.status != 'INITIALIZED':
|
2011-11-29 15:48:37 +00:00
|
|
|
if self.status == 'ACTIVE':
|
|
|
|
self.problems.append(_('The campaign is already launched'))
|
|
|
|
else:
|
|
|
|
self.problems.append(_('A campaign must initialized properly before it can be launched'))
|
2011-11-24 02:41:06 +00:00
|
|
|
may_launch = False
|
|
|
|
if self.target < Decimal(settings.UNGLUEIT_MINIMUM_TARGET):
|
|
|
|
self.problems.append(_('A campaign may not be launched with a target less than $%s' % settings.UNGLUEIT_MINIMUM_TARGET))
|
|
|
|
may_launch = False
|
2012-03-07 19:42:16 +00:00
|
|
|
if self.deadline.date()- date_today() > timedelta(days=int(settings.UNGLUEIT_LONGEST_DEADLINE)):
|
2011-11-24 02:41:06 +00:00
|
|
|
self.problems.append(_('The chosen closing date is more than %s days from now' % settings.UNGLUEIT_LONGEST_DEADLINE))
|
2011-11-29 15:48:37 +00:00
|
|
|
may_launch = False
|
2012-03-07 19:42:16 +00:00
|
|
|
elif self.deadline.date()- date_today() < timedelta(days=int(settings.UNGLUEIT_SHORTEST_DEADLINE)):
|
2011-11-29 15:48:37 +00:00
|
|
|
self.problems.append(_('The chosen closing date is less than %s days from now' % settings.UNGLUEIT_SHORTEST_DEADLINE))
|
|
|
|
may_launch = False
|
2011-11-24 02:41:06 +00:00
|
|
|
return may_launch
|
|
|
|
|
2011-12-06 14:40:04 +00:00
|
|
|
|
2012-03-09 22:18:11 +00:00
|
|
|
def update_status(self):
|
2012-04-02 23:10:56 +00:00
|
|
|
"""Updates the campaign's status. returns true if updated. Computes SUCCESSFUL or UNSUCCESSFUL only after the deadline has passed
|
|
|
|
|
2011-10-07 21:17:54 +00:00
|
|
|
"""
|
2011-12-06 15:35:05 +00:00
|
|
|
if not self.status=='ACTIVE':
|
|
|
|
return False
|
2012-04-03 00:57:30 +00:00
|
|
|
elif self.deadline < now():
|
2011-10-10 20:56:53 +00:00
|
|
|
if self.current_total >= self.target:
|
2011-12-06 15:35:05 +00:00
|
|
|
self.status = 'SUCCESSFUL'
|
2011-12-13 21:24:39 +00:00
|
|
|
action = CampaignAction(campaign=self, type='succeeded', comment = self.current_total)
|
|
|
|
action.save()
|
2012-04-02 23:10:56 +00:00
|
|
|
regluit.core.signals.successful_campaign.send(sender=None,campaign=self)
|
2011-10-08 03:11:57 +00:00
|
|
|
else:
|
2012-04-02 23:10:56 +00:00
|
|
|
self.status = 'UNSUCCESSFUL'
|
2011-12-13 21:24:39 +00:00
|
|
|
action = CampaignAction(campaign=self, type='failed', comment = self.current_total)
|
|
|
|
action.save()
|
2011-12-06 15:35:05 +00:00
|
|
|
self.save()
|
|
|
|
return True
|
2011-10-09 18:27:27 +00:00
|
|
|
else:
|
2011-12-06 15:35:05 +00:00
|
|
|
return False
|
2011-10-08 03:11:57 +00:00
|
|
|
|
2011-10-10 20:56:53 +00:00
|
|
|
@property
|
|
|
|
def current_total(self):
|
2011-12-14 05:52:42 +00:00
|
|
|
if self.left:
|
|
|
|
return self.target-self.left
|
|
|
|
else:
|
|
|
|
return 0
|
2011-10-10 20:56:53 +00:00
|
|
|
|
2012-01-03 19:49:37 +00:00
|
|
|
def transactions(self, summary=False, pledged=True, authorized=True, incomplete=True, completed=True):
|
2011-10-13 17:28:23 +00:00
|
|
|
p = PaymentManager()
|
2012-01-03 19:49:37 +00:00
|
|
|
return p.query_campaign(campaign=self, summary=summary, pledged=pledged, authorized=authorized, incomplete=incomplete,
|
|
|
|
completed=completed)
|
2011-10-13 17:28:23 +00:00
|
|
|
|
2012-04-02 18:17:18 +00:00
|
|
|
|
2011-10-08 03:11:57 +00:00
|
|
|
def activate(self):
|
2011-10-10 20:35:22 +00:00
|
|
|
status = self.status
|
2011-10-08 03:11:57 +00:00
|
|
|
if status != 'INITIALIZED':
|
2011-11-24 02:41:06 +00:00
|
|
|
raise UnglueitError(_('Campaign needs to be initialized in order to be activated'))
|
2011-12-06 15:35:05 +00:00
|
|
|
self.status= 'ACTIVE'
|
2011-12-14 05:52:42 +00:00
|
|
|
self.left = self.target
|
2011-10-09 18:27:27 +00:00
|
|
|
self.save()
|
2012-04-03 15:04:12 +00:00
|
|
|
active_claim = self.work.claim.filter(status="active")[0]
|
|
|
|
ungluers = self.work.wished_by()
|
|
|
|
notification.queue(ungluers, "active_campaign", {'campaign':self, 'active_claim':active_claim}, True)
|
2012-04-02 18:17:18 +00:00
|
|
|
return self
|
2012-04-03 15:04:12 +00:00
|
|
|
|
2011-10-08 03:11:57 +00:00
|
|
|
|
|
|
|
def suspend(self, reason):
|
2011-10-10 20:35:22 +00:00
|
|
|
status = self.status
|
2011-10-08 03:11:57 +00:00
|
|
|
if status != 'ACTIVE':
|
2011-11-24 02:41:06 +00:00
|
|
|
raise UnglueitError(_('Campaign needs to be active in order to be suspended'))
|
2011-12-13 21:24:39 +00:00
|
|
|
action = CampaignAction( campaign = self, type='suspended', comment = reason)
|
|
|
|
action.save()
|
2011-12-06 15:35:05 +00:00
|
|
|
self.status='SUSPENDED'
|
2011-10-09 18:27:27 +00:00
|
|
|
self.save()
|
|
|
|
return self
|
2011-10-08 03:11:57 +00:00
|
|
|
|
|
|
|
def withdraw(self, reason):
|
2011-10-10 20:35:22 +00:00
|
|
|
status = self.status
|
2011-10-08 03:11:57 +00:00
|
|
|
if status != 'ACTIVE':
|
2011-11-24 02:41:06 +00:00
|
|
|
raise UnglueitError(_('Campaign needs to be active in order to be withdrawn'))
|
2011-12-13 21:24:39 +00:00
|
|
|
action = CampaignAction( campaign = self, type='withdrawn', comment = reason)
|
|
|
|
action.save()
|
2011-12-06 15:35:05 +00:00
|
|
|
self.status='WITHDRAWN'
|
2011-10-09 18:27:27 +00:00
|
|
|
self.save()
|
|
|
|
return self
|
2011-10-08 03:11:57 +00:00
|
|
|
|
2011-12-13 21:24:39 +00:00
|
|
|
def resume(self, reason):
|
2011-10-08 03:11:57 +00:00
|
|
|
"""Change campaign status from SUSPENDED to ACTIVE. We may want to track reason for resuming and track history"""
|
2011-10-10 20:35:22 +00:00
|
|
|
status = self.status
|
2011-10-08 03:11:57 +00:00
|
|
|
if status != 'SUSPENDED':
|
2011-11-24 02:41:06 +00:00
|
|
|
raise UnglueitError(_('Campaign needs to be suspended in order to be resumed'))
|
2011-12-13 21:24:39 +00:00
|
|
|
if not reason:
|
|
|
|
reason=''
|
|
|
|
action = CampaignAction( campaign = self, type='restarted', comment = reason)
|
|
|
|
action.save()
|
2011-12-06 15:35:05 +00:00
|
|
|
self.status= 'ACTIVE'
|
2011-10-09 18:27:27 +00:00
|
|
|
self.save()
|
|
|
|
return self
|
2011-11-06 19:02:29 +00:00
|
|
|
|
|
|
|
def supporters(self):
|
2012-01-09 18:55:22 +00:00
|
|
|
"""nb: returns (distinct) supporter IDs, not supporter objects"""
|
2011-11-07 20:39:02 +00:00
|
|
|
translist = self.transactions().values_list('user', flat=True).distinct()
|
|
|
|
return translist
|
2011-12-13 21:24:39 +00:00
|
|
|
|
2011-11-30 16:58:26 +00:00
|
|
|
def effective_premiums(self):
|
2012-03-26 22:46:34 +00:00
|
|
|
"""returns the available premiums for the Campaign including any default premiums"""
|
|
|
|
q = Q(campaign=self) | Q(campaign__isnull=True)
|
|
|
|
return Premium.objects.filter(q).exclude(type='XX').order_by('amount')
|
2012-01-02 22:22:25 +00:00
|
|
|
|
|
|
|
class Identifier(models.Model):
|
2012-01-09 18:55:22 +00:00
|
|
|
# olib, ltwk, goog, gdrd, thng, isbn, oclc, olwk, olib
|
2012-01-02 22:22:25 +00:00
|
|
|
type = models.CharField(max_length=4, null=False)
|
|
|
|
value = models.CharField(max_length=31, null=False)
|
|
|
|
work = models.ForeignKey("Work", related_name="identifiers", null=False)
|
|
|
|
edition = models.ForeignKey("Edition", related_name="identifiers", null=True)
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
unique_together = ("type", "value")
|
2012-01-09 18:55:22 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_or_add(klass, type='goog', value=None, edition=None, work=None):
|
|
|
|
try:
|
|
|
|
return Identifier.objects.get(type=type, value=value)
|
|
|
|
except Identifier.DoesNotExist:
|
|
|
|
i=Identifier(type=type, value=value, edition=edition, work=work)
|
|
|
|
i.save()
|
|
|
|
return i
|
2012-02-06 19:26:48 +00:00
|
|
|
|
|
|
|
def __unicode__(self):
|
|
|
|
return u'{0}:{1}'.format(self.type, self.value)
|
2011-09-10 11:36:38 +00:00
|
|
|
|
2011-08-31 03:46:55 +00:00
|
|
|
class Work(models.Model):
|
|
|
|
created = models.DateTimeField(auto_now_add=True)
|
2011-09-02 04:10:54 +00:00
|
|
|
title = models.CharField(max_length=1000)
|
2011-12-13 14:55:26 +00:00
|
|
|
language = models.CharField(max_length=2, default="en", null=False)
|
2011-12-19 06:33:13 +00:00
|
|
|
openlibrary_lookup = models.DateTimeField(null=True)
|
2012-02-11 19:15:06 +00:00
|
|
|
num_wishes = models.IntegerField(default=0)
|
2011-09-10 11:36:38 +00:00
|
|
|
|
2011-12-03 23:04:53 +00:00
|
|
|
class Meta:
|
|
|
|
ordering = ['title']
|
|
|
|
|
2011-11-18 03:11:40 +00:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self._last_campaign = None
|
|
|
|
super(Work, self).__init__(*args, **kwargs)
|
|
|
|
|
2011-12-03 04:07:55 +00:00
|
|
|
@property
|
|
|
|
def googlebooks_id(self):
|
2011-12-31 18:48:54 +00:00
|
|
|
try:
|
2012-01-09 18:55:22 +00:00
|
|
|
return self.identifiers.filter(type='goog')[0].value
|
2011-12-31 18:48:54 +00:00
|
|
|
except IndexError:
|
2012-01-09 18:55:22 +00:00
|
|
|
return ''
|
2011-12-03 04:07:55 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def googlebooks_url(self):
|
2012-01-17 21:27:58 +00:00
|
|
|
if self.googlebooks_id:
|
|
|
|
return "http://books.google.com/books?id=%s" % self.googlebooks_id
|
|
|
|
else:
|
|
|
|
return ''
|
2011-12-03 04:07:55 +00:00
|
|
|
|
2011-12-05 05:56:24 +00:00
|
|
|
@property
|
|
|
|
def goodreads_id(self):
|
2012-01-09 18:55:22 +00:00
|
|
|
try:
|
|
|
|
return self.identifiers.filter(type='gdrd')[0].value
|
|
|
|
except IndexError:
|
|
|
|
return ''
|
2011-12-05 05:56:24 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def goodreads_url(self):
|
|
|
|
return "http://www.goodreads.com/book/show/%s" % self.goodreads_id
|
|
|
|
|
2012-01-09 18:55:22 +00:00
|
|
|
@property
|
|
|
|
def librarything_id(self):
|
|
|
|
try:
|
|
|
|
return self.identifiers.filter(type='ltwk')[0].value
|
|
|
|
except IndexError:
|
|
|
|
return ''
|
|
|
|
|
2011-12-05 05:56:24 +00:00
|
|
|
@property
|
|
|
|
def librarything_url(self):
|
|
|
|
return "http://www.librarything.com/work/%s" % self.librarything_id
|
|
|
|
|
2012-01-09 18:55:22 +00:00
|
|
|
@property
|
|
|
|
def openlibrary_id(self):
|
|
|
|
try:
|
|
|
|
return self.identifiers.filter(type='olwk')[0].value
|
|
|
|
except IndexError:
|
|
|
|
return ''
|
|
|
|
|
2011-12-19 07:20:24 +00:00
|
|
|
@property
|
|
|
|
def openlibrary_url(self):
|
|
|
|
return "http://openlibrary.org" + self.openlibrary_id
|
|
|
|
|
2011-09-29 06:23:50 +00:00
|
|
|
def cover_image_small(self):
|
2011-12-31 18:48:54 +00:00
|
|
|
try:
|
2012-01-09 18:55:22 +00:00
|
|
|
return self.editions.all()[0].cover_image_small()
|
2011-12-31 18:48:54 +00:00
|
|
|
except IndexError:
|
2012-01-09 18:55:22 +00:00
|
|
|
return "/static/images/generic_cover_larger.png"
|
2011-09-29 06:23:50 +00:00
|
|
|
|
2011-11-03 20:28:53 +00:00
|
|
|
def cover_image_thumbnail(self):
|
2011-12-31 18:48:54 +00:00
|
|
|
try:
|
2012-01-09 18:55:22 +00:00
|
|
|
return self.editions.all()[0].cover_image_thumbnail()
|
2011-12-31 18:48:54 +00:00
|
|
|
except IndexError:
|
2012-01-09 18:55:22 +00:00
|
|
|
return "/static/images/generic_cover_larger.png"
|
2011-11-03 20:28:53 +00:00
|
|
|
|
|
|
|
def author(self):
|
2012-03-05 16:12:41 +00:00
|
|
|
# note: if you want this to be a real list, use distinct()
|
|
|
|
# perhaps should change this to vote on authors.
|
2011-11-18 03:11:40 +00:00
|
|
|
authors = list(Author.objects.filter(editions__work=self).all())
|
2012-03-05 16:12:41 +00:00
|
|
|
try:
|
2012-03-05 22:15:23 +00:00
|
|
|
return authors[0].name
|
2012-03-05 16:12:41 +00:00
|
|
|
except:
|
2012-03-05 22:15:23 +00:00
|
|
|
return ''
|
2011-11-07 20:39:02 +00:00
|
|
|
|
2011-11-06 19:02:29 +00:00
|
|
|
def last_campaign(self):
|
2011-11-18 03:11:40 +00:00
|
|
|
# stash away the last campaign to prevent repeated lookups
|
2011-11-22 20:55:05 +00:00
|
|
|
if hasattr(self, '_last_campaign_'):
|
|
|
|
return self._last_campaign_
|
2011-10-25 23:07:44 +00:00
|
|
|
try:
|
2011-11-22 20:55:05 +00:00
|
|
|
self._last_campaign_ = self.campaigns.order_by('-created')[0]
|
2011-11-18 03:11:40 +00:00
|
|
|
except IndexError:
|
2011-11-22 20:55:05 +00:00
|
|
|
self._last_campaign_ = None
|
|
|
|
return self._last_campaign_
|
2011-11-06 19:31:08 +00:00
|
|
|
|
|
|
|
def last_campaign_status(self):
|
2011-11-06 20:24:16 +00:00
|
|
|
campaign = self.last_campaign()
|
2011-11-06 19:31:08 +00:00
|
|
|
if campaign:
|
|
|
|
status = campaign.status
|
|
|
|
else:
|
2012-01-20 00:57:51 +00:00
|
|
|
if self.first_ebook():
|
2012-01-16 17:10:38 +00:00
|
|
|
status = "Available"
|
|
|
|
else:
|
2012-01-17 21:27:58 +00:00
|
|
|
status = "No campaign yet"
|
2011-11-06 19:31:08 +00:00
|
|
|
return status
|
2011-10-14 13:43:30 +00:00
|
|
|
|
|
|
|
def percent_unglued(self):
|
2011-11-06 19:24:54 +00:00
|
|
|
status = 0
|
2011-11-06 20:24:16 +00:00
|
|
|
if self.last_campaign() is not None:
|
|
|
|
if(self.last_campaign_status() == 'SUCCESSFUL'):
|
2011-11-06 19:24:54 +00:00
|
|
|
status = 6;
|
2011-11-06 20:24:16 +00:00
|
|
|
elif(self.last_campaign_status() == 'ACTIVE'):
|
2011-11-06 19:24:54 +00:00
|
|
|
target = float(self.campaigns.order_by('-created')[0].target)
|
|
|
|
if target <= 0:
|
|
|
|
status = 6
|
2011-10-25 23:07:44 +00:00
|
|
|
else:
|
2011-11-06 19:24:54 +00:00
|
|
|
total = float(self.campaigns.order_by('-created')[0].current_total)
|
|
|
|
percent = int(total*6/target)
|
|
|
|
if percent >= 6:
|
|
|
|
status = 6
|
|
|
|
else:
|
|
|
|
status = percent;
|
|
|
|
return status;
|
2011-10-14 13:43:30 +00:00
|
|
|
|
2011-11-07 21:12:53 +00:00
|
|
|
def percent_unglued_number(self):
|
|
|
|
status = 0
|
|
|
|
if self.last_campaign() is not None:
|
|
|
|
if(self.last_campaign_status() == 'SUCCESSFUL'):
|
|
|
|
status = 100;
|
|
|
|
elif(self.last_campaign_status() == 'ACTIVE'):
|
|
|
|
target = float(self.campaigns.order_by('-created')[0].target)
|
|
|
|
if target <= 0:
|
|
|
|
status = 100
|
|
|
|
else:
|
|
|
|
total = float(self.campaigns.order_by('-created')[0].current_total)
|
2011-12-14 05:49:01 +00:00
|
|
|
percent = int(total*100/target)
|
2011-11-07 21:12:53 +00:00
|
|
|
if percent >= 100:
|
|
|
|
status = 100
|
|
|
|
else:
|
|
|
|
status = percent;
|
|
|
|
return status;
|
2012-02-28 22:27:48 +00:00
|
|
|
|
|
|
|
def ebooks(self):
|
|
|
|
return Ebook.objects.filter(edition__work=self).order_by('-created')
|
|
|
|
|
2011-11-07 20:39:02 +00:00
|
|
|
def first_pdf(self):
|
|
|
|
return self.first_ebook('pdf')
|
|
|
|
|
|
|
|
def first_epub(self):
|
|
|
|
return self.first_ebook('epub')
|
|
|
|
|
2011-11-23 17:28:59 +00:00
|
|
|
def first_pdf_url(self):
|
2011-11-24 02:41:06 +00:00
|
|
|
try:
|
|
|
|
url = self.first_ebook('pdf').url
|
|
|
|
return url
|
2011-11-23 17:28:59 +00:00
|
|
|
except:
|
2011-11-24 02:41:06 +00:00
|
|
|
return None
|
2011-11-23 17:28:59 +00:00
|
|
|
|
|
|
|
def first_epub_url(self):
|
2011-11-24 02:41:06 +00:00
|
|
|
try:
|
|
|
|
url = self.first_ebook('epub').url
|
|
|
|
return url
|
2011-11-23 17:28:59 +00:00
|
|
|
except:
|
2011-11-24 02:41:06 +00:00
|
|
|
return None
|
2011-11-23 17:28:59 +00:00
|
|
|
|
2011-11-07 20:39:02 +00:00
|
|
|
def first_ebook(self, ebook_format=None):
|
2012-01-20 00:57:51 +00:00
|
|
|
if ebook_format:
|
2012-02-28 22:27:48 +00:00
|
|
|
for ebook in self.ebooks().filter(format=ebook_format):
|
2012-01-20 00:57:51 +00:00
|
|
|
return ebook
|
|
|
|
else:
|
2012-02-28 22:27:48 +00:00
|
|
|
for ebook in self.ebooks():
|
2012-01-20 00:57:51 +00:00
|
|
|
return ebook
|
2011-11-11 22:33:58 +00:00
|
|
|
|
|
|
|
def wished_by(self):
|
|
|
|
return User.objects.filter(wishlist__works__in=[self])
|
2012-02-11 19:15:06 +00:00
|
|
|
|
|
|
|
def update_num_wishes(self):
|
2012-02-28 22:27:48 +00:00
|
|
|
self.num_wishes = Wishes.objects.filter(work=self).count()
|
|
|
|
self.save()
|
2011-11-19 16:55:35 +00:00
|
|
|
|
|
|
|
def longest_description(self):
|
2011-12-13 14:55:26 +00:00
|
|
|
"""get the longest description from an edition of this work
|
2011-12-03 04:07:55 +00:00
|
|
|
"""
|
2011-11-19 16:55:35 +00:00
|
|
|
description = ""
|
2011-12-13 14:55:26 +00:00
|
|
|
for edition in self.editions.all():
|
2011-12-03 04:07:55 +00:00
|
|
|
if len(edition.description) > len(description):
|
2011-11-19 16:55:35 +00:00
|
|
|
description = edition.description
|
|
|
|
return description
|
|
|
|
|
2011-12-20 04:26:55 +00:00
|
|
|
def first_isbn_13(self):
|
2012-01-09 18:55:22 +00:00
|
|
|
try:
|
|
|
|
return self.identifiers.filter(type='isbn')[0].value
|
|
|
|
except IndexError:
|
|
|
|
return ''
|
2011-12-05 05:56:24 +00:00
|
|
|
|
2012-01-17 04:28:34 +00:00
|
|
|
@property
|
|
|
|
def publication_date(self):
|
2012-01-17 21:27:58 +00:00
|
|
|
for edition in Edition.objects.filter(work=self):
|
|
|
|
if edition.publication_date:
|
|
|
|
return edition.publication_date
|
|
|
|
return ''
|
2012-01-17 04:28:34 +00:00
|
|
|
|
2011-09-10 11:36:38 +00:00
|
|
|
def __unicode__(self):
|
|
|
|
return self.title
|
2011-08-31 03:46:55 +00:00
|
|
|
|
2011-09-09 05:38:28 +00:00
|
|
|
|
|
|
|
class Author(models.Model):
|
|
|
|
created = models.DateTimeField(auto_now_add=True)
|
|
|
|
name = models.CharField(max_length=500)
|
2011-10-10 16:57:10 +00:00
|
|
|
editions = models.ManyToManyField("Edition", related_name="authors")
|
2011-09-09 05:38:28 +00:00
|
|
|
|
|
|
|
def __unicode__(self):
|
|
|
|
return self.name
|
|
|
|
|
2011-09-10 11:36:38 +00:00
|
|
|
|
2011-09-09 05:38:28 +00:00
|
|
|
class Subject(models.Model):
|
|
|
|
created = models.DateTimeField(auto_now_add=True)
|
2011-12-19 06:33:13 +00:00
|
|
|
name = models.CharField(max_length=200, unique=True)
|
|
|
|
works = models.ManyToManyField("Work", related_name="subjects")
|
2011-09-09 05:38:28 +00:00
|
|
|
|
2011-12-19 07:20:24 +00:00
|
|
|
class Meta:
|
|
|
|
ordering = ['name']
|
|
|
|
|
2011-09-10 11:36:38 +00:00
|
|
|
def __unicode__(self):
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
|
2011-08-31 03:46:55 +00:00
|
|
|
class Edition(models.Model):
|
|
|
|
created = models.DateTimeField(auto_now_add=True)
|
|
|
|
title = models.CharField(max_length=1000)
|
2011-09-30 01:57:12 +00:00
|
|
|
description = models.TextField(default='', null=True)
|
2012-01-16 20:15:35 +00:00
|
|
|
publisher = models.CharField(max_length=255, null=True)
|
|
|
|
publication_date = models.CharField(max_length=50, null=True)
|
2011-11-06 21:33:04 +00:00
|
|
|
public_domain = models.NullBooleanField(null=True)
|
2011-09-30 01:57:12 +00:00
|
|
|
work = models.ForeignKey("Work", related_name="editions", null=True)
|
2011-08-31 03:46:55 +00:00
|
|
|
|
2011-09-09 18:27:29 +00:00
|
|
|
def __unicode__(self):
|
2011-10-10 21:26:38 +00:00
|
|
|
return "%s (%s)" % (self.title, self.isbn_13)
|
2011-09-09 18:27:29 +00:00
|
|
|
|
2011-10-20 03:31:16 +00:00
|
|
|
def cover_image_small(self):
|
2012-01-17 21:27:58 +00:00
|
|
|
if self.googlebooks_id:
|
2012-04-03 17:55:45 +00:00
|
|
|
return "https://encrypted.google.com/books?id=%s&printsec=frontcover&img=1&zoom=5" % self.googlebooks_id
|
2012-01-17 21:27:58 +00:00
|
|
|
else:
|
|
|
|
return ''
|
|
|
|
|
2011-10-20 03:31:16 +00:00
|
|
|
def cover_image_thumbnail(self):
|
2012-01-17 21:27:58 +00:00
|
|
|
if self.googlebooks_id:
|
2012-04-03 17:55:45 +00:00
|
|
|
return "https://encrypted.google.com/books?id=%s&printsec=frontcover&img=1&zoom=1" % self.googlebooks_id
|
2012-01-17 21:27:58 +00:00
|
|
|
else:
|
|
|
|
return ''
|
2011-12-20 04:26:55 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def isbn_10(self):
|
|
|
|
return regluit.core.isbn.convert_13_to_10(self.isbn_13)
|
|
|
|
|
2012-01-09 18:55:22 +00:00
|
|
|
@property
|
|
|
|
def isbn_13(self):
|
|
|
|
try:
|
|
|
|
return self.identifiers.filter(type='isbn')[0].value
|
|
|
|
except IndexError:
|
|
|
|
return ''
|
|
|
|
|
|
|
|
@property
|
|
|
|
def googlebooks_id(self):
|
|
|
|
try:
|
|
|
|
return self.identifiers.filter(type='goog')[0].value
|
|
|
|
except IndexError:
|
|
|
|
return ''
|
|
|
|
|
|
|
|
@property
|
|
|
|
def librarything_id(self):
|
|
|
|
try:
|
|
|
|
return self.identifiers.filter(type='thng')[0].value
|
|
|
|
except IndexError:
|
|
|
|
return ''
|
|
|
|
|
|
|
|
@property
|
|
|
|
def oclc(self):
|
|
|
|
try:
|
|
|
|
return self.identifiers.filter(type='oclc')[0].value
|
|
|
|
except IndexError:
|
|
|
|
return ''
|
|
|
|
|
|
|
|
@property
|
|
|
|
def goodreads_id(self):
|
|
|
|
try:
|
|
|
|
return self.identifiers.filter(type='gdrd')[0].value
|
|
|
|
except IndexError:
|
|
|
|
return ''
|
|
|
|
|
2011-09-29 06:23:50 +00:00
|
|
|
@classmethod
|
|
|
|
def get_by_isbn(klass, isbn):
|
2012-02-10 01:49:52 +00:00
|
|
|
if len(isbn)==10:
|
2011-12-20 04:26:55 +00:00
|
|
|
isbn=regluit.core.isbn.convert_10_to_13(isbn)
|
2012-01-09 18:55:22 +00:00
|
|
|
try:
|
|
|
|
return Identifier.objects.get( type='isbn', value=isbn ).edition
|
|
|
|
except Identifier.DoesNotExist:
|
|
|
|
return None
|
2011-08-31 03:46:55 +00:00
|
|
|
|
2012-02-10 03:30:33 +00:00
|
|
|
class WasWork(models.Model):
|
2012-02-28 22:27:48 +00:00
|
|
|
work = models.ForeignKey('Work')
|
|
|
|
was = models.IntegerField(unique = True)
|
|
|
|
moved = models.DateTimeField(auto_now_add=True)
|
|
|
|
user = models.ForeignKey(User, null=True)
|
|
|
|
|
|
|
|
|
2011-11-06 21:33:04 +00:00
|
|
|
class Ebook(models.Model):
|
2012-02-28 22:27:48 +00:00
|
|
|
FORMAT_CHOICES = (('PDF','PDF'),( 'EPUB','EPUB'), ('HTML','HTML'), ('TEXT','TEXT'), ('MOBI','MOBI'))
|
|
|
|
RIGHTS_CHOICES = (('PD-US', 'Public Domain, US'),
|
|
|
|
('CC BY-NC-ND','CC BY-NC-ND'),
|
|
|
|
('CC BY-ND','CC BY-ND'),
|
|
|
|
('CC BY','CC BY'),
|
|
|
|
('CC BY-NC','CC BY-NC'),
|
|
|
|
( 'CC BY-NC-SA','CC BY-NC-SA'),
|
|
|
|
( 'CC BY-SA','CC BY-SA'),
|
|
|
|
( 'CC0','CC0'),
|
|
|
|
)
|
|
|
|
url = models.URLField(max_length=1024)
|
2011-11-19 16:55:35 +00:00
|
|
|
created = models.DateTimeField(auto_now_add=True)
|
2012-02-28 22:27:48 +00:00
|
|
|
format = models.CharField(max_length=25, choices = FORMAT_CHOICES)
|
2011-11-06 21:33:04 +00:00
|
|
|
provider = models.CharField(max_length=255)
|
2012-02-28 22:27:48 +00:00
|
|
|
|
|
|
|
# use 'PD-US', 'CC BY', 'CC BY-NC-SA', 'CC BY-NC-ND', 'CC BY-NC', 'CC BY-ND', 'CC BY-SA', 'CC0'
|
|
|
|
rights = models.CharField(max_length=255, null=True, choices = RIGHTS_CHOICES)
|
2011-11-06 21:33:04 +00:00
|
|
|
edition = models.ForeignKey('Edition', related_name='ebooks')
|
2012-02-28 22:27:48 +00:00
|
|
|
user = models.ForeignKey(User, null=True)
|
2011-10-14 14:18:38 +00:00
|
|
|
|
2012-02-28 22:27:48 +00:00
|
|
|
def set_provider(self):
|
|
|
|
self.provider=Ebook.infer_provider(self.url)
|
|
|
|
return self.provider
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def infer_provider(klass, url):
|
|
|
|
if not url:
|
|
|
|
return None
|
|
|
|
# provider derived from url. returns provider value. remember to call save() afterward
|
|
|
|
if url.startswith('http://books.google.com/'):
|
|
|
|
provider='Google Books'
|
|
|
|
elif url.startswith('http://www.gutenberg.org/'):
|
|
|
|
provider='Project Gutenberg'
|
|
|
|
elif url.startswith('http://www.archive.org/'):
|
|
|
|
provider='Internet Archive'
|
|
|
|
elif url.startswith('http://hdl.handle.net/2027/') or url.startswith('http://babel.hathitrust.org/'):
|
|
|
|
provider='Hathitrust'
|
|
|
|
elif re.match('http://\w\w\.wikisource\.org/', url):
|
|
|
|
provider='Wikisource'
|
|
|
|
else:
|
|
|
|
provider=None
|
|
|
|
return provider
|
|
|
|
|
2011-11-19 16:55:35 +00:00
|
|
|
def __unicode__(self):
|
|
|
|
return "%s (%s from %s)" % (self.edition.title, self.format, self.provider)
|
|
|
|
|
2011-09-02 04:10:54 +00:00
|
|
|
class Wishlist(models.Model):
|
|
|
|
created = models.DateTimeField(auto_now_add=True)
|
|
|
|
user = models.OneToOneField(User, related_name='wishlist')
|
2011-12-09 13:07:44 +00:00
|
|
|
works = models.ManyToManyField('Work', related_name='wishlists', through='Wishes')
|
2011-09-12 05:53:54 +00:00
|
|
|
|
2011-11-19 17:07:44 +00:00
|
|
|
def __unicode__(self):
|
|
|
|
return "%s's Wishlist" % self.user.username
|
2011-12-08 23:22:05 +00:00
|
|
|
|
|
|
|
def add_work(self, work, source):
|
|
|
|
try:
|
2012-01-09 18:55:22 +00:00
|
|
|
w = Wishes.objects.get(wishlist=self,work=work)
|
|
|
|
except:
|
2012-02-11 19:15:06 +00:00
|
|
|
Wishes.objects.create(source=source,wishlist=self,work=work)
|
|
|
|
work.update_num_wishes()
|
2011-12-08 23:22:05 +00:00
|
|
|
|
|
|
|
def remove_work(self, work):
|
|
|
|
w = Wishes.objects.filter(wishlist=self, work=work)
|
|
|
|
if w:
|
|
|
|
w.delete()
|
2012-02-11 19:15:06 +00:00
|
|
|
work.update_num_wishes()
|
2011-12-08 23:22:05 +00:00
|
|
|
|
|
|
|
def work_source(self, work):
|
|
|
|
w = Wishes.objects.filter(wishlist=self, work=work)
|
|
|
|
if w:
|
|
|
|
return w[0].source
|
|
|
|
else:
|
|
|
|
return ''
|
|
|
|
|
|
|
|
class Wishes(models.Model):
|
|
|
|
created = models.DateTimeField(auto_now_add=True)
|
|
|
|
source = models.CharField(max_length=15, blank=True)
|
|
|
|
wishlist = models.ForeignKey('Wishlist')
|
2012-01-15 23:03:54 +00:00
|
|
|
work = models.ForeignKey('Work', related_name='wishes')
|
2011-12-08 23:22:05 +00:00
|
|
|
class Meta:
|
|
|
|
db_table = 'core_wishlist_works'
|
2011-11-19 17:07:44 +00:00
|
|
|
|
2011-10-03 16:36:04 +00:00
|
|
|
class UserProfile(models.Model):
|
2011-11-19 16:55:35 +00:00
|
|
|
created = models.DateTimeField(auto_now_add=True)
|
2011-10-13 16:22:38 +00:00
|
|
|
user = models.OneToOneField(User, related_name='profile')
|
2011-10-03 16:36:04 +00:00
|
|
|
tagline = models.CharField(max_length=140, blank=True)
|
2011-11-12 20:02:47 +00:00
|
|
|
pic_url = models.URLField(blank=True)
|
2011-10-25 03:32:32 +00:00
|
|
|
home_url = models.URLField(blank=True)
|
|
|
|
twitter_id = models.CharField(max_length=15, blank=True)
|
2011-11-12 03:51:12 +00:00
|
|
|
facebook_id = models.PositiveIntegerField(null=True)
|
2011-11-14 15:17:31 +00:00
|
|
|
librarything_id = models.CharField(max_length=31, blank=True)
|
2011-10-29 22:40:00 +00:00
|
|
|
|
|
|
|
goodreads_user_id = models.CharField(max_length=32, null=True, blank=True)
|
|
|
|
goodreads_user_name = models.CharField(max_length=200, null=True, blank=True)
|
|
|
|
goodreads_auth_token = models.TextField(null=True, blank=True)
|
|
|
|
goodreads_auth_secret = models.TextField(null=True, blank=True)
|
|
|
|
goodreads_user_link = models.CharField(max_length=200, null=True, blank=True)
|
2011-09-12 05:53:54 +00:00
|
|
|
|
2011-11-16 19:45:37 +00:00
|
|
|
|
2011-09-12 05:53:54 +00:00
|
|
|
from regluit.core import signals
|
2011-10-08 03:11:57 +00:00
|
|
|
from regluit.payment.manager import PaymentManager
|
2011-11-12 18:58:31 +00:00
|
|
|
|
|
|
|
from social_auth.signals import pre_update
|
|
|
|
from social_auth.backends.facebook import FacebookBackend
|
2011-11-12 21:36:31 +00:00
|
|
|
from social_auth.backends.twitter import TwitterBackend
|
2011-11-12 18:58:31 +00:00
|
|
|
|
|
|
|
def facebook_extra_values(sender, user, response, details, **kwargs):
|
2011-11-12 20:02:47 +00:00
|
|
|
facebook_id = response.get('id')
|
|
|
|
user.profile.facebook_id = facebook_id
|
2012-04-03 18:10:56 +00:00
|
|
|
user.profile.pic_url = 'https://graph.facebook.com/' + facebook_id + '/picture'
|
2011-11-12 18:58:31 +00:00
|
|
|
user.profile.save()
|
|
|
|
return True
|
|
|
|
|
2011-11-12 21:36:31 +00:00
|
|
|
def twitter_extra_values(sender, user, response, details, **kwargs):
|
2012-04-03 18:29:26 +00:00
|
|
|
import requests, urllib
|
|
|
|
|
2011-11-12 21:36:31 +00:00
|
|
|
twitter_id = response.get('screen_name')
|
2012-04-03 18:46:31 +00:00
|
|
|
profile_image_url = response.get('profile_image_url_https')
|
2011-11-12 21:36:31 +00:00
|
|
|
user.profile.twitter_id = twitter_id
|
2012-04-03 18:46:31 +00:00
|
|
|
user.profile.pic_url = profile_image_url
|
2011-11-12 21:36:31 +00:00
|
|
|
user.profile.save()
|
|
|
|
return True
|
|
|
|
|
|
|
|
pre_update.connect(facebook_extra_values, sender=FacebookBackend)
|
2011-11-15 18:37:19 +00:00
|
|
|
pre_update.connect(twitter_extra_values, sender=TwitterBackend)
|