Merge pull request #635 from Gluejar/versions-relations-ednotes

Versions relations ednotes [merge into master not dj18]
pull/1/head
Raymond Yee 2016-09-30 11:08:53 -07:00 committed by GitHub
commit 599ac8dc69
36 changed files with 4259 additions and 3484 deletions

View File

@ -1,5 +1,6 @@
import datetime import datetime
import pytz import pytz
import re
from lxml import etree from lxml import etree
from regluit.core import models from regluit.core import models
from regluit.core.cc import ccinfo from regluit.core.cc import ccinfo
@ -134,6 +135,19 @@ def product(edition, facet=None):
subj_node.append(text_node("SubjectSchemeIdentifier", "20")) subj_node.append(text_node("SubjectSchemeIdentifier", "20"))
subj_node.append(text_node("SubjectHeadingText", subject.name)) subj_node.append(text_node("SubjectHeadingText", subject.name))
# audience range composite
if work.age_level:
range_match = re.search(r'(\d?\d?)-(\d?\d?)', work.age_level)
if range_match:
audience_range_node = etree.SubElement(descriptive_node, "AudienceRange")
audience_range_node.append(text_node("AudienceRangeQualifier", "17")) #Interest age, years
if range_match.group(1):
audience_range_node.append(text_node("AudienceRangePrecision", "03")) #from
audience_range_node.append(text_node("AudienceRangeValue", range_match.group(1)))
if range_match.group(2):
audience_range_node.append(text_node("AudienceRangePrecision", "04")) #from
audience_range_node.append(text_node("AudienceRangeValue", range_match.group(2)))
# Collateral Detail Block # Collateral Detail Block
coll_node = etree.SubElement(product_node, "CollateralDetail") coll_node = etree.SubElement(product_node, "CollateralDetail")
desc_node = etree.SubElement(coll_node, "TextContent") desc_node = etree.SubElement(coll_node, "TextContent")

View File

@ -84,25 +84,29 @@ def work_node(work, facet=None):
node.append(text_node('updated', work.first_ebook().created.isoformat())) node.append(text_node('updated', work.first_ebook().created.isoformat()))
# links for all ebooks # links for all ebooks
ebooks=facet.filter_model("Ebook",work.ebooks()) if facet else work.ebooks() ebooks = facet.filter_model("Ebook",work.ebooks()) if facet else work.ebooks()
versions = set()
for ebook in ebooks: for ebook in ebooks:
link_node = etree.Element("link") if not ebook.version_label in versions:
versions.add(ebook.version_label)
link_node = etree.Element("link")
# ebook.download_url is an absolute URL with the protocol, domain, and path baked in # ebook.download_url is an absolute URL with the protocol, domain, and path baked in
link_rel = "http://opds-spec.org/acquisition/open-access" link_rel = "http://opds-spec.org/acquisition/open-access"
link_node.attrib.update({"href":add_query_component(ebook.download_url, "feed=opds"), link_node.attrib.update({"href":add_query_component(ebook.download_url, "feed=opds"),
"rel":link_rel, "rel":link_rel,
"{http://purl.org/dc/terms/}rights": str(ebook.rights)}) "{http://purl.org/dc/terms/}rights": str(ebook.rights)})
if ebook.is_direct(): if ebook.is_direct():
link_node.attrib["type"] = FORMAT_TO_MIMETYPE.get(ebook.format, "") link_node.attrib["type"] = FORMAT_TO_MIMETYPE.get(ebook.format, "")
else: else:
""" indirect acquisition, i.e. google books """ """ indirect acquisition, i.e. google books """
link_node.attrib["type"] = "text/html" link_node.attrib["type"] = "text/html"
indirect = etree.Element("{http://opds-spec.org/}indirectAcquisition",) indirect = etree.Element("{http://opds-spec.org/}indirectAcquisition",)
indirect.attrib["type"] = FORMAT_TO_MIMETYPE.get(ebook.format, "") indirect.attrib["type"] = FORMAT_TO_MIMETYPE.get(ebook.format, "")
link_node.append(indirect) link_node.append(indirect)
if ebook.version_label:
node.append(link_node) link_node.attrib.update({"{http://schema.org/}version": ebook.version_label})
node.append(link_node)
# get the cover -- assume jpg? # get the cover -- assume jpg?
@ -163,6 +167,16 @@ def work_node(work, facet=None):
# caused by control chars in subject.name # caused by control chars in subject.name
logger.warning('Deleting subject: %s' % subject.name) logger.warning('Deleting subject: %s' % subject.name)
subject.delete() subject.delete()
# age level
# <category term="15-18" scheme="http://schema.org/typicalAgeRange" label="Teen - Grade 10-12, Age 15-18"/>
if work.age_level:
category_node = etree.Element("category")
category_node.attrib["scheme"] = 'http://schema.org/typicalAgeRange'
category_node.attrib["term"] = work.age_level
category_node.attrib["label"] = work.get_age_level_display()
node.append(category_node)
# rating # rating
rating_node = etree.Element("{http://schema.org/}Rating") rating_node = etree.Element("{http://schema.org/}Rating")
@ -227,6 +241,9 @@ def opds_feed_for_work(work_id):
works=models.Work.objects.filter(id=work_id) works=models.Work.objects.filter(id=work_id)
except models.Work.DoesNotExist: except models.Work.DoesNotExist:
works=models.Work.objects.none() works=models.Work.objects.none()
except ValueError:
# not a valid work_id
works=models.Work.objects.none()
self.works=works self.works=works
self.title='Unglue.it work #%s' % work_id self.title='Unglue.it work #%s' % work_id
self.feed_path='' self.feed_path=''

View File

@ -74,7 +74,8 @@ class WorkAdmin(ModelAdmin):
ordering = ('title',) ordering = ('title',)
list_display = ('title', 'created') list_display = ('title', 'created')
date_hierarchy = 'created' date_hierarchy = 'created'
fields = ('title', 'description', 'language', 'featured') fields = ('title', 'description', 'language', 'featured', 'publication_range',
'age_level', 'openlibrary_lookup')
class AuthorAdmin(ModelAdmin): class AuthorAdmin(ModelAdmin):
search_fields = ('name',) search_fields = ('name',)
@ -105,9 +106,9 @@ class EditionAdminForm(forms.ModelForm):
) )
publisher_name = AutoCompleteSelectField( publisher_name = AutoCompleteSelectField(
PublisherNameLookup, PublisherNameLookup,
label='Name', label='Publisher Name',
widget=AutoCompleteSelectWidget(PublisherNameLookup), widget=AutoCompleteSelectWidget(PublisherNameLookup),
required=True, required=False,
) )
class Meta(object): class Meta(object):
model = models.Edition model = models.Edition
@ -174,24 +175,76 @@ class CeleryTaskAdmin(ModelAdmin):
class PressAdmin(ModelAdmin): class PressAdmin(ModelAdmin):
list_display = ('title', 'source', 'date') list_display = ('title', 'source', 'date')
ordering = ('-date',) ordering = ('-date',)
class WorkRelationAdminForm(forms.ModelForm):
to_work = AutoCompleteSelectField(
WorkLookup,
label='To Work',
widget=AutoCompleteSelectWidget(WorkLookup),
required=True,
)
from_work = AutoCompleteSelectField(
WorkLookup,
label='From Work',
widget=AutoCompleteSelectWidget(WorkLookup),
required=True,
)
class Meta(object):
model = models.WorkRelation
exclude = ()
class WorkRelationAdmin(ModelAdmin):
form = WorkRelationAdminForm
list_display = ('to_work', 'relation', 'from_work')
class IdentifierAdminForm(forms.ModelForm):
work = AutoCompleteSelectField(
WorkLookup,
label='Work',
widget=AutoCompleteSelectWidget(WorkLookup, attrs={'size':60}),
required=False,
)
edition = AutoCompleteSelectField(
EditionLookup,
label='Edition',
widget=AutoCompleteSelectWidget(EditionLookup, attrs={'size':60}),
required=True,
)
class Meta(object):
model = models.Identifier
exclude = ()
class IdentifierAdmin(ModelAdmin):
form = IdentifierAdminForm
list_display = ('type', 'value')
search_fields = ('type', 'value')
class OfferAdmin(ModelAdmin):
list_display = ('work', 'license', 'price', 'active')
search_fields = ('work__title',)
readonly_fields = ('work',)
admin_site.register(models.Acq, AcqAdmin) admin_site.register(models.Acq, AcqAdmin)
admin_site.register(models.Work, WorkAdmin)
admin_site.register(models.Claim, ClaimAdmin)
admin_site.register(models.RightsHolder, RightsHolderAdmin)
admin_site.register(models.Premium, PremiumAdmin)
admin_site.register(models.Campaign, CampaignAdmin)
admin_site.register(models.Author, AuthorAdmin) admin_site.register(models.Author, AuthorAdmin)
admin_site.register(models.Badge, ModelAdmin)
admin_site.register(models.Campaign, CampaignAdmin)
admin_site.register(models.CeleryTask, CeleryTaskAdmin)
admin_site.register(models.Claim, ClaimAdmin)
admin_site.register(models.Ebook, EbookAdmin)
admin_site.register(models.Edition, EditionAdmin)
admin_site.register(models.Gift, GiftAdmin)
admin_site.register(models.Identifier, IdentifierAdmin)
admin_site.register(models.Offer, OfferAdmin)
admin_site.register(models.Premium, PremiumAdmin)
admin_site.register(models.Press, PressAdmin)
admin_site.register(models.Publisher, PublisherAdmin) admin_site.register(models.Publisher, PublisherAdmin)
admin_site.register(models.PublisherName, PublisherNameAdmin) admin_site.register(models.PublisherName, PublisherNameAdmin)
admin_site.register(models.Subject, SubjectAdmin)
admin_site.register(models.Edition, EditionAdmin)
admin_site.register(models.Ebook, EbookAdmin)
admin_site.register(models.Wishlist, WishlistAdmin)
admin_site.register(models.UserProfile, UserProfileAdmin)
admin_site.register(models.CeleryTask, CeleryTaskAdmin)
admin_site.register(models.Press, PressAdmin)
admin_site.register(models.Gift, GiftAdmin)
admin_site.register(models.Relation, RelationAdmin) admin_site.register(models.Relation, RelationAdmin)
admin_site.register(models.RightsHolder, RightsHolderAdmin)
admin_site.register(models.Subject, SubjectAdmin)
admin_site.register(models.UserProfile, UserProfileAdmin)
admin_site.register(models.Wishlist, WishlistAdmin)
admin_site.register(models.Work, WorkAdmin)
admin_site.register(models.WorkRelation, WorkRelationAdmin)

View File

@ -449,6 +449,7 @@ def add_related(isbn):
for w in works_to_merge: for w in works_to_merge:
logger.debug("merge_works path 2 %s %s", lang_edition.work.id, w.id ) logger.debug("merge_works path 2 %s %s", lang_edition.work.id, w.id )
merge_works(lang_edition.work, w) merge_works(lang_edition.work, w)
models.WorkRelation.objects.get_or_create(to_work=lang_edition.work, from_work=work, relation='translation')
return new_editions return new_editions
@ -901,8 +902,8 @@ def load_from_yaml(yaml_url, test_mode=False):
rights = cc.match_license(metadata.rights), rights = cc.match_license(metadata.rights),
format = ebook_format, format = ebook_format,
edition = edition, edition = edition,
# version = metadata._version
) )
ebook.set_version(metadata._version)
return work.id return work.id

View File

@ -106,7 +106,7 @@ def description(license):
else: else:
return '' return ''
class ccinfo(): class ccinfo(object):
def __init__(self, license): def __init__(self, license):
value=license_value(license) value=license_value(license)
self.license=value if value else license self.license=value if value else license

View File

@ -3,7 +3,7 @@ from selectable.registry import registry
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db.models import Count from django.db.models import Count
from regluit.core.models import Work, PublisherName, Edition, Subject from regluit.core.models import Work, PublisherName, Edition, Subject, EditionNote
class OwnerLookup(ModelLookup): class OwnerLookup(ModelLookup):
model = User model = User
@ -54,8 +54,17 @@ class SubjectLookup(ModelLookup):
def get_query(self, request, term): def get_query(self, request, term):
return super(SubjectLookup, self).get_query( request, term).annotate(Count('works')).order_by('-works__count') return super(SubjectLookup, self).get_query( request, term).annotate(Count('works')).order_by('-works__count')
class EditionNoteLookup(ModelLookup):
model = EditionNote
search_fields = ('note__icontains',)
def create_item(self, value):
new_note, created = EditionNote.objects.get_or_create(note=value)
new_note.save()
return new_note
registry.register(OwnerLookup) registry.register(OwnerLookup)
registry.register(WorkLookup) registry.register(WorkLookup)
registry.register(PublisherNameLookup) registry.register(PublisherNameLookup)
registry.register(EditionLookup) registry.register(EditionLookup)
registry.register(SubjectLookup) registry.register(SubjectLookup)
registry.register(EditionNoteLookup)

View File

