From 5d9b8ae3892e265b429b97212fab57a799a35c5c Mon Sep 17 00:00:00 2001 From: eric Date: Fri, 17 Oct 2014 17:12:24 -0400 Subject: [PATCH 01/12] stop ignoring marc directory --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index a08b5a15..1e8f5290 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,4 @@ build deploy/last-update logs/* celerybeat.pid -marc/* .gitignore~ \ No newline at end of file From 9de0474386c96c7b38c4eab08631284b9bb216d3 Mon Sep 17 00:00:00 2001 From: eric Date: Fri, 17 Oct 2014 17:14:02 -0400 Subject: [PATCH 02/12] start moving marc code to its own package --- core/models.py | 8 ++ marc/__init__.py | 0 marc/management/__init__.py | 0 marc/management/commands/__init__.py | 0 marc/management/commands/migrate_records.py | 28 +++++ marc/migrations/0001_initial.py | 73 ++++++++++++ marc/migrations/__init__.py | 0 marc/models.py | 68 ++++++++++++ marc/tests.py | 116 ++++++++++++++++++++ 9 files changed, 293 insertions(+) create mode 100755 marc/__init__.py create mode 100644 marc/management/__init__.py create mode 100644 marc/management/commands/__init__.py create mode 100644 marc/management/commands/migrate_records.py create mode 100644 marc/migrations/0001_initial.py create mode 100644 marc/migrations/__init__.py create mode 100644 marc/models.py create mode 100644 marc/tests.py diff --git a/core/models.py b/core/models.py index cdfa1527..e66b2f19 100755 --- a/core/models.py +++ b/core/models.py @@ -1646,6 +1646,14 @@ class Edition(models.Model): self.publisher_name = pub_name self.save() + # for compatibility with marc outputter + def downloads(self): + return self.ebooks.all() + + # for compatibility with marc outputter + def download_via_url(self): + return settings.BASE_URL_SECURE + reverse('download', args=[self.work.id]) + class Publisher(models.Model): created = models.DateTimeField(auto_now_add=True) name = models.ForeignKey('PublisherName', related_name='key_publisher') diff --git a/marc/__init__.py b/marc/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/marc/management/__init__.py b/marc/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/marc/management/commands/__init__.py b/marc/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/marc/management/commands/migrate_records.py b/marc/management/commands/migrate_records.py new file mode 100644 index 00000000..a627f9f1 --- /dev/null +++ b/marc/management/commands/migrate_records.py @@ -0,0 +1,28 @@ +from django.core.management.base import BaseCommand +from django.core.files.storage import default_storage + +from regluit.marc.models import MARCRecord +from regluit.core.models import MARCRecord as OldRecord + +class Command(BaseCommand): + help = "migrate records from files" + args = "" + + def handle(self, **options): + editions=[] + for old_record in OldRecord.objects.all().order_by('-id'): + if old_record.edition.pk not in editions: + new_record, created = MARCRecord.objects.get_or_create(id=old_record.pk) + try: + xml_file = default_storage.open(old_record.xml_record) + xml_data = xml_file.read() + new_record.guts = xml_data + new_record.edition = old_record.edition + editions.append(old_record.edition.pk) + xml_file.close() + new_record.save() + print 'record %s updated' % new_record.id + except IOError: + if created: + new_record.delete() + print 'failed opening %s' % old_record.xml_record diff --git a/marc/migrations/0001_initial.py b/marc/migrations/0001_initial.py new file mode 100644 index 00000000..c6d7e82f --- /dev/null +++ b/marc/migrations/0001_initial.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'MARCRecord' + db.create_table('marc_marcrecord', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('guts', self.gf('django.db.models.fields.TextField')()), + ('edition', self.gf('django.db.models.fields.related.ForeignKey')(related_name='MARCRecords', null=True, to=orm['core.Edition'])), + )) + db.send_create_signal('marc', ['MARCRecord']) + + + def backwards(self, orm): + # Deleting model 'MARCRecord' + db.delete_table('marc_marcrecord') + + + models = { + 'core.edition': { + 'Meta': {'object_name': 'Edition'}, + 'cover_image': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'publication_date': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'publisher_name': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'editions'", 'null': 'True', 'to': "orm['core.PublisherName']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '1000'}), + 'unglued': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'work': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'editions'", 'null': 'True', 'to': "orm['core.Work']"}) + }, + 'core.publisher': { + 'Meta': {'object_name': 'Publisher'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'key_publisher'", 'to': "orm['core.PublisherName']"}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}) + }, + 'core.publishername': { + 'Meta': {'object_name': 'PublisherName'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'publisher': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'alternate_names'", 'null': 'True', 'to': "orm['core.Publisher']"}) + }, + 'core.work': { + 'Meta': {'ordering': "['title']", 'object_name': 'Work'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}), + 'earliest_publication': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}), + 'featured': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '5', 'db_index': 'True'}), + 'num_wishes': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}), + 'openlibrary_lookup': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'selected_edition': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'selected_works'", 'null': 'True', 'to': "orm['core.Edition']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '1000'}) + }, + 'marc.marcrecord': { + 'Meta': {'object_name': 'MARCRecord'}, + 'edition': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'MARCRecords'", 'null': 'True', 'to': "orm['core.Edition']"}), + 'guts': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + } + } + + complete_apps = ['marc'] \ No newline at end of file diff --git a/marc/migrations/__init__.py b/marc/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/marc/models.py b/marc/models.py new file mode 100644 index 00000000..20c7eaa8 --- /dev/null +++ b/marc/models.py @@ -0,0 +1,68 @@ +import pymarc +import logging +from datetime import datetime +from StringIO import StringIO + +from django.conf import settings +from django.db import models + +# weak coupling +EDITION_MODEL = "core.Edition" + +class Edition: + # define the methods an edition should have + + # the edition should be able to report ebook downloads, with should have format and url attributes + def downloads(self): + return [] + + # the edition should be able to report "ebook via" url + def download_via_url(self): + return [] + +class MARCRecord(models.Model): + # the record goes here + guts = models.TextField() + + # note capitalization of related_name + edition = models.ForeignKey(EDITION_MODEL, related_name="MARCRecords", null=True) + + @property + def accession(self): + zeroes = 9 - len(str(self.id)) + return 'ung' + zeroes*'0' + str(self.id) + + # the record without 856 + def _record(self): + the_record = pymarc.parse_xml_to_array(StringIO(self.guts))[0] + fields856 = the_record.get_fields('856') + if fields856: + the_record.remove_field(fields856) + return the_record + + def direct_record(self): + the_record = self._record() + for book in self.edition.downloads(): + field856 = pymarc.Field( + tag='856', + indicators = ['4', '0'], + subfields = [ + '3', book.format + ' version', + 'q', settings.CONTENT_TYPES[book.format], + 'u', book.url, + ] + ) + the_record.add_ordered_field(field856) + return the_record + + def via_record(self): + the_record = self._record() + field856_via = pymarc.Field( + tag='856', + indicators = ['4', '0'], + subfields = [ + 'u', self.edition.download_via_url(), + ] + ) + the_record.add_ordered_field(field856_via) + return the_record diff --git a/marc/tests.py b/marc/tests.py new file mode 100644 index 00000000..73e2ee85 --- /dev/null +++ b/marc/tests.py @@ -0,0 +1,116 @@ +""" +django imports +""" +from django.test import TestCase +from django.test.client import Client +from django.db.models import get_model + +""" +regluit imports +""" +from . import models + +a_marc_record = ''' + 01021cam a2200301 a 4500 + 3057297 + 19970108080513.5 + 960131r19761970ke b b 000 0 eng + + (DLC) 96109467 + + + 7 + cbc + orignew + u + ncip + 19 + y-gencatlg + + + 082 done aa11 + + + 96109467 + + + 0195724135 + + + DLC + DLC + DLC + + + f------ + + + PL8010 + .F5 1976 + + + 398.2/096 + 20 + + + Finnegan, Ruth H. + + + Oral literature in Africa / + Ruth Finnegan. + + + Nairobi : + Oxford University Press, + 1976 (1994 printing). + + + xviii, 558 p. : + map ; + 21 cm. + + + Oxford library of African literature + + + Originally published: Oxford : Clarendon Press, 1970. + + + Includes index and bibliographical references (p. [522]-536). + + + Folk literature, African + History and criticism. + + + Oral tradition + Africa. + + + ap + + + c-GenColl + PL8010 + .F5 1976 + Copy 1 + BOOKS + +''' + +class MarcTests(TestCase): + work_id=None + + def test_records(self): + w = get_model('core','Work').objects.create(title="Work 1") + e = get_model('core','Edition').objects.create(title=w.title,work=w) + eb = get_model('core','Ebook').objects.create(url = "http://example.org",edition = e,format = 'epub') + + mr = models.MARCRecord.objects.create(guts=a_marc_record, edition=e ) + + print mr.direct_record() + print mr.via_record() + print mr.direct_record_xml() + print mr.direct_record_mrc() + print mr.via_record_xml() + print mr.vi_record_mrc() From 29baf69d3c640a31a48a593f479c38a1c03e270f Mon Sep 17 00:00:00 2001 From: eric Date: Fri, 17 Oct 2014 22:48:51 -0400 Subject: [PATCH 03/12] record spitting now works --- marc/models.py | 23 +++++++++++++++++++++++ marc/tests.py | 4 +--- settings/common.py | 1 + 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/marc/models.py b/marc/models.py index 20c7eaa8..8e938e7a 100644 --- a/marc/models.py +++ b/marc/models.py @@ -20,6 +20,16 @@ class Edition: def download_via_url(self): return [] +def _xml(record): + return pymarc.record_to_xml(record) + +def _mrc(record): + mrc_file = StringIO() + writer = pymarc.MARCWriter(mrc_file) + writer.write(record) + mrc_file.seek(0) + return mrc_file.read() + class MARCRecord(models.Model): # the record goes here guts = models.TextField() @@ -55,6 +65,12 @@ class MARCRecord(models.Model): the_record.add_ordered_field(field856) return the_record + def direct_record_xml(self): + return _xml(self.direct_record()) + + def direct_record_mrc(self): + return _mrc(self.direct_record()) + def via_record(self): the_record = self._record() field856_via = pymarc.Field( @@ -66,3 +82,10 @@ class MARCRecord(models.Model): ) the_record.add_ordered_field(field856_via) return the_record + + def via_record_xml(self): + return _xml(self.via_record()) + + def via_record_mrc(self): + return _mrc(self.via_record()) + diff --git a/marc/tests.py b/marc/tests.py index 73e2ee85..35578a91 100644 --- a/marc/tests.py +++ b/marc/tests.py @@ -108,9 +108,7 @@ class MarcTests(TestCase): mr = models.MARCRecord.objects.create(guts=a_marc_record, edition=e ) - print mr.direct_record() - print mr.via_record() print mr.direct_record_xml() print mr.direct_record_mrc() print mr.via_record_xml() - print mr.vi_record_mrc() + print mr.via_record_mrc() diff --git a/settings/common.py b/settings/common.py index 094689a2..2d3342e6 100644 --- a/settings/common.py +++ b/settings/common.py @@ -132,6 +132,7 @@ INSTALLED_APPS = ( 'regluit.frontend', 'regluit.api', 'regluit.core', + 'regluit.marc', 'regluit.payment', 'regluit.utils', 'registration', From a5d3dfdf4f14ab3b03615aa402babeee2fcb8878 Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 20 Oct 2014 16:54:19 -0400 Subject: [PATCH 04/12] add_author method --- core/bookloader.py | 3 +-- core/models.py | 8 ++++++++ frontend/views.py | 12 ++++-------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/core/bookloader.py b/core/bookloader.py index 9b9420cf..631c7d59 100755 --- a/core/bookloader.py +++ b/core/bookloader.py @@ -202,8 +202,7 @@ def update_edition(edition): models.Identifier.get_or_add(type='goog',value=googlebooks_id,edition=edition,work=edition.work) for a in d.get('authors', []): - a, created = models.Author.objects.get_or_create(name=a) - a.editions.add(edition) + edition.add_author(a) add_ebooks(item, edition) diff --git a/core/models.py b/core/models.py index e66b2f19..38cc4b5b 100755 --- a/core/models.py +++ b/core/models.py @@ -1634,6 +1634,14 @@ class Edition(models.Model): except Identifier.DoesNotExist: return None + def add_author(self, author_name): + if author_name: + try: + author= Author.objects.get(name=author_name) + except Author.DoesNotExist: + author= Author.objects.create(name=author_name) + author.editions.add(self) + def set_publisher(self,publisher_name): if publisher_name and publisher_name != '': try: diff --git a/frontend/views.py b/frontend/views.py index 94632625..5cbda817 100755 --- a/frontend/views.py +++ b/frontend/views.py @@ -559,10 +559,10 @@ def new_edition(request, work_id, edition_id, by=None): elif request.POST.has_key('add_subject_submit') and admin: new_subject = request.POST['add_subject'].strip() try: - author= models.Subject.objects.get(name=new_subject) + subject= models.Subject.objects.get(name=new_subject) except models.Subject.DoesNotExist: - author=models.Subject.objects.create(name=new_subject) - edition.new_subjects.append(new_subject) + subject=models.Subject.objects.create(name=new_subject) + edition.new_subjects.append(subject) form = EditionForm(instance=edition, data=request.POST, files=request.FILES) edition.ebook_form = EbookForm( instance= models.Ebook(user = request.user, edition = edition, provider = 'x' ), prefix = 'ebook_%d'%edition.id) @@ -605,11 +605,7 @@ def new_edition(request, work_id, edition_id, by=None): else: models.Identifier.set(type=id_type, value=id_val, edition=edition, work=work) for author_name in edition.new_author_names: - try: - author= models.Author.objects.get(name=author_name) - except models.Author.DoesNotExist: - author=models.Author.objects.create(name=author_name) - author.editions.add(edition) + edition.add_author(author_name) for subject_name in edition.new_subjects: try: subject= models.Subject.objects.get(name=subject_name) From 73003ba1c235bf693f946efeb8c8c9d49c198ab6 Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 20 Oct 2014 16:57:20 -0400 Subject: [PATCH 05/12] stub loader --- core/models.py | 30 +++++++++- marc/load.py | 159 +++++++++++++++++++++++++++++++++++++++++++++++++ marc/models.py | 73 +++++++++++++++++++---- marc/tests.py | 25 ++++++-- 4 files changed, 269 insertions(+), 18 deletions(-) create mode 100644 marc/load.py diff --git a/core/models.py b/core/models.py index 38cc4b5b..569d441f 100755 --- a/core/models.py +++ b/core/models.py @@ -1654,13 +1654,39 @@ class Edition(models.Model): self.publisher_name = pub_name self.save() - # for compatibility with marc outputter + #### following methods for compatibility with marc outputter def downloads(self): return self.ebooks.all() - # for compatibility with marc outputter def download_via_url(self): return settings.BASE_URL_SECURE + reverse('download', args=[self.work.id]) + + def authnames(self): + return [auth.last_name_first for auth in self.authors.all()] + + @property + def license(self): + try: + return self.ebooks.all()[0].rights + except: + return None + + @property + def funding_info(self): + if self.ebooks.all().count()==0: + return '' + if self.unglued: + return 'The book is available as a free download thanks to the generous support of interested readers and organizations, who made donations using the crowd-funding website Unglue.it.' + else: + if self.ebooks.all()[0].rights in cc.LICENSE_LIST: + return 'The book is available as a free download thanks to a Creative Commons license.' + else: + return 'The book is available as a free download because it is in the Public Domain.' + + @property + def description(self): + return self.work.description + class Publisher(models.Model): created = models.DateTimeField(auto_now_add=True) diff --git a/marc/load.py b/marc/load.py new file mode 100644 index 00000000..716867a7 --- /dev/null +++ b/marc/load.py @@ -0,0 +1,159 @@ +""" +This takes a MARCXML filename as an argument and converts it into +MARC records for the unglued edition (in .xml and .mrc formats). +Consider it a catalogolem: http://commons.wikimedia.org/wiki/File:Arcimboldo_Librarian_Stokholm.jpg +Use the MARCXML file for the non-unglued edition from Library of Congress. +""" + +import pymarc +import logging +from datetime import datetime +from StringIO import StringIO + +from django.conf import settings +from django.core.urlresolvers import reverse + +import regluit.core.cc as cc +from regluit.core import models + +def stub(edition): + + record = pymarc.Record() + + now = datetime.now() + + #mostly to identify this record as a stub + field001 = pymarc.Field(tag='001', data='stb'+now.strftime('%y%m%d%H%M%S')) + record.add_ordered_field(field001) + + # add field indicating record originator + field003 = pymarc.Field(tag='003', data='UnglueIt') + record.add_ordered_field(field003) + + # update timestamp of record + datestamp = now.strftime('%Y%m%d%H%M%S') + '.0' + field005 = pymarc.Field(tag='005', data=datestamp) + record.add_ordered_field(field005) + + # change 006, 007, 008 because this is an online resource + field006 = pymarc.Field( + tag='006', + data='m o d ' + ) + record.add_ordered_field(field006) + + field007 = pymarc.Field( + tag='007', + data='cr' + ) + record.add_ordered_field(field007) + + # fun fun fun 008 + new_field_value= now.strftime('%y%m%d')+'s' + if edition.publication_date and len(edition.publication_date)>3: + new_field_value += edition.publication_date[0:4] + else: + new_field_value += '||||' + new_field_value += '||||xx |||||o|||||||||||eng||' + field008 = pymarc.Field(tag='008', data=new_field_value) + record.add_ordered_field(field008) + + # add IBSN for if available + if edition.isbn_13: + field020 = pymarc.Field( + tag='020', + indicators = [' ', ' '], + subfields = ['a', edition.isbn_13] + ) + record.add_ordered_field(field020) + + # OCLC number + if edition.oclc: + field035 = pymarc.Field( + tag='035', + indicators = [' ', ' '], + subfields = ['a', '(OCoLC)' + edition.oclc] + ) + record.add_ordered_field(field035) + + # author name + num_auths = len(edition.authnames()) + if num_auths: + field100 = pymarc.Field( + tag='100', + indicators = ['1', ' '], + subfields = [ + 'a', edition.authnames()[0], + ] + ) + record.add_ordered_field(field100) + if num_auths > 1: + for auth in edition.authnames()[1:]: + field = pymarc.Field( + tag='700', + indicators = ['1', ' '], + subfields = [ + 'a', auth, + 'e', 'joint author.', + ] + ) + record.add_ordered_field(field) + + # add subfield to 245 indicating format + field245 = pymarc.Field( + tag='245', + indicators = ['1', '0'], + subfields = [ + 'a', edition.title, + 'a', '[electronic resource]', + ] + ) + record.add_ordered_field(field245) + + # publisher, date + if edition.publisher: + field260 = pymarc.Field( + tag='260', + indicators = [' ', ' '], + subfields = [ + 'b', edition.publisher, + ] + ) + if edition.publication_date: + field260.add_subfield('c', unicode(edition.publication_date)) + record.add_ordered_field(field260) + + if edition.description: + #add 520 field (description) + field520 = pymarc.Field( + tag='520', + indicators = [' ', ' '], + subfields = [ + 'a', edition.description, + ] + ) + record.add_ordered_field(field520) + + if edition.license: + # add 536 field (funding information) + field536 = pymarc.Field( + tag='536', + indicators = [' ', ' '], + subfields = [ + 'a', edition.funding_info, + ] + ) + record.add_ordered_field(field536) + + # add 540 field (terms governing use) + field540 = pymarc.Field( + tag='540', + indicators = [' ', ' '], + subfields = [ + 'a', dict(cc.CHOICES)[edition.license], + 'u', dict(cc.GRANTS)[edition.license], + ] + ) + record.add_ordered_field(field540) + + return record diff --git a/marc/models.py b/marc/models.py index 8e938e7a..9079d25b 100644 --- a/marc/models.py +++ b/marc/models.py @@ -6,20 +6,34 @@ from StringIO import StringIO from django.conf import settings from django.db import models +from . import load + # weak coupling EDITION_MODEL = "core.Edition" -class Edition: - # define the methods an edition should have +class AbstractEdition: + # define the methods and attributes an edition should have + isbn_13 = '' + oclc = '' + license = None + funding_info = '' + description = '' + publisher = '' + title = '' + publication_date = '' # the edition should be able to report ebook downloads, with should have format and url attributes def downloads(self): return [] - # the edition should be able to report "ebook via" url + # the edition should be able to report an "ebook via" url def download_via_url(self): return [] + # these should be last name first + def authnames(self): + return [] + def _xml(record): return pymarc.record_to_xml(record) @@ -34,20 +48,48 @@ class MARCRecord(models.Model): # the record goes here guts = models.TextField() + #storage for parsed guts + _the_record = None + # note capitalization of related_name edition = models.ForeignKey(EDITION_MODEL, related_name="MARCRecords", null=True) - + + def __init__(self, *args, **kwargs): + super(MARCRecord, self).__init__( *args, **kwargs) + edition = kwargs.pop('edition', None) + guts = kwargs.pop('guts', None) + if edition and not guts: + #make a stub _the_record from the edition + self._the_record = load.stub(edition) + @property def accession(self): zeroes = 9 - len(str(self.id)) return 'ung' + zeroes*'0' + str(self.id) - + + def save(self, *args, **kwargs): + if self.id == None and self._the_record: + # get the id first, add assession number + stash_guts = self.guts + self.guts = '' + super(MARCRecord, self).save(*args, **kwargs) + self.guts = stash_guts + field001 = self._the_record.get_fields('001') + if field001: + self._the_record.remove_field(field) + field001 = pymarc.Field(tag='001', data=self.accession) + self._the_record.add_ordered_field(field001) + super(MARCRecord, self).save(*args, **kwargs) + # the record without 856 def _record(self): - the_record = pymarc.parse_xml_to_array(StringIO(self.guts))[0] - fields856 = the_record.get_fields('856') - if fields856: - the_record.remove_field(fields856) + if self._the_record: + the_record = self._the_record + else: + the_record = pymarc.parse_xml_to_array(StringIO(self.guts))[0] + for field in the_record.get_fields('856'): + the_record.remove_field(field) + self._the_record = the_record return the_record def direct_record(self): @@ -88,4 +130,15 @@ class MARCRecord(models.Model): def via_record_mrc(self): return _mrc(self.via_record()) - + + def record(self, link_target='via', format='xml'): + if format == 'xml': + if link_target == 'via': + return self.via_record_xml() + elif link_target == 'direct': + return self.direct_record_xml() + elif format == 'mrc': + if link_target == 'via': + return self.via_record_mrc() + elif link_target == 'direct': + return self.direct_record_mrc() diff --git a/marc/tests.py b/marc/tests.py index 35578a91..01d2dde2 100644 --- a/marc/tests.py +++ b/marc/tests.py @@ -8,7 +8,7 @@ from django.db.models import get_model """ regluit imports """ -from . import models +from . import models, load a_marc_record = ''' 01021cam a2200301 a 4500 @@ -104,11 +104,24 @@ class MarcTests(TestCase): def test_records(self): w = get_model('core','Work').objects.create(title="Work 1") e = get_model('core','Edition').objects.create(title=w.title,work=w) - eb = get_model('core','Ebook').objects.create(url = "http://example.org",edition = e,format = 'epub') + eb = get_model('core','Ebook').objects.create(url = "http://example.org",edition = e,format = 'epub', rights='CC BY') mr = models.MARCRecord.objects.create(guts=a_marc_record, edition=e ) - print mr.direct_record_xml() - print mr.direct_record_mrc() - print mr.via_record_xml() - print mr.via_record_mrc() + mr.direct_record_xml() + mr.direct_record_mrc() + mr.via_record_xml() + mr.via_record_mrc() + + load.stub(e) + + w.description='test' + e.set_publisher('test pub') + e.publication_date = '2000' + e.add_author('joe writer') + id = get_model('core','Identifier').objects.create(work=w,edition=e, type='isbn', value='0030839939') + id = get_model('core','Identifier').objects.create(work=w,edition=e, type='oclc', value='0074009772') + + load.stub(e) + + From 2feda33c4fecea89a3ac25e3e743d058defe443c Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 20 Oct 2014 16:57:46 -0400 Subject: [PATCH 06/12] records now served out of db --- frontend/urls.py | 1 - frontend/views.py | 59 ------------------------------------------ marc/urls.py | 6 +++++ marc/views.py | 66 +++++++++++++++++++++++++++++++++++++++++++++++ urls.py | 1 + 5 files changed, 73 insertions(+), 60 deletions(-) create mode 100644 marc/urls.py create mode 100644 marc/views.py diff --git a/frontend/urls.py b/frontend/urls.py index 70b8ba74..763c06ef 100644 --- a/frontend/urls.py +++ b/frontend/urls.py @@ -152,7 +152,6 @@ urlpatterns = patterns( url(r"^send_to_kindle/(?P\d+)/(?P\d)/$", "send_to_kindle", name="send_to_kindle"), url(r"^marc/$", "marc_admin", name="marc"), url(r"^marc/ungluify/$", staff_member_required(MARCUngluifyView.as_view()), name="MARCUngluify"), - url(r"^marc/concatenate/$", "marc_concatenate", name="marc_concatenate"), url(r"^accounts/edit/marc_config/$", login_required(MARCConfigView.as_view()), name="marc_config"), ) diff --git a/frontend/views.py b/frontend/views.py index 5cbda817..4db8f805 100755 --- a/frontend/views.py +++ b/frontend/views.py @@ -3167,64 +3167,5 @@ class MARCConfigView(FormView): else: return super(MARCConfigView, self).form_valid(form) -def marc_concatenate(request): - if request.method == 'POST': - params=request.POST - elif request.method == 'GET': - params=request.GET - else: - return HttpResponseNotFound - format = params['format'] - - # extract the user-selected records from the params QueryDict - selected_records = list( - k for k in params if params[k] == u'on' - ) - if not selected_records: - messages.error( - request, - "Please select at least one record to download." - ) - return HttpResponseRedirect(reverse('marc', args=[])) - - preamble = ('' - '') - outfile = HttpResponse('', content_type='application/marc') - outfile['Content-Disposition'] = 'attachment; filename='+ now().strftime('%Y%m%d%H%M%S') + '.' + format - - if format == 'xml': - outfile.write(preamble) - for record in selected_records: - if record.startswith('edition_'): - record_id = long(record[8:]) - try: - edition = models.Edition.objects.get(pk=record_id) - except: - continue - record_id = marc.makestub(edition) - else: - record_id = long(record[7:]) - if format == 'xml': - record_url = models.MARCRecord.objects.get( - pk=record_id - ).xml_record - elif format == 'mrc': - record_url = models.MARCRecord.objects.get( - pk=record_id - ).mrc_record - try: - record_file = default_storage.open(record_url).read() - outfile.write(record_file) - except IOError: - logger.warning('MARC record with id number %s has a problem with its URL' % record_id) - if format == 'xml': - outfile.write('') - - return outfile diff --git a/marc/urls.py b/marc/urls.py new file mode 100644 index 00000000..fec3c263 --- /dev/null +++ b/marc/urls.py @@ -0,0 +1,6 @@ +from django.conf.urls.defaults import * + +urlpatterns = patterns( + "regluit.marc.views", + url(r"^marc/concatenate/$", "marc_records", name="marc_concatenate"), +) \ No newline at end of file diff --git a/marc/views.py b/marc/views.py new file mode 100644 index 00000000..8d7399f6 --- /dev/null +++ b/marc/views.py @@ -0,0 +1,66 @@ +from datetime import datetime +from django.core.urlresolvers import reverse +from django.db.models import get_model +from django.http import HttpResponseRedirect, HttpResponse, HttpResponseNotFound + +from . import models + +PREAMBLE = ('' + '') + +Edition = get_model(*models.EDITION_MODEL.split('.')) + +def marc_records(request): + # TODO convert to streaming when we move to Django 1.5+ + if request.method == 'POST': + params=request.POST + elif request.method == 'GET': + params=request.GET + else: + return HttpResponseNotFound + format = params.get('format', 'xml') + link_target = params.get('link_target', 'via') + + # extract the user-selected records from the params QueryDict + selected_records = list( + k for k in params if params[k] == u'on' + ) + if not selected_records: + messages.error( + request, + "Please select at least one record to download." + ) + return HttpResponseRedirect(reverse('marc', args=[])) + + outfile = HttpResponse('', content_type='application/marc') + outfile['Content-Disposition'] = 'attachment; filename='+ datetime.now().strftime('%Y%m%d%H%M%S') + '.' + format + + if format == 'xml': + outfile.write(PREAMBLE) + + for record_name in selected_records: + if record_name.startswith('edition_'): + record_id = long(record_name[8:]) + try: + edition = Edition.objects.get(pk=record_id) + except Edition.DoesNotExist: + continue + record = models.MARCRecord(edition=edition) + elif record_name.startswith('record_'): + record_id = long(record_name[7:]) + try: + record = models.MARCRecord.objects.get(id=record_id ) + except models.MARCRecord.DoesNotExist: + continue + + outfile.write(record.record(format=format, link_target=link_target)) + + if format == 'xml': + outfile.write('') + + return outfile diff --git a/urls.py b/urls.py index bc9f2b04..57ab0c2c 100755 --- a/urls.py +++ b/urls.py @@ -17,6 +17,7 @@ urlpatterns = patterns('', url(r'', include('regluit.frontend.urls')), url(r'', include('regluit.payment.urls')), url(r'', include('regluit.libraryauth.urls')), + url(r'', include('regluit.marc.urls')), url(r'^selectable/', include('selectable.urls')), url(r'^admin/', include(admin_site.urls)), url(r'^comments/', include('django.contrib.comments.urls')), From 0f6aaa4bf16cbc86a7656cd082b749be10984401 Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 20 Oct 2014 23:30:03 -0400 Subject: [PATCH 07/12] load from lc record --- marc/load.py | 169 ++++++++++++++++++++++++++++++++++++++++++------- marc/models.py | 7 ++ marc/tests.py | 8 ++- 3 files changed, 160 insertions(+), 24 deletions(-) diff --git a/marc/load.py b/marc/load.py index 716867a7..c9bdffe4 100644 --- a/marc/load.py +++ b/marc/load.py @@ -14,7 +14,6 @@ from django.conf import settings from django.core.urlresolvers import reverse import regluit.core.cc as cc -from regluit.core import models def stub(edition): @@ -25,28 +24,8 @@ def stub(edition): #mostly to identify this record as a stub field001 = pymarc.Field(tag='001', data='stb'+now.strftime('%y%m%d%H%M%S')) record.add_ordered_field(field001) - - # add field indicating record originator - field003 = pymarc.Field(tag='003', data='UnglueIt') - record.add_ordered_field(field003) - # update timestamp of record - datestamp = now.strftime('%Y%m%d%H%M%S') + '.0' - field005 = pymarc.Field(tag='005', data=datestamp) - record.add_ordered_field(field005) - - # change 006, 007, 008 because this is an online resource - field006 = pymarc.Field( - tag='006', - data='m o d ' - ) - record.add_ordered_field(field006) - - field007 = pymarc.Field( - tag='007', - data='cr' - ) - record.add_ordered_field(field007) + add_stuff(record) # fun fun fun 008 new_field_value= now.strftime('%y%m%d')+'s' @@ -134,6 +113,129 @@ def stub(edition): ) record.add_ordered_field(field520) + add_license(record, edition) + + return record + +#load a record from library of Congress +def from_lc(marcfile, edition): + + # save lccn for later (if there is one) before deleting it + print_lccn = None + record = pymarc.parse_xml_to_array(marcfile)[0] + for lccn in record.get_fields('010'): + for validlccn in lccn.get_subfields('a'): + print_lccn = validlccn + for field in record: + if field.tag in ('001', '003', '005', '006', '007', '010', '040', '856') or int( field.tag ) > 900: + record.remove_field(field) + + add_stuff(record) + + field008 = record.get_fields('008')[0] + record.remove_field(field008) + old_field_value = field008.value() + new_field_value = old_field_value[:23] + 'o' + old_field_value[24:] + field008 = pymarc.Field(tag='008', data=new_field_value) + record.add_ordered_field(field008) + + # add IBSN for ebook where applicable; relegate print ISBN to $z + isbn = edition.isbn_13 + try: + field020 = record.get_fields('020')[0] + print_isbn = field020.get_subfields('a')[0] + field020.delete_subfield('a') + if isbn: + field020.add_subfield('a', isbn) + field020.add_subfield('z', print_isbn) + except IndexError: + print_isbn = None + + # change 050 and 082 indicators because LOC is no longer responsible for these + # no easy indicator change function, so we'll just reconstruct the fields + try: + field050 = record.get_fields('050')[0] + field050_new = field050 + field050_new.indicators = [' ', '4'] + record.remove_field(field050) + record.add_ordered_field(field050_new) + except: + pass # if no 050 field, don't need to change indicator + + try: + field082 = record.get_fields('082')[0] + field082_new = field082 + field082_new.indicators = [' ', '4'] + record.remove_field(field082) + record.add_ordered_field(field082_new) + except: + pass # if no 082 field, don't need to change indicator + + # add subfield to 245 indicating format + try: + field245 = record.get_fields('245')[0] + field245.add_subfield('a', '[electronic resource]') + except IndexError: + pass + + # modify 300 field (physical description) + try: + field300 = record.get_fields('300')[0] + subfield_a = field300.get_subfields('a')[0] + if ( + subfield_a[-2:] == ' ;' or + subfield_a[-2:] == ' :' or + subfield_a[-2:] == ' +' + ): + subfield_a = subfield_a[:-2] + new300a = '1 online resource (' + subfield_a + ')' + if field300.get_subfields('b'): + new300a += ' :' + field300.delete_subfield('a') + field300.add_subfield('a', new300a) + field300.delete_subfield('c') + except: + pass + + add_license(record, edition) + + # add 588 field (source of description) - credit where credit is due + if print_lccn: + field588 = pymarc.Field( + tag='588', + indicators = [' ', ' '], + subfields = [ + 'a', 'Description based on print version record from the Library of Congress.', + ] + ) + record.add_ordered_field(field588) + + # add 776 field (related editions) - preserve pISBN, LCCN, OCLCnum + title = record.get_fields('245')[0].get_subfields('a')[0] + title = title.split('/')[0] + + subfields = ['i', 'Print version: ','t', title,] + + if print_isbn: + subfields.extend(['z', print_isbn]) + elif isbn: + subfields.extend(['z', isbn]) + if print_lccn: + subfields.extend(['w', '(DLC) ' + print_lccn, ]) + if edition.oclc: + subfields.extend(['w', '(OCoLC) ' + edition.oclc,]) + + field776 = pymarc.Field( + tag='776', + indicators = ['0', '8'], + subfields = subfields + ) + + record.add_ordered_field(field776) + + return record + +def add_license(record, edition): if edition.license: # add 536 field (funding information) field536 = pymarc.Field( @@ -155,5 +257,26 @@ def stub(edition): ] ) record.add_ordered_field(field540) + +def add_stuff(record): + # add field indicating record originator + field003 = pymarc.Field(tag='003', data='UnglueIt') + record.add_ordered_field(field003) - return record + # update timestamp of record + datestamp = datetime.now().strftime('%Y%m%d%H%M%S') + '.0' + field005 = pymarc.Field(tag='005', data=datestamp) + record.add_ordered_field(field005) + + # change 006, 007, 008 because this is an online resource + field006 = pymarc.Field( + tag='006', + data='m o d ' + ) + record.add_ordered_field(field006) + + field007 = pymarc.Field( + tag='007', + data='cr' + ) + record.add_ordered_field(field007) diff --git a/marc/models.py b/marc/models.py index 9079d25b..0f556fe5 100644 --- a/marc/models.py +++ b/marc/models.py @@ -81,6 +81,13 @@ class MARCRecord(models.Model): self._the_record.add_ordered_field(field001) super(MARCRecord, self).save(*args, **kwargs) + def load_from_lc(self): + #parse guts + marcfile = StringIO(self.guts) + self._the_record = load.from_lc(marcfile, self.edition) + self.guts = pymarc.record_to_xml(self._the_record) + self.save() + # the record without 856 def _record(self): if self._the_record: diff --git a/marc/tests.py b/marc/tests.py index 01d2dde2..bfdb4368 100644 --- a/marc/tests.py +++ b/marc/tests.py @@ -124,4 +124,10 @@ class MarcTests(TestCase): load.stub(e) - + mr2 = models.MARCRecord.objects.create(guts=a_marc_record, edition=e ) + mr2.load_from_lc() + + mr2.direct_record_xml() + mr2.direct_record_mrc() + mr2.via_record_xml() + mr2.via_record_mrc() From 892b970ae13768b50a4153a2f8ac75033cd996ba Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 20 Oct 2014 23:30:20 -0400 Subject: [PATCH 08/12] can download all records --- frontend/templates/marc.html | 35 +++++++++++++++++------------------ marc/urls.py | 1 + marc/views.py | 20 ++++++++++++++------ 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/frontend/templates/marc.html b/frontend/templates/marc.html index a02ad3d3..ed6af71c 100644 --- a/frontend/templates/marc.html +++ b/frontend/templates/marc.html @@ -64,30 +64,34 @@ ul.local li { {% endif %} {% if records %}
-
+ {% csrf_token %} Record format: +    856 Link type: +

- {% for record in records %} - - -
- {% endfor %} - + spinning wheel Fetching record(s)...
+

+

The 856 link options are:

+
    +
  • Download page link. MARC records link through Unglue.it download page ( library user authentication, context sensitive help for getting files onto user's device)
  • +
  • Links to each file type if available. MARC records link directly to files ( less help, not available for "Buy-to-unglue" titles)
  • +
{% else %} -

Sorry; we don't have any records meeting your needs at this time.

+

Sorry; we don't have any records at this time.

{% endif %} -
+ {% if request.user.is_authenticated %}

Your preference for the links in the 856 field is:

@@ -95,13 +99,8 @@ ul.local li {
  • {{ libpref.get_marc_link_target_display }}
  • -

    The options are:

    -
      -
    • Raw link if available. MARC records link direct to provider (one click, no help, not available for "Buy-to-unglue" titles)
    • -
    • Unglue.it link. MARC records link through Unglue.it download page (extra click, library user authentication, context sensitive help for getting files onto user's device)
    • -
    -

    You can change your preferences here.

    +

    You can change your library preferences here.

    {% endif %} {% endblock %} \ No newline at end of file diff --git a/marc/urls.py b/marc/urls.py index fec3c263..09d61f33 100644 --- a/marc/urls.py +++ b/marc/urls.py @@ -3,4 +3,5 @@ from django.conf.urls.defaults import * urlpatterns = patterns( "regluit.marc.views", url(r"^marc/concatenate/$", "marc_records", name="marc_concatenate"), + url(r"^marc/all/$", "all_marc_records", name="marc_all"), ) \ No newline at end of file diff --git a/marc/views.py b/marc/views.py index 8d7399f6..d7973e76 100644 --- a/marc/views.py +++ b/marc/views.py @@ -15,7 +15,8 @@ PREAMBLE = ('' Edition = get_model(*models.EDITION_MODEL.split('.')) -def marc_records(request): + +def marc_records(request, selected_records=None): # TODO convert to streaming when we move to Django 1.5+ if request.method == 'POST': params=request.POST @@ -26,14 +27,15 @@ def marc_records(request): format = params.get('format', 'xml') link_target = params.get('link_target', 'via') - # extract the user-selected records from the params QueryDict - selected_records = list( - k for k in params if params[k] == u'on' - ) + if selected_records==None: + # extract the user-selected records from the params QueryDict + selected_records = list( + k for k in params if params[k] == u'on' + ) if not selected_records: messages.error( request, - "Please select at least one record to download." + "There were no records to download." ) return HttpResponseRedirect(reverse('marc', args=[])) @@ -64,3 +66,9 @@ def marc_records(request): outfile.write('') return outfile + +def all_marc_records(request): + selected_records = list( + 'record_'+ str(record.pk) for record in models.MARCRecord.objects.all() + ) + return marc_records(request, selected_records=selected_records) From 3b4b836e6c54b417e452ae91127edd535ec3ae43 Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 27 Oct 2014 11:55:46 -0400 Subject: [PATCH 09/12] add marc download tools --- core/models.py | 20 +++++++- frontend/forms.py | 2 +- frontend/templates/bypub_list.html | 9 +++- frontend/templates/campaign_list.html | 11 +++++ frontend/templates/cc_list.html | 11 ++++- frontend/templates/marc.html | 46 +++++++----------- frontend/templates/marc_config.html | 33 ++++++++++--- frontend/templates/marc_form.html | 12 +++++ frontend/templates/supporter.html | 11 +++++ frontend/templates/unglued_list.html | 11 +++++ frontend/templates/work.html | 9 ++++ frontend/templates/work_list.html | 11 +++++ frontend/urls.py | 10 +++- frontend/views.py | 68 ++++++++++++++------------- marc/views.py | 18 ++++++- static/css/book_list.css | 2 +- static/css/campaign2.css | 2 +- static/less/book_list.less | 18 ++++++- static/less/campaign2.less | 21 +++++++++ 19 files changed, 245 insertions(+), 80 deletions(-) create mode 100644 frontend/templates/marc_form.html diff --git a/core/models.py b/core/models.py index 569d441f..d272cae4 100755 --- a/core/models.py +++ b/core/models.py @@ -37,7 +37,7 @@ import regluit.core.isbn import regluit.core.cc as cc from regluit.core.epub import personalize, ungluify, test_epub, ask_epub from regluit.core.pdf import ask_pdf, pdf_append - +from regluit.marc.models import MARCRecord as NewMARC from regluit.core.signals import ( successful_campaign, unsuccessful_campaign, @@ -1007,6 +1007,10 @@ class Campaign(models.Model): @property def user_to_pay(self): return self.rh.owner + + ### for compatibility with MARC output + def marc_records(self): + return self.work.marc_records() class Identifier(models.Model): # olib, ltwk, goog, gdrd, thng, isbn, oclc, olwk, olib, gute, glue @@ -1511,7 +1515,21 @@ class Work(models.Model): else: # assume it's several users return self.user_license(self.acqs.filter(user__in=user)) + + ### for compatibility with MARC output + def marc_records(self): + record_list = [] + record_list.extend(NewMARC.objects.filter(edition__work=self)) + for obj in record_list: + break + else: + for ebook in self.ebooks(): + record_list.append(ebook.edition) + break + return record_list + + class Author(models.Model): created = models.DateTimeField(auto_now_add=True) name = models.CharField(max_length=500) diff --git a/frontend/forms.py b/frontend/forms.py index 1124d06e..e1945c06 100644 --- a/frontend/forms.py +++ b/frontend/forms.py @@ -783,4 +783,4 @@ class MARCUngluifyForm(forms.Form): class MARCFormatForm(forms.ModelForm): class Meta: model = Libpref - fields = ('marc_link_target',) + fields = () diff --git a/frontend/templates/bypub_list.html b/frontend/templates/bypub_list.html index 55242239..e6394773 100644 --- a/frontend/templates/bypub_list.html +++ b/frontend/templates/bypub_list.html @@ -19,4 +19,11 @@ {% endif %} {% endblock %} -{% block add_more %}{% endblock %} \ No newline at end of file +{% block add_more %}{% endblock %} + +{% block marcform %} +
    + {% include 'marc_form.html' %} + +
    +{% endblock %} diff --git a/frontend/templates/campaign_list.html b/frontend/templates/campaign_list.html index 93d3370c..9a60af57 100644 --- a/frontend/templates/campaign_list.html +++ b/frontend/templates/campaign_list.html @@ -102,6 +102,17 @@ location.hash = "#2"; {% endifequal %} + {% if request.user.libpref %} +
    +

    for libraries...

    + {% block marcform %} +
    + {% include 'marc_form.html' %} + +
    + {% endblock %} +
    + {% endif %} diff --git a/frontend/templates/cc_list.html b/frontend/templates/cc_list.html index 5cf06de6..98e47122 100644 --- a/frontend/templates/cc_list.html +++ b/frontend/templates/cc_list.html @@ -93,7 +93,16 @@ {% endifequal %} - + + {% if request.user.libpref %} +
    +

    for libraries...

    +
    + {% include 'marc_form.html' %} + +
    +
    + {% endif %} diff --git a/frontend/templates/marc.html b/frontend/templates/marc.html index ed6af71c..03de81de 100644 --- a/frontend/templates/marc.html +++ b/frontend/templates/marc.html @@ -55,52 +55,38 @@ ul.local li { {% endfor %} {% endif %} -{% if userlist %} -

    Records for {{ userlist }}

    -{% else %} -{% if request.user.is_authenticated %} -

    To restrict this list to works on your list click here

    -{% endif %} -{% endif %} -{% if records %}
    - {% csrf_token %} - Record format: - -    856 Link type: - -

    + {% include 'marc_form.html' %} spinning wheel Fetching record(s)...
    +
    +{% if request.user.is_authenticated %} +

    ...or

    +
    +
    + {% include 'marc_form.html' %} + + spinning wheel Fetching record(s)... +


    +{% endif %}

    The 856 link options are:

    • Download page link. MARC records link through Unglue.it download page ( library user authentication, context sensitive help for getting files onto user's device)
    • Links to each file type if available. MARC records link directly to files ( less help, not available for "Buy-to-unglue" titles)
    -{% else %} -

    Sorry; we don't have any records at this time.

    -{% endif %} {% if request.user.is_authenticated %}
    -

    Your preference for the links in the 856 field is:

    -
      -
    • {{ libpref.get_marc_link_target_display }}
    • -
    - - -

    You can change your library preferences here.

    + {% if request.user.libpref %} +

    You have enabled librarian tools. You can change your librarian status here.

    + {% else %} +

    You have not enabled librarian tools. You can change your librarian status here.

    + {% endif %}
    {% endif %} {% endblock %} \ No newline at end of file diff --git a/frontend/templates/marc_config.html b/frontend/templates/marc_config.html index 0d39470d..2606ef42 100644 --- a/frontend/templates/marc_config.html +++ b/frontend/templates/marc_config.html @@ -10,7 +10,7 @@ {% endblock %} -{% block title %}Change your MARC record preferences{% endblock %} +{% block title %}Change your librarian preferences{% endblock %} {% block ce_content %} {% if messages %} @@ -20,14 +20,33 @@ {% endfor %} {% endif %} -Back to record download page

    +

    Librarian Tools

    +If you enable Librarian tools, you'll find links to download MARC record(s) at the bottom of many Unglue.it pages. +
      +
    • work pages
    • +
    • lists of works
    • +
    • supporter pages
    • +
    -Your current MARC record link target preference is:
    -{{ libpref.get_marc_link_target_display }} +{% if request.user.is_authenticated %}
    {% csrf_token %} - {{ form }} -
    - +
    + {% if request.user.libpref %} +

    You have enabled librarian tools.

    + {{ form }} + + {% else %} +

    You have not enabled librarian tools. + {{ form }} +
    + + + {% endif %} +

    +

    +Back to record download page +

    +{% endif %} {% endblock %} \ No newline at end of file diff --git a/frontend/templates/marc_form.html b/frontend/templates/marc_form.html new file mode 100644 index 00000000..1eb7fcc0 --- /dev/null +++ b/frontend/templates/marc_form.html @@ -0,0 +1,12 @@ +{% csrf_token %} +record format: +   +856 links: + + \ No newline at end of file diff --git a/frontend/templates/supporter.html b/frontend/templates/supporter.html index 2c96cf94..ac5e2a79 100644 --- a/frontend/templates/supporter.html +++ b/frontend/templates/supporter.html @@ -387,6 +387,17 @@ function highlightTarget(targetdiv) { {% endifequal %}
    + {% if request.user.libpref %} +
    +

    for libraries...

    + {% block marcform %} +
    + {% include 'marc_form.html' %} + +
    + {% endblock %} +
    + {% endif %} diff --git a/frontend/templates/unglued_list.html b/frontend/templates/unglued_list.html index 486e9328..238fd30d 100644 --- a/frontend/templates/unglued_list.html +++ b/frontend/templates/unglued_list.html @@ -105,6 +105,17 @@ + {% if request.user.libpref %} +
    +

    for libraries...

    + {% block marcform %} +
    + {% include 'marc_form.html' %} + +
    + {% endblock %} +
    + {% endif %} diff --git a/frontend/templates/work.html b/frontend/templates/work.html index ed26ea4e..c141d7e8 100644 --- a/frontend/templates/work.html +++ b/frontend/templates/work.html @@ -717,6 +717,15 @@ {% endif %} + {% if request.user.libpref and work.first_ebook %} +
    +

    for libraries...

    +
    + {% include 'marc_form.html' %} + +
    +
    + {% endif %} diff --git a/frontend/templates/work_list.html b/frontend/templates/work_list.html index cf6b32c3..dc270fca 100644 --- a/frontend/templates/work_list.html +++ b/frontend/templates/work_list.html @@ -131,6 +131,17 @@ {% endifequal %} + {% if request.user.libpref %} +
    +

    for libraries...

    + {% block marcform %} +
    + {% include 'marc_form.html' %} + +
    + {% endblock %} +
    + {% endif %} diff --git a/frontend/urls.py b/frontend/urls.py index 763c06ef..57b21147 100644 --- a/frontend/urls.py +++ b/frontend/urls.py @@ -47,7 +47,7 @@ urlpatterns = patterns( url(r"^landing/$", "home", {'landing': True}, name="landing"), url(r"^next/$", "next", name="next"), url(r"^supporter/(?P[^/]+)/$", "supporter", {'template_name': 'supporter.html'}, name="supporter"), - url(r"^supporter/(?P[^/]+)/marc/$", "marc_admin", name="user_marc"), + url(r"^supporter/(?P[^/]+)/marc/$", "userlist_marc", name="user_marc"), url(r"^library/(?P[^/]+)/$", "library", name="library"), url(r"^accounts/manage/$", login_required(ManageAccount.as_view()), name="manage_account"), url(r"^search/$", "search", name="search"), @@ -71,14 +71,19 @@ urlpatterns = patterns( url(r"^wishlist/$", "wishlist", name="wishlist"), url(r"^msg/$", "msg", name="msg"), url(r"^campaigns/(?P\w*)$", CampaignListView.as_view(), name='campaign_list'), + url(r"^campaigns/(?P\w*)/marc/$", CampaignListView.as_view(send_marc=True), name='campaign_list_marc'), url(r"^lists/(?P\w*)$", WorkListView.as_view(), name='work_list'), + url(r"^lists/(?P\w*)/marc/$", WorkListView.as_view(send_marc=True), name='work_list_marc'), url(r"^pid/all/(?P\d+)$", ByPubView.as_view(), name='bypubname_list'), url(r"^pid/(?P\w*)/(?P\d+)$", ByPubView.as_view(), name='bypubname_list'), + url(r"^pid/(?P\w*)/(?P\d+)/marc/$", ByPubView.as_view(send_marc=True), name='bypubname_list_marc'), url(r"^bypub/all/(?P.*)$", ByPubListView.as_view(), name='bypub_list'), url(r"^bypub/(?P\w*)/(?P.*)$", ByPubListView.as_view(), name='bypub_list'), url(r"^unglued/(?P\w*)$", UngluedListView.as_view(), name='unglued_list'), + url(r"^unglued/(?P\w*)/marc/$", UngluedListView.as_view(send_marc=True), name='unglued_list_marc'), url(r"^creativecommons/$", CCListView.as_view(), name='cc_list'), url(r"^creativecommons/(?P[\w\-]*)$", CCListView.as_view(), name='cc_list_detail'), + url(r"^creativecommons/(?P[\w\-]*)/marc/$", CCListView.as_view(send_marc=True), name='cc_list_marc'), url(r"^goodreads/auth/$", "goodreads_auth", name="goodreads_auth"), url(r"^goodreads/auth_cb/$", "goodreads_cb", name="goodreads_cb"), url(r"^goodreads/flush/$","goodreads_flush_assoc", name="goodreads_flush_assoc"), @@ -94,6 +99,7 @@ urlpatterns = patterns( url(r"^work/(?P\d+)/download/$", DownloadView.as_view(), name="thank"), url(r"^work/(?P\d+)/unglued/(?P\w+)/$", "download_campaign", name="download_campaign"), url(r"^work/(?P\d+)/borrow/$", "borrow", name="borrow"), + url(r"^work/(?P\d+)/marc/$", "work_marc", name="work_marc"), url(r"^work/(?P\d+)/reserve/$", "reserve", name="reserve"), url(r"^work/(?P\d+)/feature/$", "feature", name="feature"), url(r"^work/(?P\d+)/merge/$", login_required(MergeView.as_view()), name="merge"), @@ -150,7 +156,7 @@ urlpatterns = patterns( url(r"^accounts/edit/kindle_config/$", "kindle_config", name="kindle_config"), url(r"^accounts/edit/kindle_config/(?P\d+)/$", "kindle_config", name="kindle_config_download"), url(r"^send_to_kindle/(?P\d+)/(?P\d)/$", "send_to_kindle", name="send_to_kindle"), - url(r"^marc/$", "marc_admin", name="marc"), + url(r"^marc/$", direct_to_template, {'template': 'marc.html'}, name="marc"), url(r"^marc/ungluify/$", staff_member_required(MARCUngluifyView.as_view()), name="MARCUngluify"), url(r"^accounts/edit/marc_config/$", login_required(MARCConfigView.as_view()), name="marc_config"), ) diff --git a/frontend/views.py b/frontend/views.py index 4db8f805..d1123372 100755 --- a/frontend/views.py +++ b/frontend/views.py @@ -145,6 +145,7 @@ from regluit.booxtream.exceptions import BooXtreamError from regluit.pyepub import InvalidEpub from regluit.libraryauth.views import Authenticator from regluit.libraryauth.models import Library +from regluit.marc.views import qs_marc_records logger = logging.getLogger(__name__) @@ -779,6 +780,7 @@ def subjects(request): return render(request, 'subjects.html', {'subjects': subjects}) class FilterableListView(ListView): + send_marc = False def get_queryset(self): if self.request.GET.has_key('pub_lang'): if self.model is models.Campaign: @@ -797,7 +799,13 @@ class FilterableListView(ListView): context['show_langs']=True context['WISHED_LANGS']=settings.WISHED_LANGS return context - + + def render_to_response(self, context, **response_kwargs): + if self.send_marc: + return qs_marc_records(self.request, qs=self.object_list) + else: + return super(FilterableListView,self).render_to_response(context, **response_kwargs) + recommended_user = User.objects.filter( username=settings.UNGLUEIT_RECOMMENDED_USERNAME) class WorkListView(FilterableListView): @@ -877,6 +885,8 @@ class ByPubView(WorkListView): context = super(ByPubView, self).get_context_data(**kwargs) context['pubname'] = self.publisher_name context['publisher'] = self.publisher + context['facet'] = self.kwargs.get('facet','all') + return context class ByPubListView(ByPubView): @@ -909,7 +919,7 @@ class CCListView(FilterableListView): def get_context_data(self, **kwargs): context = super(CCListView, self).get_context_data(**kwargs) - facet = self.kwargs.get('facet','') + facet = self.kwargs.get('facet','all') qs=self.get_queryset() context['ungluers'] = userlists.work_list_users(qs,5) context['activetab'] = "#1" @@ -3087,29 +3097,18 @@ def send_to_kindle(request, work_id, javascript='0'): return local_response(request, javascript, context, 2) -def marc_admin(request, userlist=None): - link_target = 'UNGLUE' - libpref = {'marc_link_target': settings.MARC_CHOICES[1]} - try: - libpref = request.user.libpref - link_target = libpref.marc_link_target - except: - if not request.user.is_anonymous(): - libpref = models.Libpref(user=request.user) - unwanted = 'UNGLUE' if link_target == 'DIRECT' else 'DIRECT' +def userlist_marc(request, userlist=None): if userlist: - records = [] user = get_object_or_404(User,username=userlist) - for work in user.wishlist.works.all(): - records.extend(models.MARCRecord.objects.filter(edition__work=work,link_target=link_target)) - records.extend(models.MARCRecord.objects.filter(edition__work=work,link_target='B2U')) + return qs_marc_records(request, qs=user.wishlist.works.all()) else: - records = models.MARCRecord.objects.exclude(link_target=unwanted) - return render( - request, - 'marc.html', - {'records': records, 'libpref' : libpref , 'userlist' : userlist} - ) + return qs_marc_records(request, qs=request.user.wishlist.works.all()) + + return render( request,'marc.html',{'userlist' : [] }) + +def work_marc(request, work_id): + work = safe_get_work(work_id) + return qs_marc_records(request, qs=[ work ]) class MARCUngluifyView(FormView): template_name = 'marcungluify.html' @@ -3151,17 +3150,20 @@ class MARCConfigView(FormView): success_url = reverse_lazy('marc') def form_valid(self, form): - marc_link_target = form.cleaned_data['marc_link_target'] - try: - libpref = self.request.user.libpref - except: - libpref = models.Libpref(user=self.request.user) - libpref.marc_link_target = marc_link_target - libpref.save() - messages.success( - self.request, - "Your preferences have been changed." - ) + enable= form.data.has_key('enable') + if enable: + try: + libpref = self.request.user.libpref + except: + libpref = models.Libpref(user=self.request.user) + libpref.save() + messages.success(self.request,"Tools are enabled.") + else: + try: + self.request.user.libpref.delete() + except: + pass + messages.success(self.request,"Tools are disabled." ) if reverse('marc_config', args=[]) in self.request.META['HTTP_REFERER']: return HttpResponseRedirect(reverse('marc_config', args=[])) else: diff --git a/marc/views.py b/marc/views.py index d7973e76..00eace17 100644 --- a/marc/views.py +++ b/marc/views.py @@ -46,7 +46,13 @@ def marc_records(request, selected_records=None): outfile.write(PREAMBLE) for record_name in selected_records: - if record_name.startswith('edition_'): + if isinstance(record_name, models.MARCRecord): + record = record_name + elif isinstance(record_name, Edition): + record = models.MARCRecord(edition=record_name) + elif hasattr(record_name, 'edition'): + record = models.MARCRecord(edition=record_name.edition) + elif record_name.startswith('edition_'): record_id = long(record_name[8:]) try: edition = Edition.objects.get(pk=record_id) @@ -72,3 +78,13 @@ def all_marc_records(request): 'record_'+ str(record.pk) for record in models.MARCRecord.objects.all() ) return marc_records(request, selected_records=selected_records) + +def qs_marc_records(request, qs): + # translate a queryset into a record list. + selected_records = [] + for obj in qs: + # each object must have a marc_records() attribute which returns a record list. + # records can be strings: 'record_xxx', 'edition_xxx', MARCRecord objs or objs with edition attributes + selected_records.extend(obj.marc_records()) + return marc_records(request, selected_records=selected_records) + \ No newline at end of file diff --git a/static/css/book_list.css b/static/css/book_list.css index cce232ee..ef61619f 100755 --- a/static/css/book_list.css +++ b/static/css/book_list.css @@ -1 +1 @@ -.header-text{height:36px;line-height:36px;display:block;text-decoration:none;font-weight:bold;letter-spacing:-0.05em}.panelborders{border-width:1px 0;border-style:solid none;border-color:#fff}.roundedspan{border:1px solid #d4d4d4;-moz-border-radius:7px;-webkit-border-radius:7px;border-radius:7px;padding:1px;color:#fff;margin:0 8px 0 0;display:inline-block}.roundedspan>span{padding:7px 7px;min-width:15px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;text-align:center;display:inline-block}.roundedspan>span .hovertext{display:none}.roundedspan>span:hover .hovertext{display:inline}.mediaborder{padding:5px;border:solid 5px #edf3f4}.actionbuttons{width:auto;height:36px;line-height:36px;background:#8dc63f;-moz-border-radius:32px;-webkit-border-radius:32px;border-radius:32px;color:white;cursor:pointer;font-size:13px;font-weight:bold;padding:0 15px;border:0;margin:5px 0}.errors{-moz-border-radius:16px;-webkit-border-radius:16px;border-radius:16px;border:solid #e35351 3px;clear:both;width:90%;height:auto;line-height:16px;padding:7px 0;font-weight:bold;font-size:13px;text-align:center}.errors li{list-style:none;border:0}.row1 .book-list.listview{background:#f6f9f9}.row1 .book-list.listview .book-name:hover{background:#f6f9f9}.row2 .book-list.listview{background:#fff}.row2 .book-list.listview .book-name:hover{background:#fff}div.book-list.listview{clear:both;display:block;vertical-align:middle;height:43px;line-height:43px;margin:0 5px 0 0;padding:7px 0;position:relative}div.book-list.listview div.unglue-this{float:left}div.book-list.listview div.book-thumb{margin-right:5px;float:left}div.book-list.listview div.book-name{width:235px;margin-right:10px;background:url("/static/images/booklist/booklist-vline.png") right center no-repeat;float:left}div.book-list.listview div.book-name .title{display:block;line-height:normal;overflow:hidden;height:19px;line-height:19px;margin-bottom:5px;font-weight:bold}div.book-list.listview div.book-name .listview.author{overflow:hidden;display:block;line-height:normal;height:19px;line-height:19px}div.book-list.listview div.book-name.listview:hover{overflow:visible;width:auto;min-width:219px;margin-top:-1px;padding-right:15px;border:1px solid #d6dde0;-moz-border-radius:0 10px 10px 0;-webkit-border-radius:0 10px 10px 0;border-radius:0 10px 10px 0;border-left:none}div.book-list.listview div.book-name.listview{z-index:100;position:absolute;left:42px}div.book-list.listview div.add-wishlist,div.book-list.listview div.remove-wishlist,div.book-list.listview div.on-wishlist,div.book-list.listview div.create-account,div.book-list.listview div.pledge{margin-right:10px;padding-right:10px;width:136px;background:url("/static/images/booklist/booklist-vline.png") right center no-repeat;margin-left:255px;float:left}div.book-list.listview div.add-wishlist span,div.book-list.listview div.remove-wishlist span,div.book-list.listview div.on-wishlist span,div.book-list.listview div.create-account span,div.book-list.listview div.pledge span{font-weight:normal;color:#3d4e53;text-transform:none;padding-left:20px}div.book-list.listview div.add-wishlist span.booklist_pledge,div.book-list.listview div.remove-wishlist span.booklist_pledge,div.book-list.listview div.on-wishlist span.booklist_pledge,div.book-list.listview div.create-account span.booklist_pledge,div.book-list.listview div.pledge span.booklist_pledge{padding-left:18px}div.book-list.listview div.pledge span.booklist_pledge{padding-left:0}div.book-list.listview div.add-wishlist span,div.book-list.listview div.create-account span{background:url("/static/images/booklist/add-wishlist.png") left center no-repeat}div.book-list.listview div.add-wishlist span.booklist_pledge{background:0}div.book-list.listview div.remove-wishlist span{background:url("/static/images/booklist/remove-wishlist-blue.png") left center no-repeat}div.book-list.listview div.on-wishlist>span,div.book-list.listview div>span.on-wishlist{background:url("/static/images/checkmark_small.png") left center no-repeat}div.book-list.listview div.booklist-status{margin-right:85px;float:left}div.add-wishlist,div.remove-wishlist{cursor:pointer}.booklist-status.listview span.booklist-status-label{display:none}.booklist-status.listview span.booklist-status-text{float:left;display:block;padding-right:5px;max-width:180px;overflow:hidden}.booklist-status.listview .read_itbutton{margin-top:4px}div.unglue-this a{text-transform:uppercase;color:#3d4e53;font-size:11px;font-weight:bold}div.unglue-this.complete .unglue-this-inner1{background:url("/static/images/booklist/bg.png") 0 -84px no-repeat;height:42px}div.unglue-this.complete .unglue-this-inner2{background:url("/static/images/booklist/bg.png") 100% -126px no-repeat;margin-left:29px;height:42px;padding-right:10px}div.unglue-this.complete a{color:#fff;display:block}div.unglue-this.processing .unglue-this-inner1{background:url("/static/images/booklist/bg.png") 0 0 no-repeat;height:42px}div.unglue-this.processing .unglue-this-inner2{background:url("/static/images/booklist/bg.png") 100% -42px no-repeat;margin-left:25px;height:42px;padding-right:10px}ul.book-list-view{padding:0;margin:15px;float:right;list-style:none}ul.book-list-view li{float:left;margin-right:10px;display:block;vertical-align:middle;line-height:22px}div.navigation{float:left;clear:both;width:100%;color:#37414d}ul.navigation{float:right;padding:0;margin:0;list-style:none}ul.navigation li{float:left;line-height:normal;margin-right:5px}ul.navigation li a{color:#37414d;font-weight:normal}ul.navigation li.arrow-l a{background:url("/static/images/booklist/bg.png") 0 -168px no-repeat;width:10px;height:15px;display:block;text-indent:-10000px}ul.navigation li.arrow-r a{background:url("/static/images/booklist/bg.png") -1px -185px no-repeat;width:10px;height:15px;display:block;text-indent:-10000px}ul.navigation li a:hover,ul.navigation li.active a{color:#8ac3d7;text-decoration:underline}.unglue-button{display:block;border:0}.book-thumb.listview a{display:block;height:50px;width:32px;overflow:hidden;position:relative;z-index:1}.book-thumb.listview a:hover{overflow:visible;z-index:1000;border:0}.book-thumb.listview a img{position:absolute}.listview.icons{position:absolute;right:31px}.listview.icons .booklist-status-img{-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;background-color:#fff;margin-top:4px;height:37px}.listview.icons .booklist-status-img img{padding:5px}.listview.icons .booklist-status-label{display:none}.listview.icons .boolist-ebook img{margin-top:6px}div#content-block-content{padding-bottom:100px}.listview.panelback,.listview.panelback div{display:none}#toggle-list{filter:alpha(opacity=100);-moz-opacity:1;-khtml-opacity:1;opacity:1}#toggle-panel{filter:alpha(opacity=100);-moz-opacity:.2;-khtml-opacity:.2;opacity:.2}.nobold{font-weight:normal} \ No newline at end of file +.header-text{height:36px;line-height:36px;display:block;text-decoration:none;font-weight:bold;letter-spacing:-0.05em}.panelborders{border-width:1px 0;border-style:solid none;border-color:#fff}.roundedspan{border:1px solid #d4d4d4;-moz-border-radius:7px;-webkit-border-radius:7px;border-radius:7px;padding:1px;color:#fff;margin:0 8px 0 0;display:inline-block}.roundedspan>span{padding:7px 7px;min-width:15px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;text-align:center;display:inline-block}.roundedspan>span .hovertext{display:none}.roundedspan>span:hover .hovertext{display:inline}.mediaborder{padding:5px;border:solid 5px #edf3f4}.actionbuttons{width:auto;height:36px;line-height:36px;background:#8dc63f;-moz-border-radius:32px;-webkit-border-radius:32px;border-radius:32px;color:white;cursor:pointer;font-size:13px;font-weight:bold;padding:0 15px;border:0;margin:5px 0}.errors{-moz-border-radius:16px;-webkit-border-radius:16px;border-radius:16px;border:solid #e35351 3px;clear:both;width:90%;height:auto;line-height:16px;padding:7px 0;font-weight:bold;font-size:13px;text-align:center}.errors li{list-style:none;border:0}.row1 .book-list.listview{background:#f6f9f9}.row1 .book-list.listview .book-name:hover{background:#f6f9f9}.row2 .book-list.listview{background:#fff}.row2 .book-list.listview .book-name:hover{background:#fff}div.book-list.listview{clear:both;display:block;vertical-align:middle;height:43px;line-height:43px;margin:0 5px 0 0;padding:7px 0;position:relative}div.book-list.listview div.unglue-this{float:left}div.book-list.listview div.book-thumb{margin-right:5px;float:left}div.book-list.listview div.book-name{width:235px;margin-right:10px;background:url("/static/images/booklist/booklist-vline.png") right center no-repeat;float:left}div.book-list.listview div.book-name .title{display:block;line-height:normal;overflow:hidden;height:19px;line-height:19px;margin-bottom:5px;font-weight:bold}div.book-list.listview div.book-name .listview.author{overflow:hidden;display:block;line-height:normal;height:19px;line-height:19px}div.book-list.listview div.book-name.listview:hover{overflow:visible;width:auto;min-width:219px;margin-top:-1px;padding-right:15px;border:1px solid #d6dde0;-moz-border-radius:0 10px 10px 0;-webkit-border-radius:0 10px 10px 0;border-radius:0 10px 10px 0;border-left:none}div.book-list.listview div.book-name.listview{z-index:100;position:absolute;left:42px}div.book-list.listview div.add-wishlist,div.book-list.listview div.remove-wishlist,div.book-list.listview div.on-wishlist,div.book-list.listview div.create-account,div.book-list.listview div.pledge{margin-right:10px;padding-right:10px;width:136px;background:url("/static/images/booklist/booklist-vline.png") right center no-repeat;margin-left:255px;float:left}div.book-list.listview div.add-wishlist span,div.book-list.listview div.remove-wishlist span,div.book-list.listview div.on-wishlist span,div.book-list.listview div.create-account span,div.book-list.listview div.pledge span{font-weight:normal;color:#3d4e53;text-transform:none;padding-left:20px}div.book-list.listview div.add-wishlist span.booklist_pledge,div.book-list.listview div.remove-wishlist span.booklist_pledge,div.book-list.listview div.on-wishlist span.booklist_pledge,div.book-list.listview div.create-account span.booklist_pledge,div.book-list.listview div.pledge span.booklist_pledge{padding-left:18px}div.book-list.listview div.pledge span.booklist_pledge{padding-left:0}div.book-list.listview div.add-wishlist span,div.book-list.listview div.create-account span{background:url("/static/images/booklist/add-wishlist.png") left center no-repeat}div.book-list.listview div.add-wishlist span.booklist_pledge{background:0}div.book-list.listview div.remove-wishlist span{background:url("/static/images/booklist/remove-wishlist-blue.png") left center no-repeat}div.book-list.listview div.on-wishlist>span,div.book-list.listview div>span.on-wishlist{background:url("/static/images/checkmark_small.png") left center no-repeat}div.book-list.listview div.booklist-status{margin-right:85px;float:left}div.add-wishlist,div.remove-wishlist{cursor:pointer}.booklist-status.listview span.booklist-status-label{display:none}.booklist-status.listview span.booklist-status-text{float:left;display:block;padding-right:5px;max-width:180px;overflow:hidden}.booklist-status.listview .read_itbutton{margin-top:4px}div.unglue-this a{text-transform:uppercase;color:#3d4e53;font-size:11px;font-weight:bold}div.unglue-this.complete .unglue-this-inner1{background:url("/static/images/booklist/bg.png") 0 -84px no-repeat;height:42px}div.unglue-this.complete .unglue-this-inner2{background:url("/static/images/booklist/bg.png") 100% -126px no-repeat;margin-left:29px;height:42px;padding-right:10px}div.unglue-this.complete a{color:#fff;display:block}div.unglue-this.processing .unglue-this-inner1{background:url("/static/images/booklist/bg.png") 0 0 no-repeat;height:42px}div.unglue-this.processing .unglue-this-inner2{background:url("/static/images/booklist/bg.png") 100% -42px no-repeat;margin-left:25px;height:42px;padding-right:10px}ul.book-list-view{padding:0;margin:15px;float:right;list-style:none}ul.book-list-view li{float:left;margin-right:10px;display:block;vertical-align:middle;line-height:22px}div.navigation{float:left;clear:both;width:100%;color:#37414d}ul.navigation{float:right;padding:0;margin:0;list-style:none}ul.navigation li{float:left;line-height:normal;margin-right:5px}ul.navigation li a{color:#37414d;font-weight:normal}ul.navigation li.arrow-l a{background:url("/static/images/booklist/bg.png") 0 -168px no-repeat;width:10px;height:15px;display:block;text-indent:-10000px}ul.navigation li.arrow-r a{background:url("/static/images/booklist/bg.png") -1px -185px no-repeat;width:10px;height:15px;display:block;text-indent:-10000px}ul.navigation li a:hover,ul.navigation li.active a{color:#8ac3d7;text-decoration:underline}.unglue-button{display:block;border:0}.book-thumb.listview a{display:block;height:50px;width:32px;overflow:hidden;position:relative;z-index:1}.book-thumb.listview a:hover{overflow:visible;z-index:1000;border:0}.book-thumb.listview a img{position:absolute}.listview.icons{position:absolute;right:31px}.listview.icons .booklist-status-img{-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;background-color:#fff;margin-top:4px;height:37px}.listview.icons .booklist-status-img img{padding:5px}.listview.icons .booklist-status-label{display:none}.listview.icons .boolist-ebook img{margin-top:6px}div#content-block-content{padding-bottom:10px}.listview.panelback,.listview.panelback div{display:none}#toggle-list{filter:alpha(opacity=100);-moz-opacity:1;-khtml-opacity:1;opacity:1}#toggle-panel{filter:alpha(opacity=100);-moz-opacity:.2;-khtml-opacity:.2;opacity:.2}.nobold{font-weight:normal}div#libtools{margin-left:15px;margin-bottom:1em;border:1px solid #d6dde0;border-radius:10px;padding:10px}div#libtools p{margin-top:0}div#libtools div{margin-top:0;margin-left:2em} \ No newline at end of file diff --git a/static/css/campaign2.css b/static/css/campaign2.css index 6788d4e1..b7095313 100644 --- a/static/css/campaign2.css +++ b/static/css/campaign2.css @@ -1 +1 @@ -.header-text{height:36px;line-height:36px;display:block;text-decoration:none;font-weight:bold;letter-spacing:-0.05em}.panelborders{border-width:1px 0;border-style:solid none;border-color:#fff}.roundedspan{border:1px solid #d4d4d4;-moz-border-radius:7px;-webkit-border-radius:7px;border-radius:7px;padding:1px;color:#fff;margin:0 8px 0 0;display:inline-block}.roundedspan>span{padding:7px 7px;min-width:15px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;text-align:center;display:inline-block}.roundedspan>span .hovertext{display:none}.roundedspan>span:hover .hovertext{display:inline}.mediaborder{padding:5px;border:solid 5px #edf3f4}.actionbuttons{width:auto;height:36px;line-height:36px;background:#8dc63f;-moz-border-radius:32px;-webkit-border-radius:32px;border-radius:32px;color:white;cursor:pointer;font-size:13px;font-weight:bold;padding:0 15px;border:0;margin:5px 0}.errors{-moz-border-radius:16px;-webkit-border-radius:16px;border-radius:16px;border:solid #e35351 3px;clear:both;width:90%;height:auto;line-height:16px;padding:7px 0;font-weight:bold;font-size:13px;text-align:center}.errors li{list-style:none;border:0}#tabs{border-bottom:4px solid #6994a3;clear:both;float:left;margin-top:10px;width:100%}#tabs ul.book-list-view{margin-bottom:4px!important}#tabs-1,#tabs-2,#tabs-3,#tabs-4{display:none}#tabs-1.active,#tabs-2.active,#tabs-3.active,#tabs-4.active{display:inherit}#tabs-2 textarea{width:95%}ul.tabs{float:left;padding:0;margin:0;list-style:none;width:100%}ul.tabs li{float:left;height:46px;line-height:20px;padding-right:2px;width:116px;background:0;margin:0;padding:0 2px 0 0}ul.tabs li.tabs4{padding-right:0}ul.tabs li a{height:41px;line-height:18px;display:block;text-align:center;padding:0 10px;min-width:80px;-moz-border-radius:7px 7px 0 0;-webkit-border-radius:7px 7px 0 0;border-radius:7px 7px 0 0;background:#d6dde0;color:#3d4e53;padding-top:5px}ul.tabs li a:hover{text-decoration:none}ul.tabs li a div{padding-top:8px}ul.tabs li a:hover,ul.tabs li.active a{background:#6994a3;color:#fff}.header-text{height:36px;line-height:36px;display:block;text-decoration:none;font-weight:bold;letter-spacing:-0.05em}.panelborders{border-width:1px 0;border-style:solid none;border-color:#fff}.roundedspan{border:1px solid #d4d4d4;-moz-border-radius:7px;-webkit-border-radius:7px;border-radius:7px;padding:1px;color:#fff;margin:0 8px 0 0;display:inline-block}.roundedspan>span{padding:7px 7px;min-width:15px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;text-align:center;display:inline-block}.roundedspan>span .hovertext{display:none}.roundedspan>span:hover .hovertext{display:inline}.mediaborder{padding:5px;border:solid 5px #edf3f4}.actionbuttons{width:auto;height:36px;line-height:36px;background:#8dc63f;-moz-border-radius:32px;-webkit-border-radius:32px;border-radius:32px;color:white;cursor:pointer;font-size:13px;font-weight:bold;padding:0 15px;border:0;margin:5px 0}.errors{-moz-border-radius:16px;-webkit-border-radius:16px;border-radius:16px;border:solid #e35351 3px;clear:both;width:90%;height:auto;line-height:16px;padding:7px 0;font-weight:bold;font-size:13px;text-align:center}.errors li{list-style:none;border:0}.book-detail{float:left;width:100%;clear:both;display:block}#book-detail-img{float:left;margin-right:10px;width:151px}#book-detail-img img{padding:5px;border:solid 5px #edf3f4}.book-detail-info{float:left;width:309px}.book-detail-info h2.book-name,.book-detail-info h3.book-author,.book-detail-info h3.book-year{padding:0;margin:0;line-height:normal}.book-detail-info h2.book-name{font-size:19px;text-transform:capitalize;font-weight:bold;color:#3d4e53}.book-detail-info h3.book-author,.book-detail-info h3.book-year{font-size:13px;font-weight:normal;color:#6994a3}.book-detail-info>div{width:100%;clear:both;display:block;overflow:hidden;border-top:1px solid #edf3f4;padding:10px 0}.book-detail-info>div.layout{border:0;padding:0}.book-detail-info>div.layout div.pubinfo{float:left;width:auto;padding-bottom:7px}.book-detail-info .btn_wishlist span{text-align:right}.book-detail-info .find-book label{float:left;line-height:31px}.book-detail-info .find-link{float:right}.book-detail-info .find-link img{padding:2px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.book-detail-info .pledged-info{padding:10px 0;position:relative}.book-detail-info .pledged-info.noborder{border-top:0;padding-top:0}.book-detail-info .pledged-info .campaign-status-info{float:left;width:50%;margin-top:13px}.book-detail-info .pledged-info .campaign-status-info span{font-size:15px;color:#6994a3;font-weight:bold}.book-detail-info .thermometer{-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;border:solid 2px #d6dde0;width:291px;padding:7px;position:relative;overflow:visible;background:-webkit-gradient(linear,left top,right top,from(#8dc63f),to(#cf6944));background:-webkit-linear-gradient(left,#cf6944,#8dc63f);background:-moz-linear-gradient(left,#cf6944,#8dc63f);background:-ms-linear-gradient(left,#cf6944,#8dc63f);background:-o-linear-gradient(left,#cf6944,#8dc63f);background:linear-gradient(left,#cf6944,#8dc63f);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='@alert',endColorstr='@call-to-action');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr='@alert', endColorstr='@call-to-action')"}.book-detail-info .thermometer.successful{border-color:#8ac3d7;background:#edf3f4}.book-detail-info .thermometer .cover{position:absolute;right:0;-moz-border-radius:0 10px 10px 0;-webkit-border-radius:0 10px 10px 0;border-radius:0 10px 10px 0;width:50px;height:14px;margin-top:-7px;background:#f3f5f6}.book-detail-info .thermometer span{display:none}.book-detail-info .thermometer:hover span{display:block;position:absolute;z-index:200;right:0;top:-7px;font-size:19px;color:#6994a3;background:white;border:2px solid #d6dde0;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;padding:5px}.book-detail-info .explainer span.explanation{display:none}.book-detail-info .explainer:hover span.explanation{display:block;position:absolute;z-index:200;right:0;top:12px;font-size:13px;font-weight:normal;color:#3d4e53;background:white;border:2px solid #d6dde0;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;padding:5px}.book-detail-info .status{position:absolute;top:50%;right:0;height:25px;margin-top:-12px}.header-text{height:36px;line-height:36px;display:block;text-decoration:none;font-weight:bold;letter-spacing:-0.05em}.panelborders{border-width:1px 0;border-style:solid none;border-color:#fff}.roundedspan{border:1px solid #d4d4d4;-moz-border-radius:7px;-webkit-border-radius:7px;border-radius:7px;padding:1px;color:#fff;margin:0 8px 0 0;display:inline-block}.roundedspan>span{padding:7px 7px;min-width:15px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;text-align:center;display:inline-block}.roundedspan>span .hovertext{display:none}.roundedspan>span:hover .hovertext{display:inline}.mediaborder{padding:5px;border:solid 5px #edf3f4}.actionbuttons{width:auto;height:36px;line-height:36px;background:#8dc63f;-moz-border-radius:32px;-webkit-border-radius:32px;border-radius:32px;color:white;cursor:pointer;font-size:13px;font-weight:bold;padding:0 15px;border:0;margin:5px 0}.errors{-moz-border-radius:16px;-webkit-border-radius:16px;border-radius:16px;border:solid #e35351 3px;clear:both;width:90%;height:auto;line-height:16px;padding:7px 0;font-weight:bold;font-size:13px;text-align:center}.errors li{list-style:none;border:0}ul.social a:hover{text-decoration:none}ul.social li{padding:5px 0 5px 30px!important;height:28px;line-height:28px!important;margin:0!important;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}ul.social li.facebook{background:url("/static/images/icons/facebook.png") 10px center no-repeat;cursor:pointer}ul.social li.facebook span{padding-left:10px}ul.social li.facebook:hover{background:#8dc63f url("/static/images/icons/facebook-hover.png") 10px center no-repeat}ul.social li.facebook:hover span{color:#fff}ul.social li.twitter{background:url("/static/images/icons/twitter.png") 10px center no-repeat;cursor:pointer}ul.social li.twitter span{padding-left:10px}ul.social li.twitter:hover{background:#8dc63f url("/static/images/icons/twitter-hover.png") 10px center no-repeat}ul.social li.twitter:hover span{color:#fff}ul.social li.email{background:url("/static/images/icons/email.png") 10px center no-repeat;cursor:pointer}ul.social li.email span{padding-left:10px}ul.social li.email:hover{background:#8dc63f url("/static/images/icons/email-hover.png") 10px center no-repeat}ul.social li.email:hover span{color:#fff}ul.social li.embed{background:url("/static/images/icons/embed.png") 10px center no-repeat;cursor:pointer}ul.social li.embed span{padding-left:10px}ul.social li.embed:hover{background:#8dc63f url("/static/images/icons/embed-hover.png") 10px center no-repeat}ul.social li.embed:hover span{color:#fff}#js-page-wrap{overflow:hidden}#main-container{margin-top:20px}#js-leftcol .jsmodule,.pledge.jsmodule{margin-bottom:10px}#js-leftcol .jsmodule.rounded .jsmod-content,.pledge.jsmodule.rounded .jsmod-content{-moz-border-radius:20px;-webkit-border-radius:20px;border-radius:20px;background:#edf3f4;color:#3d4e53;padding:10px 20px;font-weight:bold;border:0;margin:0;line-height:16px}#js-leftcol .jsmodule.rounded .jsmod-content.ACTIVE,.pledge.jsmodule.rounded .jsmod-content.ACTIVE{background:#8dc63f;color:white;font-size:19px;font-weight:normal;line-height:20px}#js-leftcol .jsmodule.rounded .jsmod-content.No.campaign.yet,.pledge.jsmodule.rounded .jsmod-content.No.campaign.yet{background:#e18551;color:white}#js-leftcol .jsmodule.rounded .jsmod-content span,.pledge.jsmodule.rounded .jsmod-content span{display:inline-block;vertical-align:middle}#js-leftcol .jsmodule.rounded .jsmod-content span.spacer,.pledge.jsmodule.rounded .jsmod-content span.spacer{visibility:none}#js-leftcol .jsmodule.rounded .jsmod-content span.findtheungluers,.pledge.jsmodule.rounded .jsmod-content span.findtheungluers{cursor:pointer}.jsmodule.pledge{float:left;margin-left:10px}#js-slide .jsmodule{width:660px!important}a{color:#3d4e53}#js-search{margin:0 15px 0 15px!important}.alert>.errorlist{list-style-type:none;font-size:15px;border:0;text-align:left;font-weight:normal;font-size:13px}.alert>.errorlist>li{margin-bottom:14px}.alert>.errorlist .errorlist{margin-top:7px}.alert>.errorlist .errorlist li{width:auto;height:auto;padding-left:32px;padding-right:32px;font-size:13px}#js-maincol{float:left;width:470px;margin:0 10px}#js-maincol div#content-block{background:0;padding:0}.status{font-size:19px;color:#8dc63f}.add-wishlist,.add-wishlist-workpage,.remove-wishlist-workpage,.remove-wishlist,.on-wishlist,.create-account{float:right;cursor:pointer}.add-wishlist span,.add-wishlist-workpage span,.remove-wishlist-workpage span,.remove-wishlist span,.on-wishlist span,.create-account span{font-weight:normal;color:#3d4e53;text-transform:none;padding-left:20px}.add-wishlist span.on-wishlist,.add-wishlist-workpage span.on-wishlist,.remove-wishlist-workpage span.on-wishlist,.remove-wishlist span.on-wishlist,.on-wishlist span.on-wishlist,.create-account span.on-wishlist{background:url("/static/images/checkmark_small.png") left center no-repeat;cursor:default}.btn_wishlist .add-wishlist span,.add-wishlist-workpage span,.create-account span{background:url("/static/images/booklist/add-wishlist.png") left center no-repeat}.remove-wishlist-workpage span,.remove-wishlist span{background:url("/static/images/booklist/remove-wishlist-blue.png") left center no-repeat}div#content-block-content{padding-left:5px}div#content-block-content a{color:#6994a3}div#content-block-content #tabs-1 img{padding:5px;border:solid 5px #edf3f4}div#content-block-content #tabs-3{margin-left:-5px}.tabs-content{padding-right:5px}.tabs-content iframe{padding:5px;border:solid 5px #edf3f4}.tabs-content form{margin-left:-5px}.tabs-content .clearfix{margin-bottom:10px;border-bottom:2px solid #d6dde0}.work_supporter{height:auto;min-height:50px;margin-top:5px;vertical-align:middle}.work_supporter_avatar{float:left;margin-right:5px}.work_supporter_avatar img{-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.work_supporter_name{height:50px;line-height:50px;float:left}.work_supporter_nocomment{height:50px;margin-top:5px;vertical-align:middle;min-width:235px;float:left}.show_supporter_contact_form{display:block;margin-left:5px;float:right}.supporter_contact_form{display:none;margin-left:5px}.contact_form_result{display:block;margin-left:5px}.work_supporter_wide{display:block;height:65px;margin-top:5px;float:none;margin-left:5px}.info_for_managers{display:block}.show_supporter_contact_form{cursor:pointer;opacity:.5}.show_supporter_contact_form:hover{cursor:pointer;opacity:1}.official{border:3px #8ac3d7 solid;padding:3px;margin-left:-5px}.editions div{float:left;padding-bottom:5px;margin-bottom:5px}.editions .image{width:60px;overflow:hidden}.editions .metadata{display:block;overflow:hidden;margin-left:5px}.editions a:hover{text-decoration:underline}.show_more_edition,.show_more_ebooks{cursor:pointer}.show_more_edition{text-align:right}.show_more_edition:hover{text-decoration:underline}.more_edition{display:none;clear:both;padding-bottom:10px;padding-left:60px}.more_ebooks{display:none}.show_more_ebooks:hover{text-decoration:underline}#js-rightcol .add-wishlist,#js-rightcol .on-wishlist,#js-rightcol .create-account{float:none}#js-rightcol .on-wishlist{margin-left:20px}#js-rightcol,#pledge-rightcol{float:right;width:235px;margin-bottom:20px}#js-rightcol h3.jsmod-title,#pledge-rightcol h3.jsmod-title{background:#a7c1ca;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;padding:10px;height:auto;font-style:normal;font-size:15px;margin:0 0 10px 0;color:white}#js-rightcol h3.jsmod-title span,#pledge-rightcol h3.jsmod-title span{padding:0;color:#fff;font-style:normal;height:22px;line-height:22px}#js-rightcol .jsmodule,#pledge-rightcol .jsmodule{margin-bottom:10px}#js-rightcol .jsmodule a:hover,#pledge-rightcol .jsmodule a:hover{text-decoration:none}#pledge-rightcol{margin-top:7px}.js-rightcol-pad{border:1px solid #d6dde0;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;padding:10px}#widgetcode{display:none;border:1px solid #d6dde0;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;padding:10px}ul.support li{border-bottom:1px solid #d6dde0;padding:10px 5px 10px 10px;background:url("/static/images/icons/pledgearrow.png") 98% center no-repeat}ul.support li.no_link{background:0}ul.support li.last{border-bottom:0}ul.support li span{display:block;padding-right:10px}ul.support li span.menu-item-price{font-size:19px;float:left;display:inline;margin-bottom:3px}ul.support li span.menu-item-desc{float:none;clear:both;font-size:15px;font-weight:normal;line-height:19.5px}ul.support li:hover{color:#fff;background:#8dc63f url("/static/images/icons/pledgearrow-hover.png") 98% center no-repeat}ul.support li:hover a{color:#fff;text-decoration:none}ul.support li:hover.no_link{background:#fff;color:#8dc63f}.you_pledged{float:left;line-height:21px;font-weight:normal;color:#3d4e53;padding-left:20px;background:url("/static/images/checkmark_small.png") left center no-repeat}.thank-you{font-size:19px;font-weight:bold;margin:20px auto} \ No newline at end of file +.header-text{height:36px;line-height:36px;display:block;text-decoration:none;font-weight:bold;letter-spacing:-0.05em}.panelborders{border-width:1px 0;border-style:solid none;border-color:#fff}.roundedspan{border:1px solid #d4d4d4;-moz-border-radius:7px;-webkit-border-radius:7px;border-radius:7px;padding:1px;color:#fff;margin:0 8px 0 0;display:inline-block}.roundedspan>span{padding:7px 7px;min-width:15px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;text-align:center;display:inline-block}.roundedspan>span .hovertext{display:none}.roundedspan>span:hover .hovertext{display:inline}.mediaborder{padding:5px;border:solid 5px #edf3f4}.actionbuttons{width:auto;height:36px;line-height:36px;background:#8dc63f;-moz-border-radius:32px;-webkit-border-radius:32px;border-radius:32px;color:white;cursor:pointer;font-size:13px;font-weight:bold;padding:0 15px;border:0;margin:5px 0}.errors{-moz-border-radius:16px;-webkit-border-radius:16px;border-radius:16px;border:solid #e35351 3px;clear:both;width:90%;height:auto;line-height:16px;padding:7px 0;font-weight:bold;font-size:13px;text-align:center}.errors li{list-style:none;border:0}#tabs{border-bottom:4px solid #6994a3;clear:both;float:left;margin-top:10px;width:100%}#tabs ul.book-list-view{margin-bottom:4px!important}#tabs-1,#tabs-2,#tabs-3,#tabs-4{display:none}#tabs-1.active,#tabs-2.active,#tabs-3.active,#tabs-4.active{display:inherit}#tabs-2 textarea{width:95%}ul.tabs{float:left;padding:0;margin:0;list-style:none;width:100%}ul.tabs li{float:left;height:46px;line-height:20px;padding-right:2px;width:116px;background:0;margin:0;padding:0 2px 0 0}ul.tabs li.tabs4{padding-right:0}ul.tabs li a{height:41px;line-height:18px;display:block;text-align:center;padding:0 10px;min-width:80px;-moz-border-radius:7px 7px 0 0;-webkit-border-radius:7px 7px 0 0;border-radius:7px 7px 0 0;background:#d6dde0;color:#3d4e53;padding-top:5px}ul.tabs li a:hover{text-decoration:none}ul.tabs li a div{padding-top:8px}ul.tabs li a:hover,ul.tabs li.active a{background:#6994a3;color:#fff}.header-text{height:36px;line-height:36px;display:block;text-decoration:none;font-weight:bold;letter-spacing:-0.05em}.panelborders{border-width:1px 0;border-style:solid none;border-color:#fff}.roundedspan{border:1px solid #d4d4d4;-moz-border-radius:7px;-webkit-border-radius:7px;border-radius:7px;padding:1px;color:#fff;margin:0 8px 0 0;display:inline-block}.roundedspan>span{padding:7px 7px;min-width:15px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;text-align:center;display:inline-block}.roundedspan>span .hovertext{display:none}.roundedspan>span:hover .hovertext{display:inline}.mediaborder{padding:5px;border:solid 5px #edf3f4}.actionbuttons{width:auto;height:36px;line-height:36px;background:#8dc63f;-moz-border-radius:32px;-webkit-border-radius:32px;border-radius:32px;color:white;cursor:pointer;font-size:13px;font-weight:bold;padding:0 15px;border:0;margin:5px 0}.errors{-moz-border-radius:16px;-webkit-border-radius:16px;border-radius:16px;border:solid #e35351 3px;clear:both;width:90%;height:auto;line-height:16px;padding:7px 0;font-weight:bold;font-size:13px;text-align:center}.errors li{list-style:none;border:0}.book-detail{float:left;width:100%;clear:both;display:block}#book-detail-img{float:left;margin-right:10px;width:151px}#book-detail-img img{padding:5px;border:solid 5px #edf3f4}.book-detail-info{float:left;width:309px}.book-detail-info h2.book-name,.book-detail-info h3.book-author,.book-detail-info h3.book-year{padding:0;margin:0;line-height:normal}.book-detail-info h2.book-name{font-size:19px;text-transform:capitalize;font-weight:bold;color:#3d4e53}.book-detail-info h3.book-author,.book-detail-info h3.book-year{font-size:13px;font-weight:normal;color:#6994a3}.book-detail-info>div{width:100%;clear:both;display:block;overflow:hidden;border-top:1px solid #edf3f4;padding:10px 0}.book-detail-info>div.layout{border:0;padding:0}.book-detail-info>div.layout div.pubinfo{float:left;width:auto;padding-bottom:7px}.book-detail-info .btn_wishlist span{text-align:right}.book-detail-info .find-book label{float:left;line-height:31px}.book-detail-info .find-link{float:right}.book-detail-info .find-link img{padding:2px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.book-detail-info .pledged-info{padding:10px 0;position:relative}.book-detail-info .pledged-info.noborder{border-top:0;padding-top:0}.book-detail-info .pledged-info .campaign-status-info{float:left;width:50%;margin-top:13px}.book-detail-info .pledged-info .campaign-status-info span{font-size:15px;color:#6994a3;font-weight:bold}.book-detail-info .thermometer{-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;border:solid 2px #d6dde0;width:291px;padding:7px;position:relative;overflow:visible;background:-webkit-gradient(linear,left top,right top,from(#8dc63f),to(#cf6944));background:-webkit-linear-gradient(left,#cf6944,#8dc63f);background:-moz-linear-gradient(left,#cf6944,#8dc63f);background:-ms-linear-gradient(left,#cf6944,#8dc63f);background:-o-linear-gradient(left,#cf6944,#8dc63f);background:linear-gradient(left,#cf6944,#8dc63f);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='@alert',endColorstr='@call-to-action');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr='@alert', endColorstr='@call-to-action')"}.book-detail-info .thermometer.successful{border-color:#8ac3d7;background:#edf3f4}.book-detail-info .thermometer .cover{position:absolute;right:0;-moz-border-radius:0 10px 10px 0;-webkit-border-radius:0 10px 10px 0;border-radius:0 10px 10px 0;width:50px;height:14px;margin-top:-7px;background:#f3f5f6}.book-detail-info .thermometer span{display:none}.book-detail-info .thermometer:hover span{display:block;position:absolute;z-index:200;right:0;top:-7px;font-size:19px;color:#6994a3;background:white;border:2px solid #d6dde0;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;padding:5px}.book-detail-info .explainer span.explanation{display:none}.book-detail-info .explainer:hover span.explanation{display:block;position:absolute;z-index:200;right:0;top:12px;font-size:13px;font-weight:normal;color:#3d4e53;background:white;border:2px solid #d6dde0;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;padding:5px}.book-detail-info .status{position:absolute;top:50%;right:0;height:25px;margin-top:-12px}.header-text{height:36px;line-height:36px;display:block;text-decoration:none;font-weight:bold;letter-spacing:-0.05em}.panelborders{border-width:1px 0;border-style:solid none;border-color:#fff}.roundedspan{border:1px solid #d4d4d4;-moz-border-radius:7px;-webkit-border-radius:7px;border-radius:7px;padding:1px;color:#fff;margin:0 8px 0 0;display:inline-block}.roundedspan>span{padding:7px 7px;min-width:15px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;text-align:center;display:inline-block}.roundedspan>span .hovertext{display:none}.roundedspan>span:hover .hovertext{display:inline}.mediaborder{padding:5px;border:solid 5px #edf3f4}.actionbuttons{width:auto;height:36px;line-height:36px;background:#8dc63f;-moz-border-radius:32px;-webkit-border-radius:32px;border-radius:32px;color:white;cursor:pointer;font-size:13px;font-weight:bold;padding:0 15px;border:0;margin:5px 0}.errors{-moz-border-radius:16px;-webkit-border-radius:16px;border-radius:16px;border:solid #e35351 3px;clear:both;width:90%;height:auto;line-height:16px;padding:7px 0;font-weight:bold;font-size:13px;text-align:center}.errors li{list-style:none;border:0}ul.social a:hover{text-decoration:none}ul.social li{padding:5px 0 5px 30px!important;height:28px;line-height:28px!important;margin:0!important;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}ul.social li.facebook{background:url("/static/images/icons/facebook.png") 10px center no-repeat;cursor:pointer}ul.social li.facebook span{padding-left:10px}ul.social li.facebook:hover{background:#8dc63f url("/static/images/icons/facebook-hover.png") 10px center no-repeat}ul.social li.facebook:hover span{color:#fff}ul.social li.twitter{background:url("/static/images/icons/twitter.png") 10px center no-repeat;cursor:pointer}ul.social li.twitter span{padding-left:10px}ul.social li.twitter:hover{background:#8dc63f url("/static/images/icons/twitter-hover.png") 10px center no-repeat}ul.social li.twitter:hover span{color:#fff}ul.social li.email{background:url("/static/images/icons/email.png") 10px center no-repeat;cursor:pointer}ul.social li.email span{padding-left:10px}ul.social li.email:hover{background:#8dc63f url("/static/images/icons/email-hover.png") 10px center no-repeat}ul.social li.email:hover span{color:#fff}ul.social li.embed{background:url("/static/images/icons/embed.png") 10px center no-repeat;cursor:pointer}ul.social li.embed span{padding-left:10px}ul.social li.embed:hover{background:#8dc63f url("/static/images/icons/embed-hover.png") 10px center no-repeat}ul.social li.embed:hover span{color:#fff}#js-page-wrap{overflow:hidden}#main-container{margin-top:20px}#js-leftcol .jsmodule,.pledge.jsmodule{margin-bottom:10px}#js-leftcol .jsmodule.rounded .jsmod-content,.pledge.jsmodule.rounded .jsmod-content{-moz-border-radius:20px;-webkit-border-radius:20px;border-radius:20px;background:#edf3f4;color:#3d4e53;padding:10px 20px;font-weight:bold;border:0;margin:0;line-height:16px}#js-leftcol .jsmodule.rounded .jsmod-content.ACTIVE,.pledge.jsmodule.rounded .jsmod-content.ACTIVE{background:#8dc63f;color:white;font-size:19px;font-weight:normal;line-height:20px}#js-leftcol .jsmodule.rounded .jsmod-content.No.campaign.yet,.pledge.jsmodule.rounded .jsmod-content.No.campaign.yet{background:#e18551;color:white}#js-leftcol .jsmodule.rounded .jsmod-content span,.pledge.jsmodule.rounded .jsmod-content span{display:inline-block;vertical-align:middle}#js-leftcol .jsmodule.rounded .jsmod-content span.spacer,.pledge.jsmodule.rounded .jsmod-content span.spacer{visibility:none}#js-leftcol .jsmodule.rounded .jsmod-content span.findtheungluers,.pledge.jsmodule.rounded .jsmod-content span.findtheungluers{cursor:pointer}.jsmodule.pledge{float:left;margin-left:10px}#js-slide .jsmodule{width:660px!important}a{color:#3d4e53}#js-search{margin:0 15px 0 15px!important}.alert>.errorlist{list-style-type:none;font-size:15px;border:0;text-align:left;font-weight:normal;font-size:13px}.alert>.errorlist>li{margin-bottom:14px}.alert>.errorlist .errorlist{margin-top:7px}.alert>.errorlist .errorlist li{width:auto;height:auto;padding-left:32px;padding-right:32px;font-size:13px}#js-maincol{float:left;width:470px;margin:0 10px}#js-maincol div#content-block{background:0;padding:0}.status{font-size:19px;color:#8dc63f}.add-wishlist,.add-wishlist-workpage,.remove-wishlist-workpage,.remove-wishlist,.on-wishlist,.create-account{float:right;cursor:pointer}.add-wishlist span,.add-wishlist-workpage span,.remove-wishlist-workpage span,.remove-wishlist span,.on-wishlist span,.create-account span{font-weight:normal;color:#3d4e53;text-transform:none;padding-left:20px}.add-wishlist span.on-wishlist,.add-wishlist-workpage span.on-wishlist,.remove-wishlist-workpage span.on-wishlist,.remove-wishlist span.on-wishlist,.on-wishlist span.on-wishlist,.create-account span.on-wishlist{background:url("/static/images/checkmark_small.png") left center no-repeat;cursor:default}.btn_wishlist .add-wishlist span,.add-wishlist-workpage span,.create-account span{background:url("/static/images/booklist/add-wishlist.png") left center no-repeat}.remove-wishlist-workpage span,.remove-wishlist span{background:url("/static/images/booklist/remove-wishlist-blue.png") left center no-repeat}div#content-block-content{padding-left:5px}div#content-block-content a{color:#6994a3}div#content-block-content #tabs-1 img{padding:5px;border:solid 5px #edf3f4}div#content-block-content #tabs-3{margin-left:-5px}.tabs-content{padding-right:5px}.tabs-content iframe{padding:5px;border:solid 5px #edf3f4}.tabs-content form{margin-left:-5px}.tabs-content .clearfix{margin-bottom:10px;border-bottom:2px solid #d6dde0}.work_supporter{height:auto;min-height:50px;margin-top:5px;vertical-align:middle}.work_supporter_avatar{float:left;margin-right:5px}.work_supporter_avatar img{-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.work_supporter_name{height:50px;line-height:50px;float:left}.work_supporter_nocomment{height:50px;margin-top:5px;vertical-align:middle;min-width:235px;float:left}.show_supporter_contact_form{display:block;margin-left:5px;float:right}.supporter_contact_form{display:none;margin-left:5px}.contact_form_result{display:block;margin-left:5px}.work_supporter_wide{display:block;height:65px;margin-top:5px;float:none;margin-left:5px}.info_for_managers{display:block}.show_supporter_contact_form{cursor:pointer;opacity:.5}.show_supporter_contact_form:hover{cursor:pointer;opacity:1}.official{border:3px #8ac3d7 solid;padding:3px;margin-left:-5px}.editions div{float:left;padding-bottom:5px;margin-bottom:5px}.editions .image{width:60px;overflow:hidden}.editions .metadata{display:block;overflow:hidden;margin-left:5px}.editions a:hover{text-decoration:underline}.show_more_edition,.show_more_ebooks{cursor:pointer}.show_more_edition{text-align:right}.show_more_edition:hover{text-decoration:underline}.more_edition{display:none;clear:both;padding-bottom:10px;padding-left:60px}.more_ebooks{display:none}.show_more_ebooks:hover{text-decoration:underline}#js-rightcol .add-wishlist,#js-rightcol .on-wishlist,#js-rightcol .create-account{float:none}#js-rightcol .on-wishlist{margin-left:20px}#js-rightcol,#pledge-rightcol{float:right;width:235px;margin-bottom:20px}#js-rightcol h3.jsmod-title,#pledge-rightcol h3.jsmod-title{background:#a7c1ca;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;padding:10px;height:auto;font-style:normal;font-size:15px;margin:0 0 10px 0;color:white}#js-rightcol h3.jsmod-title span,#pledge-rightcol h3.jsmod-title span{padding:0;color:#fff;font-style:normal;height:22px;line-height:22px}#js-rightcol .jsmodule,#pledge-rightcol .jsmodule{margin-bottom:10px}#js-rightcol .jsmodule a:hover,#pledge-rightcol .jsmodule a:hover{text-decoration:none}#pledge-rightcol{margin-top:7px}.js-rightcol-pad{border:1px solid #d6dde0;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;padding:10px}#widgetcode{display:none;border:1px solid #d6dde0;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;padding:10px}ul.support li{border-bottom:1px solid #d6dde0;padding:10px 5px 10px 10px;background:url("/static/images/icons/pledgearrow.png") 98% center no-repeat}ul.support li.no_link{background:0}ul.support li.last{border-bottom:0}ul.support li span{display:block;padding-right:10px}ul.support li span.menu-item-price{font-size:19px;float:left;display:inline;margin-bottom:3px}ul.support li span.menu-item-desc{float:none;clear:both;font-size:15px;font-weight:normal;line-height:19.5px}ul.support li:hover{color:#fff;background:#8dc63f url("/static/images/icons/pledgearrow-hover.png") 98% center no-repeat}ul.support li:hover a{color:#fff;text-decoration:none}ul.support li:hover.no_link{background:#fff;color:#8dc63f}.you_pledged{float:left;line-height:21px;font-weight:normal;color:#3d4e53;padding-left:20px;background:url("/static/images/checkmark_small.png") left center no-repeat}.thank-you{font-size:19px;font-weight:bold;margin:20px auto}div#libtools{border:1px solid #d6dde0;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;margin-left:0;margin-top:1em;padding:10px}div#libtools p{margin-top:0;margin-bottom:0}div#libtools span{margin-top:0;margin-left:.5em;display:inline-block}div#libtools input[type="submit"]{margin-left:4em} \ No newline at end of file diff --git a/static/less/book_list.less b/static/less/book_list.less index 0d6a6450..84a3b317 100755 --- a/static/less/book_list.less +++ b/static/less/book_list.less @@ -309,7 +309,7 @@ ul.navigation li a:hover, ul.navigation li.active a { } div#content-block-content { - padding-bottom: 100px; + padding-bottom: 10px; } .listview.panelback, .listview.panelback div { @@ -327,4 +327,20 @@ div#content-block-content { .nobold { font-weight: normal; +} + +div#libtools { + margin-left: 15px; + margin-bottom: 1em; + border:1px solid @blue-grey; + border-radius: 10px; + padding: 10px; + + p { + margin-top: 0px; + } + div { + margin-top: 0px; + margin-left: 2em ; + } } \ No newline at end of file diff --git a/static/less/campaign2.less b/static/less/campaign2.less index 2c1e14f6..2c35e1b3 100644 --- a/static/less/campaign2.less +++ b/static/less/campaign2.less @@ -419,4 +419,25 @@ ul.support li { font-size: @font-size-header; font-weight: bold; margin: 20px auto; +} + +div#libtools { + border:1px solid @blue-grey; + .one-border-radius(10px); + padding:10px; + margin-left: 0px; + margin-top: 1em; + padding: 10px; + p { + margin-top: 0px; + margin-bottom: 0px; + } + span { + margin-top: 0px; + margin-left: 0.5em ; + display: inline-block; + } + input[type="submit"]{ + margin-left: 4em; + } } \ No newline at end of file From e1409ceac1c06ebeefa01f7fda480cb621d5cfa5 Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 27 Oct 2014 16:57:35 -0400 Subject: [PATCH 10/12] redo marc file upload --- admin.py | 14 ++- frontend/forms.py | 9 -- frontend/templates/edition_display.html | 13 +- frontend/templates/libraries.html | 8 +- frontend/templates/marc.html | 4 +- frontend/templates/marc/upload.html | 62 ++++++++++ frontend/templates/marcungluify.html | 42 ------- frontend/urls.py | 2 - frontend/views.py | 35 ------ marc/forms.py | 22 ++++ marc/load.py | 9 ++ marc/management/commands/migrate_records.py | 11 +- ...cord_user__add_field_marcrecord_created.py | 116 ++++++++++++++++++ marc/models.py | 18 ++- marc/urls.py | 4 + marc/views.py | 44 ++++++- 16 files changed, 295 insertions(+), 118 deletions(-) create mode 100644 frontend/templates/marc/upload.html delete mode 100644 frontend/templates/marcungluify.html create mode 100644 marc/forms.py create mode 100644 marc/migrations/0002_auto__add_field_marcrecord_user__add_field_marcrecord_created.py diff --git a/admin.py b/admin.py index bf72172c..e1e16bd4 100644 --- a/admin.py +++ b/admin.py @@ -35,6 +35,7 @@ regluit imports """ from regluit import payment from regluit.core import models +from regluit.marc.models import MARCRecord from regluit.core.lookups import ( PublisherNameLookup, WorkLookup, @@ -207,12 +208,17 @@ class MARCRecordAdminForm(forms.ModelForm): widget=AutoCompleteSelectWidget(EditionLookup), required=True, ) - + user = AutoCompleteSelectField( + OwnerLookup, + widget=AutoCompleteSelectWidget(OwnerLookup), + required=True, + ) class Meta(object): - model = models.MARCRecord + model = MARCRecord class MARCRecordAdmin(ModelAdmin): - list_display = ('edition',) + list_display = ('edition', 'user') + date_hierarchy = 'created' form = MARCRecordAdminForm admin_site = RegluitAdmin("Admin") @@ -238,7 +244,7 @@ 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.MARCRecord, MARCRecordAdmin) +admin_site.register(MARCRecord, MARCRecordAdmin) # payments diff --git a/frontend/forms.py b/frontend/forms.py index e1945c06..527d00ad 100644 --- a/frontend/forms.py +++ b/frontend/forms.py @@ -770,15 +770,6 @@ class PressForm(forms.ModelForm): class KindleEmailForm(forms.Form): kindle_email = forms.EmailField() -class MARCUngluifyForm(forms.Form): - edition = AutoCompleteSelectField( - EditionLookup, - label='Edition', - widget=AutoCompleteSelectWidget(EditionLookup), - required=True, - error_messages={'required': 'Please specify an edition.'}, - ) - file = forms.FileField(label='Download a MARCXML file from Library of Congress; then upload it here.') class MARCFormatForm(forms.ModelForm): class Meta: diff --git a/frontend/templates/edition_display.html b/frontend/templates/edition_display.html index 6a1b0d23..c5e3110f 100644 --- a/frontend/templates/edition_display.html +++ b/frontend/templates/edition_display.html @@ -43,17 +43,8 @@ See this edition on Google Books
    {% endif %} {% endif %} - {% if edition.MARCrecords.all %} - {% for record in edition.MARCrecords.all %} - Download {{record.link_target}} MARC record for this edition: (XML) (mrc)
    - {% endfor %} - {% else %} - {% if edition.ebooks.all %} - Download {{record.link_target}} MARC record for this edition: (XML) (mrc)
    - {% endif %} - {% endif %} - {% if user.is_staff %}{% if edition.ebooks.0 or edition.ebook_files.0 %} - Upload a MARC record for this edition.
    + {% if user.libpref %}{% if edition.ebooks.all or edition.ebook_files.all %} + Upload a MARC record for this edition.
    {% endif %} {% endif %} diff --git a/frontend/templates/libraries.html b/frontend/templates/libraries.html index 6d517a00..e20fb085 100644 --- a/frontend/templates/libraries.html +++ b/frontend/templates/libraries.html @@ -31,6 +31,8 @@ We’ve added "Buy-to-Unglue" campaigns to so that libraries can lend ebooks on
    Sign up. It's Free!
    Starting an account, for yourself or your library, is free. Use it to help us distribute unglued and ungluing ebooks.
    +
    Add free ebooks to your collection with our MARC records!
    +
    We're building a comprehensive database of freely licensed ebooks. We provide MARC records for these ebooks wherever we can. They can live in the catalog in your ILS; they link to an epub file in the Internet Archive. Of course, because of unglued ebooks' Creative Commons licenses, you're free to shift that epub to other formats, host the file on your own servers, and edit the MARC record accordingly. And if you want, help us improve our records.
    Become an Unglue.it participating library
    1. Make your library page on Unglue.it. It takes just a few minutes. It's the first step towards becoming an Unglue.it participating library.
    2. Review our LIBRARY LICENSE AGREEMENT.
    3. @@ -42,10 +44,6 @@ The library license gives download access to one library member at a time for 14
    Stay in touch.
    You can follow us on Twitter (@unglueit), Facebook, and our blog, and subscribe to our newsletter (1-2 emails per month).
    -
    Add unglued ebooks to your collection.
    -
    We provide MARC records for unglued ebooks wherever we can. We also strive to have MARC records available for all unglued ebooks. They can live in the catalog in your ILS; they link to an epub file in the Internet Archive. Of course, because of unglued ebooks' Creative Commons licenses, you're free to shift that epub to other formats, host the file on your own servers, and edit the MARC record accordingly.
    -
    Join our catalogers list.
    -
    We're able to get MARC records into OCLC and SkyRiver because of the support of catalogers like you. Want to help? Add yourself to the list. Usually all you'll need to do is modify an existing record for an earlier edition and upload the record. We'll supply you with the link and the unglued edition. We'll only contact you when there's an unglued book to be cataloged.
    Spread the word.
    There are social media sharing links on most pages on the site. There are some right here!
    Copy/paste this into your site:
    @@ -59,7 +57,7 @@ The library license gives download access to one library member at a time for 14
    Educate yourself and your patrons about ebook issues and Creative Commons licenses.
    Checkout limits, publishers who won't sell ebooks to libraries, DRM, companies tracking readers' behavior, library prices far in excess of consumer costs, incompatible technologies and formats, cataloging silos...you know why it's hard for you to deliver a seamless ereading experience to your patrons. Make sure they know, too. And make sure everyone knows how solutions, like Creative Commons licenses, can help libraries and readers while respecting copyright and protecting creators' rights.
    Support our active campaigns.
    -
    Ultimately ebooks can't be unglued unless authors and publishers are paid for their work. Many of our staunchest supporters are librarians. There are also several libraries which have supported campaigns, including Leddy Library (University of Windsor, Ontario); the University of Alberta library ; and the Z. Smith Reynolds library (Wake Forest University.
    +
    Ultimately ebooks can't be unglued unless authors and publishers are paid for their work. Many of our staunchest supporters are librarians. There are also several libraries which have supported campaigns, including Leddy Library (University of Windsor, Ontario); the University of Alberta library ; and the Z. Smith Reynolds library (Wake Forest University).
    Give feedback and ask questions.
    Want to know more? Need help? Have ideas for how we could improve the site or make it more library-friendly? Contact us at: libraries@gluejar.com.
    diff --git a/frontend/templates/marc.html b/frontend/templates/marc.html index 03de81de..b61f18fa 100644 --- a/frontend/templates/marc.html +++ b/frontend/templates/marc.html @@ -44,8 +44,8 @@ ul.local li {

    Go ahead: add unglued ebooks to your library catalog!

    -{% if request.user.is_staff %} -

    Hi, {{ request.user.username }}. Unglue.it staffers can also add new records.

    +{% if request.user.libpref %} +

    Hi, {{ request.user.username }}. Librarians can also add new records.

    {% endif %} {% if messages %} diff --git a/frontend/templates/marc/upload.html b/frontend/templates/marc/upload.html new file mode 100644 index 00000000..7546a671 --- /dev/null +++ b/frontend/templates/marc/upload.html @@ -0,0 +1,62 @@ +{% extends "basedocumentation.html" %} + +{% block title %}Add new MARC records{% endblock %} + +{% block extra_extra_head %} +{{ block.super }} + + + + +{% endblock %} + +{% block doccontent %} +{% if not request.user.libpref %} +

    If you want to load MARC records, you probably want to enable Unglue.it's librarian tools first!

    +{% endif %} +

    Adding MARC records to Unglue.it

    +{% if messages %} +
      + {% for message in messages %} + {{ message }} + {% endfor %} +
    +{% endif %} +{% if not form.initial.edition %} +

    Selecting an edition

    +

    +MARC records are associated with unglue.it edition records. Look for links in the "more" tab of any unglue.it work page that has a "Download" button on it. +{% endif %} +

    Adapting records from LoC

    +

    + For ebooks which have existing print editions cataloged in the Library of Congress, we can automatically convert those to Unglue.it ebook MARC records, which will then be automatically affixed to the ebook edition in our database and provided to ungluers. +

    + +

    + To get the XML record, search http://catalog.loc.gov/, select the print edition, and click on the permalink (will look something like http://lccn.loc.gov/63008219). This page has a MARCXML link. +

    + +

    + The record loader will automatically add links to all the unglued ebook files known to the Unglue.it database. Make sure those links are in the database before adding the record.

    + +

    Editing stub records from LoC

    +{% if form.initial.edition %} +The current records for this edition are here. They may be auto-generated stubs: +
    +
    + {% include 'marc_form.html' %} + + +
    +
    +{% endif %} + +

    + Edit the record, then upload it as a "record prepared for Unglue.it". The record loader strips 001, 003, 005, 006, 007, 856 and >900 fields, and adds them back in on download. +

    +

    Load a record

    +
    {% csrf_token %} + {{ form.as_p }} + +
    +{% endblock %} \ No newline at end of file diff --git a/frontend/templates/marcungluify.html b/frontend/templates/marcungluify.html deleted file mode 100644 index 623968d1..00000000 --- a/frontend/templates/marcungluify.html +++ /dev/null @@ -1,42 +0,0 @@ -{% extends "basedocumentation.html" %} - -{% block title %}Add new MARC records{% endblock %} - -{% block extra_extra_head %} -{{ block.super }} - - - - -{% endblock %} - -{% block doccontent %} -

    Make your unglued MARC records here

    -{% if messages %} -
      - {% for message in messages %} - {{ message }} - {% endfor %} -
    -{% endif %} -

    - For unglued ebooks which have existing print editions cataloged in the Library of Congress, we can automatically convert those to unglued ebook MARC records, which will then be automatically affixed to the ebook edition in our database and provided to ungluers. -

    - -

    - To get the XML record, search http://catalog.loc.gov/, select the print edition, and click on the permalink (will look something like http://lccn.loc.gov/63008219). This page has a MARCXML link. -

    - -

    - The robot cataloger will automatically add links to all the unglued ebook files known to the Unglue.it database. Make sure you have added those links to the database before creating the record. Likewise, it will autofill the ISBN, and you should have already provided that. -

    - -

    - If you want to add a MARC record for a non-unglued but publicly available book, create a MARCRecord instance in the admin interface and populate its fields there. (This assumes that the MARC record is for the freely available ebook version and exists in a linkable form. If it does not, upload it to S3 first, by analogy with the django-storages documentation, and use that link.) -

    - -
    {% csrf_token %} - {{ form.as_p }} - -
    -{% endblock %} \ No newline at end of file diff --git a/frontend/urls.py b/frontend/urls.py index 57b21147..a735c800 100644 --- a/frontend/urls.py +++ b/frontend/urls.py @@ -36,7 +36,6 @@ from regluit.frontend.views import ( ByPubView, kindle_config, send_to_kindle, - MARCUngluifyView, MARCConfigView, DownloadView, ) @@ -157,7 +156,6 @@ urlpatterns = patterns( url(r"^accounts/edit/kindle_config/(?P\d+)/$", "kindle_config", name="kindle_config_download"), url(r"^send_to_kindle/(?P\d+)/(?P\d)/$", "send_to_kindle", name="send_to_kindle"), url(r"^marc/$", direct_to_template, {'template': 'marc.html'}, name="marc"), - url(r"^marc/ungluify/$", staff_member_required(MARCUngluifyView.as_view()), name="MARCUngluify"), url(r"^accounts/edit/marc_config/$", login_required(MARCConfigView.as_view()), name="marc_config"), ) diff --git a/frontend/views.py b/frontend/views.py index d1123372..523448b0 100755 --- a/frontend/views.py +++ b/frontend/views.py @@ -17,7 +17,6 @@ from notification import models as notification from random import randint from re import sub from xml.etree import ElementTree as ET -from xml.sax import SAXParseException from tastypie.models import ApiKey ''' @@ -116,7 +115,6 @@ from regluit.frontend.forms import ( MsgForm, PressForm, KindleEmailForm, - MARCUngluifyForm, MARCFormatForm, DateCalculatorForm ) @@ -3110,39 +3108,6 @@ def work_marc(request, work_id): work = safe_get_work(work_id) return qs_marc_records(request, qs=[ work ]) -class MARCUngluifyView(FormView): - template_name = 'marcungluify.html' - form_class = MARCUngluifyForm - success_url = reverse_lazy('MARCUngluify') - - # allow a get param to specify the edition - def get_initial(self): - if self.request.method == 'GET': - edition = self.request.GET.get('edition',None) - if models.Edition.objects.filter(id=edition).count(): - edition = models.Edition.objects.filter(id=edition)[0] - if edition.ebooks.count() or edition.ebook_files.count(): - return {'edition':edition.id} - return {} - - def form_valid(self, form): - edition = form.cleaned_data['edition'] - - try: - marc.makemarc( - marcfile=self.request.FILES['file'], - edition=edition - ) - messages.success( - self.request, - "You have successfully added a MARC record. Hooray! Add another?" - ) - except SAXParseException: - messages.error( - self.request, - "Sorry, couldn't parse that file." - ) - return super(MARCUngluifyView,self).form_valid(form) class MARCConfigView(FormView): template_name = 'marc_config.html' diff --git a/marc/forms.py b/marc/forms.py new file mode 100644 index 00000000..09a7e951 --- /dev/null +++ b/marc/forms.py @@ -0,0 +1,22 @@ +from django import forms + +from selectable.forms import ( + AutoCompleteSelectWidget, + AutoCompleteSelectField +) + +from regluit.core.lookups import EditionLookup + +class MARCUploadForm(forms.Form): + edition = AutoCompleteSelectField( + EditionLookup, + label='Edition', + widget=AutoCompleteSelectWidget(EditionLookup), + required=True, + error_messages={'required': 'Please specify an edition.'}, + ) + file = forms.FileField(label='Select a MARCXML file.') + source = forms.ChoiceField(label='This file is ...', choices=[ + ( 'loc' , 'from Library of Congress (print)'), + ( 'raw' , 'prepared for Unglue.it'), + ]) diff --git a/marc/load.py b/marc/load.py index c9bdffe4..a22e7903 100644 --- a/marc/load.py +++ b/marc/load.py @@ -117,6 +117,15 @@ def stub(edition): return record +#load a with minimal change +def raw(marcfile, edition): + record = pymarc.parse_xml_to_array(marcfile)[0] + for field in record: + if field.tag in ('001', '003', '005', '006', '007', '856') or int( field.tag ) > 900: + record.remove_field(field) + add_stuff(record) + return record + #load a record from library of Congress def from_lc(marcfile, edition): diff --git a/marc/management/commands/migrate_records.py b/marc/management/commands/migrate_records.py index a627f9f1..79d94e51 100644 --- a/marc/management/commands/migrate_records.py +++ b/marc/management/commands/migrate_records.py @@ -5,12 +5,15 @@ from regluit.marc.models import MARCRecord from regluit.core.models import MARCRecord as OldRecord class Command(BaseCommand): - help = "migrate records from files" - args = "" + help = "migrate records (id' ' Date: Mon, 27 Oct 2014 19:11:44 -0400 Subject: [PATCH 11/12] rip out code --- core/marc.py | 367 ---------------------------------------------- frontend/forms.py | 2 +- frontend/urls.py | 4 +- frontend/views.py | 8 +- 4 files changed, 7 insertions(+), 374 deletions(-) delete mode 100644 core/marc.py diff --git a/core/marc.py b/core/marc.py deleted file mode 100644 index db199168..00000000 --- a/core/marc.py +++ /dev/null @@ -1,367 +0,0 @@ -""" -This takes a MARCXML filename as an argument and converts it into -MARC records for the unglued edition (in .xml and .mrc formats). -Consider it a catalogolem: http://commons.wikimedia.org/wiki/File:Arcimboldo_Librarian_Stokholm.jpg -Use the MARCXML file for the non-unglued edition from Library of Congress. -""" - -import pymarc -import logging -from copy import deepcopy -from datetime import datetime -from StringIO import StringIO - -from django.conf import settings -from django.core.files.storage import default_storage -from django.core.urlresolvers import reverse - -import regluit.core.cc as cc -from regluit.core import models - -def makestub(edition): - return makemarc(None, edition) - - -def makemarc(marcfile, edition): - logger = logging.getLogger(__name__) - - try: - license = edition.ebooks.all()[0].rights - ebf = None - except IndexError: - license = None - try: - ebf = edition.ebook_files.all()[0] - except IndexError: - # no record if no ebooks - return None - - logger.info("Making MARC records for edition %s " % edition) - - # save lccn for later (if there is one) before deleting it - print_lccn = None - if marcfile: - record = pymarc.parse_xml_to_array(marcfile)[0] - for lccn in record.get_fields('010'): - for validlccn in lccn.get_subfields('a'): - print_lccn = validlccn - fields_to_delete = [] - fields_to_delete += record.get_fields('001') - fields_to_delete += record.get_fields('003') - fields_to_delete += record.get_fields('005') - fields_to_delete += record.get_fields('006') - fields_to_delete += record.get_fields('007') - fields_to_delete += record.get_fields('010') - fields_to_delete += record.get_fields('040') - for field in fields_to_delete: - record.remove_field(field) - else: - record = pymarc.Record() - - - # create accession number and write 001 field - # (control field syntax is special) - if ebf: - (marc_record, created) = models.MARCRecord.objects.get_or_create(edition=edition,link_target='B2U') - else: - (marc_record, created) = models.MARCRecord.objects.get_or_create(edition=edition,link_target='UNGLUE') - field001 = pymarc.Field(tag='001', data=marc_record.accession) - record.add_ordered_field(field001) - - # add field indicating record originator - field003 = pymarc.Field(tag='003', data='UnglueIt') - record.add_ordered_field(field003) - - # update timestamp of record - now = datetime.now() - datestamp = now.strftime('%Y%m%d%H%M%S') + '.0' - field005 = pymarc.Field(tag='005', data=datestamp) - record.add_ordered_field(field005) - - # change 006, 007, 008 because this is an online resource - field006 = pymarc.Field( - tag='006', - data='m o d ' - ) - record.add_ordered_field(field006) - - field007 = pymarc.Field( - tag='007', - data='cr' - ) - record.add_ordered_field(field007) - - try: - field008 = record.get_fields('008')[0] - record.remove_field(field008) - old_field_value = field008.value() - new_field_value = old_field_value[:23] + 'o' + old_field_value[24:] - except IndexError: - # fun fun fun - new_field_value= now.strftime('%y%m%d')+'s' - if len(edition.publication_date)>3: - new_field_value += edition.publication_date[0:4] - else: - new_field_value += '||||' - new_field_value += '||||xx |||||o|||||||||||eng||' - field008 = pymarc.Field(tag='008', data=new_field_value) - record.add_ordered_field(field008) - - # add IBSN for ebook where applicable; relegate print ISBN to $z - isbn = '' - try: - isbn = edition.identifiers.filter(type='isbn')[0].value - except IndexError: - pass - try: - field020 = record.get_fields('020')[0] - print_isbn = field020.get_subfields('a')[0] - field020.delete_subfield('a') - if isbn: - field020.add_subfield('a', isbn) - field020.add_subfield('z', print_isbn) - except IndexError: - print_isbn = None - - # change 050 and 082 indicators because LOC is no longer responsible for these - # no easy indicator change function, so we'll just reconstruct the fields - try: - field050 = record.get_fields('050')[0] - field050_new = field050 - field050_new.indicators = [' ', '4'] - record.remove_field(field050) - record.add_ordered_field(field050_new) - except: - pass # if no 050 field, don't need to change indicator - - try: - field082 = record.get_fields('082')[0] - field082_new = field082 - field082_new.indicators = [' ', '4'] - record.remove_field(field082) - record.add_ordered_field(field082_new) - except: - pass # if no 082 field, don't need to change indicator - - # author name - try: - field100 = record.get_fields('100')[0] - except IndexError: - num_auths = edition.authors.count() - if num_auths: - field100 = pymarc.Field( - tag='100', - indicators = ['1', ' '], - subfields = [ - 'a', edition.authors.all()[0].last_name_first, - ] - ) - record.add_ordered_field(field100) - if num_auths > 1: - for auth in edition.authors.all()[1:]: - field = pymarc.Field( - tag='700', - indicators = ['1', ' '], - subfields = [ - 'a', auth.last_name_first, - 'e', 'joint author.', - ] - ) - record.add_ordered_field(field) - # add subfield to 245 indicating format - try: - field245 = record.get_fields('245')[0] - except IndexError: - field245 = pymarc.Field( - tag='245', - indicators = ['1', '0'], - subfields = [ - 'a', edition.title, - ] - ) - record.add_ordered_field(field245) - field245.add_subfield('a', '[electronic resource]') - - # publisher, date - try: - field260 = record.get_fields('260')[0] - except IndexError: - field260 = pymarc.Field( - tag='260', - indicators = [' ', ' '], - subfields = [ - 'b', edition.publisher_name.name, - 'c', unicode(edition.publication_date), - ] - ) - record.add_ordered_field(field260) - - # modify 300 field (physical description) - try: - field300 = record.get_fields('300')[0] - subfield_a = field300.get_subfields('a')[0] - if ( - subfield_a[-2:] == ' ;' or - subfield_a[-2:] == ' :' or - subfield_a[-2:] == ' +' - ): - subfield_a = subfield_a[:-2] - new300a = '1 online resource (' + subfield_a + ')' - if field300.get_subfields('b'): - new300a += ' :' - field300.delete_subfield('a') - field300.add_subfield('a', new300a) - field300.delete_subfield('c') - except: - pass - - if license: - # add 536 field (funding information) - if edition.unglued: - funding_info = 'The book is available as a free download thanks to the generous support of interested readers and organizations, who made donations using the crowd-funding website Unglue.it.' - else: - if edition.ebooks.all()[0].rights in cc.LICENSE_LIST: - funding_info = 'The book is available as a free download thanks to a Creative Commons license.' - else: - funding_info = 'The book is available as a free download because it is in the Public Domain.' - field536 = pymarc.Field( - tag='536', - indicators = [' ', ' '], - subfields = [ - 'a', funding_info, - ] - ) - record.add_ordered_field(field536) - - # add 540 field (terms governing use) - field540 = pymarc.Field( - tag='540', - indicators = [' ', ' '], - subfields = [ - 'a', dict(cc.CHOICES)[license], - 'u', dict(cc.GRANTS)[license], - ] - ) - record.add_ordered_field(field540) - - # add 588 field (source of description) - credit where credit is due - if print_lccn: - field588 = pymarc.Field( - tag='588', - indicators = [' ', ' '], - subfields = [ - 'a', 'Description based on print version record from the Library of Congress.', - ] - ) - record.add_ordered_field(field588) - - # add 776 field (related editions) - preserve pISBN, LCCN, OCLCnum - title = record.get_fields('245')[0].get_subfields('a')[0] - title = title.split('/')[0] - try: - oclcnum = edition.identifiers.filter(type='oclc')[0].value - except IndexError: - oclcnum = None - - subfields = ['i', 'Print version: ','t', title,] - - if print_isbn: - subfields.extend(['z', print_isbn]) - elif isbn: - subfields.extend(['z', isbn]) - if print_lccn: - subfields.extend(['w', '(DLC) ' + print_lccn, ]) - if oclcnum: - subfields.extend(['w', '(OCoLC) ' + oclcnum,]) - - field776 = pymarc.Field( - tag='776', - indicators = ['0', '8'], - subfields = subfields - ) - - record.add_ordered_field(field776) - """ - add 776 fields - indicators: 0 8 - '$i Print version: ' - $t Title. <--note space - $d is optional - $z pISBN goes here - harvest from 020 (was moved from $a to $z) - $w (DLC) LCCN_goes_here - harvest from 010 field before deletion - $w (OCoLC) OCLCnum_goes_here - harvest from identifiers db - """ - - # strip any 9XX fields (they're for local use) - for i in range(900, 1000): - fields = record.get_fields(str(i)) - for field in fields: - record.remove_field(field) - - # add 856 fields with links for each available file - # doing this out of order as it's the only thing that differs - # between direct-link and via-unglue.it versions - if not ebf: - # need deepcopy() because omg referential transparency! - record_direct = deepcopy(record) # 2 records for unglued stuff - - for format_tuple in settings.FORMATS: - format = format_tuple[0] - ebooks = edition.ebooks.filter(format=format) - if ebooks: - for book in ebooks: - field856 = pymarc.Field( - tag='856', - indicators = ['4', '0'], - subfields = [ - '3', format + ' version', - 'q', settings.CONTENT_TYPES[format], - 'u', book.url, - ] - ) - record_direct.add_ordered_field(field856) - - unglued_url = settings.BASE_URL_SECURE + reverse('download', args=[edition.work.id]) - field856_via = pymarc.Field( - tag='856', - indicators = ['4', '0'], - subfields = [ - 'u', unglued_url, - ] - ) - record.add_ordered_field(field856_via) - - if not ebf: - # this via_unglueit record needs its own accession number - field001 = record_direct.get_fields('001')[0] - record_direct.remove_field(field001) - (marc_record_direct, created) = models.MARCRecord.objects.get_or_create(edition=edition,link_target='DIRECT') - field001 = pymarc.Field(tag='001', data=marc_record_direct.accession) - record_direct.add_ordered_field(field001) - - # write the unglued MARCxml records - xmlrecord = pymarc.record_to_xml(record_direct) - xml_file = default_storage.open(marc_record_direct.xml_record, 'w') - xml_file.write(xmlrecord) - xml_file.close() - - # write the unglued .mrc records, then save to s3 - mrc_file = default_storage.open(marc_record_direct.mrc_record, 'w') - writer = pymarc.MARCWriter(mrc_file) - writer.write(record_direct) - mrc_file.close() - - xmlrecord = pymarc.record_to_xml(record) - xml_file = default_storage.open(marc_record.xml_record, 'w') - xml_file.write(xmlrecord) - xml_file.close() - - mrc_file = default_storage.open(marc_record.mrc_record, 'w') - writer = pymarc.MARCWriter(mrc_file) - writer.write(record) - mrc_file.close() - - return marc_record.pk diff --git a/frontend/forms.py b/frontend/forms.py index 527d00ad..ead23e9d 100644 --- a/frontend/forms.py +++ b/frontend/forms.py @@ -771,7 +771,7 @@ class KindleEmailForm(forms.Form): kindle_email = forms.EmailField() -class MARCFormatForm(forms.ModelForm): +class LibModeForm(forms.ModelForm): class Meta: model = Libpref fields = () diff --git a/frontend/urls.py b/frontend/urls.py index a735c800..b71aaf99 100644 --- a/frontend/urls.py +++ b/frontend/urls.py @@ -36,7 +36,7 @@ from regluit.frontend.views import ( ByPubView, kindle_config, send_to_kindle, - MARCConfigView, + LibModeView, DownloadView, ) @@ -156,7 +156,7 @@ urlpatterns = patterns( url(r"^accounts/edit/kindle_config/(?P\d+)/$", "kindle_config", name="kindle_config_download"), url(r"^send_to_kindle/(?P\d+)/(?P\d)/$", "send_to_kindle", name="send_to_kindle"), url(r"^marc/$", direct_to_template, {'template': 'marc.html'}, name="marc"), - url(r"^accounts/edit/marc_config/$", login_required(MARCConfigView.as_view()), name="marc_config"), + url(r"^accounts/edit/marc_config/$", login_required(LibModeView.as_view()), name="marc_config"), ) if settings.DEBUG: diff --git a/frontend/views.py b/frontend/views.py index 523448b0..437671a1 100755 --- a/frontend/views.py +++ b/frontend/views.py @@ -115,7 +115,7 @@ from regluit.frontend.forms import ( MsgForm, PressForm, KindleEmailForm, - MARCFormatForm, + LibModeForm, DateCalculatorForm ) @@ -3109,9 +3109,9 @@ def work_marc(request, work_id): return qs_marc_records(request, qs=[ work ]) -class MARCConfigView(FormView): +class LibModeView(FormView): template_name = 'marc_config.html' - form_class = MARCFormatForm + form_class = LibModeForm success_url = reverse_lazy('marc') def form_valid(self, form): @@ -3132,7 +3132,7 @@ class MARCConfigView(FormView): if reverse('marc_config', args=[]) in self.request.META['HTTP_REFERER']: return HttpResponseRedirect(reverse('marc_config', args=[])) else: - return super(MARCConfigView, self).form_valid(form) + return super(LibModeView, self).form_valid(form) From fa8f03eb0a3a120c20ef2fc5bee0f4f645fb1f32 Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 27 Oct 2014 20:11:23 -0400 Subject: [PATCH 12/12] pass tests --- frontend/views.py | 1 - marc/tests.py | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/views.py b/frontend/views.py index 437671a1..3b027ec2 100755 --- a/frontend/views.py +++ b/frontend/views.py @@ -71,7 +71,6 @@ from regluit.core import ( librarything, userlists, goodreads, - marc ) import regluit.core.cc as cc from regluit.core.bookloader import merge_works, detach_edition diff --git a/marc/tests.py b/marc/tests.py index bfdb4368..d146a526 100644 --- a/marc/tests.py +++ b/marc/tests.py @@ -125,7 +125,9 @@ class MarcTests(TestCase): load.stub(e) mr2 = models.MARCRecord.objects.create(guts=a_marc_record, edition=e ) - mr2.load_from_lc() + mr2.load_from_file('loc') + mr3 = models.MARCRecord.objects.create(guts=a_marc_record, edition=e ) + mr3.load_from_file('raw') mr2.direct_record_xml() mr2.direct_record_mrc()