Merge branch 'master' of github.com:Gluejar/regluit

pull/1/head
Andromeda Yelton 2011-11-16 12:38:18 -05:00
commit 8bbee39312
13 changed files with 156 additions and 33 deletions

View File

@ -16,11 +16,11 @@ Here are some instructions for setting up regluit for development on
an Ubuntu system. If you are on OS X see notes below an Ubuntu system. If you are on OS X see notes below
to install python-setuptools in step 1: to install python-setuptools in step 1:
1. `aptitude install python-setuptools git` 1. `aptitude install python-setuptools git python-lxml`
1. `sudo easy_install virtualenv virtualenvwrapper` 1. `sudo easy_install virtualenv virtualenvwrapper`
1. `git clone git@github.com:Gluejar/regluit.git` 1. `git clone git@github.com:Gluejar/regluit.git`
1. `cd regluit` 1. `cd regluit`
1. `mkvirtualenv --no-site-packages regluit` 1. `mkvirtualenv regluit`
1. `pip install -r requirements.pip` 1. `pip install -r requirements.pip`
1. `add2virtualenv ..` 1. `add2virtualenv ..`
1. `cp settings/dev.py settings/me.py` 1. `cp settings/dev.py settings/me.py`
@ -41,23 +41,22 @@ Below are the steps for getting regluit running on EC2 with Apache and mod_wsgi,
1. create an ubuntu natty ec2 instance using ami-1aad5273 1. create an ubuntu natty ec2 instance using ami-1aad5273
1. `sudo aptitude update` 1. `sudo aptitude update`
1. `sudo aptitude upgrade` 1. `sudo aptitude upgrade`
1. `sudo aptitude install git apache libapache2-mod-wsgi mysql-client python-virtualenv python-mysqldb redis-server` 1. `sudo aptitude install git apache libapache2-mod-wsgi mysql-client python-virtualenv python-mysqldb redis-server python-lxml`
1. `sudo mkdir /opt/regluit` 1. `sudo mkdir /opt/regluit`
1. `sudo chown ubuntu:ubuntu /opt/regluit` 1. `sudo chown ubuntu:ubuntu /opt/regluit`
1. `cd /opt` 1. `cd /opt`
1. `git config --global user.name "Ed Summers"` 1. `git config --global user.name "Ed Summers"`
1. `git config --global user.email "ehs@pobox.com"` 1. `git config --global user.email "ehs@pobox.com"`
1. `ssh-keygen` 1. `ssh-keygen`
1. add `~/.ssh/id_rsa.pub` as a deploy key on github 1. add `~/.ssh/id_rsa.pub` as a deploy key on github https://github.com/Gluejar/regluit/admin/keys
1. `git clone git@github.com:Gluejar/regluit.git` 1. `git clone git@github.com:Gluejar/regluit.git`
1. `cd /opt/regluit` 1. `cd /opt/regluit`
1. `cp settings/dev.py settings/prod.py`
1. create an Amazon RDS instance 1. create an Amazon RDS instance
1. connect to it, e.g. `mysql -u root -h gluejardb.cboagmr25pjs.us-east-1.rds.amazonaws.com -p` 1. connect to it, e.g. `mysql -u root -h gluejardb.cboagmr25pjs.us-east-1.rds.amazonaws.com -p`
1. `CREATE DATABASE unglueit CHARSET utf8;` 1. `CREATE DATABASE unglueit CHARSET utf8;`
1. `GRANT ALL ON unglueit.* TO unglueit@ip-10-244-250-168.ec2.internal IDENTIFIED BY 'unglueit' REQUIRE SSL` 1. `GRANT ALL ON unglueit.* TO unglueit@ip-10-244-250-168.ec2.internal IDENTIFIED BY 'unglueit' REQUIRE SSL`
1. update settings/prod.py with database credentials 1. update settings/prod.py with database credentials
1. `virtualenv --no-site-packages ENV` 1. `virtualenv ENV`
1. `source ENV/bin/activate` 1. `source ENV/bin/activate`
1. `pip install -r requirements.pip` 1. `pip install -r requirements.pip`
1. `echo "/opt/" > ENV/lib/python2.7/site-packages/regluit.pth` 1. `echo "/opt/" > ENV/lib/python2.7/site-packages/regluit.pth`
@ -66,9 +65,9 @@ Below are the steps for getting regluit running on EC2 with Apache and mod_wsgi,
1. `sudo a2ensite regluit` 1. `sudo a2ensite regluit`
1. `sudo /etc/init.d/apache2 restart` 1. `sudo /etc/init.d/apache2 restart`
1. `sudo adduser --no-create-home celery --disabled-password --disabled-login` 1. `sudo adduser --no-create-home celery --disabled-password --disabled-login`
1. `sudo cp celeryd /etc/init.d/celeryd` 1. `sudo cp deploy/celeryd /etc/init.d/celeryd`
1. `sudo chmod 755 /etc/init.d/celeryd` 1. `sudo chmod 755 /etc/init.d/celeryd`
1. `sudo cp celeryd.conf /etc/default/celeryd` 1. `sudo cp deploy/celeryd.conf /etc/default/celeryd`
1. `sudo mkdir /var/log/celery` 1. `sudo mkdir /var/log/celery`
1. `sudo chown celery:celery /var/log/celery` 1. `sudo chown celery:celery /var/log/celery`
1. `sudo mkdir /var/run/celery` 1. `sudo mkdir /var/run/celery`

