Merge branch 'master' of github.com:Gluejar/regluit
commit
f774d9d552
|
@ -0,0 +1,103 @@
|
|||
import logging
|
||||
|
||||
from django.contrib import auth
|
||||
from django.contrib.auth.models import User, AnonymousUser
|
||||
from django.conf.urls.defaults import url
|
||||
from django.db.models import Q
|
||||
|
||||
from tastypie import fields
|
||||
from tastypie.constants import ALL, ALL_WITH_RELATIONS
|
||||
from tastypie.resources import ModelResource, Resource, Bundle
|
||||
from tastypie.utils import trailing_slash
|
||||
from tastypie.authentication import ApiKeyAuthentication
|
||||
|
||||
from regluit.core import models
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UserResource(ModelResource):
|
||||
class Meta:
|
||||
authentication = ApiKeyAuthentication()
|
||||
queryset = User.objects.all()
|
||||
resource_name = 'user'
|
||||
fields = ['username', 'first_name', 'last_name']
|
||||
|
||||
class EditionResource(ModelResource):
|
||||
class Meta:
|
||||
authentication = ApiKeyAuthentication()
|
||||
queryset = models.Edition.objects.all()
|
||||
resource_name = 'edition'
|
||||
filtering = {
|
||||
"isbn_10": ALL,
|
||||
}
|
||||
|
||||
class WorkResource(ModelResource):
|
||||
editions = fields.ToManyField(EditionResource, 'editions')
|
||||
class Meta:
|
||||
authentication = ApiKeyAuthentication()
|
||||
queryset = models.Work.objects.all()
|
||||
resource_name = 'work'
|
||||
filtering = {'editions': ALL_WITH_RELATIONS, 'id': ALL}
|
||||
|
||||
class CampaignResource(ModelResource):
|
||||
work = fields.ToOneField(WorkResource, 'work')
|
||||
|
||||
def alter_list_data_to_serialize(self, request, data):
|
||||
"""
|
||||
annotate the list of campaigns with information from the logged in
|
||||
user. note: this isn't the user identified by the api username/api_key
|
||||
it's the the user that client might be logged into unglue.it as.
|
||||
"""
|
||||
u = auth.get_user(request)
|
||||
if isinstance(u, User):
|
||||
data['meta']['logged_in_username'] = u.username
|
||||
wishlist_work_ids = [w.id for w in u.wishlist.works.all()]
|
||||
else:
|
||||
data['meta']['logged_in_username'] = None
|
||||
wishlist_work_ids = []
|
||||
|
||||
for o in data['objects']:
|
||||
o.data['in_wishlist'] = o.obj.id in wishlist_work_ids
|
||||
|
||||
# TODO: add pledging information
|
||||
return data
|
||||
|
||||
class Meta:
|
||||
authentication = ApiKeyAuthentication()
|
||||
queryset = models.Campaign.objects.all()
|
||||
resource_name = 'campaign'
|
||||
excludes = ['amazon_receiver', 'paypal_receiver']
|
||||
filtering = {
|
||||
"work": ALL_WITH_RELATIONS,
|
||||
}
|
||||
|
||||
class AuthorResource(ModelResource):
|
||||
works = fields.ToManyField(WorkResource, 'works')
|
||||
class Meta:
|
||||
authentication = ApiKeyAuthentication()
|
||||
queryset = models.Author.objects.all()
|
||||
resource_name = 'author'
|
||||
|
||||
class SubjectResource(ModelResource):
|
||||
works = fields.ToManyField(WorkResource, 'works')
|
||||
class Meta:
|
||||
authentication = ApiKeyAuthentication()
|
||||
queryset = models.Subject.objects.all()
|
||||
resource_name = 'subject'
|
||||
|
||||
class EditionCoverResource(ModelResource):
|
||||
edition = fields.ToManyField(EditionResource, 'editions')
|
||||
class Meta:
|
||||
authentication = ApiKeyAuthentication()
|
||||
queryset = models.EditionCover.objects.all()
|
||||
resource_name = 'editioncover'
|
||||
|
||||
class WishlistResource(ModelResource):
|
||||
user = fields.ToOneField(UserResource, 'user')
|
||||
works = fields.ToManyField(WorkResource, 'works')
|
||||
class Meta:
|
||||
authentication = ApiKeyAuthentication()
|
||||
queryset = models.Wishlist.objects.all()
|
||||
resource_name = 'wishlist'
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>editions</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!--- editions --->
|
||||
|
||||
{% if editions %}
|
||||
<ul>
|
||||
{% for edition in editions %}
|
||||
<li> <img src="http://covers.openlibrary.org/b/isbn/{{edition.isbn_10}}-S.jpg" /> {{edition.id}} | {{edition.title}} |
|
||||
<a href="{% url isbn isbn=edition.isbn_10 %}">{{edition.isbn_10}}</a> |
|
||||
<a href="{% url isbn isbn=edition.isbn_13 %}">{{edition.isbn_13}}</a>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p>No editions are available.</p>
|
||||
{% endif %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>isbn</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p>You entered isbn: {{isbn}}</p>
|
||||
|
||||
<!--- editions --->
|
||||
|
||||
{% if editions %}
|
||||
<ul>
|
||||
{% for edition in editions %}
|
||||
<li>{{edition.id}} | {{edition.title}}
|
||||
|
||||
<a href="http://openlibrary.org/isbn/{{edition.isbn_10}}"><img src="http://covers.openlibrary.org/b/isbn/{{edition.isbn_10}}-M.jpg"/ width="120" height="182" ></a>
|
||||
|
||||
<!-- editioncovers for given edition -->
|
||||
{% if edition.covers.all %}
|
||||
<ul id="id">
|
||||
{% for editioncover in edition.covers.all %}
|
||||
<li>cover OL id: {{editioncover.openlibrary_id}}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="classname">No editioncover for this edition</p>
|
||||
{% endif %}
|
||||
|
||||
<!-- related work -->
|
||||
{% if edition.work %}
|
||||
<p class="classname">Related work: {{edition.work.id}} | {{edition.work.title}} | first author: {{edition.work.authors.all.0.name}} <br/>
|
||||
All authors: {{edition.work.authors.all}} </p>
|
||||
{% endif %}
|
||||
|
||||
<!-- related campaigns -->
|
||||
{% if edition.work.campaigns.all %}
|
||||
<ul id="id">
|
||||
{% for campaign in edition.work.campaigns.all %}
|
||||
<li>Campaign: id:{{campaign.id}} | name:{{campaign.name}} | desc:{{campaign.description}} <br/>
|
||||
target:{{campaign.target}} created:{{campaign.created}} deadline:{{campaign.deadline}}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="classname">No associated campaigns</p>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p>No editions are available.</p>
|
||||
{% endif %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
106
api/tests.py
106
api/tests.py
|
@ -1,23 +1,95 @@
|
|||
"""
|
||||
This file demonstrates two different styles of tests (one doctest and one
|
||||
unittest). These will both pass when you run "manage.py test".
|
||||
|
||||
Replace these with more appropriate tests for your application.
|
||||
"""
|
||||
import json
|
||||
import datetime
|
||||
|
||||
from django.test import TestCase
|
||||
from django.test.client import Client
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.failUnlessEqual(1 + 1, 2)
|
||||
from regluit.core import bookloader, models
|
||||
|
||||
__test__ = {"doctest": """
|
||||
Another way to test that 1 + 1 is equal to 2.
|
||||
class ApiTests(TestCase):
|
||||
|
||||
>>> 1 + 1 == 2
|
||||
True
|
||||
"""}
|
||||
def setUp(self):
|
||||
edition = bookloader.add_book(isbn='0441012035')
|
||||
campaign = models.Campaign.objects.create(
|
||||
name=edition.work.title,
|
||||
work=edition.work,
|
||||
description='Test Campaign',
|
||||
deadline=datetime.datetime.now(),
|
||||
target=1000.0,
|
||||
)
|
||||
self.user = User.objects.create_user('test', 'test@example.com', 'testpass')
|
||||
self.client = Client()
|
||||
|
||||
def test_user(self):
|
||||
self.assertEqual(User.objects.all().count(), 1)
|
||||
self.assertTrue(User.objects.all()[0].api_key.key)
|
||||
|
||||
def test_no_auth(self):
|
||||
r = self.client.get('/api/v1/campaign/', data={'format': 'json'})
|
||||
self.assertEqual(r.status_code, 401)
|
||||
|
||||
def test_campaigns(self):
|
||||
r = self.client.get('/api/v1/campaign/', data={
|
||||
'format': 'json',
|
||||
'username': self.user.username,
|
||||
'api_key': self.user.api_key.key
|
||||
})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
j = json.loads(r.content)
|
||||
self.assertEqual(len(j['objects']), 1)
|
||||
self.assertEqual(j['objects'][0]['name'], 'Neuromancer')
|
||||
self.assertEqual(j['objects'][0]['work'], '/api/v1/work/1/')
|
||||
|
||||
def test_campaign(self):
|
||||
r = self.client.get('/api/v1/campaign/1/', data={
|
||||
'format': 'json',
|
||||
'username': self.user.username,
|
||||
'api_key': self.user.api_key.key
|
||||
})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
j = json.loads(r.content)
|
||||
self.assertEqual(j['name'], 'Neuromancer')
|
||||
self.assertEqual(j['work'], '/api/v1/work/1/')
|
||||
|
||||
def test_campaign_lookup_by_isbn(self):
|
||||
r = self.client.get('/api/v1/campaign/', data={
|
||||
'format': 'json',
|
||||
'work__editions__isbn_10': '0441012035',
|
||||
'username': self.user.username,
|
||||
'api_key': self.user.api_key.key
|
||||
})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
j = json.loads(r.content)
|
||||
self.assertEqual(len(j['objects']), 1)
|
||||
self.assertEqual(j['objects'][0]['name'], 'Neuromancer')
|
||||
self.assertEqual(j['meta']['logged_in_username'], None)
|
||||
self.assertEqual(j['objects'][0]['in_wishlist'], False)
|
||||
|
||||
def test_logged_in_user_info(self):
|
||||
# login and see if adding a work to the users wishlist causes
|
||||
# it to show up as in_wishlist in the campaign info
|
||||
self.client.login(username='test', password='testpass')
|
||||
|
||||
r = self.client.get('/api/v1/campaign/', data={
|
||||
'format': 'json',
|
||||
'work__editions__isbn_10': '0441012035',
|
||||
'username': self.user.username,
|
||||
'api_key': self.user.api_key.key
|
||||
})
|
||||
j = json.loads(r.content)
|
||||
self.assertEqual(j['meta']['logged_in_username'], 'test')
|
||||
self.assertEqual(j['objects'][0]['in_wishlist'], False)
|
||||
|
||||
w = models.Work.objects.get(editions__isbn_10='0441012035')
|
||||
self.user.wishlist.works.add(w)
|
||||
r = self.client.get('/api/v1/campaign/', data={
|
||||
'format': 'json',
|
||||
'work__editions__isbn_10': '0441012035',
|
||||
'username': self.user.username,
|
||||
'api_key': self.user.api_key.key
|
||||
})
|
||||
j = json.loads(r.content)
|
||||
self.assertEqual(j['meta']['logged_in_username'], 'test')
|
||||
self.assertEqual(j['objects'][0]['in_wishlist'], True)
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
from django.conf.urls.defaults import *
|
||||
from tastypie.api import Api
|
||||
|
||||
from regluit.api import resources
|
||||
|
||||
v1_api = Api(api_name='v1')
|
||||
v1_api.register(resources.UserResource())
|
||||
v1_api.register(resources.WorkResource())
|
||||
v1_api.register(resources.EditionResource())
|
||||
v1_api.register(resources.EditionCoverResource())
|
||||
v1_api.register(resources.CampaignResource())
|
||||
v1_api.register(resources.AuthorResource())
|
||||
v1_api.register(resources.SubjectResource())
|
||||
v1_api.register(resources.WishlistResource())
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^', include(v1_api.urls)),
|
||||
)
|
22
api/views.py
22
api/views.py
|
@ -0,0 +1,22 @@
|
|||
from django.template import RequestContext
|
||||
from django.shortcuts import render_to_response, get_object_or_404
|
||||
from django.db.models import Q
|
||||
|
||||
from regluit.core import models
|
||||
|
||||
def isbn(request,isbn):
|
||||
|
||||
editions = models.Edition.objects.filter(Q(isbn_10 = isbn) | Q(isbn_13 = isbn))
|
||||
# models.Campaign.objects.filter(work__editions__isbn_13='9780811216999')
|
||||
|
||||
return render_to_response('isbn.html',
|
||||
{'isbn':isbn, 'editions':editions},
|
||||
context_instance=RequestContext(request)
|
||||
)
|
||||
|
||||
def editions(request):
|
||||
editions = models.Edition.objects.all()
|
||||
return render_to_response('editions.html',
|
||||
{'editions':editions},
|
||||
context_instance=RequestContext(request)
|
||||
)
|
|
@ -0,0 +1,73 @@
|
|||
# code modified from http://stackoverflow.com/questions/4734645/is-there-a-tool-to-check-database-integrity-in-django/4736176#4736176
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.core.management.base import NoArgsCommand
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db import models
|
||||
from optparse import make_option
|
||||
|
||||
def model_name(model):
|
||||
return '%s.%s' % (model._meta.app_label, model._meta.object_name)
|
||||
|
||||
class Command(BaseCommand):
|
||||
args = '[-e|--exclude app_name.ModelName]'
|
||||
help = 'Checks constraints in the database and reports violations on stdout'
|
||||
|
||||
option_list = NoArgsCommand.option_list + (
|
||||
make_option('-e', '--exclude', action='append', type='string', dest='exclude'),
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
# TODO once we're on Django 1.2, write to self.stdout and self.stderr instead of plain print
|
||||
|
||||
exclude = options.get('exclude', None) or []
|
||||
|
||||
failed_instance_count = 0
|
||||
failed_model_count = 0
|
||||
for app in models.get_apps():
|
||||
for model in models.get_models(app):
|
||||
if model_name(model) in exclude:
|
||||
print 'Skipping model %s' % model_name(model)
|
||||
continue
|
||||
fail_count = self.check_model(app, model)
|
||||
if fail_count > 0:
|
||||
failed_model_count += 1
|
||||
failed_instance_count += fail_count
|
||||
print 'Detected %d errors in %d models' % (failed_instance_count, failed_model_count)
|
||||
|
||||
def check_model(self, app, model):
|
||||
meta = model._meta
|
||||
if meta.proxy:
|
||||
print 'WARNING: proxy models not currently supported; ignored'
|
||||
return
|
||||
|
||||
# Define all the checks we can do; they return True if they are ok,
|
||||
# False if not (and print a message to stdout)
|
||||
def check_foreign_key(model, field):
|
||||
foreign_model = field.related.parent_model
|
||||
def check_instance(instance):
|
||||
try:
|
||||
# name: name of the attribute containing the model instance (e.g. 'user')
|
||||
# attname: name of the attribute containing the id (e.g. 'user_id')
|
||||
getattr(instance, field.name)
|
||||
return True
|
||||
except ObjectDoesNotExist:
|
||||
print '%s with pk %s refers via field %s to nonexistent %s with pk %s' % \
|
||||
(model_name(model), str(instance.pk), field.name, model_name(foreign_model), getattr(instance, field.attname))
|
||||
return check_instance
|
||||
|
||||
# Make a list of checks to run on each model instance
|
||||
checks = []
|
||||
for field in meta.local_fields + meta.local_many_to_many + meta.virtual_fields:
|
||||
if isinstance(field, models.ForeignKey):
|
||||
checks.append(check_foreign_key(model, field))
|
||||
|
||||
# Run all checks
|
||||
fail_count = 0
|
||||
if checks:
|
||||
for instance in model.objects.all():
|
||||
print 'Checking model %s ...' % model_name(model)
|
||||
for check in checks:
|
||||
if not check(instance):
|
||||
fail_count += 1
|
||||
return fail_count
|
|
@ -0,0 +1,11 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
|
||||
from regluit.core import models
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "list all editions in the database"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
editions = models.Edition.objects.all()
|
||||
for edition in editions:
|
||||
print edition.id, edition.title, edition.isbn_10, edition.isbn_13
|
|
@ -6,8 +6,8 @@ class Campaign(models.Model):
|
|||
created = models.DateTimeField(auto_now_add=True)
|
||||
name = models.CharField(max_length=500, null=False)
|
||||
description = models.CharField(max_length=10000, null=False)
|
||||
target = models.FloatField()
|
||||
deadline = models.DateTimeField()
|
||||
target = models.FloatField(null=False)
|
||||
deadline = models.DateTimeField(null=False)
|
||||
paypal_receiver = models.CharField(max_length=100, null=True)
|
||||
amazon_receiver = models.CharField(max_length=100, null=True)
|
||||
work = models.ForeignKey("Work", related_name="campaigns")
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.contrib.auth.models import User
|
|||
|
||||
from social_auth.signals import pre_update
|
||||
from social_auth.backends.facebook import FacebookBackend
|
||||
from tastypie.models import create_api_key
|
||||
|
||||
|
||||
def facebook_extra_values(sender, user, response, details, **kwargs):
|
||||
|
@ -17,14 +18,15 @@ pre_update.connect(facebook_extra_values, sender=FacebookBackend)
|
|||
|
||||
def create_wishlist(sender, created, instance, **kwargs):
|
||||
# use get_model to avoid circular import problem with models
|
||||
# this fails when a superuser is being created as part of a syncdb
|
||||
# since the database table for wishlist doesn't exist yet
|
||||
try:
|
||||
Wishlist = get_model('core', 'Wishlist')
|
||||
if created:
|
||||
Wishlist.objects.create(user=instance)
|
||||
|
||||
except DatabaseError:
|
||||
# this can happen when creating superuser during syncdb since the
|
||||
# core_wishlist table doesn't exist yet
|
||||
return
|
||||
|
||||
|
||||
post_save.connect(create_wishlist, sender=User)
|
||||
post_save.connect(create_api_key, sender=User)
|
||||
|
|
|
@ -40,6 +40,12 @@
|
|||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<ul class="menu">
|
||||
<li><a href="{% url privacy %}"><span>Privacy</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>Gluejar Privacy Policy</h1>
|
||||
|
||||
Date of last revision: September 13, 2011
|
||||
|
@ -46,3 +50,6 @@ Date of last revision: September 13, 2011
|
|||
<h2>Contact Us</h2>
|
||||
|
||||
<p>If you have any questions regarding this privacy policy, you can contact us at <a href="mailto:contact@gluejar.com?Subject=Questions%about%20privacy%20policy">contact@gluejar.com</a>, or using our contact page.</p>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -4,4 +4,5 @@ urlpatterns = patterns(
|
|||
"regluit.frontend.views",
|
||||
url(r"^$", "home", name="home"),
|
||||
url(r"^supporter/(?P<supporter_username>.+)/$", "supporter", name="supporter"),
|
||||
url(r"^privacy$", "textpage", {'page': 'privacy'}, name="privacy"),
|
||||
)
|
||||
|
|
|
@ -16,3 +16,9 @@ def supporter(request, supporter_username):
|
|||
{"supporter": supporter},
|
||||
context_instance=RequestContext(request)
|
||||
)
|
||||
|
||||
def textpage(request, page):
|
||||
return render_to_response(page + '.html',
|
||||
{},
|
||||
context_instance=RequestContext(request)
|
||||
)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
django
|
||||
south
|
||||
django-extensions
|
||||
django-tastypie
|
||||
https://github.com/toastdriven/django-tastypie/tarball/master
|
||||
requests
|
||||
https://bitbucket.org/ubernostrum/django-registration/get/tip.tar.gz
|
||||
django-social-auth
|
||||
|
|
|
@ -105,6 +105,7 @@ INSTALLED_APPS = (
|
|||
'regluit.core',
|
||||
'registration',
|
||||
'social_auth',
|
||||
'tastypie',
|
||||
)
|
||||
|
||||
# A sample logging configuration. The only tangible logging
|
||||
|
|
|
@ -20,6 +20,7 @@ body{
|
|||
#header{ height:90px;}
|
||||
|
||||
.logo{ float:left; padding-top:20px;}
|
||||
.logo a img{border: none;}
|
||||
.topmenu{ float:right; padding-top:25px;}
|
||||
.topmenu ul li{ float:left; padding:0 10px;}
|
||||
.topmenu ul li a{ height:36px; line-height:36px; display:block; color:#fff; text-decoration:none; font-weight:bold; font-size:13px; letter-spacing:1px; text-transform:capitalize;}
|
||||
|
@ -132,3 +133,9 @@ a{ font-weight:bold; font-size:13px; text-decoration:none; cursor:pointer;}
|
|||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
#footer{
|
||||
border-top: 7px solid #edf3f4;
|
||||
clear: both;
|
||||
height:90px;
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.5 KiB |
Loading…
Reference in New Issue