diff --git a/core/migrations/0010_auto__add_claim__add_rightsholder.py b/core/migrations/0010_auto__add_claim__add_rightsholder.py new file mode 100644 index 00000000..deb9750a --- /dev/null +++ b/core/migrations/0010_auto__add_claim__add_rightsholder.py @@ -0,0 +1,185 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding model 'Claim' + db.create_table('core_claim', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('rights_holder', self.gf('django.db.models.fields.related.ForeignKey')(related_name='claim', to=orm['core.RightsHolder'])), + ('work', self.gf('django.db.models.fields.related.ForeignKey')(related_name='claim', to=orm['core.Work'])), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='user', to=orm['auth.User'])), + ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + )) + db.send_create_signal('core', ['Claim']) + + # Adding model 'RightsHolder' + db.create_table('core_rightsholder', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('email', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)), + ('rights_holder_name', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)), + ('owner', self.gf('django.db.models.fields.related.ForeignKey')(related_name='rights_holder', to=orm['auth.User'])), + )) + db.send_create_signal('core', ['RightsHolder']) + + + def backwards(self, orm): + + # Deleting model 'Claim' + db.delete_table('core_claim') + + # Deleting model 'RightsHolder' + db.delete_table('core_rightsholder') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'core.author': { + 'Meta': {'object_name': 'Author'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'editions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'authors'", 'symmetrical': 'False', 'to': "orm['core.Edition']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500'}) + }, + 'core.campaign': { + 'Meta': {'object_name': 'Campaign'}, + 'activated': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'amazon_receiver': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'deadline': ('django.db.models.fields.DateTimeField', [], {}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True'}), + 'paypal_receiver': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'suspended': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'suspended_reason': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'target': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '14', 'decimal_places': '2'}), + 'withdrawn': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'withdrawn_reason': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'work': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'campaigns'", 'to': "orm['core.Work']"}) + }, + 'core.claim': { + 'Meta': {'object_name': 'Claim'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'rights_holder': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'claim'", 'to': "orm['core.RightsHolder']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user'", 'to': "orm['auth.User']"}), + 'work': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'claim'", 'to': "orm['core.Work']"}) + }, + 'core.ebook': { + 'Meta': {'object_name': 'Ebook'}, + 'edition': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ebooks'", 'to': "orm['core.Edition']"}), + 'format': ('django.db.models.fields.CharField', [], {'max_length': '25'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'rights': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}) + }, + 'core.edition': { + 'Meta': {'object_name': 'Edition'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True'}), + 'googlebooks_id': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'isbn_10': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True'}), + 'isbn_13': ('django.db.models.fields.CharField', [], {'max_length': '13', 'null': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'max_length': '2', 'null': 'True'}), + 'oclc': ('django.db.models.fields.CharField', [], {'max_length': '25', 'null': 'True'}), + 'public_domain': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'publication_date': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'publisher': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '1000'}), + 'work': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'editions'", 'null': 'True', 'to': "orm['core.Work']"}) + }, + 'core.premium': { + 'Meta': {'object_name': 'Premium'}, + 'amount': ('django.db.models.fields.DecimalField', [], {'max_digits': '10', 'decimal_places': '0'}), + 'campaign': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'premiums'", 'null': 'True', 'to': "orm['core.Campaign']"}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '2'}) + }, + 'core.rightsholder': { + 'Meta': {'object_name': 'RightsHolder'}, + 'email': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'rights_holder'", 'to': "orm['auth.User']"}), + 'rights_holder_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}) + }, + 'core.subject': { + 'Meta': {'object_name': 'Subject'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'editions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subjects'", 'symmetrical': 'False', 'to': "orm['core.Edition']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '500'}) + }, + 'core.userprofile': { + 'Meta': {'object_name': 'UserProfile'}, + 'goodreads_auth_secret': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'goodreads_auth_token': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'goodreads_user_id': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + 'goodreads_user_link': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'goodreads_user_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'home_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'tagline': ('django.db.models.fields.CharField', [], {'max_length': '140', 'blank': 'True'}), + 'twitter_id': ('django.db.models.fields.CharField', [], {'max_length': '15', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"}) + }, + 'core.wishlist': { + 'Meta': {'object_name': 'Wishlist'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'wishlist'", 'unique': 'True', 'to': "orm['auth.User']"}), + 'works': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'wishlists'", 'symmetrical': 'False', 'to': "orm['core.Work']"}) + }, + 'core.work': { + 'Meta': {'object_name': 'Work'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'openlibrary_id': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '1000'}) + } + } + + complete_apps = ['core'] diff --git a/core/models.py b/core/models.py index e0722262..4d061b93 100755 --- a/core/models.py +++ b/core/models.py @@ -9,6 +9,17 @@ from django.contrib.auth.models import User class UnglueitError(RuntimeError): pass +class Claim(models.Model): + rights_holder = models.ForeignKey("RightsHolder", related_name="claim", null=False ) + work = models.ForeignKey("Work", related_name="claim", null=False ) + user = models.ForeignKey(User, related_name="user", null=False ) + created = models.DateTimeField(auto_now_add=True) + +class RightsHolder(models.Model): + email = models.CharField(max_length=100, blank=True) + rights_holder_name = models.CharField(max_length=100, blank=True) + owner = models.ForeignKey(User, related_name="rights_holder", null=False ) + class Premium(models.Model): PREMIUM_TYPES = ((u'00', u'Default'),(u'CU', u'Custom')) type = models.CharField(max_length=2, choices=PREMIUM_TYPES) @@ -103,8 +114,8 @@ class Campaign(models.Model): return self def supporters(self): - translist = self.transactions().values_list('user', flat=True).distinct() - return translist + translist = self.transactions().values_list('user', flat=True).distinct() + return translist class Work(models.Model): created = models.DateTimeField(auto_now_add=True) @@ -118,15 +129,15 @@ class Work(models.Model): return self.editions.all()[0].cover_image_thumbnail() def author(self): - authorlist = self.editions.all()[0].authors.all() - if authorlist.count() == 1: - myauthor = authorlist[0].name - elif authorlist.count() > 1: - myauthor = authorlist[0].name + ' et al.' - else: - myauthor = '' - return myauthor - + authorlist = self.editions.all()[0].authors.all() + if authorlist.count() == 1: + myauthor = authorlist[0].name + elif authorlist.count() > 1: + myauthor = authorlist[0].name + ' et al.' + else: + myauthor = '' + return myauthor + def last_campaign(self): try: last = self.campaigns.order_by('-created')[0] @@ -160,6 +171,19 @@ class Work(models.Model): status = percent; return status; + def first_pdf(self): + return self.first_ebook('pdf') + + def first_epub(self): + return self.first_ebook('epub') + + def first_ebook(self, ebook_format=None): + for edition in self.editions.all(): + for ebook in edition.ebooks.all(): + if ebook_format == None or ebook.format == ebook_format: + return ebook + return None + def __unicode__(self): return self.title @@ -237,6 +261,5 @@ class UserProfile(models.Model): goodreads_auth_secret = models.TextField(null=True, blank=True) goodreads_user_link = models.CharField(max_length=200, null=True, blank=True) - from regluit.core import signals from regluit.payment.manager import PaymentManager diff --git a/core/search.py b/core/search.py index d69842d3..9dcb4c82 100644 --- a/core/search.py +++ b/core/search.py @@ -7,8 +7,6 @@ def gluejar_search(q): results = [] for item in googlebooks_search(q)['items']: - # TODO: better to think in terms of editions with titles - # instead of titles with names? v = item['volumeInfo'] r = {'title': v.get('title', ""), 'description': v.get('description', ""), @@ -32,10 +30,18 @@ def gluejar_search(q): # cover image if v.has_key('imageLinks'): - r['image'] = v['imageLinks'].get('smallThumbnail', "") + r['image'] = v['imageLinks'].get('thumbnail', "") else: r['image'] = "" + access_info = item.get('accessInfo') + if access_info: + epub = access_info.get('epub') + if epub and epub.get('downloadLink'): + r['epub'] = epub['downloadLink'] + pdf = access_info.get('pdf') + if pdf and pdf.get('downloadLink'): + r['pdf'] = pdf['downloadLink'] results.append(r) return results diff --git a/core/tests.py b/core/tests.py index af5ace3f..6f4b76ea 100755 --- a/core/tests.py +++ b/core/tests.py @@ -121,6 +121,10 @@ class TestBookLoader(TestCase): self.assertEqual(ebook_pdf.url, 'http://books.google.com/books/download/The_Latin_language.pdf?id=U3FXAAAAYAAJ&ie=ISO-8859-1&output=pdf&sig=ACfU3U2yLt3nmTncB8ozxOWUc4iHKUznCA&source=gbs_api') self.assertEqual(ebook_pdf.provider, 'google') + w = edition.work + self.assertEqual(w.first_epub().url, "http://books.google.com/books/download/The_Latin_language.epub?id=U3FXAAAAYAAJ&ie=ISO-8859-1&output=epub&source=gbs_api") + self.assertEqual(w.first_pdf().url, "http://books.google.com/books/download/The_Latin_language.pdf?id=U3FXAAAAYAAJ&ie=ISO-8859-1&output=pdf&sig=ACfU3U2yLt3nmTncB8ozxOWUc4iHKUznCA&source=gbs_api") + def test_add_no_ebook(self): # this edition lacks an ebook, but we should still be able to load it e = bookloader.add_by_isbn('0465019358') diff --git a/frontend/forms.py b/frontend/forms.py index ff6444a2..9015a94b 100644 --- a/frontend/forms.py +++ b/frontend/forms.py @@ -42,18 +42,8 @@ class UserData(forms.Form): raise forms.ValidationError(_("Your username is already "+oldusername)) class CampaignPledgeForm(forms.Form): - pledge_amount = forms.DecimalField(initial=D('0.00'), min_value=D('0.00'), max_value=D('10000.00'), decimal_places=2) preapproval_amount = forms.DecimalField(initial=D('20.00'), min_value=D('0.00'), max_value=D('10000.00'), decimal_places=2) anonymous = forms.BooleanField(required=False, label="Don't display my username in the supporters list") - def clean(self): - # force a choice: only one of pledge_amount and pre_approval can be non-zero. - cleaned_data = self.cleaned_data - pledge_amount = cleaned_data.get("pledge_amount") - preapproval_amount = cleaned_data.get("preapproval_amount") - if pledge_amount > D('0.00') and preapproval_amount > D('0.00'): - raise forms.ValidationError("Only one of pledge_amount and pre_approval can be non-zero.") - - return cleaned_data class GoodreadsShelfLoadingForm(forms.Form): goodreads_shelf_name = forms.CharField(widget=forms.Select(choices=( diff --git a/frontend/templates/base.html b/frontend/templates/base.html index 5fcb9d35..1127d7f2 100644 --- a/frontend/templates/base.html +++ b/frontend/templates/base.html @@ -23,7 +23,7 @@ {% if not suppress_search_box %} -