View File

@ -62,6 +62,13 @@ class LibraryThing(object):
# title # title
book_data["title"] = {"href":cols[1].xpath('.//a')[0].attrib['href'], book_data["title"] = {"href":cols[1].xpath('.//a')[0].attrib['href'],
"title":cols[1].xpath('.//a')[0].text} "title":cols[1].xpath('.//a')[0].text}
# extract work_id and book_id from href
try:
(book_data["work_id"], book_data["book_id"]) = re.match("^/work/(.*)/book/(.*)$",book_data["title"]["href"]).groups()
except:
(book_data["work_id"], book_data["book_id"]) = (None, None)
# author -- what if there is more than 1? or none? # author -- what if there is more than 1? or none?
try: try:
book_data["author"] = {"display_name":cols[2].xpath('.//a')[0].text, book_data["author"] = {"display_name":cols[2].xpath('.//a')[0].text,
@ -86,7 +93,46 @@ class LibraryThing(object):
yield book_data yield book_data
def viewstyle_5(self, rows): def viewstyle_5(self, rows):
raise NotImplementedError() # implement this view to get at the ISBNs
for (i,row) in enumerate(rows):
book_data = {}
cols = row.xpath('td')
# title
book_data["title"] = {"href":cols[0].xpath('.//a')[0].attrib['href'],
"title":cols[0].xpath('.//a')[0].text}
# extract work_id and book_id from href
try:
(book_data["work_id"], book_data["book_id"]) = re.match("^/work/(.*)/book/(.*)$",book_data["title"]["href"]).groups()
except:
(book_data["work_id"], book_data["book_id"]) = (None, None)
# tags
tag_links = cols[1].xpath('.//a')
book_data["tags"] = filter(lambda x: x is not None, [a.text for a in tag_links])
# lc classification
try:
book_data["lc_call_number"] = cols[2].xpath('.//span')[0].text
except Exception, e:
logger.info("book lc call number exception: %s %s", book_data["title"], e)
book_data["lc_call_number"] = None
# subject
subjects = cols[3].xpath('.//div[@class="subjectLine"]')
book_data["subjects"] = [{'href':s.xpath('a')[0].attrib['href'],
'text':s.xpath('a')[0].text} for s in subjects]
# isbn
try:
book_data["isbn"] = cols[4].xpath('.//span')[0].text
except Exception, e:
book_data["isbn"] = None
yield book_data
def parse_user_catalog(self, view_style=1): def parse_user_catalog(self, view_style=1):
from lxml import html from lxml import html

View File

@ -11,6 +11,6 @@ class Command(BaseCommand):
def handle(self, lt_username, **options): def handle(self, lt_username, **options):
lt = librarything.LibraryThing(username=lt_username) lt = librarything.LibraryThing(username=lt_username)
for (i, book) in enumerate(lt.parse_user_catalog()): for (i, book) in enumerate(lt.parse_user_catalog(view_style=5)):
print i, book["title"] print i, book["title"], book["isbn"], book["work_id"], book["book_id"]

View File

@ -17,7 +17,7 @@ class CeleryTask(models.Model):
user = models.ForeignKey(User, related_name="tasks", null=True) user = models.ForeignKey(User, related_name="tasks", null=True)
description = models.CharField(max_length=2048, null=True) # a description of what the task is description = models.CharField(max_length=2048, null=True) # a description of what the task is
function_name = models.CharField(max_length=1024) # used to reconstitute the AsyncTask with which to get status function_name = models.CharField(max_length=1024) # used to reconstitute the AsyncTask with which to get status
function_args = models.IntegerField() # not full generalized here -- takes only a single arg for now. function_args = models.IntegerField(null=True) # not full generalized here -- takes only a single arg for now.
active = models.NullBooleanField(default=True) active = models.NullBooleanField(default=True)
def __unicode__(self): def __unicode__(self):
@ -43,13 +43,15 @@ class CeleryTask(models.Model):
class Claim(models.Model): class Claim(models.Model):
rights_holder = models.ForeignKey("RightsHolder", related_name="claim", null=False ) rights_holder = models.ForeignKey("RightsHolder", related_name="claim", null=False )
work = models.ForeignKey("Work", related_name="claim", null=False ) work = models.ForeignKey("Work", related_name="claim", null=False )
user = models.ForeignKey(User, related_name="user", null=False ) user = models.ForeignKey(User, related_name="claim", null=False )
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
class RightsHolder(models.Model): class RightsHolder(models.Model):
email = models.CharField(max_length=100, blank=True) email = models.CharField(max_length=100, blank=True)
rights_holder_name = models.CharField(max_length=100, blank=True) rights_holder_name = models.CharField(max_length=100, blank=True)
owner = models.ForeignKey(User, related_name="rights_holder", null=False ) owner = models.ForeignKey(User, related_name="rights_holder", null=False )
def __unicode__(self):
return self.rights_holder_name
class Premium(models.Model): class Premium(models.Model):
PREMIUM_TYPES = ((u'00', u'Default'),(u'CU', u'Custom')) PREMIUM_TYPES = ((u'00', u'Default'),(u'CU', u'Custom'))

View File

@ -1,9 +1,15 @@
from django import forms from django import forms
from django.db import models from django.db import models
from regluit.core.models import UserProfile, RightsHolder
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from decimal import Decimal as D from decimal import Decimal as D
from regluit.core.models import UserProfile, RightsHolder, Claim
class ClaimForm(forms.ModelForm):
i_agree=forms.BooleanField()
class Meta:
model = Claim
widgets = { 'user': forms.HiddenInput, 'work': forms.HiddenInput }
class RightsHolderForm(forms.ModelForm): class RightsHolderForm(forms.ModelForm):
class Meta: class Meta:

View File

@ -0,0 +1,20 @@
{% extends "basedocumentation.html" %}
{% block doccontent %}
<h1>Rights Holder Claim Form </h1>
<form method="POST" action="#">
{% csrf_token %}
<h2> Rightsholder making claim </h2>
{{ rights_holder.rights_holder_name }}
<h2> Work being claimed </h2>
{{ work.title }}<br />
{{ work.author }}
<h2> Terms and Conditions </h2>
{{ form.as_p }}
[legal stuff goes here]
<input type="submit" name="submit" value="Confirm Claim" id="submit">
</form>
{% endblock %}

View File

@ -3,7 +3,7 @@
{% block doccontent %} {% block doccontent %}
<h1>unglue.it Tools for Rightsholders</h1> <h1>unglue.it Tools for Rightsholders</h1>
{% if request.user.rights_holder %} {% if request.user.rights_holder.count %}
<h2>Rights Holders That You Administer</h2> <h2>Rights Holders That You Administer</h2>
{% for rights_holder in request.user.rights_holder.all %} {% for rights_holder in request.user.rights_holder.all %}
<h3>Name: {{ rights_holder.rights_holder_name }}</h3> <h3>Name: {{ rights_holder.rights_holder_name }}</h3>
@ -12,6 +12,16 @@
</p> </p>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% if request.user.rights_holder.count %}
<h2>Works You Have Claimed</h2>
{% for claim in request.user.claim.all %}
<h3>Title: <a href="{% url work work_id=claim.work.id %}">{{claim.work.title }}</a></h3>
<p>Author: {{claim.work.author }}</p>
<p>On Behalf of: {{ claim.rights_holder.rights_holder_name }}</p>
<p>PSA #: {{ claim.rights_holder.id }}</p>
<p></p>
{% endfor %}
{% endif %}
<h2>Rights Holder FAQ/How to launch a campaign</h2> <h2>Rights Holder FAQ/How to launch a campaign</h2>

View File

@ -10,7 +10,7 @@
{{ form.as_p }} {{ form.as_p }}
<input type="submit" name="submit" value="Create" id="submit"> <input type="submit" name="submit" value="Create" id="submit">
</form> </form>
</form>
<h2> Accepted Rights Holders </h2> <h2> Accepted Rights Holders </h2>
{% for rights_holder in rights_holders %} {% for rights_holder in rights_holders %}
<h3>{{ rights_holder.rights_holder_name }}</h3> <h3>{{ rights_holder.rights_holder_name }}</h3>

View File

@ -110,7 +110,26 @@
</div> </div>
<div id="tabs-4" class="tabs"> <div id="tabs-4" class="tabs">
<div class="tabs-content"> <div class="tabs-content">
Want to Unglue infomation xxx <h3 class="tabcontent-title">Details for {{ work.title }}</h3>
{% if work.claim.count %}
<h4> Rights Information </h4>
<p> This work has been claimed by:</p>
<ul>
{% for claim in work.claim.all %}
<li>{{ claim.rights_holder.rights_holder_name }} </li>
{% endfor %}
</ul>
{% endif %}
{% if request.user.rights_holder.all.count %}
<h4> Claim this work:</h4>
<form method="GET" action="{% url claim %}">
{% csrf_token %}
{{ claimform.user }}
{{ claimform.work }}
{{ claimform.rights_holder }}
<input type="submit" name="submit" value="Claim" id="submit">
</form>
{% endif %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -17,6 +17,7 @@ urlpatterns = patterns(
name="privacy"), name="privacy"),
url(r"^rightsholders/$", TemplateView.as_view(template_name="rhtools.html"), url(r"^rightsholders/$", TemplateView.as_view(template_name="rhtools.html"),
name="rightsholders"), name="rightsholders"),
url(r"^rightsholders/claim/$", "claim", name="claim"),
url(r"^rh_admin/$", "rh_admin", name="rh_admin"), url(r"^rh_admin/$", "rh_admin", name="rh_admin"),
url(r"^faq/$", TemplateView.as_view(template_name="faq.html"), url(r"^faq/$", TemplateView.as_view(template_name="faq.html"),
name="faq"), name="faq"),

View File

@ -26,7 +26,7 @@ from regluit.core import models, bookloader
from regluit.core import userlists from regluit.core import userlists
from regluit.core.search import gluejar_search from regluit.core.search import gluejar_search
from regluit.core.goodreads import GoodreadsClient from regluit.core.goodreads import GoodreadsClient
from regluit.frontend.forms import UserData, ProfileForm, CampaignPledgeForm, GoodreadsShelfLoadingForm, RightsHolderForm from regluit.frontend.forms import UserData, ProfileForm, CampaignPledgeForm, GoodreadsShelfLoadingForm, RightsHolderForm, ClaimForm
from regluit.payment.manager import PaymentManager from regluit.payment.manager import PaymentManager
from regluit.payment.parameters import TARGET_TYPE_CAMPAIGN from regluit.payment.parameters import TARGET_TYPE_CAMPAIGN
@ -53,6 +53,8 @@ def stub(request):
def work(request, work_id, action='display'): def work(request, work_id, action='display'):
work = get_object_or_404(models.Work, id=work_id) work = get_object_or_404(models.Work, id=work_id)
campaign = work.last_campaign() campaign = work.last_campaign()
claimform = ClaimForm(data={'work':work_id, 'user':request.user.id })
if campaign: if campaign:
q = Q(campaign=campaign) | Q(campaign__isnull=True) q = Q(campaign=campaign) | Q(campaign__isnull=True)
premiums = models.Premium.objects.filter(q) premiums = models.Premium.objects.filter(q)
@ -61,15 +63,20 @@ def work(request, work_id, action='display'):
if action == 'setup_campaign': if action == 'setup_campaign':
return render(request, 'setup_campaign.html', {'work': work}) return render(request, 'setup_campaign.html', {'work': work})
else: else:
return render(request, 'work.html', {'work': work, 'premiums': premiums, 'ungluers': userlists.supporting_users(work, 5)}) return render(request, 'work.html', {
'work': work,
'premiums': premiums,
'ungluers': userlists.supporting_users(work, 5),
'claimform': claimform,
})
def workstub(request, title, imagebase, image, author, googlebooks_id, action='display'): def workstub(request, title, imagebase, image, author, googlebooks_id, action='display'):
premiums = None premiums = None
title = urllib.unquote_plus(title) title = urllib.unquote_plus(title)
imagebase = urllib.unquote_plus(imagebase) imagebase = urllib.unquote_plus(imagebase)
image = urllib.unquote_plus(image) image = urllib.unquote_plus(image)
author = urllib.unquote_plus(author) author = urllib.unquote_plus(author)
return render(request, 'workstub.html', {'title': title, 'image': image, 'imagebase': imagebase, 'author': author, 'googlebooks_id': googlebooks_id, 'premiums': premiums, 'ungluers': userlists.other_users(supporter, 5)}) return render(request, 'workstub.html', {'title': title, 'image': image, 'imagebase': imagebase, 'author': author, 'googlebooks_id': googlebooks_id, 'premiums': premiums, 'ungluers': userlists.other_users(supporter, 5)})
def subjects(request): def subjects(request):
order = request.GET.get('order') order = request.GET.get('order')
@ -100,6 +107,22 @@ def pledge(request,work_id):
return render(request,'pledge.html',{'work':work,'campaign':campaign, 'premiums':premiums, 'form':form}) return render(request,'pledge.html',{'work':work,'campaign':campaign, 'premiums':premiums, 'form':form})
def claim(request):
if request.method == 'GET':
data = request.GET
else:
data = request.POST
form = ClaimForm(data=data)
if form.is_valid():
if not models.Claim.objects.filter(work=data['work'], rights_holder=data['rights_holder']).count():
form.save()
return HttpResponseRedirect(reverse('work', kwargs={'work_id': data['work']}))
else:
work = models.Work.objects.get(id=data['work'])
rights_holder = models.RightsHolder.objects.get(id=data['rights_holder'])
context = {'form': form, 'work': work, 'rights_holder':rights_holder }
return render(request, "claim.html", context)
def rh_admin(request): def rh_admin(request):
if not is_admin(request.user): if not is_admin(request.user):
return render(request, "admins_only.html") return render(request, "admins_only.html")
@ -225,9 +248,9 @@ def search(request):
# also urlencode some parameters we'll need to pass to workstub in the title links # also urlencode some parameters we'll need to pass to workstub in the title links
# needs to be done outside the if condition # needs to be done outside the if condition
for result in results: for result in results:
result['urlimage'] = urllib.quote_plus(sub('^https?:\/\/','', result['image']).encode("utf-8"), safe='') result['urlimage'] = urllib.quote_plus(sub('^https?:\/\/','', result['image']).encode("utf-8"), safe='')
result['urlauthor'] = urllib.quote_plus(result['author'].encode("utf-8"), safe='') result['urlauthor'] = urllib.quote_plus(result['author'].encode("utf-8"), safe='')
result['urltitle'] = urllib.quote_plus(result['title'].encode("utf-8"), safe='') result['urltitle'] = urllib.quote_plus(result['title'].encode("utf-8"), safe='')
context = { context = {
"q": q, "q": q,

View File

@ -16,4 +16,3 @@ oauth2
mechanize mechanize
pyzotero pyzotero
freebase freebase
lxml

View File

@ -17,8 +17,8 @@ DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'NAME': 'unglueit_dev', 'NAME': 'unglueit_dev',
'USER': 'unglueit_dev', 'USER': 'please',
'PASSWORD': 'unglu3it', 'PASSWORD': 'unglueit',
'HOST': 'gluejardb.cboagmr25pjs.us-east-1.rds.amazonaws.com', 'HOST': 'gluejardb.cboagmr25pjs.us-east-1.rds.amazonaws.com',
'PORT': '', 'PORT': '',
} }
@ -88,5 +88,3 @@ BROKER_TRANSPORT = "redis"
BROKER_HOST = "localhost" BROKER_HOST = "localhost"
BROKER_PORT = 6379 BROKER_PORT = 6379
BROKER_VHOST = "0" BROKER_VHOST = "0"