@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0002_auto_20160722_1716'),
]
operations = [
migrations.CreateModel(
name='EditionNote',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('note', models.CharField(max_length=64, unique=True, null=True, blank=True)),
],
),
migrations.CreateModel(
name='WorkRelation',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('relation', models.CharField(max_length=15, choices=[(b'translation', b'translation'), (b'revision', b'revision'), (b'sequel', b'sequel'), (b'compilation', b'compilation')])),
],
),
migrations.AddField(
model_name='ebook',
name='version',
field=models.CharField(max_length=255, null=True),
),
migrations.AddField(
model_name='work',
name='age_level',
field=models.CharField(default=b'', max_length=5, choices=[(b'', b'No Rating'), (b'5-6', b"Children's - Kindergarten, Age 5-6"), (b'6-7', b"Children's - Grade 1-2, Age 6-7"), (b'7-8', b"Children's - Grade 2-3, Age 7-8"), (b'8-9', b"Children's - Grade 3-4, Age 8-9"), (b'9-11', b"Children's - Grade 4-6, Age 9-11"), (b'12-14', b'Teen - Grade 7-9, Age 12-14'), (b'15-18', b'Teen - Grade 10-12, Age 15-18'), (b'18-', b'Adult/Advanced Reader')]),
),
migrations.AddField(
model_name='workrelation',
name='from_work',
field=models.ForeignKey(related_name='works_related_from', to='core.Work'),
),
migrations.AddField(
model_name='workrelation',
name='to_work',
field=models.ForeignKey(related_name='works_related_to', to='core.Work'),
),
migrations.AddField(
model_name='edition',
name='note',
field=models.ForeignKey(to='core.EditionNote', null=True),
),
migrations.AddField(
model_name='work',
name='related',
field=models.ManyToManyField(related_name='reverse_related', null=True, through='core.WorkRelation', to='core.Work'),
),
]

View File

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models, transaction
from django.db.utils import IntegrityError
class Migration(migrations.Migration):
def url_to_doi(apps, schema_editor):
Indentifier = apps.get_model('core', 'Identifier')
for doi in Indentifier.objects.filter(type='http', value__icontains='dx.doi.org'):
if doi.value.startswith('http://dx.doi.org/10.'):
doi.value = doi.value[18:]
elif doi.value.startswith('https://dx.doi.org/10.'):
doi.value = doi.value[19:]
else:
continue
doi.type = 'doi'
try:
with transaction.atomic():
doi.save()
except IntegrityError:
continue
def doi_to_url(apps, schema_editor):
Indentifier = apps.get_model('core', 'Identifier')
for doi in Indentifier.objects.filter(type='doi'):
doi.value = 'https://dx.doi.org/{}'.format(doi.value)
doi.type = 'http'
try:
with transaction.atomic():
doi.save()
except IntegrityError:
continue
dependencies = [
('core', '0003_auto_20160816_1645'),
]
operations = [
migrations.RunPython(url_to_doi, reverse_code=doi_to_url, hints={'core': 'Identifier'}),
]

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0004_auto_20160808_1548'),
]
operations = [
migrations.AddField(
model_name='ebookfile',
name='ebook',
field=models.ForeignKey(related_name='ebook_files', to='core.Ebook', null=True),
),
]

View File

@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
def add_ebooks_to_ebfs(apps, schema_editor):
"""
Now that EbookFile has ebook foreign key, this migration populates that key
"""
EbookFile = apps.get_model('core', 'EbookFile')
Ebook = apps.get_model('core', 'Ebook')
for ebf in EbookFile.objects.all():
# Connect each ebf (ebookfile) based on common edition (excluding the unglue.it provider) or URL.
for ebook in Ebook.objects.filter(edition=ebf.edition, format=ebf.format).exclude(provider='Unglue.it'):
ebf.ebook = ebook
ebf.save()
for ebook in Ebook.objects.filter(url=ebf.file.url):
ebf.ebook = ebook
ebf.save()
# if the ebookfile is still not connected to an ebook...
if not ebf.ebook:
# and the edition is associated with a THANKS campaign
if ebf.edition.work.campaigns.filter(type=3):
ebf.ebook = Ebook.objects.create(
edition=ebf.edition,
active=False,
url=ebf.file.url,
provider='Unglue.it',
format=ebf.format,
rights=ebf.edition.work.campaigns.order_by('-created')[0].license
)
ebf.save()
# Buy to unglue campaign
elif ebf.edition.work.campaigns.filter(type=2):
pass
else:
print 'ebf {} is dangling'.format(ebf.id)
def noop(apps, schema_editor):
pass
dependencies = [
('core', '0005_ebookfile_ebook'),
]
operations = [
migrations.RunPython(add_ebooks_to_ebfs, reverse_code=noop, hints={'core': 'EbookFile'}),
]

View File

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0006_auto_20160818_1809'),
]
operations = [
migrations.RemoveField(
model_name='ebook',
name='version',
),
migrations.AddField(
model_name='ebook',
name='version_iter',
field=models.PositiveIntegerField(default=0),
),
migrations.AddField(
model_name='ebook',
name='version_label',
field=models.CharField(default=b'', max_length=255, blank=True),
),
migrations.AlterField(
model_name='edition',
name='note',
field=models.ForeignKey(blank=True, to='core.EditionNote', null=True),
),
migrations.AlterField(
model_name='edition',
name='publisher_name',
field=models.ForeignKey(related_name='editions', blank=True, to='core.PublisherName', null=True),
),
migrations.AlterField(
model_name='userprofile',
name='badges',
field=models.ManyToManyField(related_name='holders', to='core.Badge', blank=True),
),
migrations.AlterField(
model_name='userprofile',
name='facebook_id',
field=models.BigIntegerField(null=True, blank=True),
),
migrations.AlterField(
model_name='work',
name='age_level',
field=models.CharField(default=b'', max_length=5, blank=True, choices=[(b'', b'No Rating'), (b'5-6', b"Children's - Kindergarten, Age 5-6"), (b'6-7', b"Children's - Grade 1-2, Age 6-7"), (b'7-8', b"Children's - Grade 2-3, Age 7-8"), (b'8-9', b"Children's - Grade 3-4, Age 8-9"), (b'9-11', b"Children's - Grade 4-6, Age 9-11"), (b'12-14', b'Teen - Grade 7-9, Age 12-14'), (b'15-18', b'Teen - Grade 10-12, Age 15-18'), (b'18-', b'Adult/Advanced Reader')]),
),
migrations.AlterField(
model_name='work',
name='openlibrary_lookup',
field=models.DateTimeField(null=True, blank=True),
),
migrations.AlterField(
model_name='work',
name='publication_range',
field=models.CharField(max_length=50, null=True, blank=True),
),
]

File diff suppressed because it is too large Load Diff

1391
core/models/__init__.py Executable file

File diff suppressed because it is too large Load Diff

1191
core/models/bibmodels.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,30 @@
(REWARDS, BUY2UNGLUE, THANKS) = (1, 2, 3) (REWARDS, BUY2UNGLUE, THANKS) = (1, 2, 3)
(INDIVIDUAL, LIBRARY, BORROWED, RESERVE, THANKED) = (1, 2, 3, 4, 5) (INDIVIDUAL, LIBRARY, BORROWED, RESERVE, THANKED) = (1, 2, 3, 4, 5)
TESTING = 0 TESTING = 0
OFFER_CHOICES = ((INDIVIDUAL,'Individual license'),(LIBRARY,'Library License'))
ACQ_CHOICES = ((INDIVIDUAL,'Individual license'),(LIBRARY,'Library License'),(BORROWED,'Borrowed from Library'), (TESTING,'Just for Testing'), (RESERVE,'On Reserve'),(THANKED,'Already Thanked'),)
AGE_LEVEL_CHOICES = (
('', 'No Rating'),
('5-6', 'Children\'s - Kindergarten, Age 5-6'),
('6-7', 'Children\'s - Grade 1-2, Age 6-7'),
('7-8', 'Children\'s - Grade 2-3, Age 7-8'),
('8-9', 'Children\'s - Grade 3-4, Age 8-9'),
('9-11', 'Children\'s - Grade 4-6, Age 9-11'),
('12-14', 'Teen - Grade 7-9, Age 12-14'),
('15-18', 'Teen - Grade 10-12, Age 15-18'),
('18-', 'Adult/Advanced Reader')
)
TEXT_RELATION_CHOICES = (
('translation', 'translation'),
('revision', 'revision'),
('sequel', 'sequel'),
('compilation', 'compilation')
)

View File

@ -134,12 +134,13 @@ def process_ebfs(campaign):
if campaign.use_add_ask: if campaign.use_add_ask:
campaign.add_ask_to_ebfs() campaign.add_ask_to_ebfs()
else: else:
campaign.work.make_ebooks_from_ebfs(add_ask=False) campaign.revert_asks()
campaign.work.remove_old_ebooks() campaign.make_mobis()
@task @task
def make_mobi(campaign): def make_mobi(ebookfile):
return campaign.make_mobi() return ebookfile.make_mobi()
@task @task
def refresh_acqs(): def refresh_acqs():

View File

@ -192,6 +192,16 @@ class BookLoaderTests(TestCase):
self.assertTrue(models.Edition.objects.count() > 15) self.assertTrue(models.Edition.objects.count() > 15)
self.assertEqual(models.Work.objects.filter(language=lang).count(), 1) self.assertEqual(models.Work.objects.filter(language=lang).count(), 1)
self.assertTrue(edition.work.editions.count() > 9) self.assertTrue(edition.work.editions.count() > 9)
self.assertTrue(edition.work.reverse_related.count() > 0)
# is edition.work found among the from_work of all the to_work of edition.work?
back_point = True
to_works = [wr.to_work for wr in edition.work.works_related_from.all()]
for to_work in to_works:
if edition.work.id not in [wr1.from_work.id for wr1 in to_work.works_related_to.all()]:
back_point = False
break
self.assertTrue(back_point)
def test_populate_edition(self): def test_populate_edition(self):
@ -1009,6 +1019,11 @@ class EbookFileTests(TestCase):
dj_file = DjangoFile(temp_file) dj_file = DjangoFile(temp_file)
ebf = EbookFile( format='pdf', edition=e, file=dj_file) ebf = EbookFile( format='pdf', edition=e, file=dj_file)
ebf.save() ebf.save()
eb = Ebook( format='pdf', edition=e, url=ebf.file.url, provider='Unglue.it')
eb.save()
ebf.ebook = eb
ebf.save()
temp_file.close() temp_file.close()
finally: finally:
@ -1016,7 +1031,7 @@ class EbookFileTests(TestCase):
os.remove(temp.name) os.remove(temp.name)
#test the ask-appender #test the ask-appender
c.add_ask_to_ebfs() c.add_ask_to_ebfs()
asking_pdf = c.work.ebookfiles().filter(asking = True)[0].file.url asking_pdf = c.work.ebookfiles().filter(asking=True)[0].file.url
assert test_pdf(asking_pdf) assert test_pdf(asking_pdf)
#Now do the same with epub #Now do the same with epub
@ -1032,16 +1047,25 @@ class EbookFileTests(TestCase):
dj_file = DjangoFile(temp_file) dj_file = DjangoFile(temp_file)
ebf = EbookFile( format='epub', edition=e, file=dj_file) ebf = EbookFile( format='epub', edition=e, file=dj_file)
ebf.save() ebf.save()
eb = Ebook( format='epub', edition=e, url=ebf.file.url, provider='Unglue.it')
eb.save()
ebf.ebook = eb
ebf.save()
temp_file.close() temp_file.close()
ebf.make_mobi()
finally: finally:
# make sure we get rid of temp file # make sure we get rid of temp file
os.remove(temp.name) os.remove(temp.name)
#test the ask-appender #test the ask-appender
c.add_ask_to_ebfs() c.add_ask_to_ebfs()
self.assertTrue( c.work.ebookfiles().filter(asking = True, format='epub').count >0) self.assertTrue( c.work.ebookfiles().filter(asking = True, format='epub').count() > 0)
self.assertTrue( c.work.ebookfiles().filter(asking = True, format='mobi').count >0) self.assertTrue( c.work.ebookfiles().filter(asking = True, format='mobi').count() > 0)
self.assertTrue( c.work.ebookfiles().filter(asking = True, ebook__active=True).count() > 0)
self.assertTrue( c.work.ebookfiles().filter(asking = False, ebook__active=True).count() == 0)
#test the unasker
c.revert_asks()
self.assertTrue( c.work.ebookfiles().filter(asking = True, ebook__active=True).count() == 0)
self.assertTrue( c.work.ebookfiles().filter(asking = False, ebook__active=True).count() > 0)
class MobigenTests(TestCase): class MobigenTests(TestCase):
def test_convert_to_mobi(self): def test_convert_to_mobi(self):

View File

@ -7,6 +7,9 @@
# ssh ubuntu@please.unglueit.com "/opt/regluit/deploy/update-regluit" # ssh ubuntu@please.unglueit.com "/opt/regluit/deploy/update-regluit"
cd /opt/regluit cd /opt/regluit
find . -name "*.pyc" -delete
find . -type d -empty -delete
sudo -u ubuntu /usr/bin/git pull sudo -u ubuntu /usr/bin/git pull
source ENV/bin/activate source ENV/bin/activate
pip install --upgrade -r requirements_versioned.pip pip install --upgrade -r requirements_versioned.pip

View File

@ -1,6 +1,9 @@
#!/bin/bash #!/bin/bash
cd /opt/regluit cd /opt/regluit
find . -name "*.pyc" -delete
find . -type d -empty -delete
sudo -u ubuntu /usr/bin/git pull origin production sudo -u ubuntu /usr/bin/git pull origin production
source ENV/bin/activate source ENV/bin/activate
pip install --upgrade -r requirements_versioned.pip pip install --upgrade -r requirements_versioned.pip

View File

@ -7,6 +7,9 @@
# ssh ubuntu@please.unglueit.com "/opt/regluit/deploy/update-regluit" # ssh ubuntu@please.unglueit.com "/opt/regluit/deploy/update-regluit"
cd /opt/regluit cd /opt/regluit
find . -name "*.pyc" -delete
find . -type d -empty -delete
sudo -u ubuntu /usr/bin/git pull sudo -u ubuntu /usr/bin/git pull
source ENV/bin/activate source ENV/bin/activate
#pip install -r requirements.pip #pip install -r requirements.pip

