just merging my local codebase post-internet librarian...

pull/1/head
Andromeda Yelton 2011-10-21 12:51:07 -04:00
commit e41e1e7b6d
10 changed files with 372 additions and 196 deletions

View File

@ -10,7 +10,11 @@ from regluit.core import models
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def add_by_isbn(isbn): def add_by_isbn(isbn, work=None):
"""add a book to the UnglueIt database based on ISBN. The work parameter
is optional, and if not supplied the edition will be associated with
a stub work.
"""
url = "https://www.googleapis.com/books/v1/volumes" url = "https://www.googleapis.com/books/v1/volumes"
results = _get_json(url, {"q": "isbn:%s" % isbn}) results = _get_json(url, {"q": "isbn:%s" % isbn})
@ -18,21 +22,27 @@ def add_by_isbn(isbn):
logger.warn("no google hits for %s" % isbn) logger.warn("no google hits for %s" % isbn)
return None return None
return add_by_googlebooks_id(results['items'][0]['id']) return add_by_googlebooks_id(results['items'][0]['id'], work)
def add_by_googlebooks_id(googlebooks_id): def add_by_googlebooks_id(googlebooks_id, work=None):
url = "https://www.googleapis.com/books/v1/volumes/%s" % googlebooks_id """add a book to the UnglueIt database based on the GoogleBooks ID. The
d = _get_json(url)['volumeInfo'] work parameter is optional, and if not supplied the edition will be
associated with a stub work.
"""
# don't ping google again if we already know about the edition
e, created = models.Edition.objects.get_or_create(googlebooks_id=googlebooks_id) e, created = models.Edition.objects.get_or_create(googlebooks_id=googlebooks_id)
if not created: if not created:
return e return e
url = "https://www.googleapis.com/books/v1/volumes/%s" % googlebooks_id
d = _get_json(url)['volumeInfo']
e.title = d.get('title') e.title = d.get('title')
e.description = d.get('description') e.description = d.get('description')
e.publisher = d.get('publisher') e.publisher = d.get('publisher')
e.publication_date = d.get('publishedDate') e.publication_date = d.get('publishedDate')
e.language = d.get('language')
for i in d.get('industryIdentifiers', []): for i in d.get('industryIdentifiers', []):
if i['type'] == 'ISBN_10': if i['type'] == 'ISBN_10':
@ -48,20 +58,44 @@ def add_by_googlebooks_id(googlebooks_id):
s, created = models.Subject.objects.get_or_create(name=s) s, created = models.Subject.objects.get_or_create(name=s)
s.editions.add(e) s.editions.add(e)
# add a stub Work for the edition # if we know what work to add the edition to do it
if e.work == None: if work:
work.editions.add(e)
# otherwise we need to create a stub work
else:
w = models.Work.objects.create(title=e.title) w = models.Work.objects.create(title=e.title)
w.editions.add(e) w.editions.add(e)
return e return e
def add_related(isbn):
"""add all books related to a particular ISBN to the UnglueIt database.
The initial seed ISBN will be added if it's not already there.
"""
# make sure the seed edition is there
edition = add_by_isbn(isbn)
# this is the work everything will hang off
work = edition.work
for other_isbn in thingisbn(isbn):
# TODO: if the other book is there already we have some surgery
# to do on the works, and the wishlists
add_by_isbn(other_isbn, work)
def thingisbn(isbn): def thingisbn(isbn):
"""given an ISBN return a list of related edition ISBNs, according to
Library Thing.
"""
url = "http://www.librarything.com/api/thingISBN/%s" % isbn url = "http://www.librarything.com/api/thingISBN/%s" % isbn
xml = requests.get(url, headers={"User-Agent": settings.USER_AGENT}).content xml = requests.get(url, headers={"User-Agent": settings.USER_AGENT}).content
doc = ElementTree.fromstring(xml) doc = ElementTree.fromstring(xml)
return [e.text for e in doc.findall('isbn')] return [e.text for e in doc.findall('isbn')]
def _get_json(url, params={}): def _get_json(url, params={}):
# TODO: should X-Forwarded-For change based on the request from client? # TODO: should X-Forwarded-For change based on the request from client?
headers = {'User-Agent': settings.USER_AGENT, headers = {'User-Agent': settings.USER_AGENT,

View File

@ -0,0 +1,125 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Edition.language'
db.add_column('core_edition', 'language', self.gf('django.db.models.fields.CharField')(max_length=2, null=True), keep_default=False)
def backwards(self, orm):
# Deleting field 'Edition.language'
db.delete_column('core_edition', 'language')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'core.author': {
'Meta': {'object_name': 'Author'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'editions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'authors'", 'symmetrical': 'False', 'to': "orm['core.Edition']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '500'})
},
'core.campaign': {
'Meta': {'object_name': 'Campaign'},
'activated': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'amazon_receiver': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'deadline': ('django.db.models.fields.DateTimeField', [], {}),
'description': ('django.db.models.fields.TextField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
'paypal_receiver': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
'supended_reason': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'suspended': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'target': ('django.db.models.fields.DecimalField', [], {'max_digits': '14', 'decimal_places': '2'}),
'withdrawn': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'withdrawn_reason': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'work': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'campaigns'", 'to': "orm['core.Work']"})
},
'core.edition': {
'Meta': {'object_name': 'Edition'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True'}),
'googlebooks_id': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'isbn_10': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True'}),
'isbn_13': ('django.db.models.fields.CharField', [], {'max_length': '13', 'null': 'True'}),
'language': ('django.db.models.fields.CharField', [], {'max_length': '2', 'null': 'True'}),
'publication_date': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
'publisher': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
'work': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'editions'", 'null': 'True', 'to': "orm['core.Work']"})
},
'core.subject': {
'Meta': {'object_name': 'Subject'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'editions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subjects'", 'symmetrical': 'False', 'to': "orm['core.Edition']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '500'})
},
'core.userprofile': {
'Meta': {'object_name': 'UserProfile'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'tagline': ('django.db.models.fields.CharField', [], {'max_length': '140', 'blank': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"})
},
'core.wishlist': {
'Meta': {'object_name': 'Wishlist'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'wishlist'", 'unique': 'True', 'to': "orm['auth.User']"}),
'works': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'wishlists'", 'symmetrical': 'False', 'to': "orm['core.Work']"})
},
'core.work': {
'Meta': {'object_name': 'Work'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'openlibrary_id': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '1000'})
}
}
complete_apps = ['core']

View File

@ -152,6 +152,7 @@ class Edition(models.Model):
isbn_10 = models.CharField(max_length=10, null=True) isbn_10 = models.CharField(max_length=10, null=True)
isbn_13 = models.CharField(max_length=13, null=True) isbn_13 = models.CharField(max_length=13, null=True)
work = models.ForeignKey("Work", related_name="editions", null=True) work = models.ForeignKey("Work", related_name="editions", null=True)
language = models.CharField(max_length=2, null=True)
def __unicode__(self): def __unicode__(self):
return "%s (%s)" % (self.title, self.isbn_13) return "%s (%s)" % (self.title, self.isbn_13)

View File

@ -21,6 +21,7 @@ class TestBookLoader(TestCase):
self.assertEqual(edition.isbn_10, '0441012035') self.assertEqual(edition.isbn_10, '0441012035')
self.assertEqual(edition.isbn_13, '9780441012039') self.assertEqual(edition.isbn_13, '9780441012039')
self.assertEqual(edition.googlebooks_id, "2NyiPwAACAAJ") self.assertEqual(edition.googlebooks_id, "2NyiPwAACAAJ")
self.assertEqual(edition.language, "en")
# subjects # subjects
subject_names = [subject.name for subject in edition.subjects.all()] subject_names = [subject.name for subject in edition.subjects.all()]
@ -52,6 +53,18 @@ class TestBookLoader(TestCase):
self.assertTrue('0441012035' in isbns) self.assertTrue('0441012035' in isbns)
self.assertTrue('3453313895' in isbns) self.assertTrue('3453313895' in isbns)
def test_add_related(self):
# add one edition
edition = bookloader.add_by_isbn('0441012035')
self.assertEqual(models.Edition.objects.count(), 1)
self.assertEqual(models.Work.objects.count(), 1)
# ask for related editions to be added using the work we just created
bookloader.add_related('0441012035')
self.assertTrue(models.Edition.objects.count() > 20)
self.assertEqual(models.Work.objects.count(), 1)
self.assertTrue(edition.work.editions.count() > 20)
class SearchTests(TestCase): class SearchTests(TestCase):
def test_basic_search(self): def test_basic_search(self):
@ -86,10 +99,10 @@ class CampaignTests(TestCase):
c = Campaign(target=D('1000.00'), deadline=datetime(2012, 1, 1)) c = Campaign(target=D('1000.00'), deadline=datetime(2012, 1, 1))
self.assertRaises(IntegrityError, c.save) self.assertRaises(IntegrityError, c.save)
#w = Work() w = Work()
#w.save() w.save()
#c = Campaign(target=D('1000.00'), deadline=datetime(2012, 1, 1), work=w) c = Campaign(target=D('1000.00'), deadline=datetime(2012, 1, 1), work=w)
#c.save() c.save()
def test_campaign_status(self): def test_campaign_status(self):

View File

@ -17,13 +17,23 @@
<body> <body>
<div id="js-page-wrap"> <div id="js-page-wrap">
<div id="js-header"> <div id="js-header">
<div class="js-main"> <div class="js-main">
<div class="js-logo"> <div class="js-logo">
<a href="/"><img src="/static/images/logo.png" alt="unglue.it" title="unglue.it" /></a> <a href="/"><img src="/static/images/logo.png" alt="unglue.it" title="unglue.it" /></a>
</div> </div>
{% if not suppress_search_box %}
<div class="js-search" style="float: left; margin-left: 50px;">
<div class="js-search-inner">
<form action="{% url search %}" method="get">
<input type="text" placeholder="Search for a book..." size="30" class="inputbox" name="q" value="{{ q }}">
<input type="button" onclick="this.form.searchword.focus();" class="button" value="Search">
</form>
</div>
</div>
{% endif %}
<div class="js-topmenu"> <div class="js-topmenu">
<ul class="menu"> <ul class="menu">
{% if user.is_authenticated %} {% if user.is_authenticated %}
<li class="first"><a href="/supporter/{{user.username}}"><span id="welcome">Welcome, {{ user.username }}</span></a></li> <li class="first"><a href="/supporter/{{user.username}}"><span id="welcome">Welcome, {{ user.username }}</span></a></li>
<li><a href="{% url auth_logout %}"><span>Sign Out</span></a></li> <li><a href="{% url auth_logout %}"><span>Sign Out</span></a></li>
@ -47,12 +57,12 @@
</div> </div>
<div id="footer"> <div id="footer">
<p> <p>
<a href="{{privacyurl}}"><span>Privacy</span></a>&nbsp;&nbsp;&nbsp;&nbsp; <a href="{{privacyurl}}"><span>Privacy</span></a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="{{rhtoolsurl}}"><span>Rightsholder tools</span></a>&nbsp;&nbsp;&nbsp;&nbsp; <a href="{{rhtoolsurl}}"><span>Rightsholder tools</span></a>&nbsp;&nbsp;&nbsp;&nbsp;
{% if user.is_authenticated %} {% if user.is_authenticated %}
<a href="{{editurl}}"><span>Settings</span></a>&nbsp;&nbsp;&nbsp;&nbsp; <a href="{{editurl}}"><span>Settings</span></a>&nbsp;&nbsp;&nbsp;&nbsp;
{% endif %} {% endif %}
</p> </p>
</div> </div>

View File

@ -1,24 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block extra_head %} {% block extra_head %}
<script type="text/javascript"> <script type="text/javascript" src="/static/js/wishlist.js"></script>
$(document).ready(function() {
$(".add-wishlist").each(function (index, element) {
$(element).click(function() {
var span = $(element).find("span");
var gb_id = span.attr('id')
if (!gb_id) return;
$.post('/wishlist/', {'googlebooks_id': gb_id}, function(data) {
span.fadeOut();
var newSpan = $("<span>On Your Wishlist!</span>").hide();
span.replaceWith(newSpan);
newSpan.fadeIn();
newSpan.removeAttr("id");
});
});
});
});
</script>
{% endblock %} {% endblock %}
{% block title %}Search Results{% endblock %} {% block title %}Search Results{% endblock %}

View File

@ -3,77 +3,45 @@
{% block title %} &#8212; {{ supporter.username }}{% endblock %} {% block title %} &#8212; {{ supporter.username }}{% endblock %}
{% block extra_head %} {% block extra_head %}
<script type="text/javascript"> <script type="text/javascript" src="/static/js/wishlist.js"></script>
$$(document).ready(function() {
$(".add-wishlist").each(function (index, element) {
$(element).click(function() {
var span = $(element).find("span");
var gb_id = span.attr('id')
if (!gb_id) return;
$.post('/wishlist/', {'googlebooks_id': gb_id}, function(data) {
span.fadeOut();
var newSpan = $("<span>On Your Wishlist!</span>").hide();
span.replaceWith(newSpan);
newSpan.fadeIn();
newSpan.removeAttr("id");
});
});
});
});
</script>
<script type="text/javascript">
$(document).ready(function() {
$(".remove-wishlist").each(function (index, element) {
$(element).click(function() {
var span = $(element).find("span");
var work_id = span.attr('id')
$.post('/wishlist/', {'remove_work_id': work_id}, function(data) {
var book = $(element).parent();
book.fadeOut();
});
});
});
});
</script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript" src="/static/js/jquery-ui-1.8.16.custom.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
var $j = jQuery.noConflict(); var $j = jQuery.noConflict();
$j(document).ready(function(){ $j(document).ready(function(){
$j('.user-block-hide').hide(); $j('.user-block-hide').hide();
$j('.user-block1 a').click( $j('.user-block1 a').click(
function() { function() {
$j(this).toggleClass("active"); $j(this).toggleClass("active");
$j(".user-block-hide").slideToggle(300); $j(".user-block-hide").slideToggle(300);
} }
); );
$j('.tabs1').click(function(){ $j('.tabs1').click(function(){
$j('#tabs').find('.active').removeClass('active'); $j('#tabs').find('.active').removeClass('active');
$j(this).addClass('active'); $j(this).addClass('active');
$j('.content-block-content').find('.active').removeClass('active'); $j('.content-block-content').find('.active').removeClass('active');
$j('#tabs-1').addClass('active').show(300); $j('#tabs-1').addClass('active').show(300);
$j('#tabs-2').hide(200); $j('#tabs-2').hide(200);
$j('#tabs-3').hide(200); $j('#tabs-3').hide(200);
}); });
$j('.tabs2').click(function(){ $j('.tabs2').click(function(){
$j('#tabs').find('.active').removeClass('active'); $j('#tabs').find('.active').removeClass('active');
$j(this).addClass('active'); $j(this).addClass('active');
$j('.content-block-content').find('.active').removeClass('active'); $j('.content-block-content').find('.active').removeClass('active');
$j('#tabs-2').addClass('active').show(300); $j('#tabs-2').addClass('active').show(300);
$j('#tabs-1').hide(200); $j('#tabs-1').hide(200);
$j('#tabs-3').hide(200); $j('#tabs-3').hide(200);
}); });
$j('.tabs3').click(function(){ $j('.tabs3').click(function(){
$j('#tabs').find('.active').removeClass('active'); $j('#tabs').find('.active').removeClass('active');
$j(this).addClass('active'); $j(this).addClass('active');
$j('.content-block-content').find('.active').removeClass('active'); $j('.content-block-content').find('.active').removeClass('active');
$j('#tabs-3').addClass('active').show(300); $j('#tabs-3').addClass('active').show(300);
$j('#tabs-2').hide(200); $j('#tabs-2').hide(200);
$j('#tabs-1').hide(200); $j('#tabs-1').hide(200);
}); });
}); });
</script> </script>
@ -83,24 +51,22 @@ $(document).ready(function() {
/* /*
To do: To do:
create topsection file for inclusion in multiple contexts, if needed create topsection file for inclusion in multiple contexts, if needed
do we really want to display date joined in profile? if so, with any text? "supporter since (date) is too long" figure out how to configure date display
add support for userpics/avatars/something decide if we're even including date joined in profile
crop/resize? other infrastructure we need? can we pull from twitter/fb? add support for userpics
add support for twitter/fb/other links crop/resize? other infrastructure we need? can we pull from twitter/fb?
part of user profile? can I just add to db model? add support for twitter/fb/other links?
loosely coupled vs actually integrated with signin part of user profile? can I just add to db model?
canonicalize -- user enters cut/paste link vs username loosely coupled vs actually integrated with signin
canonicalize -- user enters cut/paste link vs username
Goodreads & Librarything support Goodreads & Librarything support
If people lack homepage, etc., do we have greyed-out icons or no icons? If people lack homepage, etc., do we have greyed-out icons or no icons?
if greyed-out, can they add in place by clicking on them? if greyed-out, can they add in place by clicking on them?
make sure profile settings do something
make sure we have the profile settings we need make sure we have the profile settings we need
resurrect add/remove functionality resurrect add/remove functionality
be sure words display correctly be sure words display correctly
do I need both add-wishlist and remove-wishlist classes? do they differ?
better alignment on I am ungluing & badges better alignment on I am ungluing & badges
progress indicators
change status text to something more user-friendly
js is not DRY right now -- need to put into scripts folder and reference
make sure backed/backing/wishlist is the order we want the badges to be in make sure backed/backing/wishlist is the order we want the badges to be in
test code with other campaign statuses -- random_campaigns needs to set a variety of statuses! test code with other campaign statuses -- random_campaigns needs to set a variety of statuses!
@ -108,101 +74,91 @@ why is there a status in regluit.payment.models.Transaction? does it duplicate
there's no tab for seeing ALL my books, only the filters! huh. there's no tab for seeing ALL my books, only the filters! huh.
Values I need: Values I need:
how do I integrate the your wishlist thing with the tabs thing? how do I integrate the your wishlist thing with the tabs thing?
*/ */
/*
*/
{% block topsection %} {% block topsection %}
<div id="js-topsection"> <div id="js-topsection">
<div class="js-main"> <div class="js-main">
<div class="js-topnews"> <div class="js-topnews">
<div class="js-topnews1"> <div class="js-topnews1">
<div class="js-topnews2"> <div class="js-topnews2">
<div class="js-topnews3"> <div class="js-topnews3">
<div class="user-block"> <div class="user-block">
{% ifequal supporter request.user %} {% ifequal supporter request.user %}
<div class="user-block1"> <div class="user-block1">
<div class="block-inner"> <div class="block-inner">
<a class="my-setting" href="#">My Settings</a> <a class="my-setting" href="#">My Settings</a>
</div> </div>
</div> </div>
{% endifequal %} {% endifequal %}
<div class="user-block2"> <div class="user-block2">
<img class="user-avatar" src="/static/images/header/avatar.png" height="50" width="50" alt="User avatar" title="User avatar" /> <img class="user-avatar" src="/static/images/header/avatar.png" height="50" width="50" alt="User avatar" title="User avatar" />
<span class="user-name"><a href="#">{{ supporter.username }}</a></span> <span class="user-name"><a href="#">{{ supporter.username }}</a></span>
<span class="user-date">{{ date }}</span> <span class="user-date">{{ date }}</span>
<span class="user-short-info">{{ supporter.profile.tagline }}</span> <span class="user-short-info">{{ supporter.profile.tagline }}</span>
</div> </div>
<div class="user-block4"> <div class="user-block4">
<div class="social"> <div class="social">
<a href="#"><img src="/static/images/header/icon-home.png" alt="Home" title="Home" /></a> <a href="#"><img src="/static/images/header/icon-home.png" alt="Home" title="Home" /></a>
<a href="#"><img src="/static/images/header/icon-facebook.png" alt="Facebook" title="Facebook" /></a> <a href="#"><img src="/static/images/header/icon-facebook.png" alt="Facebook" title="Facebook" /></a>
<a href="#"><img src="/static/images/header/icon-tweeter.png" alt="tweeter" title="tweeter" /></a> <a href="#"><img src="/static/images/header/icon-twitter.png" alt="twitter" title="twitter" /></a>
<a href="#"><img src="/static/images/header/icon-google.png" alt="google" title="google" /></a> <a href="#"><img src="/static/images/header/icon-google.png" alt="google" title="google" /></a>
<a href="#"><img src="/static/images/header/icon-group.png" alt="group" title="group" /></a></div> <a href="#"><img src="/static/images/header/icon-group.png" alt="group" title="group" /></a></div>
</div> </div>
<div class="user-block3"> <div class="user-block3">
<div class="user-block"> <div class="user-block">
<span class="rounded"><span class="blue">{{ backed }}</span></span> <span class="rounded"><span class="blue">{{ backed }}</span></span>
<span class="rounded"><span class="orange">{{ backing }}</span></span> <span class="rounded"><span class="orange">{{ backing }}</span></span>
<span class="rounded"><span class="grey">{{ wished }}</span></span> <span class="rounded"><span class="grey">{{ wished }}</span></span>
</div> </div>
<span class="user-status-title">I am ungluing</span> <span class="user-status-title">I am ungluing</span>
</div> </div>
</div> </div>
{% ifequal supporter request.user %}
<div class="user-block-hide"> <div class="user-block-hide">
<div class="block block1"> <div class="block block1">
<div class="block-inner"> <div class="block-inner">
<h3><a class="profile-edit" href="#">Profile / edit</a></h3> <h3><a class="profile-edit" href="#">Profile / edit</a></h3>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. <form method="POST" action="">
<div class="profile-save"> {% csrf_token %}
<a href="#" class="profile-save">Save setting</a> {{ profile_form.as_p }}
</div>
</div> <input class="profile-save" type="submit" name="submit" value="Update" id="submit">
</div> </form>
</div>
<div class="block block2"> </div>
<h3 class="title">Pledges</h3>
<div class="check-list"> <div class="block block2">
<input type="checkbox" /> <h3 class="title">Links</h3>
<label>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</label> <div class="check-list">
</div> <input type="checkbox" />
<div class="check-list"> <label>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</label>
<input type="checkbox" /> </div>
<label>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</label> <div class="check-list">
</div> <input type="checkbox" />
</div> <label>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</label>
<div class="block block3"> </div>
<h3 class="title">Links</h3> </div>
<div class="check-list"> <div class="block block3">
<input type="checkbox" /> <h3 class="title">Privacy</h3>
<label>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</label> <div class="check-list">
</div> <input type="checkbox" />
<div class="check-list"> <label>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</label>
<input type="checkbox" /> </div>
<label>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</label> <div class="check-list">
</div> <input type="checkbox" />
</div> <label>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</label>
<div class="block block4"> </div>
<h3 class="title">Privacy</h3> </div>
<div class="check-list">
<input type="checkbox" /> </div>
<label>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</label> {% endifequal %}
</div> </div>
<div class="check-list">
<input type="checkbox" />
<label>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</label>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -223,7 +179,7 @@ how do I integrate the your wishlist thing with the tabs thing?
<div class="js-maincol-inner"> <div class="js-maincol-inner">
<div class="content-block"> <div class="content-block">
<div class="content-block-heading" id="tabs"> <div class="content-block-heading" id="tabs">
<ul class="tabs"> <ul class="tabs">
<li class="tabs1"><a href="#">Unglued</a></li> <li class="tabs1"><a href="#">Unglued</a></li>
<li class="tabs2"><a href="#">Being Unglued</a></li> <li class="tabs2"><a href="#">Being Unglued</a></li>
<li class="tabs3"><a href="#">Want to Unglue</a></li> <li class="tabs3"><a href="#">Want to Unglue</a></li>
@ -249,26 +205,49 @@ how do I integrate the your wishlist thing with the tabs thing?
</ul> </ul>
</div> </div>
<div class="content-block-content"> <div class="content-block-content">
{% ifequal wishlist.works.all.count 0 %}
{% ifequal request.user supporter %}
<div class="empty-wishlist">
Your wishlist is currently empty.<br><br>
Go ahead and <span class="bounce-search">find</span> some books to give to the world, and add them to your Wishlist!<br><br>
We double dog dare you...
</div>
{% else %}
<div class="empty-wishlist">
It looks like {{ supporter.username }} is just
getting started, and hasn't added anything to their
wishlist just yet.<br><br>
Nudge, nudge, say no more.
{% endifequal %}
{% else %}
{% for work in wishlist.works.all %} {% for work in wishlist.works.all %}
<div id="tabs-1" class="tabs"> <div id="tabs-1" class="tabs">
<div class="book-list {% cycle 'row1' 'row2' %}"> <div class="book-list {% cycle 'row1' 'row2' %}">
<div class="book-thumb"> <div class="book-thumb">
<a href="#"><img src="{{ work.cover_image_small }}" alt="Book name" title="book name" /></a> <a href="#"><img src="{{ work.cover_image_small }}" alt="Book name" title="book name" /></a>
</div> </div>
<div class="book-name"> <div class="book-name">
<span> <span>
{{ work.title }} <a href="{% url work work.id %}">{{ work.title }}</a>
</span> </span>
</div> </div>
{% if work in request.user.wishlist.works.all %} {% ifequal supporter request.user %}
<div class="remove-wishlist"> <div class="remove-wishlist">
<a href="#"><span id="{{ work.id }}">Remove from Wishlist</span></a> <span id="{{ work.id }}">Remove from Wishlist</span>
</div> </div>
{% else %} {% else %}{% if work in shared_works %}
<div>
<span>On Your Wishlist!</span>
</div>
{% else %}{% if request.user.is_anonymous %}
<div class="create-account">
<span>Add to Wishlist</span>
</div>
{% else %}
<div class="add-wishlist"> <div class="add-wishlist">
<a href="#"><span id="{{ work.id }}">Add to Wishlist</span></a> <span id="{{ work.editions.all.0.googlebooks_id }}">Add to Wishlist</span>
</div> </div>
{% endif %} {% endif %}{% endif %}{% endifequal %}
<div class="booklist-status"> <div class="booklist-status">
<span class="booklist-status-text">{{ work.last_campaign_status }}</span> <span class="booklist-status-text">{{ work.last_campaign_status }}</span>
<span class="booklist-status-img"> <span class="booklist-status-img">
@ -284,6 +263,7 @@ how do I integrate the your wishlist thing with the tabs thing?
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
{% endifequal %}
</div> </div>
</div> </div>

View File

@ -19,7 +19,7 @@ def home(request):
if request.user.is_authenticated(): if request.user.is_authenticated():
return HttpResponseRedirect(reverse('supporter', return HttpResponseRedirect(reverse('supporter',
args=[request.user.username])) args=[request.user.username]))
return render(request, 'home.html') return render(request, 'home.html', {'suppress_search_box': True})
def supporter(request, supporter_username): def supporter(request, supporter_username):
supporter = get_object_or_404(User, username=supporter_username) supporter = get_object_or_404(User, username=supporter_username)

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

30
static/js/wishlist.js Normal file
View File

@ -0,0 +1,30 @@
$(document).ready(function() {
$(".add-wishlist").each(function (index, element) {
$(element).click(function() {
var span = $(element).find("span");
var gb_id = span.attr('id')
if (!gb_id) return;
$.post('/wishlist/', {'googlebooks_id': gb_id}, function(data) {
span.fadeOut();
var newSpan = $("<span>On Your Wishlist!</span>").hide();
span.replaceWith(newSpan);
newSpan.fadeIn();
newSpan.removeAttr("id");
});
});
});
$(".remove-wishlist").each(function (index, element) {
$(element).click(function() {
var span = $(element).find("span");
var work_id = span.attr('id')
$.post('/wishlist/', {'remove_work_id': work_id}, function(data) {
var book = $(element).parent();
book.fadeOut();
});
});
});
});