View File

@ -4,7 +4,7 @@ from StringIO import StringIO
from regluit.core.facets import BaseFacet from regluit.core.facets import BaseFacet
from regluit.core.models import Work from regluit.core.models import Work, good_providers
from regluit.api.onix import onix_feed from regluit.api.onix import onix_feed
from .models import Target from .models import Target
@ -45,7 +45,7 @@ def get_target_facet(target, start=datetime(1900,1,1), new=False):
editions__ebooks__created__gt = start, editions__ebooks__created__gt = start,
identifiers__type="isbn", identifiers__type="isbn",
editions__ebooks__format__in = formats, editions__ebooks__format__in = formats,
editions__ebooks__provider__in = ('Internet Archive', 'Unglue.it', 'Github', 'OAPEN Library'), editions__ebooks__provider__in = good_providers,
).distinct().order_by('-featured') ).distinct().order_by('-featured')
model_filters = {"Ebook": format_filter, "Edition": edition_format_filter} model_filters = {"Ebook": format_filter, "Edition": edition_format_filter}

View File

@ -1,22 +1,17 @@
""" #external library imports
external library imports
"""
import logging import logging
import re import re
import zipfile
from datetime import timedelta, datetime, date from datetime import timedelta, date
from decimal import Decimal as D from decimal import Decimal as D
""" #django imports
django imports
"""
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.conf.global_settings import LANGUAGES from django.conf.global_settings import LANGUAGES
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.validators import validate_email
from django.db import models
from django.forms.widgets import RadioSelect from django.forms.widgets import RadioSelect
from django.forms.extras.widgets import SelectDateWidget from django.forms.extras.widgets import SelectDateWidget
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -32,10 +27,8 @@ from selectable.forms import (
from PyPDF2 import PdfFileReader from PyPDF2 import PdfFileReader
#regluit imports
"""
regluit imports
"""
from regluit.core.models import ( from regluit.core.models import (
UserProfile, UserProfile,
RightsHolder, RightsHolder,
@ -51,23 +44,28 @@ from regluit.core.models import (
Work, Work,
Press, Press,
Libpref, Libpref,
Subject,
TWITTER, TWITTER,
FACEBOOK, FACEBOOK,
GRAVATAR,
UNGLUEITAR UNGLUEITAR
) )
from regluit.libraryauth.models import Library from regluit.libraryauth.models import Library
from regluit.core.parameters import LIBRARY, REWARDS, BUY2UNGLUE, THANKS from regluit.core.parameters import (
LIBRARY,
REWARDS,
BUY2UNGLUE,
THANKS,
AGE_LEVEL_CHOICES,
TEXT_RELATION_CHOICES,
)
from regluit.core.lookups import ( from regluit.core.lookups import (
OwnerLookup, OwnerLookup,
WorkLookup, WorkLookup,
PublisherNameLookup, PublisherNameLookup,
EditionLookup,
SubjectLookup, SubjectLookup,
EditionNoteLookup,
) )
from regluit.utils.localdatetime import now from regluit.utils.localdatetime import now
from regluit.utils.fields import EpubFileField, ISBNField from regluit.utils.fields import ISBNField
from regluit.mobi import Mobi from regluit.mobi import Mobi
from regluit.pyepub import EPUB from regluit.pyepub import EPUB
from regluit.bisac.models import BisacHeading from regluit.bisac.models import BisacHeading
@ -90,15 +88,15 @@ class SurveyForm(forms.Form):
label = forms.CharField(max_length=64, required=True) label = forms.CharField(max_length=64, required=True)
survey = forms.ModelChoiceField(Questionnaire.objects.all(), widget=RadioSelect(), empty_label=None, required = True,) survey = forms.ModelChoiceField(Questionnaire.objects.all(), widget=RadioSelect(), empty_label=None, required = True,)
isbn = ISBNField( isbn = ISBNField(
label=_("ISBN"), label=_("ISBN"),
max_length=17, max_length=17,
required = False, required = False,
help_text = _("13 digits, no dash."), help_text = _("13 digits, no dash."),
error_messages = { error_messages = {
'invalid': _("This must be a valid ISBN-13."), 'invalid': _("This must be a valid ISBN-13."),
} }
) )
def clean_isbn(self): def clean_isbn(self):
isbn = self.cleaned_data['isbn'] isbn = self.cleaned_data['isbn']
if not isbn: if not isbn:
@ -115,24 +113,36 @@ class EditionForm(forms.ModelForm):
add_author = forms.CharField(max_length=500, required=False) add_author = forms.CharField(max_length=500, required=False)
add_author_relation = forms.ChoiceField(choices=CREATOR_RELATIONS, initial=('aut', 'Author')) add_author_relation = forms.ChoiceField(choices=CREATOR_RELATIONS, initial=('aut', 'Author'))
add_subject = AutoCompleteSelectField( add_subject = AutoCompleteSelectField(
SubjectLookup, SubjectLookup,
widget=AutoCompleteSelectWidget(SubjectLookup,allow_new=True), widget=AutoCompleteSelectWidget(SubjectLookup, allow_new=True),
label='Keyword', label='Keyword',
required =False required=False,
) )
add_related_work = AutoCompleteSelectField(
WorkLookup,
widget=AutoCompleteSelectWidget(WorkLookup, allow_new=False, attrs={'size': 40}),
label='Related Work',
required=False,
)
add_work_relation = forms.ChoiceField(
choices=TEXT_RELATION_CHOICES,
initial=('translation', 'translation'),
required=False,
)
bisac = forms.ModelChoiceField( bisac_headings, required=False ) bisac = forms.ModelChoiceField( bisac_headings, required=False )
publisher_name = AutoCompleteSelectField( publisher_name = AutoCompleteSelectField(
PublisherNameLookup, PublisherNameLookup,
label='Publisher Name', label='Publisher Name',
widget=AutoCompleteSelectWidget(PublisherNameLookup,allow_new=True), widget=AutoCompleteSelectWidget(PublisherNameLookup,allow_new=True),
required=False, required=False,
allow_new=True, allow_new=True,
) )
isbn = ISBNField( isbn = ISBNField(
label=_("ISBN"), label=_("ISBN"),
max_length=17, max_length=17,
required = False, required = False,
help_text = _("13 digits, no dash."), help_text = _("13 digits, no dash."),
error_messages = { error_messages = {
@ -140,8 +150,8 @@ class EditionForm(forms.ModelForm):
} }
) )
goog = forms.RegexField( goog = forms.RegexField(
label=_("Google Books ID"), label=_("Google Books ID"),
max_length=12, max_length=12,
regex=r'^([a-zA-Z0-9\-_]{12}|delete)$', regex=r'^([a-zA-Z0-9\-_]{12}|delete)$',
required = False, required = False,
help_text = _("12 alphanumeric characters, dash or underscore, case sensitive."), help_text = _("12 alphanumeric characters, dash or underscore, case sensitive."),
@ -150,8 +160,8 @@ class EditionForm(forms.ModelForm):
} }
) )
gdrd = forms.RegexField( gdrd = forms.RegexField(
label=_("GoodReads ID"), label=_("GoodReads ID"),
max_length=8, max_length=8,
regex=r'^(\d+|delete)$', regex=r'^(\d+|delete)$',
required = False, required = False,
help_text = _("1-8 digits."), help_text = _("1-8 digits."),
@ -160,8 +170,8 @@ class EditionForm(forms.ModelForm):
} }
) )
thng = forms.RegexField( thng = forms.RegexField(
label=_("LibraryThing ID"), label=_("LibraryThing ID"),
max_length=8, max_length=8,
regex=r'(^\d+|delete)$', regex=r'(^\d+|delete)$',
required = False, required = False,
help_text = _("1-8 digits."), help_text = _("1-8 digits."),
@ -170,7 +180,7 @@ class EditionForm(forms.ModelForm):
} }
) )
oclc = forms.RegexField( oclc = forms.RegexField(
label=_("OCLCnum"), label=_("OCLCnum"),
regex=r'^(\d\d\d\d\d\d\d\d\d*|delete)$', regex=r'^(\d\d\d\d\d\d\d\d\d*|delete)$',
required = False, required = False,
help_text = _("8 or more digits."), help_text = _("8 or more digits."),
@ -182,132 +192,191 @@ class EditionForm(forms.ModelForm):
label=_("HTTP URL"), label=_("HTTP URL"),
# https://mathiasbynens.be/demo/url-regex # https://mathiasbynens.be/demo/url-regex
regex=re.compile(r"(https?|ftp)://(-\.)?([^\s/?\.#]+\.?)+(/[^\s]*)?$", regex=re.compile(r"(https?|ftp)://(-\.)?([^\s/?\.#]+\.?)+(/[^\s]*)?$",
flags=re.IGNORECASE|re.S ), flags=re.IGNORECASE|re.S ),
required = False, required = False,
help_text = _("no spaces of funny stuff."), help_text = _("no spaces of funny stuff."),
error_messages = { error_messages = {
'invalid': _("This value must be a valid http(s) URL."), 'invalid': _("This value must be a valid http(s) URL."),
} }
) )
doi = forms.RegexField(
label=_("DOI"),
regex=r'^(https?://dx\.doi\.org/)?(10.\d\d\d\d/\w+|delete)$',
required = False,
help_text = _("starts with '10.' or 'http://dx.doi.org'"),
error_messages = {
'invalid': _("This value must be a valid DOI."),
}
)
language = forms.ChoiceField(choices=LANGUAGES) language = forms.ChoiceField(choices=LANGUAGES)
age_level = forms.ChoiceField(choices=AGE_LEVEL_CHOICES, required=False)
description = forms.CharField( required=False, widget=CKEditorWidget()) description = forms.CharField( required=False, widget=CKEditorWidget())
coverfile = forms.ImageField(required=False) coverfile = forms.ImageField(required=False)
note = AutoCompleteSelectField(
EditionNoteLookup,
widget=AutoCompleteSelectWidget(EditionNoteLookup, allow_new=True),
label='Edition Note',
required=False,
allow_new=True,
)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(EditionForm, self).__init__(*args, **kwargs) super(EditionForm, self).__init__(*args, **kwargs)
self.relators = [] self.relators = []
if self.instance: if self.instance:
for relator in self.instance.relators.all(): for relator in self.instance.relators.all():
select = forms.Select(choices=CREATOR_RELATIONS).render('change_relator_%s' % relator.id , relator.relation.code ) select = forms.Select(choices=CREATOR_RELATIONS).render('change_relator_%s' % relator.id , relator.relation.code )
self.relators.append({'relator':relator,'select':select}) self.relators.append({'relator':relator, 'select':select})
def clean_doi(self):
doi = self.cleaned_data["doi"]
if doi:
if doi.startswith("https"):
return doi[19:]
elif doi.startswith("http"):
return doi[18:]
return doi
def clean(self): def clean(self):
has_isbn = self.cleaned_data.get("isbn", False) not in nulls has_isbn = self.cleaned_data.get("isbn", False) not in nulls
has_oclc = self.cleaned_data.get("oclc", False) not in nulls has_oclc = self.cleaned_data.get("oclc", False) not in nulls
has_goog = self.cleaned_data.get("goog", False) not in nulls has_goog = self.cleaned_data.get("goog", False) not in nulls
has_http = self.cleaned_data.get("http", False) not in nulls has_http = self.cleaned_data.get("http", False) not in nulls
if not has_isbn and not has_oclc and not has_goog and not has_http: has_doi = self.cleaned_data.get("doi", False) not in nulls
raise forms.ValidationError(_("There must be either an ISBN or an OCLC number.")) try:
has_id = self.instance.work.identifiers.all().count() > 0
except AttributeError:
has_id = False
if not has_id and not has_isbn and not has_oclc and not has_goog and not has_http and not has_doi:
raise forms.ValidationError(_("There must be either an ISBN, a DOI, a URL or an OCLC number."))
return self.cleaned_data return self.cleaned_data
class Meta: class Meta:
model = Edition model = Edition
exclude = ('created', 'work') exclude = ('created', 'work')
widgets = { widgets = {
'title': forms.TextInput(attrs={'size': 40}), 'title': forms.TextInput(attrs={'size': 40}),
'add_author': forms.TextInput(attrs={'size': 30}), 'add_author': forms.TextInput(attrs={'size': 30}),
'add_subject': forms.TextInput(attrs={'size': 30}), 'add_subject': forms.TextInput(attrs={'size': 30}),
'unglued': forms.CheckboxInput(), 'unglued': forms.CheckboxInput(),
'cover_image': forms.TextInput(attrs={'size': 60}), 'cover_image': forms.TextInput(attrs={'size': 60}),
} }
def test_file(the_file):
if the_file and the_file.name:
if format == 'epub':
try:
book = EPUB(the_file.file)
except Exception as e:
raise forms.ValidationError(_('Are you sure this is an EPUB file?: %s' % e) )
elif format == 'mobi':
try:
book = Mobi(the_file.file)
book.parse()
except Exception as e:
raise forms.ValidationError(_('Are you sure this is a MOBI file?: %s' % e) )
elif format == 'pdf':
try:
doc = PdfFileReader(the_file.file)
except Exception, e:
raise forms.ValidationError(_('%s is not a valid PDF file' % the_file.name) )
class EbookFileForm(forms.ModelForm): class EbookFileForm(forms.ModelForm):
file = forms.FileField(max_length=16777216) file = forms.FileField(max_length=16777216)
version_label = forms.CharField(max_length=512, required=False)
new_version_label = forms.CharField(required=False)
def __init__(self, campaign_type=BUY2UNGLUE, *args, **kwargs): def __init__(self, campaign_type=BUY2UNGLUE, *args, **kwargs):
super(EbookFileForm, self).__init__(*args, **kwargs) super(EbookFileForm, self).__init__(*args, **kwargs)
self.campaign_type = campaign_type self.campaign_type = campaign_type
if campaign_type == BUY2UNGLUE: if campaign_type == BUY2UNGLUE:
self.fields['format'].widget=forms.HiddenInput() self.fields['format'].widget = forms.HiddenInput()
if campaign_type == THANKS: if campaign_type == THANKS:
self.fields['format'].widget=forms.Select(choices=(('pdf','PDF'),( 'epub','EPUB'), ('mobi','MOBI'))) self.fields['format'].widget = forms.Select(
choices = (('pdf', 'PDF'), ('epub', 'EPUB'), ('mobi', 'MOBI'))
)
def clean_version_label(self):
new_label = self.data.get('new_version_label','')
return new_label if new_label else self.cleaned_data['version_label']
def clean_format(self): def clean_format(self):
if self.campaign_type is BUY2UNGLUE: if self.campaign_type is BUY2UNGLUE:
return 'epub' return 'epub'
else: else:
logger.info("EbookFileForm "+self.cleaned_data.get('format','')) logger.info("EbookFileForm "+self.cleaned_data.get('format',''))
return self.cleaned_data.get('format','') return self.cleaned_data.get('format','')
def clean(self): def clean(self):
format = self.cleaned_data['format'] format = self.cleaned_data['format']
the_file = self.cleaned_data.get('file',None) the_file = self.cleaned_data.get('file', None)
if the_file and the_file.name: test_file(the_file)
if format == 'epub':
try:
book = EPUB(the_file.file)
except Exception as e:
raise forms.ValidationError(_('Are you sure this is an EPUB file?: %s' % e) )
elif format == 'mobi':
try:
book = Mobi(the_file.file)
book.parse()
except Exception as e:
raise forms.ValidationError(_('Are you sure this is a MOBI file?: %s' % e) )
elif format == 'pdf':
try:
doc = PdfFileReader(the_file.file)
except Exception, e:
raise forms.ValidationError(_('%s is not a valid PDF file' % the_file.name) )
return self.cleaned_data return self.cleaned_data
class Meta: class Meta:
model = EbookFile model = EbookFile
widgets = { 'edition': forms.HiddenInput} widgets = { 'edition': forms.HiddenInput}
exclude = { 'created', 'asking' } exclude = { 'created', 'asking', 'ebook' }
class EbookForm(forms.ModelForm): class EbookForm(forms.ModelForm):
file = forms.FileField(max_length=16777216, required=False)
url = forms.CharField(required=False, widget=forms.TextInput(attrs={'size' : 60},))
version_label = forms.CharField(required=False)
new_version_label = forms.CharField(required=False)
class Meta: class Meta:
model = Ebook model = Ebook
exclude =( 'created', 'download_count', 'active', 'filesize') exclude = ('created', 'download_count', 'active', 'filesize', 'version_iter')
widgets = { widgets = {
'edition': forms.HiddenInput, 'edition': forms.HiddenInput,
'user': forms.HiddenInput, 'user': forms.HiddenInput,
'provider': forms.HiddenInput, 'provider': forms.HiddenInput,
'url': forms.TextInput(attrs={'size' : 60}),
} }
def clean_version_label(self):
new_label = self.data.get('new_version_label','')
return new_label if new_label else self.cleaned_data['version_label']
def clean_provider(self): def clean_provider(self):
new_provider= Ebook.infer_provider(self.data[self.prefix + '-url']) new_provider = Ebook.infer_provider(self.cleaned_data['url'])
if not new_provider: if not new_provider:
raise forms.ValidationError(_("At this time, ebook URLs must point at Internet Archive, Wikisources, Wikibooks, Hathitrust, Project Gutenberg, raw files at Github, or Google Books.")) raise forms.ValidationError(_("At this time, ebook URLs must point at Internet Archive, Wikisources, Wikibooks, Hathitrust, Project Gutenberg, raw files at Github, or Google Books."))
return new_provider return new_provider
def clean_url(self): def clean_url(self):
url = self.data[self.prefix + '-url'] url = self.cleaned_data['url']
try: try:
Ebook.objects.get(url=url) Ebook.objects.get(url=url)
except Ebook.DoesNotExist: except Ebook.DoesNotExist:
return url return url
raise forms.ValidationError(_("There's already an ebook with that url.")) raise forms.ValidationError(_("There's already an ebook with that url."))
def clean(self):
format = self.cleaned_data['format']
the_file = self.cleaned_data.get('file', None)
url = self.cleaned_data.get('url', None)
test_file(the_file)
if not the_file and not url:
raise forms.ValidationError(_("Either a link or a file is required."))
if the_file and url:
self.cleaned_data['url'] = ''
return self.cleaned_data
def UserClaimForm ( user_instance, *args, **kwargs ): def UserClaimForm ( user_instance, *args, **kwargs ):
class ClaimForm(forms.ModelForm): class ClaimForm(forms.ModelForm):
i_agree=forms.BooleanField(error_messages={'required': 'You must agree to the Terms in order to claim a work.'}) i_agree = forms.BooleanField(error_messages={'required': 'You must agree to the Terms in order to claim a work.'})
rights_holder=forms.ModelChoiceField(queryset=user_instance.rights_holder.all(), empty_label=None) rights_holder = forms.ModelChoiceField(queryset=user_instance.rights_holder.all(), empty_label=None)
class Meta: class Meta:
model = Claim model = Claim
exclude = ('status',) exclude = ('status',)
widgets = { widgets = {
'user': forms.HiddenInput, 'user': forms.HiddenInput,
'work': forms.HiddenInput, 'work': forms.HiddenInput,
} }
def __init__(self): def __init__(self):
super(ClaimForm, self).__init__(*args, **kwargs) super(ClaimForm, self).__init__(*args, **kwargs)
return ClaimForm() return ClaimForm()
class RightsHolderForm(forms.ModelForm): class RightsHolderForm(forms.ModelForm):
owner = AutoCompleteSelectField( owner = AutoCompleteSelectField(
OwnerLookup, OwnerLookup,
@ -317,7 +386,7 @@ class RightsHolderForm(forms.ModelForm):
error_messages={'required': 'Please ensure the owner is a valid Unglue.it account.'}, error_messages={'required': 'Please ensure the owner is a valid Unglue.it account.'},
) )
email = forms.EmailField( email = forms.EmailField(
label=_("notification email address for rights holder"), label=_("notification email address for rights holder"),
max_length=100, max_length=100,
error_messages={'required': 'Please enter an email address for the rights holder.'}, error_messages={'required': 'Please enter an email address for the rights holder.'},
) )
@ -333,12 +402,12 @@ class RightsHolderForm(forms.ModelForm):
return rights_holder_name return rights_holder_name
raise forms.ValidationError(_("Another rights holder with that name already exists.")) raise forms.ValidationError(_("Another rights holder with that name already exists."))
class ProfileForm(forms.ModelForm): class ProfileForm(forms.ModelForm):
clear_facebook=forms.BooleanField(required=False) clear_facebook = forms.BooleanField(required=False)
clear_twitter=forms.BooleanField(required=False) clear_twitter = forms.BooleanField(required=False)
clear_goodreads=forms.BooleanField(required=False) clear_goodreads = forms.BooleanField(required=False)
class Meta: class Meta:
model = UserProfile model = UserProfile
fields = 'tagline', 'librarything_id', 'home_url', 'clear_facebook', 'clear_twitter', 'clear_goodreads', 'avatar_source' fields = 'tagline', 'librarything_id', 'home_url', 'clear_facebook', 'clear_twitter', 'clear_goodreads', 'avatar_source'
@ -361,10 +430,10 @@ class ProfileForm(forms.ModelForm):
def clean(self): def clean(self):
# check that if a social net is cleared, we're not using it a avatar source # check that if a social net is cleared, we're not using it a avatar source
if self.cleaned_data.get("clear_facebook", False) and self.cleaned_data.get("avatar_source", None)==FACEBOOK: if self.cleaned_data.get("clear_facebook", False) and self.cleaned_data.get("avatar_source", None) == FACEBOOK:
self.cleaned_data["avatar_source"]==UNGLUEITAR self.cleaned_data["avatar_source"] == UNGLUEITAR
if self.cleaned_data.get("clear_twitter", False) and self.cleaned_data.get("avatar_source", None)==TWITTER: if self.cleaned_data.get("clear_twitter", False) and self.cleaned_data.get("avatar_source", None) == TWITTER:
self.cleaned_data["avatar_source"]==UNGLUEITAR self.cleaned_data["avatar_source"] == UNGLUEITAR
return self.cleaned_data return self.cleaned_data
class CloneCampaignForm(forms.Form): class CloneCampaignForm(forms.Form):
@ -395,30 +464,33 @@ def getTransferCreditForm(maximum, data=None, *args, **kwargs ):
) )
amount = forms.IntegerField( amount = forms.IntegerField(
required=True, required=True,
min_value=1, min_value=1,
max_value=maximum, max_value=maximum,
label="Transfer Amount", label="Transfer Amount",
error_messages={'min_value': 'Transfer amount must be positive', 'max_value': 'You only have %(limit_value)s available for transfer'}, error_messages={
'min_value': 'Transfer amount must be positive',
'max_value': 'You only have %(limit_value)s available for transfer'
},
) )
return TransferCreditForm( data=data ) return TransferCreditForm( data=data )
class WorkForm(forms.Form): class WorkForm(forms.Form):
other_work = forms.ModelChoiceField(queryset=Work.objects.all(), other_work = forms.ModelChoiceField(queryset=Work.objects.all(),
widget=forms.HiddenInput(), widget=forms.HiddenInput(),
required=True, required=True,
error_messages={'required': 'Missing work to merge with.'}, error_messages={'required': 'Missing work to merge with.'},
) )
work=None work = None
def clean_other_work(self): def clean_other_work(self):
if self.cleaned_data["other_work"].id== self.work.id: if self.cleaned_data["other_work"].id == self.work.id:
raise forms.ValidationError(_("You can't merge a work into itself")) raise forms.ValidationError(_("You can't merge a work into itself"))
return self.cleaned_data["other_work"] return self.cleaned_data["other_work"]
def __init__(self, work=None, *args, **kwargs): def __init__(self, work=None, *args, **kwargs):
super(WorkForm, self).__init__(*args, **kwargs) super(WorkForm, self).__init__(*args, **kwargs)
self.work=work self.work = work
class OtherWorkForm(WorkForm): class OtherWorkForm(WorkForm):
other_work = AutoCompleteSelectField( other_work = AutoCompleteSelectField(
@ -427,12 +499,12 @@ class OtherWorkForm(WorkForm):
widget=AutoCompleteSelectWidget(WorkLookup), widget=AutoCompleteSelectWidget(WorkLookup),
required=True, required=True,
error_messages={'required': 'Missing work to merge with.'}, error_messages={'required': 'Missing work to merge with.'},
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(OtherWorkForm, self).__init__(*args, **kwargs) super(OtherWorkForm, self).__init__(*args, **kwargs)
self.fields['other_work'].widget.update_query_parameters({'language':self.work.language}) self.fields['other_work'].widget.update_query_parameters({'language':self.work.language})
class EditManagersForm(forms.ModelForm): class EditManagersForm(forms.ModelForm):
managers = AutoCompleteSelectMultipleField( managers = AutoCompleteSelectMultipleField(
OwnerLookup, OwnerLookup,
@ -451,7 +523,7 @@ class CustomPremiumForm(forms.ModelForm):
class Meta: class Meta:
model = Premium model = Premium
fields = 'campaign', 'amount', 'description', 'type', 'limit' fields = 'campaign', 'amount', 'description', 'type', 'limit'
widgets = { widgets = {
'description': forms.Textarea(attrs={'cols': 80, 'rows': 4}), 'description': forms.Textarea(attrs={'cols': 80, 'rows': 4}),
'campaign': forms.HiddenInput, 'campaign': forms.HiddenInput,
'type': forms.HiddenInput(attrs={'value':'XX'}), 'type': forms.HiddenInput(attrs={'value':'XX'}),
@ -459,21 +531,24 @@ class CustomPremiumForm(forms.ModelForm):
} }
def clean_type(self): def clean_type(self):
return 'CU' return 'CU'
class OfferForm(forms.ModelForm): class OfferForm(forms.ModelForm):
class Meta: class Meta:
model = Offer model = Offer
fields = 'work', 'price', 'license' fields = 'work', 'price', 'license'
widgets = { widgets = {
'work': forms.HiddenInput, 'work': forms.HiddenInput,
'license': forms.HiddenInput, 'license': forms.HiddenInput,
} }
date_selector=range(date.today().year, settings.MAX_CC_DATE.year+1) date_selector = range(date.today().year, settings.MAX_CC_DATE.year+1)
class CCDateForm(object): class CCDateForm(object):
target = forms.DecimalField( min_value= D(settings.UNGLUEIT_MINIMUM_TARGET), error_messages={'required': 'Please specify a Revenue Target.'} ) target = forms.DecimalField(
min_value= D(settings.UNGLUEIT_MINIMUM_TARGET),
error_messages={'required': 'Please specify a Revenue Target.'}
)
minimum_target = settings.UNGLUEIT_MINIMUM_TARGET minimum_target = settings.UNGLUEIT_MINIMUM_TARGET
maximum_target = settings.UNGLUEIT_MAXIMUM_TARGET maximum_target = settings.UNGLUEIT_MAXIMUM_TARGET
max_cc_date = settings.MAX_CC_DATE max_cc_date = settings.MAX_CC_DATE
@ -490,26 +565,26 @@ class CCDateForm(object):
new_cc_date_initial = self.cleaned_data['cc_date_initial'] new_cc_date_initial = self.cleaned_data['cc_date_initial']
if new_cc_date_initial.date() > settings.MAX_CC_DATE: if new_cc_date_initial.date() > settings.MAX_CC_DATE:
raise forms.ValidationError('The initial Ungluing Date cannot be after %s'%settings.MAX_CC_DATE) raise forms.ValidationError('The initial Ungluing Date cannot be after %s'%settings.MAX_CC_DATE)
elif new_cc_date_initial - now() < timedelta(days=0): elif new_cc_date_initial - now() < timedelta(days=0):
raise forms.ValidationError('The initial Ungluing date must be in the future!') raise forms.ValidationError('The initial Ungluing date must be in the future!')
return new_cc_date_initial return new_cc_date_initial
class DateCalculatorForm(CCDateForm, forms.ModelForm): class DateCalculatorForm(CCDateForm, forms.ModelForm):
revenue = forms.DecimalField() revenue = forms.DecimalField()
cc_date_initial = forms.DateTimeField( cc_date_initial = forms.DateTimeField(
widget = SelectDateWidget(years=date_selector) widget = SelectDateWidget(years=date_selector)
) )
class Meta: class Meta:
model = Campaign model = Campaign
fields = 'target', 'cc_date_initial', 'revenue', fields = 'target', 'cc_date_initial', 'revenue',
def getManageCampaignForm ( instance, data=None, initial=None, *args, **kwargs ): def getManageCampaignForm ( instance, data=None, initial=None, *args, **kwargs ):
def get_queryset(): def get_queryset():
work=instance.work work = instance.work
return Edition.objects.filter(work = work) return Edition.objects.filter(work = work)
class ManageCampaignForm(CCDateForm,forms.ModelForm): class ManageCampaignForm(CCDateForm, forms.ModelForm):
target = forms.DecimalField( required= (instance.type in {REWARDS, BUY2UNGLUE})) target = forms.DecimalField( required= (instance.type in {REWARDS, BUY2UNGLUE}))
deadline = forms.DateTimeField( deadline = forms.DateTimeField(
required = (instance.type==REWARDS), required = (instance.type==REWARDS),
@ -520,91 +595,102 @@ def getManageCampaignForm ( instance, data=None, initial=None, *args, **kwargs )
widget = SelectDateWidget(years=date_selector) if instance.status=='INITIALIZED' else forms.HiddenInput widget = SelectDateWidget(years=date_selector) if instance.status=='INITIALIZED' else forms.HiddenInput
) )
paypal_receiver = forms.EmailField( paypal_receiver = forms.EmailField(
label=_("contact email address for this campaign"), label=_("contact email address for this campaign"),
max_length=100, max_length=100,
error_messages={'required': 'You must enter the email we should contact you at for this campaign.'}, error_messages={'required': 'You must enter the email we should contact you at for this campaign.'},
) )
edition = forms.ModelChoiceField(get_queryset(), widget=RadioSelect(),empty_label='no edition selected',required = False,) edition = forms.ModelChoiceField(
publisher = forms.ModelChoiceField(instance.work.publishers(), empty_label='no publisher selected', required = False,) get_queryset(),
widget=RadioSelect(),
empty_label='no edition selected',
required=False,
)
publisher = forms.ModelChoiceField(
instance.work.publishers(),
empty_label='no publisher selected',
required=False,
)
work_description = forms.CharField( required=False , widget=CKEditorWidget()) work_description = forms.CharField( required=False , widget=CKEditorWidget())
class Meta: class Meta:
model = Campaign model = Campaign
fields = 'description', 'details', 'license', 'target', 'deadline', 'paypal_receiver', 'edition', 'email', 'publisher', 'cc_date_initial', "do_watermark", "use_add_ask", fields = ('description', 'details', 'license', 'target', 'deadline', 'paypal_receiver',
'edition', 'email', 'publisher', 'cc_date_initial', "do_watermark", "use_add_ask",
)
widgets = { 'deadline': SelectDateWidget } widgets = { 'deadline': SelectDateWidget }
def clean_target(self): def clean_target(self):
if self.instance.type == THANKS: if self.instance.type == THANKS:
return None return None
new_target = super(ManageCampaignForm,self).clean_target() new_target = super(ManageCampaignForm, self).clean_target()
if self.instance: if self.instance:
if self.instance.status == 'ACTIVE' and self.instance.target < new_target: if self.instance.status == 'ACTIVE' and self.instance.target < new_target:
raise forms.ValidationError(_('The fundraising target for an ACTIVE campaign cannot be increased.')) raise forms.ValidationError(_('The fundraising target for an ACTIVE campaign cannot be increased.'))
return new_target return new_target
def clean_cc_date_initial(self): def clean_cc_date_initial(self):
if self.instance.type in {REWARDS,THANKS} : if self.instance.type in {REWARDS, THANKS} :
return None return None
if self.instance: if self.instance:
if self.instance.status != 'INITIALIZED': if self.instance.status != 'INITIALIZED':
# can't change this once launched # can't change this once launched
return self.instance.cc_date_initial return self.instance.cc_date_initial
return super(ManageCampaignForm,self).clean_cc_date_initial() return super(ManageCampaignForm, self).clean_cc_date_initial()
def clean_deadline(self): def clean_deadline(self):
if self.instance.type in {BUY2UNGLUE, THANKS} : if self.instance.type in {BUY2UNGLUE, THANKS} :
return None return None
new_deadline_date = self.cleaned_data['deadline'] new_deadline_date = self.cleaned_data['deadline']
new_deadline= new_deadline_date + timedelta(hours=23,minutes=59) new_deadline = new_deadline_date + timedelta(hours=23, minutes=59)
if self.instance: if self.instance:
if self.instance.status == 'ACTIVE': if self.instance.status == 'ACTIVE':
return self.instance.deadline return self.instance.deadline
if new_deadline_date - now() > timedelta(days=int(settings.UNGLUEIT_LONGEST_DEADLINE)): if new_deadline_date - now() > timedelta(days=int(settings.UNGLUEIT_LONGEST_DEADLINE)):
raise forms.ValidationError(_('The chosen closing date is more than %s days from now' % settings.UNGLUEIT_LONGEST_DEADLINE)) raise forms.ValidationError(_('The chosen closing date is more than %s days from now' % settings.UNGLUEIT_LONGEST_DEADLINE))
elif new_deadline - now() < timedelta(days=0): elif new_deadline - now() < timedelta(days=0):
raise forms.ValidationError(_('The chosen closing date is in the past')) raise forms.ValidationError(_('The chosen closing date is in the past'))
return new_deadline return new_deadline
def clean_license(self): def clean_license(self):
new_license = self.cleaned_data['license'] new_license = self.cleaned_data['license']
if self.instance: if self.instance:
if self.instance.status == 'ACTIVE' and self.instance.license != new_license: if self.instance.status == 'ACTIVE' and self.instance.license != new_license:
# should only allow change to a less restrictive license # should only allow change to a less restrictive license
if self.instance.license == 'CC BY-ND' and new_license in ['CC BY-NC-ND','CC BY-NC-SA','CC BY-NC']: if self.instance.license == 'CC BY-ND' and new_license in ['CC BY-NC-ND', 'CC BY-NC-SA', 'CC BY-NC']:
raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.')) raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.'))
elif self.instance.license == 'CC BY' and new_license != 'CC0': elif self.instance.license == 'CC BY' and new_license != 'CC0':
raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.')) raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.'))
elif self.instance.license == 'CC BY-NC' and new_license in ['CC BY-NC-ND','CC BY-NC-SA','CC BY-SA','CC BY-ND']: elif self.instance.license == 'CC BY-NC' and new_license in ['CC BY-NC-ND', 'CC BY-NC-SA', 'CC BY-SA', 'CC BY-ND']:
raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.')) raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.'))
elif self.instance.license == 'CC BY-ND' and new_license in ['CC BY-NC-ND','CC BY-NC-SA','CC BY-SA','CC BY-NC']: elif self.instance.license == 'CC BY-ND' and new_license in ['CC BY-NC-ND', 'CC BY-NC-SA', 'CC BY-SA', 'CC BY-NC']:
raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.')) raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.'))
elif self.instance.license == 'CC BY-SA' and new_license in ['CC BY-NC-ND','CC BY-NC-SA','CC BY-ND','CC BY-NC']: elif self.instance.license == 'CC BY-SA' and new_license in ['CC BY-NC-ND', 'CC BY-NC-SA', 'CC BY-ND', 'CC BY-NC']:
raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.')) raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.'))
elif self.instance.license == 'CC BY-NC-SA' and new_license in ['CC BY-NC-ND','CC BY-ND']: elif self.instance.license == 'CC BY-NC-SA' and new_license in ['CC BY-NC-ND', 'CC BY-ND']:
raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.')) raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.'))
elif self.instance.license == 'CC0' : elif self.instance.license == 'CC0' :
raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.')) raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.'))
elif self.instance.license in ['GDFL' , 'LAL']: elif self.instance.license in ['GDFL', 'LAL']:
raise forms.ValidationError(_('Once you start a campaign with GDFL or LAL, you can\'t use any other license.')) raise forms.ValidationError(_('Once you start a campaign with GDFL or LAL, you can\'t use any other license.'))
return new_license return new_license
if initial and not initial.get('edition', None) and not instance.edition: if initial and not initial.get('edition', None) and not instance.edition:
initial['edition']= instance.work.editions.all()[0] initial['edition'] = instance.work.editions.all()[0]
return ManageCampaignForm(instance = instance, data=data, initial=initial) return ManageCampaignForm(instance=instance, data=data, initial=initial)
class CampaignPurchaseForm(forms.Form): class CampaignPurchaseForm(forms.Form):
anonymous = forms.BooleanField(required=False, label=_("Make this purchase anonymous, please")) anonymous = forms.BooleanField(required=False, label=_("Make this purchase anonymous, please"))
offer_id = forms.IntegerField(required=False) offer_id = forms.IntegerField(required=False)
offer=None offer = None
library_id = forms.IntegerField(required=False) library_id = forms.IntegerField(required=False)
library = None library = None
copies = forms.IntegerField(required=False,min_value=1) copies = forms.IntegerField(required=False, min_value=1)
give_to = forms.EmailField(required = False) give_to = forms.EmailField(required = False)
give_message = forms.CharField(required = False, max_length=512, ) give_message = forms.CharField(required = False, max_length=512, )
def clean_offer_id(self): def clean_offer_id(self):
offer_id = self.cleaned_data['offer_id'] offer_id = self.cleaned_data['offer_id']
try: try:
self.offer= Offer.objects.get(id=offer_id) self.offer = Offer.objects.get(id=offer_id)
except Offer.DoesNotExist: except Offer.DoesNotExist:
raise forms.ValidationError(_("Sorry, that offer is not valid.")) raise forms.ValidationError(_("Sorry, that offer is not valid."))
@ -615,23 +701,23 @@ class CampaignPurchaseForm(forms.Form):
self.library = Library.objects.get(id=library_id) self.library = Library.objects.get(id=library_id)
except Library.DoesNotExist: except Library.DoesNotExist:
raise forms.ValidationError(_("Sorry, that Library is not valid.")) raise forms.ValidationError(_("Sorry, that Library is not valid."))
def clean_copies(self): def clean_copies(self):
copies = self.cleaned_data.get('copies',1) copies = self.cleaned_data.get('copies', 1)
return copies if copies else 1 return copies if copies else 1
def clean_anonymous(self): def clean_anonymous(self):
if self.data.get('give', False): if self.data.get('give', False):
return True return True
else: else:
return self.cleaned_data['anonymous'] return self.cleaned_data['anonymous']
def clean(self): def clean(self):
if self.offer.license == LIBRARY: if self.offer.license == LIBRARY:
if not self.library: if not self.library:
raise forms.ValidationError(_("No library specified." )) raise forms.ValidationError(_("No library specified." ))
if self.data.get('give', False): if self.data.get('give', False):
if not self.cleaned_data.get('give_to',None): if not self.cleaned_data.get('give_to', None):
raise forms.ValidationError(_("Gift recipient email is needed." )) raise forms.ValidationError(_("Gift recipient email is needed." ))
else: else:
if 'give_to' in self._errors: if 'give_to' in self._errors:
@ -639,28 +725,31 @@ class CampaignPurchaseForm(forms.Form):
return self.cleaned_data return self.cleaned_data
def amount(self): def amount(self):
return self.offer.price * self.cleaned_data.get('copies',1) if self.offer else None return self.offer.price * self.cleaned_data.get('copies', 1) if self.offer else None
@property @property
def trans_extra(self): def trans_extra(self):
pe = PledgeExtra( anonymous=self.cleaned_data['anonymous'], pe = PledgeExtra( anonymous=self.cleaned_data['anonymous'],
offer = self.offer ) offer = self.offer )
if self.library: if self.library:
pe.extra['library_id']=self.library.id pe.extra['library_id'] = self.library.id
pe.extra['copies']=self.cleaned_data.get('copies',1) pe.extra['copies'] = self.cleaned_data.get('copies', 1)
if self.data.get('give', False): if self.data.get('give', False):
pe.extra['give_to']=self.cleaned_data['give_to'] pe.extra['give_to'] = self.cleaned_data['give_to']
pe.extra['give_message']=self.cleaned_data['give_message'] pe.extra['give_message'] = self.cleaned_data['give_message']
return pe return pe
class CampaignThanksForm(forms.Form): class CampaignThanksForm(forms.Form):
anonymous = forms.BooleanField(required=False, label=_("Make this contribution anonymous, please")) anonymous = forms.BooleanField(
required=False,
label=_("Make this contribution anonymous, please")
)
preapproval_amount = forms.DecimalField( preapproval_amount = forms.DecimalField(
required = True, required = True,
min_value=D('1.00'), min_value=D('1.00'),
max_value=D('2000.00'), max_value=D('2000.00'),
decimal_places=2, decimal_places=2,
label="Pledge Amount", label="Pledge Amount",
) )
@property @property
@ -672,43 +761,47 @@ class CampaignPledgeForm(forms.Form):
preapproval_amount = forms.DecimalField( preapproval_amount = forms.DecimalField(
required = False, required = False,
min_value=D('1.00'), min_value=D('1.00'),
max_value=D('2000.00'), max_value=D('2000.00'),
decimal_places=2, decimal_places=2,
label="Pledge Amount", label="Pledge Amount",
) )
def amount(self): def amount(self):
return self.cleaned_data["preapproval_amount"] if self.cleaned_data else None return self.cleaned_data["preapproval_amount"] if self.cleaned_data else None
anonymous = forms.BooleanField(required=False, label=_("Make this pledge anonymous, please")) anonymous = forms.BooleanField(required=False, label=_("Make this pledge anonymous, please"))
ack_name = forms.CharField(required=False, max_length=64, label=_("What name should we display?")) ack_name = forms.CharField(
required=False,
max_length=64,
label=_("What name should we display?")
)
ack_dedication = forms.CharField(required=False, max_length=140, label=_("Your dedication:")) ack_dedication = forms.CharField(required=False, max_length=140, label=_("Your dedication:"))
premium_id = forms.IntegerField(required=False) premium_id = forms.IntegerField(required=False)
premium=None premium = None
@property @property
def trans_extra(self): def trans_extra(self):
return PledgeExtra( anonymous=self.cleaned_data['anonymous'], return PledgeExtra( anonymous=self.cleaned_data['anonymous'],
ack_name=self.cleaned_data['ack_name'], ack_name=self.cleaned_data['ack_name'],
ack_dedication=self.cleaned_data['ack_dedication'], ack_dedication=self.cleaned_data['ack_dedication'],
premium=self.premium) premium=self.premium)
def clean_preapproval_amount(self): def clean_preapproval_amount(self):
preapproval_amount = self.cleaned_data['preapproval_amount'] preapproval_amount = self.cleaned_data['preapproval_amount']
if preapproval_amount is None: if preapproval_amount is None:
raise forms.ValidationError(_("Please enter a pledge amount.")) raise forms.ValidationError(_("Please enter a pledge amount."))
return preapproval_amount return preapproval_amount
def clean_premium_id(self): def clean_premium_id(self):
premium_id = self.cleaned_data['premium_id'] premium_id = self.cleaned_data['premium_id']
try: try:
self.premium= Premium.objects.get(id=premium_id) self.premium = Premium.objects.get(id=premium_id)
if self.premium.limit>0: if self.premium.limit > 0:
if self.premium.limit<=self.premium.premium_count: if self.premium.limit <= self.premium.premium_count:
raise forms.ValidationError(_("Sorry, that premium is fully subscribed.")) raise forms.ValidationError(_("Sorry, that premium is fully subscribed."))
except Premium.DoesNotExist: except Premium.DoesNotExist:
raise forms.ValidationError(_("Sorry, that premium is not valid.")) raise forms.ValidationError(_("Sorry, that premium is not valid."))
def clean(self): def clean(self):
# check on whether the preapproval amount is < amount for premium tier. If so, put an error message # check on whether the preapproval amount is < amount for premium tier. If so, put an error message
preapproval_amount = self.cleaned_data.get("preapproval_amount") preapproval_amount = self.cleaned_data.get("preapproval_amount")
@ -716,7 +809,7 @@ class CampaignPledgeForm(forms.Form):
# preapproval_amount failed validation, that error is the relevant one # preapproval_amount failed validation, that error is the relevant one
return self.cleaned_data return self.cleaned_data
elif self.premium is None: elif self.premium is None:
raise forms.ValidationError(_("Please select a premium." )) raise forms.ValidationError(_("Please select a premium." ))
elif preapproval_amount < self.premium.amount: elif preapproval_amount < self.premium.amount:
logger.info("raising form validating error") logger.info("raising form validating error")
raise forms.ValidationError(_("Sorry, you must pledge at least $%s to select that premium." % (self.premium.amount))) raise forms.ValidationError(_("Sorry, you must pledge at least $%s to select that premium." % (self.premium.amount)))
@ -727,11 +820,11 @@ class TokenCCMixin(forms.Form):
class BaseCCMixin(forms.Form): class BaseCCMixin(forms.Form):
work_id = forms.IntegerField(required=False, widget=forms.HiddenInput()) work_id = forms.IntegerField(required=False, widget=forms.HiddenInput())
preapproval_amount= forms.DecimalField( preapproval_amount = forms.DecimalField(
required=False, required=False,
min_value=D('1.00'), min_value=D('1.00'),
max_value=D('100000.00'), max_value=D('100000.00'),
decimal_places=2, decimal_places=2,
label="Amount", label="Amount",
) )
class UserCCMixin(forms.Form): class UserCCMixin(forms.Form):
@ -751,7 +844,7 @@ class CCForm(UserCCMixin, BaseCCForm):
class AccountCCForm( BaseCCMixin, UserCCMixin, forms.Form): class AccountCCForm( BaseCCMixin, UserCCMixin, forms.Form):
pass pass
class GoodreadsShelfLoadingForm(forms.Form): class GoodreadsShelfLoadingForm(forms.Form):
goodreads_shelf_name_number = forms.CharField(widget=forms.Select(choices=( goodreads_shelf_name_number = forms.CharField(widget=forms.Select(choices=(
('all','all'), ('all','all'),
@ -767,40 +860,60 @@ class PledgeCancelForm(forms.Form):
class CampaignAdminForm(forms.Form): class CampaignAdminForm(forms.Form):
campaign_id = forms.IntegerField() campaign_id = forms.IntegerField()
class EmailShareForm(forms.Form): class EmailShareForm(forms.Form):
recipient = forms.EmailField(error_messages={'required': 'Please specify a recipient.'}) recipient = forms.EmailField(error_messages={'required': 'Please specify a recipient.'})
subject = forms.CharField(max_length=100, error_messages={'required': 'Please specify a subject.'}) subject = forms.CharField(max_length=100, error_messages={'required': 'Please specify a subject.'})
message = forms.CharField(widget=forms.Textarea(), error_messages={'required': 'Please include a message.'}) message = forms.CharField(
widget=forms.Textarea(),
error_messages={'required': 'Please include a message.'}
)
# allows us to return user to original page by passing it as hidden form input # allows us to return user to original page by passing it as hidden form input
# we can't rely on POST or GET since the emailshare view handles both # we can't rely on POST or GET since the emailshare view handles both
# and may iterate several times as it catches user errors, losing URL info # and may iterate several times as it catches user errors, losing URL info
next = forms.CharField(widget=forms.HiddenInput()) next = forms.CharField(widget=forms.HiddenInput())
class FeedbackForm(forms.Form): class FeedbackForm(forms.Form):
sender = forms.EmailField(widget=forms.TextInput(attrs={'size':50}), label="Your email", error_messages={'required': 'Please specify your email address.'}) sender = forms.EmailField(
subject = forms.CharField(max_length=500, widget=forms.TextInput(attrs={'size':50}), error_messages={'required': 'Please specify a subject.'}) widget=forms.TextInput(attrs={'size':50}),
message = forms.CharField(widget=forms.Textarea(), error_messages={'required': 'Please specify a message.'}) label="Your email",
error_messages={'required': 'Please specify your email address.'}
)
subject = forms.CharField(
max_length=500,
widget=forms.TextInput(attrs={'size':50}),
error_messages={'required': 'Please specify a subject.'}
)
message = forms.CharField(
widget=forms.Textarea(),
error_messages={'required': 'Please specify a message.'}
)
page = forms.CharField(widget=forms.HiddenInput()) page = forms.CharField(widget=forms.HiddenInput())
notarobot = forms.IntegerField(label="Please prove you're not a robot", error_messages={'required': "You must do the sum to prove you're not a robot."}) notarobot = forms.IntegerField(
label="Please prove you're not a robot",
error_messages={'required': "You must do the sum to prove you're not a robot."}
)
answer = forms.IntegerField(widget=forms.HiddenInput()) answer = forms.IntegerField(widget=forms.HiddenInput())
num1 = forms.IntegerField(widget=forms.HiddenInput()) num1 = forms.IntegerField(widget=forms.HiddenInput())
num2 = forms.IntegerField(widget=forms.HiddenInput()) num2 = forms.IntegerField(widget=forms.HiddenInput())
def clean(self): def clean(self):
cleaned_data = self.cleaned_data cleaned_data = self.cleaned_data
notarobot = str(cleaned_data.get("notarobot")) notarobot = str(cleaned_data.get("notarobot"))
answer = str(cleaned_data.get("answer")) answer = str(cleaned_data.get("answer"))
if notarobot != answer: if notarobot != answer:
raise forms.ValidationError(_("Whoops, try that sum again.")) raise forms.ValidationError(_("Whoops, try that sum again."))
return cleaned_data return cleaned_data
class MsgForm(forms.Form): class MsgForm(forms.Form):
msg = forms.CharField(widget=forms.Textarea(), error_messages={'required': 'Please specify a message.'}) msg = forms.CharField(
widget=forms.Textarea(),
error_messages={'required': 'Please specify a message.'}
)
def full_clean(self): def full_clean(self):
super(MsgForm,self).full_clean() super(MsgForm, self).full_clean()
if self.data.has_key("supporter"): if self.data.has_key("supporter"):
try: try:
self.cleaned_data['supporter'] = User.objects.get(id=self.data["supporter"]) self.cleaned_data['supporter'] = User.objects.get(id=self.data["supporter"])
@ -815,20 +928,20 @@ class MsgForm(forms.Form):
raise ValidationError("Work does not exist") raise ValidationError("Work does not exist")
else: else:
raise ValidationError("Work is not specified") raise ValidationError("Work is not specified")
class PressForm(forms.ModelForm): class PressForm(forms.ModelForm):
class Meta: class Meta:
model = Press model = Press
exclude = () exclude = ()
widgets = { widgets = {
'date': SelectDateWidget(years=range(2010,2025)), 'date': SelectDateWidget(years=range(2010,2025)),
} }
class KindleEmailForm(forms.Form): class KindleEmailForm(forms.Form):
kindle_email = forms.EmailField() kindle_email = forms.EmailField()
class LibModeForm(forms.ModelForm): class LibModeForm(forms.ModelForm):
class Meta: class Meta:
model = Libpref model = Libpref
@ -836,7 +949,11 @@ class LibModeForm(forms.ModelForm):
class RegiftForm(forms.Form): class RegiftForm(forms.Form):
give_to = forms.EmailField(label="email address of recipient") give_to = forms.EmailField(label="email address of recipient")
give_message = forms.CharField( max_length=512, label="your gift message", initial="Here's an ebook from unglue.it, I hope you like it! - me") give_message = forms.CharField(
max_length=512,
label="your gift message",
initial="Here's an ebook from unglue.it, I hope you like it! - me",
)
class SubjectSelectForm(forms.Form): class SubjectSelectForm(forms.Form):
add_kw = AutoCompleteSelectField( add_kw = AutoCompleteSelectField(
@ -855,4 +972,4 @@ class MapSubjectForm(forms.Form):
widget=AutoCompleteSelectWidget(SubjectLookup,allow_new=False), widget=AutoCompleteSelectWidget(SubjectLookup,allow_new=False),
label='Target Subject', label='Target Subject',
) )

View File

@ -149,7 +149,7 @@ $j(document).ready(function() {
<a href="{% url 'download_ebook' ebook.id %}"> <a href="{% url 'download_ebook' ebook.id %}">
<img src="{{ ebook.rights_badge }}" alt="{{ ebook.rights}}" title="{{ ebook.rights}}" /></a> <img src="{{ ebook.rights_badge }}" alt="{{ ebook.rights}}" title="{{ ebook.rights}}" /></a>
<a href="{% url 'download_ebook' ebook.id %}"><img src="/static/images/{{ ebook.format }}32.png" height="32" alt="{{ ebook.format }}" title="{{ ebook.format }}" /></a> <a href="{% url 'download_ebook' ebook.id %}"><img src="/static/images/{{ ebook.format }}32.png" height="32" alt="{{ ebook.format }}" title="{{ ebook.format }}" /></a>
<a href="{% url 'download_ebook' ebook.id %}">{{ ebook.format }}</a> <a href="{% url 'download_ebook' ebook.id %}">{{ ebook.format }}</a> {% if ebook.version_label %} ({{ ebook.version_label }}) {% endif %}
{% if ebook.is_direct %}<a class="dropbox-saver" href="{{ ebook.download_url }}" data-filename="unglueit-{{ work.id }}.{{ ebook.format }}"></a>{% endif %} {% if ebook.is_direct %}<a class="dropbox-saver" href="{{ ebook.download_url }}" data-filename="unglueit-{{ work.id }}.{{ ebook.format }}"></a>{% endif %}
@ -168,7 +168,7 @@ $j(document).ready(function() {
<a href="{% url 'download_ebook' ebook.id %}"> <a href="{% url 'download_ebook' ebook.id %}">
<img src="{{ ebook.rights_badge }}" alt="{{ ebook.rights}}" title="{{ ebook.rights}}" /></a> <img src="{{ ebook.rights_badge }}" alt="{{ ebook.rights}}" title="{{ ebook.rights}}" /></a>
<a href="{% url 'download_ebook' ebook.id %}"><img src="/static/images/{{ ebook.format }}32.png" height="32" alt="{{ ebook.format }} at {{ebook.provider}}" title="{{ ebook.format }} at {{ebook.provider}}" /></a> <a href="{% url 'download_ebook' ebook.id %}"><img src="/static/images/{{ ebook.format }}32.png" height="32" alt="{{ ebook.format }} at {{ebook.provider}}" title="{{ ebook.format }} at {{ebook.provider}}" /></a>
<a href="{% url 'download_ebook' ebook.id %}">{{ ebook.format }} at {{ ebook.provider }}</a> <a href="{% url 'download_ebook' ebook.id %}">{{ ebook.format }} {% if ebook.version_label %} ({{ ebook.version_label }}) {% endif %} at {{ ebook.provider }}</a>
{% if ebook.is_direct %}<a class="dropbox-saver" href="{{ ebook.download_url }}" data-filename="unglueit-{{ work.id }}.{{ ebook.format }}"></a>{% endif %} {% if ebook.is_direct %}<a class="dropbox-saver" href="{{ ebook.download_url }}" data-filename="unglueit-{{ work.id }}.{{ ebook.format }}"></a>{% endif %}
{% if not forloop.last %}<br /><br />{% endif %} {% if not forloop.last %}<br /><br />{% endif %}

View File

@ -16,6 +16,12 @@
{% endfor %} {% endfor %}
<br /> <br />
{% endif %} {% endif %}
{% if edition.note %}
{{ edition.note }}.<br />
{% endif %}
{% if edition.downloads.count %}
{{ edition.downloads.count }} ebooks<br />
{% endif %}
{% if edition.publisher %} {% if edition.publisher %}
Publisher: <a href="{% url 'bypubname_list' edition.publisher_name.id %}">{{edition.publisher}}</a><br /> Publisher: <a href="{% url 'bypubname_list' edition.publisher_name.id %}">{{edition.publisher}}</a><br />
{% endif %} {% endif %}
@ -28,6 +34,9 @@
{% if edition.oclc %} {% if edition.oclc %}
OCLC: <a href="http://www.worldcat.org/oclc/{{ edition.oclc }}">{{ edition.oclc }}</a><br /> OCLC: <a href="http://www.worldcat.org/oclc/{{ edition.oclc }}">{{ edition.oclc }}</a><br />
{% endif %} {% endif %}
{% if edition.doi %}
DOI: <a href="https://dx.doi.org/{{ edition.doi }}">{{ edition.doi }}</a><br />
{% endif %}
{% if edition.http_id %} {% if edition.http_id %}
web: <a href="{{ edition.http_id }}">{{ edition.http_id }}</a><br /> web: <a href="{{ edition.http_id }}">{{ edition.http_id }}</a><br />
{% endif %} {% endif %}

View File

@ -1,31 +1,48 @@
<div class="clearfix"> <div class="clearfix">
{% if edition.ebook_form %} {% if ebook_form and show_ebook_form %}
{% if show_ebook_form %} <div id="add_ebook">
<div id="add_ebook">
{% if alert %}<div class="yikes">{{alert}}</div>{% endif %} {% if alert %}<div class="yikes">{{alert}}</div>{% endif %}
{% if edition.ebooks.all.0 %} {% if edition.ebooks.all.0 %}
<h2>eBooks for this Edition</h2> <h2>eBooks for this Edition</h2>
{% for ebook in edition.ebooks.all %}
<a href="{% url 'download_ebook' ebook.id %}">{{ ebook.format }}</a> {{ebook.rights}} at {{ebook.provider}}. Downloaded {{ ebook.download_count }} times.<br />
{% endfor %}
{% endif %}
<h2>Add an eBook for this Edition:</h2>
<span>If you know that this edition is available as a public domain or Creative Commons ebook, you can enter the link here and "unglue" it. Right now, we're only accepting URLs that point to Internet Archive, Wikisources, Wikibooks, Hathitrust, Project Gutenberg, raw files at Github, or Google Books.</span>
<form method="POST" action="#add_ebook">
{% csrf_token %}{{ edition.ebook_form.edition.errors }}{{ edition.ebook_form.edition }}{{ edition.ebook_form.user.errors }}{{ edition.ebook_form.user }}{{ edition.ebook_form.provider.errors }}{{ edition.ebook_form.provider }}
{{ edition.ebook_form.url.errors }}<span>URL: {{ edition.ebook_form.url }}</span><br />
{{ edition.ebook_form.format.errors }}<span>File Format: {{ edition.ebook_form.format }}</span>&nbsp;&nbsp;&nbsp;
{{ edition.ebook_form.rights.errors }}<span>License: {{ edition.ebook_form.rights }}</span><br />
<input type="submit" name="add_ebook" value="add ebook" />
</form>
</div>
{% else %}
<div> Adding ebook links is disabled for this work.</div>
{% endif %}
{% for ebook in edition.ebooks.all %}
{% if ebook.active %}
<a href="{% url 'download_ebook' ebook.id %}">{{ ebook.format }}</a> {{ebook.rights}} at {{ebook.provider}}.
{% if ebook.version_label %} {{ ebook.version_label }} (v{{ ebook.version_iter }}). {% endif %}
Downloaded {{ ebook.download_count }} times since {{ ebook.created }}<br />
{% endif %}
{% endfor %}
{% endif %}
<h2>Add an eBook for this Edition:</h2>
<span>If you know that this edition is available as a public domain or Creative Commons ebook, you can enter the link here and "unglue" it. Right now, we're only accepting URLs that point to Internet Archive, Wikisources, Wikibooks, Hathitrust, Project Gutenberg, OApen, raw files at Github, or Google Books.</span>
<form method="POST" action="#add_ebook" enctype="multipart/form-data">
{% csrf_token %}
{{ ebook_form.edition.errors }}{{ ebook_form.edition }}{{ ebook_form.user.errors }}{{ ebook_form.user }}{{ ebook_form.provider.errors }}{{ ebook_form.provider }}
<br />
{{ ebook_form.url.errors }}<span>Add a Link URL: {{ ebook_form.url }}</span><br />
or...<br />
{{ ebook_form.file.errors }}<span>Upload an ebook file: {{ ebook_form.file }}</span><br /><br />
{{ ebook_form.format.errors }}<span>File Format: {{ ebook_form.format }}</span>&nbsp;&nbsp;&nbsp;
{{ ebook_form.rights.errors }}<span>License: {{ ebook_form.rights }}</span><br /><br />
<span>Version Label (optional): {% if edition.work.versions %}
<select id='version_label'>
<option value=""> (no label) </option>
{% for vers in edition.work.versions %}<option value="{{ vers }}" >{{ vers }}</option>{% endfor %}
</select> or add a new version label:
{% endif %}
{{ ebook_form.new_version_label.errors }} {{ ebook_form.new_version_label }} </span> <br />
<br /><input type="submit" name="add_ebook" value="add link/upload ebook" />
</form>
<h3>Note on versions</h4>
<p>
If you want ebooks from two editions with the same format and provider to display, give them different version labels.
</p>
</div>
{% else %}
<div> Adding ebook links is disabled for this work.</div>
{% endif %} {% endif %}
</div> </div>

View File

@ -20,12 +20,20 @@
<b>LibraryThing ID</b>: {{ edition.librarything_id }}<br /> <b>LibraryThing ID</b>: {{ edition.librarything_id }}<br />
</div> </div>
{% if edition.ebooks.all.0 %}
<h2>Active eBooks for this Work</h2>
{% for ebook in edition.work.ebooks %}
<a href="{% url 'download_ebook' ebook.id %}">{{ ebook.format }}</a> {{ebook.rights}} at {{ebook.provider}}.
{% if ebook.version_label %} {{ ebook.version_label }}. {% endif %} v{{ ebook.version_iter }}
Downloaded {{ ebook.download_count }} times since {{ ebook.created }}<br />
{% endfor %}
{% endif %}
{% if edition.ebook_files.all %} {% if edition.ebook_files.all %}
<h2> Ebook Files for this Edition</h2> <h2> Ebook Files for this Edition</h2>
<ul> <ul>
{% for ebook_file in edition.ebook_files.all %} {% for ebook_file in edition.ebook_files.all %}
{% if ebook_file.file %} {% if ebook_file.file %}
<li>{% if ebook_file.active %}<span class="yikes">ACTIVE</span> {% endif %}<a href="{{ebook_file.file.url}}">{{ebook_file.file}}</a> created {{ebook_file.created}} {% if ebook_file.asking %}(This file has had the campaign 'ask' added.){% endif %}</li> <li>{% if ebook_file.active %}<span class="yikes">ACTIVE</span> {% elif ebook_file.ebook.active %} MIRROR {% endif %}<a href="{{ebook_file.file.url}}">{{ebook_file.file}}</a> created {{ebook_file.created}} {% if ebook_file.asking %}(This file has had the campaign 'ask' added.){% endif %}</li>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</ul> </ul>
@ -66,15 +74,28 @@
For ePUB files, use the <a href=https://code.google.com/p/epubcheck/">epubcheck</a> tool to make sure everything will work properly.</p> For ePUB files, use the <a href=https://code.google.com/p/epubcheck/">epubcheck</a> tool to make sure everything will work properly.</p>
<form method="POST" action="#" enctype="multipart/form-data"> <form method="POST" action="#" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
{{form.as_p}} {{form.edition.errors}}{{form.edition}}
<p>{{form.format.errors}}Format: {{form.format}}</p>
<h3>Note on versions</h4>
<p>
If you want ebooks from two editions with the same format and provider to display, give them different version labels.
</p>
<span>Version Label (optional): {% if edition.work.versions %}
<select id='version_label'>
<option value=""> (no label) </option>
{% for vers in edition.work.versions %}<option value="{{ vers }}" >{{ vers }}</option>{% endfor %}
</select> or add a new version label:
{% endif %}
{{ form.new_version_label.errors }} {{ form.new_version_label }} </span> <br />
<p>{{form.file.errors}}Upload File: {{form.file}}</p>
<input type="submit" id="submit_file" value="submit ebook file"> <input type="submit" id="submit_file" value="submit ebook file">
</form> </form>
{% if edition.work %}
<h2>More Edition Management</h2> <h2>More Edition Management</h2>
<div><a href="{% url 'new_edition' edition.work.id edition.id %}">Edit this edition</a></div> <div><a href="{% url 'new_edition' edition.work.id edition.id %}">Edit this edition</a></div>
{% if edition.work.last_campaign %} {% if edition.work.last_campaign %}
<div><a href="{% url 'manage_campaign' edition.work.last_campaign.id %}">Manage this campaign</a></div> <div><a href="{% url 'manage_campaign' edition.work.last_campaign.id %}">Manage this campaign</a></div>
{% endif %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -136,14 +136,13 @@ Please fix the following before launching your campaign:
<div class="edition_form" id="edition_{{edition.id}}"> <div class="edition_form" id="edition_{{edition.id}}">
<p> Edition {{ edition.id }}: <input type="radio" {% ifequal edition.id form.edition.value %}checked="checked" {% endifequal %}id="id_edition_{{forloop.counter}}" value="{{edition.id}}" name="edition" /><label for="id_edition_{{forloop.counter}}"> Prefer this edition </label> <p> Edition {{ edition.id }}: <input type="radio" {% ifequal edition.id form.edition.value %}checked="checked" {% endifequal %}id="id_edition_{{forloop.counter}}" value="{{edition.id}}" name="edition" /><label for="id_edition_{{forloop.counter}}"> Prefer this edition </label>
<ul style="text-indent:1em"> <ul style="text-indent:1em">
<li style="text-indent:2.5em">There are {{ edition.downloads.all.count }} downloadable ebook for this edition</li>
<li style="text-indent:2.5em"><a href="{% url 'new_edition' edition.work.id edition.id %}"> Edit </a> this edition</li> <li style="text-indent:2.5em"><a href="{% url 'new_edition' edition.work.id edition.id %}"> Edit </a> this edition</li>
{% ifnotequal campaign.type 1 %} {% ifnotequal campaign.type 1 %}
{% if campaign.rh.can_sell %} {% if campaign.rh.can_sell %}
{% if edition.ebook_files.all.0 %} {% if edition.ebook_files.all.0 %}
<li style="text-indent:2.5em">You have uploaded ebook files for this edition. <a href="{% url 'edition_uploads' edition.id %}"> Upload another</a></li> <li style="text-indent:2.5em">You have uploaded ebook files for this edition. You can <a href="{% url 'edition_uploads' edition.id %}"> manage its ebooks or upload another</a></li>
{% else %} {% else %}
<li style="text-indent:2.5em">You can <a href="{% url 'edition_uploads' edition.id %}"> Load a file</a> for this edition.</li> <li style="text-indent:2.5em">You can <a href="{% url 'edition_uploads' edition.id %}"> Manage ebooks</a> for this edition.</li>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% endifnotequal %} {% endifnotequal %}
@ -156,11 +155,11 @@ Please fix the following before launching your campaign:
<p>If you don't see an edition that matches what you want to release, you can <a href="{% url 'rh_edition' work.id '' %}">create a new edition</a>.</p> <p>If you don't see an edition that matches what you want to release, you can <a href="{% url 'rh_edition' work.id '' %}">create a new edition</a>.</p>
{% if campaign.work.ebookfiles.0 %} {% if campaign.work.ebookfiles.0 %}
<h3>Uploaded Files</h3> <h3>All ebook files for this book</h3>
{% endif %} {% endif %}
{% if campaign.work.epubfiles.0 %} {% if campaign.work.epubfiles.0 %}
{% for ebf in campaign.work.epubfiles %} {% for ebf in campaign.work.epubfiles %}
<p>{% if ebf.active %}<span class="yikes">ACTIVE</span> {% endif %}EPUB file: <a href="{{ebf.file.url}}">{{ebf.file}}</a> <br />created {{ebf.created}} for edition <a href="#edition_{{ebf.edition.id}}">{{ebf.edition.id}}</a> {% if ebf.asking %}(This file has had the campaign 'ask' added.){% endif %}<br />{% if ebf.active %}{% ifequal action 'mademobi' %}<span class="yikes">A MOBI file is being generated. </span> (Takes a minute or two.) {% else %}You can <a href="{% url 'makemobi' campaign.id %}">generate a MOBI file.</a> {% endifequal %}{% endif %}</p> <p>{% if ebf.active %}<span class="yikes">ACTIVE</span> {% elif ebf.ebook.active %} MIRROR {% endif %}EPUB file: <a href="{{ebf.file.url}}">{{ebf.file}}</a> <br />created {{ebf.created}} for edition <a href="#edition_{{ebf.edition.id}}">{{ebf.edition.id}}</a> {% if ebf.asking %}(This file has had the campaign 'ask' added.){% endif %}<br />{% if ebf.active %}{% ifequal action 'mademobi' %}<span class="yikes">A MOBI file is being generated. </span> (Takes a minute or two.) {% else %}You can <a href="{% url 'makemobi' campaign.id ebf.id %}">generate a MOBI file.</a> {% endifequal %}{% endif %}</p>
{% endfor %} {% endfor %}
{% if campaign.work.test_acqs.0 %} {% if campaign.work.test_acqs.0 %}
<ul> <ul>

View File

@ -6,7 +6,27 @@
<link rel="stylesheet" href="/static/css/ui-lightness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen"> <link rel="stylesheet" href="/static/css/ui-lightness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen">
<script type="text/javascript" src="{{ jquery_ui_home }}" ></script> <script type="text/javascript" src="{{ jquery_ui_home }}" ></script>
<script type="text/javascript">
onload = function(){
var urlInput = document.getElementById('id_url');
var formatInput = document.getElementById('id_format');
urlInput.oninput = function(){
if(urlInput.value.endsWith('.pdf')){
formatInput.value = 'pdf'
}
else if(urlInput.value.endsWith('.epub')){
formatInput.value = 'epub'
}
else if(urlInput.value.endsWith('.mobi')){
formatInput.value = 'mobi'
}
else if(urlInput.value.endsWith('.html')){
formatInput.value = 'html'
};
};
};
</script>
{% endblock %} {% endblock %}
{% block doccontent %} {% block doccontent %}

View File

@ -93,11 +93,12 @@ ul.fancytree-container {
<h2>Create New Edition</h2> <h2>Create New Edition</h2>
{% endif %} {% endif %}
<p>Title and ISBN 13 are required; the rest is optional, though a cover image is strongly recommended.</p> <p>Title and ISBN 13 (or DOI, OCLCNum or URL) are required; the rest is optional, though a cover image is strongly recommended.</p>
<form id="editform" enctype="multipart/form-data" method="POST" action="#"> <form id="editform" enctype="multipart/form-data" method="POST" action="#">
{% csrf_token %} {% csrf_token %}
{{ form.work }} {{ form.work }}
{{ form.non_field_errors }} {{ form.non_field_errors }}
<!--{{ form.errors }}-->
<div> <div>
<p><b>Title</b>: {{ form.title.errors }}{{ form.title }}</p> <p><b>Title</b>: {{ form.title.errors }}{{ form.title }}</p>
<p><b>Publisher Name</b> : {{ form.publisher_name.errors }}{{ form.publisher_name }}<br />(If you change this, click another form element before submitting)</p> <p><b>Publisher Name</b> : {{ form.publisher_name.errors }}{{ form.publisher_name }}<br />(If you change this, click another form element before submitting)</p>
@ -122,6 +123,25 @@ ul.fancytree-container {
<input type="submit" name="add_author_submit" value="Add Author" id="submit_author"></p> <input type="submit" name="add_author_submit" value="Add Author" id="submit_author"></p>
<p><b>Language</b>: {{ form.language.errors }}{{ form.language }}</p> <p><b>Language</b>: {{ form.language.errors }}{{ form.language }}</p>
{% if edition.pk %}
<p><b>Add a Related Work</b>: {{ form.add_work_relation.errors }}{{ form.add_work_relation }} of {{ form.add_related_work.errors }}{{ form.add_related_work }}</p>
<ul>{% for work_rel in edition.work.works_related_to.all %}
<li>
This work is a {{ work_rel.relation }} of <a href="{% url 'work' work_rel.from_work.id %}">{{ work_rel.from_work }}</a>.
<input type="submit" name="delete_work_rel_{{ work_rel.id }}" value="x" class="deletebutton" title="delete work relation">
</li>
{% endfor %}
{% for work_rel in edition.work.works_related_from.all %}
<li>
<a href="{% url 'work' work_rel.to_work.id %}">{{ work_rel.to_work }}</a> is a {{ work_rel.relation }} of this work.
<input type="submit" name="delete_work_rel_{{ work_rel.id }}" value="x" class="deletebutton" title="delete work relation">
</li>
{% endfor %}</ul>
{% endif %}
<p><b>Age Level</b>: {{ form.age_level.errors }}{{ form.age_level }}</p>
<p><b>Edition Note</b>: {{ form.note.errors }}{{ form.note }}</p>
<h4> Identifiers </h4> <h4> Identifiers </h4>
{% if id_msg %} <span class="errorlist">{{ id_msg }} </span>{% endif %} {% if id_msg %} <span class="errorlist">{{ id_msg }} </span>{% endif %}
<p> Enter 'delete' to remove the identifier. </p> <p> Enter 'delete' to remove the identifier. </p>
@ -130,9 +150,8 @@ ul.fancytree-container {
<p><b>Google Books ID</b>: {{ form.goog.errors }}{{ form.goog }}</p> <p><b>Google Books ID</b>: {{ form.goog.errors }}{{ form.goog }}</p>
<p><b>GoodReads ID</b>: {{ form.gdrd.errors }}{{ form.gdrd }}</p> <p><b>GoodReads ID</b>: {{ form.gdrd.errors }}{{ form.gdrd }}</p>
<p><b>LibraryThing ID</b>: {{ form.thng.errors }}{{ form.thng }}</p> <p><b>LibraryThing ID</b>: {{ form.thng.errors }}{{ form.thng }}</p>
{% if request.user.is_staff %} <p><b>DOI</b>: {{ form.doi.errors }}{{ form.doi }}</p>
<p><b>HTTP ID</b>: {{ form.http.errors }}{{ form.http }}</p> <p><b>HTTP(S) ID</b>: {{ form.http.errors }}{{ form.http }}</p>
{% endif %}
<p><b>Description</b>: <br /> <p><b>Description</b>: <br />
{{ form.description.errors }}{{ form.description }}<br /> {{ form.description.errors }}{{ form.description }}<br />
(<i>{% if work.last_campaign %} (<i>{% if work.last_campaign %}
@ -175,7 +194,7 @@ ul.fancytree-container {
[ no cover specified for this edition ]<br /> [ no cover specified for this edition ]<br />
{% endif %} {% endif %}
{{ form.cover_image.errors }}{{ form.cover_image }}{{ form.cover_image.help_text }} {{ form.cover_image.errors }}{{ form.cover_image }}{{ form.cover_image.help_text }}
(<i>Enter a URL for an image, ideally 120px wide. </i>)<br /> (<i>Enter a URL for an image, at least 300 px wide. The image will be scaled to the proportions of a 6x9 cover. </i>)<br />
OR...<br /> OR...<br />
{{ form.coverfile.errors }}{{ form.coverfile }}{{ form.coverfile.help_text }} {{ form.coverfile.errors }}{{ form.coverfile }}{{ form.coverfile.help_text }}

View File

@ -102,6 +102,8 @@
<span itemprop="publisher"><a href="{% url 'bypubname_list' work.last_campaign.publisher.name.id %}">{{ work.last_campaign.publisher }}</a></span> <span itemprop="publisher"><a href="{% url 'bypubname_list' work.last_campaign.publisher.name.id %}">{{ work.last_campaign.publisher }}</a></span>
{% endif %} {% endif %}
<span itemprop="datePublished">{{ work.publication_date }}</span> <span itemprop="datePublished">{{ work.publication_date }}</span>
<meta itemprop="inLanguage" content="work.language" />
<meta itemprop="typicalAgeRange" content="work.age_range" />
</h3> </h3>
</div> </div>
</div> </div>
@ -276,6 +278,18 @@
{{ work.last_campaign.description|safe }} {{ work.last_campaign.description|safe }}
{% endif %} {% endif %}
</div> </div>
<div>
{% for work_rel in work.works_related_to.all %}
<p>
This work is a {{ work_rel.relation }} of <a href="{% url 'work' work_rel.from_work.id %}">{{ work_rel.from_work }}</a>.
</p>
{% endfor %}
{% for work_rel in work.works_related_from.all %}
<p>
<a href="{% url 'work' work_rel.to_work.id %}">{{ work_rel.to_work }}</a> is a {{ work_rel.relation }} of this work.
</p>
{% endfor %}
</div>
</div> </div>
</div> </div>
<div id="tabs-2" class="tabs {% if activetab == '2' %}active{% endif %}"> <div id="tabs-2" class="tabs {% if activetab == '2' %}active{% endif %}">
@ -417,7 +431,7 @@
This work has been downloaded {{ work.download_count }} times via unglue.it ebook links. This work has been downloaded {{ work.download_count }} times via unglue.it ebook links.
<ol> <ol>
{% for ebook in work.ebooks.all %} {% for ebook in work.ebooks.all %}
<li>{{ ebook.download_count }} - {{ ebook.format }} ({{ ebook.rights }}) at {{ ebook.provider }}</li> <li>{{ ebook.download_count }} - {{ ebook.format }} {% if ebook.version_label %} ({{ ebook.version_label }}) {% endif %}({{ ebook.rights }}) at {{ ebook.provider }}. </li>
{% endfor %} {% endfor %}
</ol> </ol>
</div> </div>

View File

@ -27,7 +27,7 @@ urlpatterns = [
url(r"^rightsholders/$", views.rh_tools, name="rightsholders"), url(r"^rightsholders/$", views.rh_tools, name="rightsholders"),
url(r"^rightsholders/campaign/(?P<id>\d+)/$", views.manage_campaign, name="manage_campaign"), url(r"^rightsholders/campaign/(?P<id>\d+)/$", views.manage_campaign, name="manage_campaign"),
url(r"^rightsholders/campaign/(?P<id>\d+)/results/$", views.manage_campaign, {'action': 'results'}, name="campaign_results"), url(r"^rightsholders/campaign/(?P<id>\d+)/results/$", views.manage_campaign, {'action': 'results'}, name="campaign_results"),
url(r"^rightsholders/campaign/(?P<id>\d+)/makemobi/$", views.manage_campaign, {'action': 'makemobi'}, name="makemobi"), url(r"^rightsholders/campaign/(?P<id>\d+)/(?P<ebf>\d+)/makemobi/$", views.manage_campaign, {'action': 'makemobi'}, name="makemobi"),
url(r"^rightsholders/campaign/(?P<id>\d+)/mademobi/$", views.manage_campaign, {'action': 'mademobi'}, name="mademobi"), url(r"^rightsholders/campaign/(?P<id>\d+)/mademobi/$", views.manage_campaign, {'action': 'mademobi'}, name="mademobi"),
url(r"^rightsholders/edition/(?P<work_id>\d*)/(?P<edition_id>\d*)$", views.new_edition, {'by': 'rh'}, name="rh_edition"), url(r"^rightsholders/edition/(?P<work_id>\d*)/(?P<edition_id>\d*)$", views.new_edition, {'by': 'rh'}, name="rh_edition"),
url(r"^rightsholders/edition/(?P<edition_id>\d*)/upload/$", views.edition_uploads, name="edition_uploads"), url(r"^rightsholders/edition/(?P<edition_id>\d*)/upload/$", views.edition_uploads, name="edition_uploads"),

File diff suppressed because it is too large Load Diff

View File

@ -89,6 +89,17 @@ def stub(edition):
) )
record.add_ordered_field(field245) record.add_ordered_field(field245)
#edition statement
if edition.note:
field250 = pymarc.Field(
tag='250',
indicators = [' ', ' '],
subfields = [
'a', unicode(edition.note),
]
)
record.add_ordered_field(field250)
# publisher, date # publisher, date
if edition.publisher: if edition.publisher:
field260 = pymarc.Field( field260 = pymarc.Field(
@ -101,7 +112,7 @@ def stub(edition):
if edition.publication_date: if edition.publication_date:
field260.add_subfield('c', unicode(edition.publication_date)) field260.add_subfield('c', unicode(edition.publication_date))
record.add_ordered_field(field260) record.add_ordered_field(field260)
if edition.description: if edition.description:
#add 520 field (description) #add 520 field (description)
field520 = pymarc.Field( field520 = pymarc.Field(

View File

@ -56,6 +56,7 @@ class AbstractEdition:
publisher = '' publisher = ''
title = '' title = ''
publication_date = '' publication_date = ''
note = ''
# the edition should be able to report ebook downloads, with should have format and url attributes # the edition should be able to report ebook downloads, with should have format and url attributes
def downloads(self): def downloads(self):

File diff suppressed because one or more lines are too long

View File

@ -63,10 +63,6 @@
width: 660px !important; width: 660px !important;
} }
a {
color:#3d4e53;
}
#js-search { #js-search {
margin: 0 15px 0 15px !important; margin: 0 15px 0 15px !important;
} }