added files

This commit is contained in:
sanj 2010-03-02 22:47:02 +05:30
parent 2869955951
commit d3aefba165
43 changed files with 3148 additions and 0 deletions

22
index/README.txt Executable file
View File

@ -0,0 +1,22 @@
Settings assumes mysql db name of 'idx' - change if your db is something else
Requires django-tagging from svn:
svn checkout http://django-tagging.googlecode.com/svn/trunk/ tagging-trunk
Requires python-dateutil
Requires elementtree: sudo easy_install elementtree
Requires simplejson: sudo easy_install simplejson
Setup DB:
mysql -u root
mysql> create database idx;
mysql> quit;
Create tables:
python manage.py syncdb (or python dropandcreatedb.py)
Run Server:
python manage.py runserver
Open Browser:
http://localhost:8000

0
index/__init__.py Executable file
View File

0
index/books/__init__.py Executable file
View File

80
index/books/admin.py Executable file
View File

@ -0,0 +1,80 @@
from books.models import *
from django.contrib import admin
from django.contrib.auth.models import User
def showCategories(obj):
cats = obj.categories.iterator()
s = ''
for c in cats:
s += c.name + " "
return s
def labelID(obj):
s = ''
if obj.box:
try:
loc = Location.objects.get(pk=obj.box.id)
s += loc.code + "/"
except:
s += ""
s += obj.box.box_no + "/"
# if obj.box.location:
# s += obj.box.location.code + "/"
s += str(obj.id)
return s
class BookImageInline(admin.TabularInline):
model = Image
extra = 3
class BookAdmin(admin.ModelAdmin):
ordering = ('title',)
search_fields = ['title', 'description']
inlines = [BookImageInline]
save_on_top = True;
class HardwareAdmin(admin.ModelAdmin):
ordering = ('-id',)
list_display = ('title', labelID, 'box', 'category', 'tags',)
list_filter = ('category', 'box', 'tags',)
list_editable = ('box', 'tags',)
search_fields = ['title', 'description',]
save_as = True
save_on_top = True
radio_fields = {'category': admin.HORIZONTAL}
class HardwareInline(admin.StackedInline):
model = Hardware
extra = 5
save_on_top = True;
class BoxAdmin(admin.ModelAdmin):
ordering = ('-id',)
search_fields = ['box_no', 'description']
list_display = ('box_no', 'location', 'tags',)
inlines = [HardwareInline]
list_filter = ('location',)
list_editable = ('location', 'tags',)
save_on_top = True;
class CatalogueAdmin(admin.ModelAdmin):
ordering = ('title',)
search_fields = ['title', 'artist']
save_on_top = True;
class Link(admin.ModelAdmin):
pass
class HardwareCategoryAdmin(admin.ModelAdmin):
pass
admin.site.register(Hardware, HardwareAdmin)
admin.site.register(Box, BoxAdmin)
admin.site.register(HardwareCategory, HardwareCategoryAdmin)
#admin.site.register(Link)
admin.site.register(HardwareLink)
admin.site.register(Catalogue, CatalogueAdmin)
admin.site.register(Book, BookAdmin)
admin.site.register(Owner)
admin.site.register(Location)

58
index/books/fields.py Executable file
View File

@ -0,0 +1,58 @@
from django.db import models
from django import forms
from django.utils.text import capfirst
class MultiSelectFormField(forms.MultipleChoiceField):
widget = forms.CheckboxSelectMultiple
def __init__(self, *args, **kwargs):
self.max_choices = kwargs.pop('max_choices', 0)
super(MultiSelectFormField, self).__init__(*args, **kwargs)
def clean(self, value):
if not value and self.required:
raise forms.ValidationError(self.error_messages['required'])
if value and self.max_choices and len(value) > self.max_choices:
raise forms.ValidationError('You must select a maximum of %s choice%s.'
% (apnumber(self.max_choices), pluralize(self.max_choices)))
return value
class MultiSelectField(models.Field):
__metaclass__ = models.SubfieldBase
def get_internal_type(self):
return "CharField"
def get_choices_default(self):
return self.get_choices(include_blank=False)
def _get_FIELD_display(self, field):
value = getattr(self, field.attname)
choicedict = dict(field.choices)
def formfield(self, **kwargs):
# don't call super, as that overrides default widget if it has choices
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name),
'help_text': self.help_text, 'choices':self.choices}
if self.has_default():
defaults['initial'] = self.get_default()
defaults.update(kwargs)
return MultiSelectFormField(**defaults)
def get_db_prep_value(self, value):
if isinstance(value, basestring):
return value
elif isinstance(value, list):
return ",".join(value)
def to_python(self, value):
if isinstance(value, list):
return value
return value.split(",")
def contribute_to_class(self, cls, name):
super(MultiSelectField, self).contribute_to_class(cls, name)
if self.choices:
func = lambda self, fieldname = name, choicedict = dict(self.choices):",".join([choicedict.get(value,value) for value in getattr(self,fieldname)])
setattr(cls, 'get_%s_display' % self.name, func)

226
index/books/isbnlookup.py Executable file
View File

@ -0,0 +1,226 @@
#!/usr/bin/python
import sys
import types
import time
import string
import fileinput
import re
import urllib
import isbnvalidate
from dateutil.parser import parse as parseDate import elementtree.ElementTree as ET
import feedparser
import simplejson
def myDate(datestr):
dateObj = parseDate(datestr)
month = dateObj.month
yr = dateObj.year
d = {
'year': yr,
'month': month
}
return d
def getHtmlFromUrl(url):
try:
u = urllib.urlopen(url)
t = u.read()
u.close
except:
return False
return t
def getimagesrc(isbn):
if (len(isbn) == 13):
newisbn = isbnvalidate.convertISBN13toISBN10(isbn)
isbn = newisbn.strip()
inf = urllib.urlopen('http://www.amazon.com/gp/product/images/' + isbn)
allhtml = inf.read()
inf.close()
imghtml = re.search('<div id="imageViewerDiv"><img src="(.*?)" id="prodImage" /></div>', allhtml)
imgsrc1 = imghtml.group(1)
return imgsrc1
#TODO: Try avoiding wrapping entire search in 'try'.
def ChaptersCa(isbn):
url = 'http://www.chapters.ca/books/details/default.asp?ISBN=' + isbn
html = getHtmlFromUrl(url)
if not html:
return False
if (string.find(html, 'chapters.indigo.ca: Error:') >= 0):
return False
titlehtml = re.search('<h1>(.*?)</h1>', html)
authorhtml = re.search('<p class="link">Author: (<a href=.*</a>)</p>', html)
dimensionshtml = re.search('<label>Dimensions:</label>(.*?)</p>', html)
publisherhtml = re.search('<label>Publisher:</label>(.*?)</p>', html)
publishedhtml = re.search('<label>Published:</label>(.*?)</p>', html)
#print (titlehtml, authorhtml, dimensionshtml, publisherhtml, publishedhtml)
try:
authorlinks = authorhtml.group(1).strip()
authors = re.findall('<a .*?>(.*?)</a>', authorlinks) # returns list of authors
for a in authors:
if re.search('See more ', a):
tmp = a
authors.remove(a)
d = {}
d['source'] = "ChaptersCa"
d['pages'] = dimensionshtml.group(1).split(",")[0].strip()
d['dimensions'] = dimensionshtml.group(1).split(",")[1].strip()
# dimensions = dimensionshtml.group(1).strip()
d['publisher'] = publisherhtml.group(1).strip()
d['published'] = myDate(publishedhtml.group(1).strip())
d['title'] = titlehtml.group(1).strip()
d['coverimg'] = getimagesrc(isbn)
d['url'] = url
d['authors'] = authors
except:
return False
return d
#TODO: Regex failing. Test.
def Amazon(isbn):
url = 'http://www.amazon.com/gp/search/ref=sr_adv_b/?field-isbn=' + isbn
html = getHtmlFromUrl(url)
if not html:
return False
if (string.find(html, 'Your search did not match any products.') >= 0):
return False
titlehtml = re.search('<span id="btAsinTitle" style="">(.*?)</span>', html)
paperbackhtml = re.search('<li><b>Paperback:</b>(.*?)</li>', html)
weighthtml = re.search('<li><b>Shipping Weight:</b>(.*?)\(.*?\)</li>', html)
publisherhtml = re.search('<li><b>Publisher:</b>(.*?) \((.*?)\)</li>', html)
authors = re.findall('<a href=".*?">(.*?)</a> \(Author\)', html) # returns list of authors
try:
d = {}
d['source'] = 'Amazon'
d['pages'] = paperbackhtml.group(1).strip()
d['dimensions'] = weighthtml.group(1).strip()
d['publisher'] = publisherhtml.group(1).strip()
d['published'] = myDate(publisherhtml.group(2).strip())
d['title'] = titlehtml.group(1).strip()
d['coverimg'] = getimagesrc(isbn)
d['url'] = url
except:
return False
# sourceurl = 'http://www.amazon.com/gp/search/ref=sr_adv_b/?field-isbn=' + isbn
return d
#TODO: Dont return False if a single key does not exist
def ISBNdb(isbn):
ISBNdb_KEY = 'HLRQD9WN'
# http://isbndb.com/api/books.xml?access_key=HLRQD9WN&index1=isbn&value1=1400082463
url = 'http://isbndb.com/api/books.xml?access_key=' + ISBNdb_KEY + '&index1=isbn&value1=' + isbn
try:
xml = getHtmlFromUrl(url)
tree = ET.XML(xml)
except:
return False
results = tree.find("BookList").get("total_results")
if (results < 1):
return False
d = {}
d['source'] = 'ISBNdb'
try:
book = tree.getchildren()[0].getchildren()[0]
d['title'] = book.findtext("Title")
titleLong = book.findtext("TitleLong").strip()
if (titleLong != ''):
d['title'] = titleLong
d['publisher'] = book.findtext("PublisherText")
d['summary'] = book.findtext("Summary")
d['notes'] = book.findtext("Notes")
authorsTxt = book.findtext("AuthorsText")
d['authors'] = authorsTxt.split(",")
except:
return False
return d
#TODO: Fetch weblinks.
#TODO: Clean-up to not go to exception when can't find a single attribute
#TODO: Go through XML and fetch more relevant fields.
#TODO: Make URL more specific to look for ISBN codes. [IMP!!]
def GoogleBooks(isbn):
url = 'http://books.google.com/books/feeds/volumes?q=' + isbn
try:
feed = feedparser.parse(url)
book = feed.entries[0]
except:
return False
try:
d = {}
d['source'] = 'GoogleBooks'
d['title'] = book.title
# d['summary'] = book.description
d['pages'] = book.dc_format
d['author'] = [book.author]
d['publisher'] = book.publisher
except:
return False
return d
#TODO: Don't return book! Return dict according to our fields.
#TODO: Fetch Chapters in a meaningful way
#TODO: Add relevant fields - first_sentence, etc.
def OpenLibrary(isbn):
if (len(isbn) == 10):
url1 = 'http://openlibrary.org/api/things?query={%22type%22%3A+%22\%2Ftype\%2Fedition%22%2C+%22isbn_10%22%3A+%22' + isbn + '%22}&prettyprint=true&text=true'
elif (len(isbn) == 13):
url1 = 'http://openlibrary.org/api/things?query={%22type%22%3A+%22\%2Ftype\%2Fedition%22%2C+%22isbn_13%22%3A+%22' + isbn + '%22}&prettyprint=true&text=true'
else:
return False
json = getHtmlFromUrl(url1)
print json
if not json:
return False
j = simplejson.loads(json)
if (j['status'] != 'ok' or len(j['result']) == 0):
return False
olId = j['result'][0]
url2 = "http://openlibrary.org/api/get?key=" + olId
json2 = getHtmlFromUrl(url2)
book = simplejson.loads(json2)
return book
#TODO: write regex parser for LookUpByIsbn to return esp. Reviews
def LookUpByIsbn(isbn):
url = "http://www.lookupbyisbn.com/itemDetail.aspx?type=Books&id=" + isbn
return False
def searchAll(isbn, **kwargs):
sources = [OpenLibrary, GoogleBooks, ISBNdb, Amazon, ChaptersCa]
return getAggregate(sources, isbn)
#TODO: Handle specific cases of 'author' and 'weblink' fields. Also, other lists, like Chapters.
def getAggregate(sources, isbn, **kwargs):
if not isbnvalidate.isValidISBN(isbn):
return {'errors': "Invalid ISBN"}
# if kwargs['isbnformat'] == 10 and len(isbn) == 13:
# isbn = isbnvalidate.convertISBN13toISBN10(isbn)
# if kwargs['isbnformat'] == 13 and len(isbn) == 10:
# isbn = isbnvalidate.convertISBN10toI3(isbn)
results = []
for source in sources:
rDict = source(isbn)
if (rDict):
results.append(rDict)
dFinal = {}
for result in results:
for key in r.keys():
if (key not in dFinal.keys()):
dFinal[key] = result[key]
return dFinal

418
index/books/isbnlookup2.py Executable file
View File

@ -0,0 +1,418 @@
#!/usr/bin/python
# Usage: searchAll(isbn) with isbn passed as a string.
# Returns values defined in returnDict
import sys
import types
import time
import string
import fileinput
import re
import urllib
import isbnvalidate
from dateutil.parser import parse as parseDate import elementtree.ElementTree as ET
import feedparser
import simplejson
# ISBNlist = [ '9780596001322', '9781590593912', '9780596003135', '0596102062', '0131103628', '1905789041', '1400082463' ]
def searchAll(isbn, **kwargs):
sources = [OpenLibrary, GoogleBooks, ISBNdb, Amazon, ChaptersCa]
# sources = [Amazon]
return getAggregate(sources, isbn)
def getAggregate(sources, isbn, **kwargs):
if not isbnvalidate.isValidISBN(isbn):
return {'errors': "Invalid ISBN"}
returnDict = {
'title': u'',
'titleLong': u'',
'authors': [],
'weblinks': [],
'published': {},
'publisher': u'',
'isbn': u'',
'first_sentence': u'',
'chapters': [],
'netReviews': [],
'summaries': [],
'pages': 0,
'dimensions': u'',
'weight': 0,
'relatedBooks': [],
'tags': [],
'sources': [],
'imageUrls': [],
'coverImage': u''
}
# if kwargs['isbnformat'] == 10 and len(isbn) == 13:
# isbn = isbnvalidate.convertISBN13toISBN10(isbn)
# if kwargs['isbnformat'] == 13 and len(isbn) == 10:
# isbn = isbnvalidate.convertISBN10toI3(isbn)
results = []
for source in sources:
rDict = source(isbn)
if (rDict):
results.append(rDict)
lists = []
# lists = ['authors', 'weblinks', 'chapters', 'netReviews', 'summaries', 'relatedBooks', 'source']
for result in results:
for key in result.keys():
if returnDict.has_key(key):
if isinstance(result[key], list):
if key not in lists:
lists.append(key)
for r in result[key]:
returnDict[key].append(r)
else:
returnDict[key] = result[key]
for l in lists:
if len(returnDict[l]) > 1:
returnDict[l] = unique(returnDict[l])
returnDict['isbn'] = isbn
returnDict['coverImage'] = getCoverImageUrl(isbn)
return returnDict
def ChaptersCa(isbn):
url = 'http://www.chapters.ca/books/details/default.asp?ISBN=' + isbn
html = getHtmlFromUrl(url)
if not html:
return False
if (string.find(html, 'chapters.indigo.ca: Error:') >= 0):
return False
titlehtml = re.search('<h1>(.*?)</h1>', html)
authorhtml = re.search('<p class="link">Author: (<a href=.*</a>)</p>', html)
dimensionshtml = re.search('<label>Dimensions:</label>(.*?)</p>', html)
publisherhtml = re.search('<label>Publisher:</label>(.*?)</p>', html)
publishedhtml = re.search('<label>Published:</label>(.*?)</p>', html)
#print (titlehtml, authorhtml, dimensionshtml, publisherhtml, publishedhtml)
try:
authorlinks = authorhtml.group(1).strip()
authors = re.findall('<a .*?>(.*?)</a>', authorlinks) # returns list of authors
i = 0
for a in authors:
if re.search('See more ', a):
authors.remove(a)
else:
authors[i] = a.strip()
if (authors[i] == ''):
authors.remove(a)
i += 1
except:
authors = []
d = {}
d['sources'] = [{'name': 'ChaptersCa', 'url': url}]
try:
d['pages'] = dimensionshtml.group(1).split(",")[0].strip()
d['dimensions'] = dimensionshtml.group(1).split(",")[1].strip()
except:
pass
# dimensions = dimensionshtml.group(1).strip()
try:
d['publisher'] = publisherhtml.group(1).strip()
except:
pass
try:
d['published'] = myDate(publishedhtml.group(1).strip())
except:
pass
try:
d['title'] = titlehtml.group(1).strip()
except:
pass
try:
d['imageUrls'] = [getimagesrc(isbn)]
except:
pass
d['authors'] = authors
return d
def Amazon(isbn):
url = 'http://www.amazon.com/gp/search/ref=sr_adv_b/?field-isbn=' + isbn
html = getHtmlFromUrl(url)
if not html:
return False
if (string.find(html, 'Your search did not match any products.') >= 0):
return False
titlehtml = re.search('<span id="btAsinTitle" style="">(.*?)</span>', html)
paperbackhtml = re.search('<li><b>Paperback:</b>(.*?)</li>', html)
weighthtml = re.search('<li><b>Shipping Weight:</b>(.*?)\(.*?\)</li>', html)
publisherhtml = re.search('<li><b>Publisher:</b>(.*?) \((.*?)\)</li>', html)
authors = re.findall('<a href=".*?">(.*?)</a> \(Author\)', html) # returns list of authors
d = {}
d['sources'] = [{'name': 'Amazon', 'url': url}]
try:
d['pages'] = paperbackhtml.group(1).strip()
except:
pass
try:
d['dimensions'] = weighthtml.group(1).strip()
except:
pass
try:
d['publisher'] = publisherhtml.group(1).strip()
except:
pass
try:
d['published'] = myDate(publisherhtml.group(2).strip())
except:
pass
try:
d['title'] = titlehtml.group(1).strip()
except:
pass
# sourceurl = 'http://www.amazon.com/gp/search/ref=sr_adv_b/?field-isbn=' + isbn
d['authors'] = authors
return d
def ISBNdb(isbn):
ISBNdb_KEY = 'HLRQD9WN'
# http://isbndb.com/api/books.xml?access_key=HLRQD9WN&index1=isbn&value1=1400082463
url = 'http://isbndb.com/api/books.xml?access_key=' + ISBNdb_KEY + '&index1=isbn&value1=' + isbn
try:
xml = getHtmlFromUrl(url)
tree = ET.XML(xml)
except:
return False
results = tree.find("BookList").get("total_results")
if (results < 1):
return False
d = {}
d['sources'] = [{'name': 'ISBNdb', 'url': url}]
try:
book = tree.getchildren()[0].getchildren()[0]
except:
return False
try:
d['titleLong'] = book.findtext("TitleLong").strip()
except:
pass
try:
d['title'] = book.findtext("Title")
except:
pass
try:
d['publisher'] = book.findtext("PublisherText")
except:
pass
try:
d['summaries'] = [book.findtext("Summary")]
except:
pass
try:
d['notes'] = book.findtext("Notes")
except:
pass
try:
authors = book.findtext("AuthorsText").split(",")
i = 0
for a in authors:
if a.strip() == '':
authors.remove(a)
except:
pass
return d
#TODO: Make URL more specific to look for ISBN codes. [IMP!!] - Alden
def GoogleBooks(isbn):
url = 'http://books.google.com/books/feeds/volumes?q=' + isbn
try:
feed = feedparser.parse(url)
book = feed.entries[0]
except:
return False
d = {}
d['sources'] = [{'name': 'GoogleBooks', 'url': url}]
try:
d['title'] = book.title
except:
pass
try:
d['summaries'] = [book.dc_description]
except:
pass
try:
d['pages'] = book.dc_format
except:
pass
try:
d['author'] = [book.dc_creator]
except:
pass
try:
d['publisher'] = book.publisher
except:
pass
try:
d['published'] = myDate(book.dc_date)
except:
pass
try:
d['weblinks'] = []
for link in book['links']:
l = {'url': link['href'], 'description': link['rel'] }
d['weblinks'].append(l)
except:
pass
return d
def OpenLibrary(isbn):
# 'http://openlibrary.org/api/things?query={%22type%22%3A+%22\%2Ftype\%2Fedition%22%2C+%22isbn_13%22%3A+%221400082463%22}&prettyprint=true&text=true'
if (len(isbn) == 10):
url1 = 'http://openlibrary.org/api/things?query={%22type%22%3A+%22\%2Ftype\%2Fedition%22%2C+%22isbn_10%22%3A+%22' + isbn + '%22}&prettyprint=true&text=true'
elif (len(isbn) == 13):
url1 = 'http://openlibrary.org/api/things?query={%22type%22%3A+%22\%2Ftype\%2Fedition%22%2C+%22isbn_13%22%3A+%22' + isbn + '%22}&prettyprint=true&text=true'
else:
return False
json = getHtmlFromUrl(url1)
# print json
if not json:
return False
j = simplejson.loads(json)
if (j['status'] != 'ok' or len(j['result']) == 0):
return False
olId = j['result'][0]
url2 = "http://openlibrary.org/api/get?key=" + olId
try:
json2 = getHtmlFromUrl(url2)
book = simplejson.loads(json2)
r = book['result']
except:
return False
d = {}
d['sources'] = [{'name': "OpenLibrary", 'url': url2}]
try:
d['first_sentence'] = r['first_sentence']['value']
except:
pass
try:
d['pages'] = r['number_of_pages']
except:
pass
try:
d['dimensions'] = r['physical_dimensions']
except:
pass
try:
d['published'] = myDate(r['publish_date'])
except:
pass
chapters = []
try:
for chapter in r['table_of_contents']:
c = {}
c['title'] = chapter['title']
c['synopsis'] = ''
c['link'] = ''
chapters.append(c)
d['chapters'] = chapters
except:
pass
try:
d['weight'] = r['weight']
except:
pass
tags = []
try:
for tag in r['subjects']:
tags.append(tag)
d['tags'] = tags
except:
pass
try:
d['titleLong'] = r['subtitle']
except:
pass
return d
#TODO: write regex parser for LookUpByIsbn to return esp. Reviews. - Alden (low priority)
def LookUpByIsbn(isbn):
url = "http://www.lookupbyisbn.com/itemDetail.aspx?type=Books&id=" + isbn
return False
#TODO: Write regex parser for CB-India computer books. -Alden (low priority)
def CBIndia(isbn):
return False
#TODO: This seems like a fairly large collection of Indian books
def FirstAndSecond(isbn):
url = "http://www.firstandsecond.com/store/books/info/search.asp?styp=isb&isb=" + isbn
return False
def myDate(datestr):
dateObj = parseDate(datestr)
month = dateObj.month
yr = dateObj.year
day = dateObj.day
d = {
'year': yr,
'month': month,
'day': day
}
return d
def getHtmlFromUrl(url):
try:
u = urllib.urlopen(url)
t = u.read()
u.close
except:
return False
return t
def getCoverImageUrl(isbn):
if (len(isbn) == 13):
newisbn = isbnvalidate.convertISBN13toISBN10(isbn)
isbn = newisbn.strip()
try:
inf = urllib.urlopen('http://www.amazon.com/gp/product/images/' + isbn)
allhtml = inf.read()
inf.close()
imghtml = re.search('<div id="imageViewerDiv"><img src="(.*?)" id="prodImage" /></div>', allhtml)
imgsrc1 = imghtml.group(1)
except:
# print "Unable to retrieve book cover image"
return ''
return imgsrc1
def unique(seq):
checked = []
for e in seq:
if e not in checked:
checked.append(e)
return checked

View File

@ -0,0 +1,380 @@
#!/usr/bin/python
# (c) CAMP - http://camputer.org
# Licensed GPLv3
import sys
import types
import time
import string
import fileinput
import re
import urllib
import isbnvalidate
from dateutil.parser import parse as parseDate import elementtree.ElementTree as ET
import feedparser
import simplejson
#ISBNlist = [ '9780596001322', '9781590593912', '9780596003135', '0596102062', '0131103628', '1905789041', '1400082463' ]
returnDict = {
'title': u'',
'titleLong': u'',
'authors': [],
'weblinks': [],
'published': {},
'publisher': u'',
'isbn': u'',
'first_sentence': u'',
'chapters': [],
'netReviews': [],
'summaries': [],
'pages': 0,
'dimensions': u'',
'weight': 0,
'relatedBooks': [],
'tags': [],
'source': [],
'imageUrls': []
}
def searchAll(isbn, **kwargs):
sources = [OpenLibrary, ISBNdb, Amazon, ChaptersCa]
return getAggregate(sources, isbn)
#TODO: Author field sometimes has a space.
def getAggregate(sources, isbn, **kwargs):
if not isbnvalidate.isValidISBN(isbn):
return {'errors': "Invalid ISBN"}
# if kwargs['isbnformat'] == 10 and len(isbn) == 13:
# isbn = isbnvalidate.convertISBN13toISBN10(isbn)
# if kwargs['isbnformat'] == 13 and len(isbn) == 10:
# isbn = isbnvalidate.convertISBN10toI3(isbn)
results = []
for source in sources:
rDict = source(isbn)
if (rDict):
results.append(rDict)
lists = []
# lists = ['authors', 'weblinks', 'chapters', 'netReviews', 'summaries', 'relatedBooks', 'source']
for result in results:
for key in result.keys():
if returnDict.has_key(key):
if isinstance(result[key], list):
if key not in lists:
lists.append(key)
for r in result[key]:
if r:
returnDict[key].append(r)
else:
returnDict[key] = result[key]
for l in lists:
if len(returnDict[l]) > 1:
returnDict[l] = unique(returnDict[l])
returnDict['isbn'] = isbn
return returnDict
def ChaptersCa(isbn):
url = 'http://www.chapters.ca/books/details/default.asp?ISBN=' + isbn
html = getHtmlFromUrl(url)
if not html:
return False
if (string.find(html, 'chapters.indigo.ca: Error:') >= 0):
return False
titlehtml = re.search('<h1>(.*?)</h1>', html)
authorhtml = re.search('<p class="link">Author: (<a href=.*</a>)</p>', html)
dimensionshtml = re.search('<label>Dimensions:</label>(.*?)</p>', html)
publisherhtml = re.search('<label>Publisher:</label>(.*?)</p>', html)
publishedhtml = re.search('<label>Published:</label>(.*?)</p>', html)
#print (titlehtml, authorhtml, dimensionshtml, publisherhtml, publishedhtml)
try:
authorlinks = authorhtml.group(1).strip()
authors = re.findall('<a .*?>(.*?)</a>', authorlinks) # returns list of authors
for a in authors:
if re.search('See more ', a):
authors.remove(a)
except:
authors = []
d = {}
d['source'] = [{'name': 'ChaptersCa', 'url': url}]
try:
d['pages'] = dimensionshtml.group(1).split(",")[0].strip()
d['dimensions'] = dimensionshtml.group(1).split(",")[1].strip()
except:
pass
# dimensions = dimensionshtml.group(1).strip()
try:
d['publisher'] = publisherhtml.group(1).strip()
except:
pass
try:
d['published'] = myDate(publishedhtml.group(1).strip())
except:
pass
try:
d['title'] = titlehtml.group(1).strip()
except:
pass
try:
d['coverimg'] = getimagesrc(isbn)
except:
pass
d['authors'] = authors
return d
#TODO: Regex failing. Test. - Alden
def Amazon(isbn):
url = 'http://www.amazon.com/gp/search/ref=sr_adv_b/?field-isbn=' + isbn
html = getHtmlFromUrl(url)
if not html:
return False
if (string.find(html, 'Your search did not match any products.') >= 0):
return False
titlehtml = re.search('<span id="btAsinTitle" style="">(.*?)</span>', html)
paperbackhtml = re.search('<li><b>Paperback:</b>(.*?)</li>', html)
weighthtml = re.search('<li><b>Shipping Weight:</b>(.*?)\(.*?\)</li>', html)
publisherhtml = re.search('<li><b>Publisher:</b>(.*?) \((.*?)\)</li>', html)
authors = re.findall('<a href=".*?">(.*?)</a> \(Author\)', html) # returns list of authors
d = {}
d['source'] = [{'name': 'Amazon', 'url': url}]
try:
d['pages'] = paperbackhtml.group(1).strip()
except:
pass
try:
d['dimensions'] = weighthtml.group(1).strip()
except:
pass
try:
d['publisher'] = publisherhtml.group(1).strip()
except:
pass
try:
d['published'] = myDate(publisherhtml.group(2).strip())
except:
pass
try:
d['title'] = titlehtml.group(1).strip()
except:
pass
try:
d['coverimg'] = getimagesrc(isbn)
except:
pass
# sourceurl = 'http://www.amazon.com/gp/search/ref=sr_adv_b/?field-isbn=' + isbn
return d
def ISBNdb(isbn):
ISBNdb_KEY = 'HLRQD9WN'
# http://isbndb.com/api/books.xml?access_key=HLRQD9WN&index1=isbn&value1=1400082463
url = 'http://isbndb.com/api/books.xml?access_key=' + ISBNdb_KEY + '&index1=isbn&value1=' + isbn
try:
xml = getHtmlFromUrl(url)
tree = ET.XML(xml)
except:
return False
results = tree.find("BookList").get("total_results")
if (results < 1):
return False
d = {}
d['source'] = [{'name': 'ISBNdb', 'url': url}]
try:
book = tree.getchildren()[0].getchildren()[0]
except:
return False
try:
d['titleLong'] = book.findtext("TitleLong").strip()
except:
pass
try:
d['title'] = book.findtext("Title")
except:
pass
try:
d['publisher'] = book.findtext("PublisherText")
except:
pass
try:
d['summaries'] = [book.findtext("Summary")]
except:
pass
try:
d['notes'] = book.findtext("Notes")
except:
pass
try:
authorsTxt = book.findtext("AuthorsText")
d['authors'] = authorsTxt.split(",")
except:
pass
return d
#TODO: Fetch weblinks. - Sanj
#TODO: Go through XML and fetch more relevant fields. - Sanj
#TODO: Make URL more specific to look for ISBN codes. [IMP!!] - Alden
def GoogleBooks(isbn):
url = 'http://books.google.com/books/feeds/volumes?q=' + isbn
try:
feed = feedparser.parse(url)
book = feed.entries[0]
except:
return False
d = {}
d['source'] = [{'name': 'GoogleBooks', 'url': url}]
try:
d['title'] = book.title
except:
pass
# d['summary'] = book.description
try:
d['pages'] = book.dc_format
except:
pass
try:
d['author'] = [book.author]
except:
pass
try:
d['publisher'] = book.publisher
except:
pass
return d
def OpenLibrary(isbn):
# 'http://openlibrary.org/api/things?query={%22type%22%3A+%22\%2Ftype\%2Fedition%22%2C+%22isbn_13%22%3A+%221400082463%22}&prettyprint=true&text=true'
if (len(isbn) == 10):
url1 = 'http://openlibrary.org/api/things?query={%22type%22%3A+%22\%2Ftype\%2Fedition%22%2C+%22isbn_10%22%3A+%22' + isbn + '%22}&prettyprint=true&text=true'
elif (len(isbn) == 13):
url1 = 'http://openlibrary.org/api/things?query={%22type%22%3A+%22\%2Ftype\%2Fedition%22%2C+%22isbn_13%22%3A+%22' + isbn + '%22}&prettyprint=true&text=true'
else:
return False
json = getHtmlFromUrl(url1)
print json
if not json:
return False
j = simplejson.loads(json)
if (j['status'] != 'ok' or len(j['result']) == 0):
return False
olId = j['result'][0]
url2 = "http://openlibrary.org/api/get?key=" + olId
try:
json2 = getHtmlFromUrl(url2)
book = simplejson.loads(json2)
r = book['result']
except:
return False
d = {}
d['source'] = [{'name': "OpenLibrary", 'url': url2}]
try:
d['first_sentence'] = r['first_sentence']['value']
except:
pass
try:
d['pages'] = r['number_of_pages']
except:
pass
try:
d['dimensions'] = r['physical_dimensions']
except:
pass
try:
d['published'] = myDate(r['publish_date'])
except:
pass
chapters = []
try:
for chapter in r['table_of_contents']:
chapters.append(chapter['title'])
d['chapters'] = chapters
except:
pass
try:
d['weight'] = r['weight']
except:
pass
tags = []
try:
for tag in r['subjects']:
tags.append(tag)
d['tags'] = tags
except:
pass
try:
d['titleLong'] = r['subtitle']
except:
pass
return d
#TODO: write regex parser for LookUpByIsbn to return esp. Reviews. - Alden (low priority)
def LookUpByIsbn(isbn):
url = "http://www.lookupbyisbn.com/itemDetail.aspx?type=Books&id=" + isbn
return False
#TODO: Write regex parser for CB-India computer books. -Alden (low priority)
def CBIndia(isbn):
return False
def myDate(datestr):
dateObj = parseDate(datestr)
month = dateObj.month
yr = dateObj.year
day = dateObj.day
d = {
'year': yr,
'month': month,
'day': day
}
return d
def getHtmlFromUrl(url):
try:
u = urllib.urlopen(url)
t = u.read()
u.close
except:
return False
return t
def getimagesrc(isbn):
if (len(isbn) == 13):
newisbn = isbnvalidate.convertISBN13toISBN10(isbn)
isbn = newisbn.strip()
inf = urllib.urlopen('http://www.amazon.com/gp/product/images/' + isbn)
allhtml = inf.read()
inf.close()
imghtml = re.search('<div id="imageViewerDiv"><img src="(.*?)" id="prodImage" /></div>', allhtml)
imgsrc1 = imghtml.group(1)
return imgsrc1
def unique(seq):
checked = []
for e in seq:
if e not in checked:
checked.append(e)
return checked

166
index/books/isbnvalidate.py Executable file
View File

@ -0,0 +1,166 @@
##############################################################################
#
# Copyright (c) 2006 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Kirbi ISBN handling functions
"""
class InvalidISBN(ValueError):
"""This is not a valid ISBN-10 or ISBN-13"""
def filterDigits(input):
""" Strip the input of all non-digits, but retain last X if present. """
input = input.strip()
digits = [c for c in input if c.isdigit()]
# an X may appear as check digit in ISBN-10
if input[-1].upper() == 'X':
digits.append('X')
return ''.join(digits)
def checksumISBN10(digits):
""" Return ISBN-10 check digit as a string of len 1 (may be 0-9 or X)
References:
http://www.isbn-international.org/en/userman/chapter4.html#usm4_4
http://www.bisg.org/isbn-13/conversions.html
"""
sum = 0
for i, weight in enumerate(range(10,1,-1)):
sum += int(digits[i]) * weight
check = 11 - sum % 11
if check == 10: return 'X'
elif check == 11: return '0'
return str(check)
def checksumEAN(digits):
""" Return EAN check digit as a string (may be 0-9)
Every ISBN-13 is a valid EAN
Reference:
http://www.bisg.org/isbn-13/conversions.html
"""
sum = 0
for i, d in enumerate(digits[:12]):
weight = i%2*2+1
sum += int(d) * weight
check = 10 - sum % 10
if check == 10: return '0'
return str(check)
def validatedEAN(digits):
if not digits:
return None
if len(digits) != 13:
digits = filterDigits(digits)
if len(digits) != 13:
return None
if digits[-1] == checksumEAN(digits):
return digits
def validatedISBN10(digits):
if not digits:
return None
if len(digits) != 10:
digits = filterDigits(digits)
if len(digits) != 10:
return None
if digits[-1] == checksumISBN10(digits):
return digits
def validatedISBN13(digits):
if not digits:
return None
if digits.strip()[:3] not in ['978','979']:
return None
return validatedEAN(digits)
def isValidISBN10(digits):
return validatedISBN10(digits) is not None
def isValidISBN13(digits):
return validatedISBN13(digits) is not None
def isValidEAN(digits):
return validatedEAN(digits) is not None
def isValidISBN(digits):
return isValidISBN10(digits) or isValidISBN13(digits)
def convertISBN10toISBN13(digits):
if len(digits) > 10:
digits = filterDigits(digits)
if len(digits) != 10:
raise InvalidISBN, '%s is not a valid ISBN-10'
else:
digits = '978' + digits[:-1]
return digits + checksumEAN(digits)
def convertISBN13toISBN10(digits):
if len(digits) > 13:
digits = filterDigits(digits)
if len(digits) != 13:
raise InvalidISBN, '%s is not a valid ISBN-13'
if digits.startswith('978'):
digits = digits[3:-1]
return digits + checksumISBN10(digits)
elif digits.startswith('979'):
raise InvalidISBN, '%s is a valid ISBN-13 but has no ISBN-10 equivalent'
else:
raise InvalidISBN, '%s is not a valid ISBN-13 (wrong prefix)'
def toISBN13(digits):
digits = filterDigits(digits)
if isValidISBN13(digits): return digits
else:
return convertISBN10toISBN13(digits)
# Note: ISBN group identifiers related to languages
# http://www.isbn-international.org/en/identifiers/allidentifiers.html
# http://www.loc.gov/standards/iso639-2/php/code_list.php
lang_groups = {
'en':(0,1),'fr':(2,),'de':(3,),'jp':(4,), 'ru':(5,),
'es':(84, # Spain
950, 987, # Argentina
956, # Chile
958, # Colombia
959, # Cuba
968, 970, # Mexico
980, # Venezuela
9942, 9978, # Ecuador
9945, 99934, # Dominican Republic
9962, # Panama
9968, # Costa Rica (and 9977)
9972, # Peru
9974, # Uruguay
99922, 99939, # Guatemala
99923, # El Salvador
99924, # Nicaragua
99925, 99953, # Paraguay
99926, # Honduras
),
'pt':(85, # Brazil
972, 989, # Portugal
),
}
group_lang = {}
for lang, groups in lang_groups.iteritems():
for group in groups:
group_lang[str(group)] = lang
def convertISBN13toLang(isbn13):
assert len(isbn13)==13
registration_group_field = isbn13[3:8]
for i in range(1,6):
possible_group = registration_group_field[:i]
if possible_group in group_lang:
return group_lang[possible_group]
return None

228
index/books/models.py Executable file
View File

@ -0,0 +1,228 @@
from django.db import models
from thumbs import ImageWithThumbsField
from django.contrib.auth.models import User, Group
from tagging.fields import TagField
from tagging.models import Tag
from myfields import ISBNField
from books.fields import MultiSelectField, MultiSelectFormField
class Book(models.Model):
title = models.CharField(max_length=255)
isbn = models.CharField(max_length=20, help_text='ISBN Code - digits only, no dashes, etc.', unique=True)
pages = models.CharField(max_length=100, blank=True)
weight = models.CharField(max_length=100, blank=True)
dimensions = models.CharField(max_length=120, blank=True, help_text='no. of pages and dimensions of book')
published = models.DateField(blank=True)
first_sentence = models.TextField(blank=True)
publisher = models.CharField(max_length=255, blank=True)
description = models.TextField(blank=True)
owner = models.ForeignKey('Owner', blank=True)
location = models.ForeignKey('Location', blank=True)
tags = TagField("Tags", blank=True, help_text="Enter as many tags as you like, separated by commas")
autotags = TagField("AutoTags", blank=True, help_text="Will be auto filled by lookup script")
authors = models.ManyToManyField('Author')
mainimg = ImageWithThumbsField(upload_to='images/uploads', blank=True, sizes=((125,125),(200,200)), height_field="image_height", width_field='image_width')
added_by = models.ForeignKey(User, editable=False)
date_added = models.DateTimeField(auto_now=True, editable=False)
def __unicode__(self):
return self.title
class ChapterEntry(models.Model):
title = models.TextField()
synopsis = models.TextField(blank=True)
link = models.URLField(max_length=255, blank=True)
book = models.ForeignKey(Book)
def __unicode__(self):
return self.title
class Weblink(models.Model):
url = models.URLField()
description = models.CharField(max_length=255, blank=True)
book = models.ForeignKey(Book)
def __unicode__(self):
return self.url
class Author(models.Model):
name = models.CharField(max_length=255)
bio = models.TextField(blank=True)
def __unicode__(self):
return self.name
class WebReview(models.Model):
txt = models.TextField(blank=True)
book = models.ForeignKey(Book)
def __unicode__(self):
return self.txt
class Summary(models.Model):
txt = models.TextField(blank=True)
book = models.ForeignKey(Book)
def __unicode__(self):
return self.txt
class RelatedBook(models.Model):
url = models.URLField(blank=True)
title = models.CharField(max_length=255)
isbn = models.CharField(max_length=16, blank=True)
book = models.ForeignKey(Book)
def __unicode__(self):
return self.title
class ImageUrl(models.Model):
typ = models.CharField(max_length=255)
url = models.URLField(blank=True)
book = models.ForeignKey(Book)
def __unicode__(self):
return self.url
class Source(models.Model):
name = models.CharField(max_length=255)
url = models.URLField()
book = models.ForeignKey(Book)
def __unicode__(self):
return self.name
class Image(models.Model):
image = ImageWithThumbsField(upload_to='images/uploads', sizes=((125,125),(200,200)), height_field='height', width_field='width')
book = models.ForeignKey(Book)
typ = models.CharField(max_length=255)
def __unicode__(self):
return self.image
class Owner(models.Model):
name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
class Location(models.Model):
name = models.CharField(max_length=255)
code = models.CharField(max_length=10, unique=True)
def __unicode__(self):
return "%s (%s)" % (self.name, self.code)
##################################### CATALOGUES ######################################################
class Catalogue(models.Model):
title = models.CharField(max_length=255)
artist = models.CharField(max_length=255, blank=True)
exhibition = models.CharField(max_length=255, blank=True)
published_date = models.DateField(blank=True, verbose_name="Published on")
condition = models.TextField(blank=True)
description = models.TextField(blank=True)
owner = models.ForeignKey('Owner', blank=True)
location = models.ForeignKey(Location, blank=True)
tags = TagField("Tags", blank=True, help_text="Enter as many tags as you like, separated by commas")
added_by = models.ForeignKey(User, editable=False)
date_added = models.DateTimeField(auto_now=True, editable=False)
def __unicode__(self):
return self.name
class Meta:
verbose_name_plural = "Catalogues"
##################################### HARDWARE ######################################################
HARDWARE_CATEGORY_CHOICES = (
('artworks', 'Artworks'),
('control', 'Control'),
('cables', 'Cables'),
('connectors', 'Connectors'),
('computer', 'Computer'),
('storage', 'Storage Devices'),
('av', 'Audio / Video Equipment'),
('electronics', 'Electronics'),
('electrical', 'Electrical & Lighting'),
('mechanical', 'Mechanical Parts'),
('media', 'Media'),
('tools', 'Tools'),
('supplies', 'Supplies'),
('other', 'Other'),
)
class Box(models.Model):
box_no = models.CharField(max_length=255, blank=True, unique=True)
description = models.TextField(blank=True)
owner = models.ForeignKey('Owner', blank=True, null=True)
location = models.ForeignKey(Location, blank=True, null=True)
tags = TagField("Tags", blank=True, help_text="Enter as many tags as you like, separated by commas")
def __unicode__(self):
s = ''
if self.location:
s += self.location.code + "/"
s += self.box_no
return s
class Meta:
verbose_name_plural = "Boxes"
class Hardware(models.Model):
title = models.CharField(max_length=255, verbose_name="Title / Name")
category = models.ForeignKey("HardwareCategory", blank=True, null=True)
# category = MultiSelectField(max_length=355, choices=HARDWARE_CATEGORY_CHOICES, verbose_name="Category")
model_number = models.CharField(max_length=255, blank=True)
serial_number = models.CharField(max_length=255, blank=True)
description = models.TextField(blank=True)
quantity = models.IntegerField(blank=True, null=True, default=1, help_text="This will NOT create separate entries for the item. Use 'Save as New' to duplicate items")
# location = models.ForeignKey(Location, blank=True)
tags = TagField("Tags", blank=True, help_text="Enter as many tags as you like, separated by commas")
# added_by = models.ForeignKey(User, editable=False)
date_added = models.DateTimeField(auto_now=True, editable=False)
links = models.ManyToManyField('HardwareLink', blank=True, null=True)
box = models.ForeignKey('Box', blank=True, null=True)
published = models.BooleanField(default=False)
image = models.ImageField(upload_to='hw_images/', blank=True, null=True)
def __unicode__(self):
return self.labelID()
def labelID(self):
s = ''
if self.box:
try:
loc = Location.objects.get(pk=self.box.id)
s += loc.code + "/"
except:
s += ""
s += self.box.box_no + "/"
# if obj.box.location:
# s += obj.box.location.code + "/"
s += str(self.id)
return s
class Meta:
verbose_name_plural = "Hardware"
class HardwareCategory(models.Model):
name = models.CharField(max_length=255)
def __unicode__(self):
return self.name
class Meta:
verbose_name = "Hardware Category"
verbose_name_plural = "Hardware Categories"
class HardwareLink(models.Model):
name = models.CharField(max_length=255)
url = models.URLField()
def __unicode__(self):
return self.name

15
index/books/myfields.py Executable file
View File

@ -0,0 +1,15 @@
from django.db import models
from isbnvalidate import isValidISBN
from django import forms
class ISBNField(models.Field):
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 13
super(ISBNField, self).__init__(*args, **kwargs)
def clean(self, value):
if not isValidISBN(value):
raise forms.ValidationError("not a valid ISBN")
else:
return value

164
index/books/thumbs.py Executable file
View File

@ -0,0 +1,164 @@
# -*- encoding: utf-8 -*-
"""
django-thumbs by Antonio Melé
http://django.es
"""
from django.db.models import ImageField
from django.db.models.fields.files import ImageFieldFile
from PIL import Image
from django.core.files.base import ContentFile
import cStringIO
def generate_thumb(img, thumb_size, format):
"""
Generates a thumbnail image and returns a ContentFile object with the thumbnail
Parameters:
===========
img File object
thumb_size desired thumbnail size, ie: (200,120)
format format of the original image ('jpeg','gif','png',...)
(this format will be used for the generated thumbnail, too)
"""
img.seek(0) # see http://code.djangoproject.com/ticket/8222 for details
image = Image.open(img)
# Convert to RGB if necessary
if image.mode not in ('L', 'RGB'):
image = image.convert('RGB')
# get size
thumb_w, thumb_h = thumb_size
# If you want to generate a square thumbnail
if thumb_w == thumb_h:
# quad
xsize, ysize = image.size
# get minimum size
minsize = min(xsize,ysize)
# largest square possible in the image
xnewsize = (xsize-minsize)/2
ynewsize = (ysize-minsize)/2
# crop it
image2 = image.crop((xnewsize, ynewsize, xsize-xnewsize, ysize-ynewsize))
# load is necessary after crop
image2.load()
# thumbnail of the cropped image (with ANTIALIAS to make it look better)
image2.thumbnail(thumb_size, Image.ANTIALIAS)
else:
# not quad
image2 = image
image2.thumbnail(thumb_size, Image.ANTIALIAS)
io = cStringIO.StringIO()
# PNG and GIF are the same, JPG is JPEG
if format.upper()=='JPG':
format = 'JPEG'
image2.save(io, format)
return ContentFile(io.getvalue())
class ImageWithThumbsFieldFile(ImageFieldFile):
"""
See ImageWithThumbsField for usage example
"""
def __init__(self, *args, **kwargs):
super(ImageWithThumbsFieldFile, self).__init__(*args, **kwargs)
self.sizes = self.field.sizes
if self.sizes:
def get_size(self, size):
if not self:
return ''
else:
split = self.url.rsplit('.',1)
thumb_url = '%s.%sx%s.%s' % (split[0],w,h,split[1])
return thumb_url
for size in self.sizes:
(w,h) = size
setattr(self, 'url_%sx%s' % (w,h), get_size(self, size))
def save(self, name, content, save=True):
super(ImageWithThumbsFieldFile, self).save(name, content, save)
if self.sizes:
for size in self.sizes:
(w,h) = size
split = self._name.rsplit('.',1)
thumb_name = '%s.%sx%s.%s' % (split[0],w,h,split[1])
# you can use another thumbnailing function if you like
thumb_content = generate_thumb(content, size, split[1])
thumb_name_ = self.storage.save(thumb_name, thumb_content)
if not thumb_name == thumb_name_:
raise ValueError('There is already a file named %s' % thumb_name)
def delete(self, save=True):
name=self.name
super(ImageWithThumbsFieldFile, self).delete(save)
if self.sizes:
for size in self.sizes:
(w,h) = size
split = name.rsplit('.',1)
thumb_name = '%s.%sx%s.%s' % (split[0],w,h,split[1])
try:
self.storage.delete(thumb_name)
except:
pass
class ImageWithThumbsField(ImageField):
attr_class = ImageWithThumbsFieldFile
"""
Usage example:
==============
photo = ImageWithThumbsField(upload_to='images', sizes=((125,125),(300,200),)
To retrieve image URL, exactly the same way as with ImageField:
my_object.photo.url
To retrieve thumbnails URL's just add the size to it:
my_object.photo.url_125x125
my_object.photo.url_300x200
Note: The 'sizes' attribute is not required. If you don't provide it,
ImageWithThumbsField will act as a normal ImageField
How it works:
=============
For each size in the 'sizes' atribute of the field it generates a
thumbnail with that size and stores it following this format:
available_filename.[width]x[height].extension
Where 'available_filename' is the available filename returned by the storage
backend for saving the original file.
Following the usage example above: For storing a file called "photo.jpg" it saves:
photo.jpg (original file)
photo.125x125.jpg (first thumbnail)
photo.300x200.jpg (second thumbnail)
With the default storage backend if photo.jpg already exists it will use these filenames:
photo_.jpg
photo_.125x125.jpg
photo_.300x200.jpg
Note: django-thumbs assumes that if filename "any_filename.jpg" is available
filenames with this format "any_filename.[widht]x[height].jpg" will be available, too.
To do:
======
Add method to regenerate thubmnails
"""
def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, sizes=None, **kwargs):
self.verbose_name=verbose_name
self.name=name
self.width_field=width_field
self.height_field=height_field
self.sizes = sizes
super(ImageField, self).__init__(**kwargs)

183
index/books/views.py Executable file
View File

@ -0,0 +1,183 @@
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.shortcuts import render_to_response
# from django.contrib.auth import authenticate, login, logout
from books.models import *
from django.utils import simplejson
from django import forms
from django.forms.models import inlineformset_factory
from isbnlookup2 import searchAll
#from django.contrib.auth.messages import *
import datetime
import sys
from django.template import RequestContext
from dateutil.parser import parse as parseDate
class BookForm(forms.ModelForm):
title = forms.CharField(widget = forms.TextInput(attrs = {'class': 'autoisbn'}))
authors = forms.CharField(widget = forms.TextInput(attrs = {'class': 'autoisbn'}), required=False)
dimensions = forms.CharField(widget = forms.TextInput(attrs = {'class': 'autoisbn'}), required=False)
publisher = forms.CharField(widget = forms.TextInput(attrs = {'class': 'autoisbn'}), required=False)
published = forms.CharField(widget = forms.TextInput(attrs = {'class': 'autoisbn'}), required=False)
pages = forms.CharField(widget = forms.TextInput(attrs = {'class': 'autoisbn'}), required=False)
weight = forms.CharField(widget = forms.TextInput(attrs = {'class': 'autoisbn'}), required=False)
first_sentence = forms.CharField(widget = forms.TextInput(attrs = {'class': 'autoisbn'}), required=False)
tags = forms.CharField(widget = forms.TextInput(attrs = {'size': '50'}), required=False)
autotags = forms.CharField(widget = forms.TextInput(attrs = {'size': '50', 'class' : 'autoisbn'}), required=False)
class Meta:
model = Book
def dateformat(datestr):
return datetime.date(*time.strptime(datestr, "%B %d, %Y")[0:3]).isoformat()
def index(request):
books = Book.objects.all()
return render_to_response("index.html", {'books': books})
# return render_to_response("index.html", context_instance=RequestContext(request))
@login_required
def add(request):
linksInline = inlineformset_factory(Book, Weblink)
reviewsInline = inlineformset_factory(Book, WebReview)
summariesInline = inlineformset_factory(Book, Summary)
relatedBookInline = inlineformset_factory(Book, RelatedBook)
imageUrlInline = inlineformset_factory(Book, ImageUrl)
chapterInline = inlineformset_factory(Book, ChapterEntry)
sourcesInline = inlineformset_factory(Book, Source)
#
if (request.method == 'POST'):
bookform = BookForm(request.POST)
# bookform.published = dateformat(bookform.published)
linksFormset = linksInline(request.POST, prefix='weblinks')
reviewsFormset = reviewsInline(request.POST, prefix='webreviews')
summariesFormset = summariesInline(request.POST, prefix='summaries')
relatedBookFormset = relatedBookInline(request.POST, prefix='relatedbooks')
imageUrlFormset = imageUrlInline(request.POST, prefix='imageurls')
chapterFormset = chapterInline(request.POST, prefix='chapters')
sourcesFormset = sourcesInline(request.POST, prefix='sources')
if bookform.is_valid():
newbook = bookform.save(commit=False)
newbook.added_by = request.user
request.session['owner'] = newbook.owner.id
request.session['location'] = newbook.location.id
newbook.published = parseDate(bookform.cleaned_data['published'])
newbook.save()
astring = bookform.cleaned_data['authors']
alist = astring.split(';')
authors = []
for a in alist:
if (a.strip() != ''):
au = Author(name = a)
au.save()
authors.append(au)
newbook.authors = authors
# weblinks = linksFormset.save()
# reviews = reviewsFormset.save()
# summaries = summariesFormset.save()
# relatedBooks = relatedBookFormset.save()
# imageUrls = imageUrlFormset.save()
# chapters = chapterFormset.save()
# newbook.weblinks = weblinks
newbook.save()
for f in linksFormset.forms:
if f.is_valid():
f.cleaned_data['book'] = newbook
if f.cleaned_data.has_key('url'):
f.save()
for f in summariesFormset.forms:
if f.is_valid():
f.cleaned_data['book'] = newbook
if f.cleaned_data.has_key('txt'):
f.save()
for f in relatedBookFormset.forms:
if f.is_valid():
f.cleaned_data['book'] = newbook
if f.cleaned_data.has_key('url'):
f.save()
for f in imageUrlFormset.forms:
if f.is_valid():
f.cleaned_data['book'] = newbook
if f.cleaned_data.has_key('url'):
f.save()
for f in chapterFormset.forms:
if f.is_valid():
f.cleaned_data['book'] = newbook
if f.cleaned_data.has_key('link'):
f.save()
for f in sourcesFormset.forms:
if f.is_valid():
f.cleaned_data['book'] = newbook
if f.cleaned_data.has_key('name'):
f.save()
# if f.cleaned_data['url'] != '':
# f.save()
# weblink = form.save(commit=False)
# weblink.book = newbook
# if form.is_valid():
# weblink = Weblink()
# weblink['url'] = form.cleaned_data['url']
# weblink['description'] = form.cleaned_data['description']
# weblink['book'] = newbook
# weblink.save()
# weblink.book = thisBook
# weblink.save()
request.user.message_set.create(message="Your book was added successfully.")
return HttpResponseRedirect("/add")
else:
linksFormset = linksInline(prefix='weblinks')
reviewsFormset = reviewsInline(prefix='webreviews')
summariesFormset = summariesInline(prefix='summaries')
relatedBookFormset = relatedBookInline(prefix='relatedbooks')
imageUrlFormset = imageUrlInline(prefix='imageurls')
chapterFormset = chapterInline(prefix='chapters')
sourcesFormset = sourcesInline(prefix='sources')
bookform = BookForm()
# formsets = [linksFormset, chapterFormset]
formsets = [linksFormset, reviewsFormset, summariesFormset, relatedBookFormset, imageUrlFormset, chapterFormset, sourcesFormset]
defaultsD = {}
if request.session.has_key('location'):
defaultsD['location'] = request.session['location']
if request.session.has_key('owner'):
defaultsD['owner'] = request.session['owner']
# print repr(defaultsD)
return render_to_response("addbook.html", {'form': bookform, 'formsets': formsets, 'defaults': defaultsD}, context_instance=RequestContext(request))
def isbnLookup(request):
isbn = request.GET.get('isbn')
isbnDict = searchAll(isbn)
if (request.is_ajax()):
return HttpResponse(simplejson.dumps(isbnDict), mimetype='application/javascript')
else:
#Ideally we would not have an if and else statement doing the same thing ;-)
return HttpResponse(simplejson.dumps(isbnDict), mimetype='application/javascript')
def addLocation(request):
if request.is_ajax():
locationName = request.GET['name']
l = Location(name = locationName)
l.save()
return HttpResponse(l.id)
else:
return HttpResponse(str(o.id))
def addOwner(request):
if request.is_ajax():
ownerName = request.GET['name']
o = Owner(name = ownerName)
o.save()
return HttpResponse(str(o.id))
else:
return HttpResponse("you are not ajax.")

0
index/catalogues/__init__.py Executable file
View File

10
index/catalogues/admin.py Executable file
View File

@ -0,0 +1,10 @@
from catalogues.models import *
from django.contrib import admin
from django.contrib.auth.models import User
class CatalogueAdmin(admin.ModelAdmin):
ordering = ('title',)
search_fields = ['title', 'artist']
admin.site.register(Catalogue, CatalogueAdmin)

24
index/catalogues/models.py Executable file
View File

@ -0,0 +1,24 @@
from django.db import models
from django.contrib.auth.models import User
from books.models import Owner, Location
from tagging.fields import TagField
from tagging.models import Tag
class Catalogue(models.Model):
title = models.CharField(max_length=255)
artist = models.CharField(max_length=255, blank=True)
exhibition = models.CharField(max_length=255, blank=True)
published_date = models.DateField(blank=True, verbose_name="Published on")
condition = models.TextField(blank=True)
description = models.TextField(blank=True)
owner = models.ForeignKey(Owner, blank=True)
location = models.ForeignKey(Location, blank=True)
tags = TagField("Tags", blank=True, help_text="Enter as many tags as you like, separated by commas")
added_by = models.ForeignKey(User, editable=False)
date_added = models.DateTimeField(auto_now=True, editable=False)
def __unicode__(self):
return self.name
class Meta:
verbose_name_plural = "Catalogues"

8
index/catalogues/views.py Executable file
View File

@ -0,0 +1,8 @@
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response
from catalogue.models import *
from django import forms
def add(request):
return render_to_response("addcatalogue.html") #, {'form': bookform, 'formset': formset})

20
index/dropandcreatedb.py Executable file
View File

@ -0,0 +1,20 @@
import sys
import os
from settings import DATABASE_NAME
try:
import settings # Assumed to be in the same directory.
except ImportError:
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
sys.exit(1)
filename = "/tmp/tmp.txt"
tmpfile = open(filename, "w")
tmpfile.write("DROP DATABASE %s;\nCREATE DATABASE %s;\n" % (DATABASE_NAME, DATABASE_NAME) )
tmpfile.close()
os.system("mysql -u root < " + filename)
os.system("python manage.py syncdb --noinput")
os.system("python manage.py createsuperuser --username admin --email user@user.com")

View File

13
index/harddrives/admin.py Executable file
View File

@ -0,0 +1,13 @@
from harddrives.models import *
from django.contrib import admin
class FileAdmin(admin.ModelAdmin):
search_fields = ('fullPath',)
list_display = ('filename', 'fullPath', 'hd',)
list_filter = ['hd']
class HardDriveAdmin(admin.ModelAdmin):
pass
admin.site.register(HardDrive, HardDriveAdmin)
admin.site.register(File, FileAdmin)

View File

@ -0,0 +1,53 @@
import os
from os.path import join
from models import HardDrive, File
ERROR_LOG = open("errors.txt", "a")
def loopThroughDir(path):
dir = os.listdir(path)
for f in dir:
if f.endswith('txt'):
addHardDrive(f, path)
ERROR_LOG.close()
def addHardDrive(filename, path):
filePath = u''
filePath = join(path, filename)
hdName = filename.replace(".txt", "")
file = open(filePath)
if hardDriveExists(hdName):
return False
else:
hd = HardDrive(name = hdName)
hd.save()
print hdName
for f in file:
addFile(f, hd)
def addFile(filePath, hd):
filename = u''
filename = getFileNameFromPath(filePath)
newfile = File(filename = filename, fullPath = filePath, hd = hd)
try:
newfile.save()
except:
err = hd.name + ":\n"
err += filePath + "\n\n"
ERROR_LOG.write(err)
print "added " + filename
def hardDriveExists(hdName):
return False
def getFileNameFromPath(filePath):
return os.path.basename(filePath)
if __name__ == '__main__':
import sys
if len(sys.argv) < 2:
print "usage: %s <directory path>" % sys.argv[0]
sys.exit()
else:
path = sys.argv[1]
loopThroughDir(path)

View File

@ -0,0 +1,20 @@
from django.db import models
class HardDrive(models.Model):
name = models.CharField(max_length = 255, unique = True)
def __unicode__(self):
return self.name
# volumeLabel = models.CharField(max_length = 255)
class File(models.Model):
filename = models.CharField(max_length = 512)
fullPath = models.TextField()
hd = models.ForeignKey(HardDrive)
def __unicode__(self):
return self.fullPath
# Create your models here.

View File

@ -0,0 +1 @@
# Create your views here.

0
index/hardware/__init__.py Executable file
View File

12
index/hardware/admin.py Executable file
View File

@ -0,0 +1,12 @@
from hardware.models import *
from django.contrib import admin
from django.contrib.auth.models import User
class HardwareAdmin(admin.ModelAdmin):
ordering = ('title',)
list_display = ('title', 'category', 'box',)
list_filter = ('category', 'box',)
search_fields = ['title', 'description']
admin.site.register(Hardware, HardwareAdmin)
admin.site.register(Link)

50
index/hardware/models.py Normal file
View File

@ -0,0 +1,50 @@
from django.db import models
from django.contrib.auth.models import User
from books.models import Owner, Location
from tagging.fields import TagField
from tagging.models import Tag
from books.fields import MultiSelectField, MultiSelectFormField
CATEGORY_CHOICES = (
('artworks', 'Artworks'),
('control', 'Control'),
('cables', 'Cables'),
('connectors', 'Connectors'),
('computer', 'Computer'),
('storage', 'Storage Devices'),
('av', 'Audio / Video Equipment'),
('electronics', 'Electronics'),
('electrical', 'Electrical & Lighting'),
('mechanical', 'Mechanical Parts'),
('media', 'Media'),
('tools', 'Tools'),
('supplies', 'Supplies'),
('other', 'Other'),
)
class Hardware(models.Model):
title = models.CharField(max_length=255)
model_number = models.CharField(max_length=255, blank=True)
serial_number = models.CharField(max_length=255, blank=True)
description = models.TextField(blank=True)
owner = models.ForeignKey(Owner, blank=True)
location = models.ForeignKey(Location, blank=True)
tags = TagField("Tags", blank=True, help_text="Enter as many tags as you like, separated by commas")
added_by = models.ForeignKey(User, editable=False)
date_added = models.DateTimeField(auto_now=True, editable=False)
category = MultiSelectField(max_length=355, choices=CATEGORY_CHOICES, verbose_name="Category")
def __unicode__(self):
return self.name
class Meta:
verbose_name_plural = "Hardware"
class Link(models.Model):
name = models.CharField(max_length=255)
url = models.URLField()
def __unicode__(self):
return self.name

8
index/hardware/views.py Executable file
View File

@ -0,0 +1,8 @@
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response
from hardware.models import *
from django import forms
#def add(request):
#return render_to_response("addhardware.html") #, {'form': bookform, 'formset': formset})

11
index/manage.py Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/python
from django.core.management import execute_manager
try:
import settings # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
sys.exit(1)
if __name__ == "__main__":
execute_manager(settings)

57
index/media/css/base.css Executable file
View File

@ -0,0 +1,57 @@
body {
background: #000;
overflow-x: hidden;
}
#header {
position: relative;
width: 100%;
background: #333;
color: #333;
height: 40px;
margin-bottom: 4px;
padding: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
#buttons {
position: absolute;
bottom: 10px;
right: 10px;
}
#pageTitle {
width: 100%;
color: #fff;
font-weight: bold;
font-size: 18px;
padding-top: 8px;
}
.button {
padding: 2px;
padding-top: 6px;
float: left;
border: 2px solid #000;
background: #000;
color: #00f;
height: 20px;
margin-left: 5px;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
width: 100px;
font-size: 14px;
text-align: center;
cursor: pointer;
}
.button a {
color: #f33;
text-decoration: none;
}
.button a:hover {
color: yellow;
}

151
index/media/css/form.css Executable file
View File

@ -0,0 +1,151 @@
#isbnlookup {
color: blue;
cursor: pointer;
}
#loading {
display: none;
}
#userFields {
float: left;
width: 50%;
}
#userFields input {
width: 80%;
}
#userFields input, textarea, select {
margin-left: 10px;
}
#lookupIsbn {
width: 8em;
background: #db1010;
color: #fff;
font-weight: bold;
border: 1px solid #f39b0f;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
cursor: pointer;
}
#lookupIsbn:hover {
background-color: #f39b0f;
}
#userFields textarea {
width: 50%;
}
#autoFields {
float: left;
width: 50%;
}
#autoFields input {
width: 90%;
}
#formErrors {
margin-left: auto;
margin-right: auto;
text-align: center;
color: #f00;
font-weight: bold;
font-size: 12px;
background: yellow;
width: 40%;
}
.loadingimage {
display: none;
}
.button_loadingimage {
display: none;
text-align: right;
}
.formset {
clear: both;
}
.formset_title {
cursor: pointer;
margin-bottom: 3px;
margin-left: 40%;
font-weight: bold;
border: 1px solid #f00;
text-align: center;
-moz-border-radius: 10px;
width: 10em;
color: #fff;
}
.formset_no_results {
font-size: 12px;
color: #f00;
font-weight: bold;
}
.formset_forms {
display: none;
border: 1px dotted #f00;
}
.formset_form {
border: 1px dotted #f00;
}
#formsets {
padding: 5px;
}
.addButton {
font-style: bold;
font-size: 14px;
cursor: pointer;
color: #00f;
}
label {
color: #f19f9f;
font-weight: bold 70%;
position: relative;
left: 15px;
}
#submitBtn {
width: 40%;
margin-left: auto;
margin-right: auto;
}
#submitButton {
width: 20px;
background-color: #1d0213;
color: #fff;
font-weight: bold;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border: 1px solid #f39b0f;
cursor: pointer;
}
#submitButton:hover {
color: yellow;
}
input, textarea {
background: #ccc;
}
#isbn input {
width: 13em;
}

BIN
index/media/images/loading.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 847 B

BIN
index/media/images/plus.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

146
index/media/js/bookform.js Executable file
View File

@ -0,0 +1,146 @@
$(document).ready(function() {
$('#lookupIsbn').click(isbnLookup);
//To handle colour changing on focus / blur of form elems:
$('input, textarea').focus(function() {
$(this).css("background", "#fff");
}).blur(function() {
$(this).css("background", "#ccc");
});
//To add Owner / Location fields dynamically.
$('.addButton').click(function() {
var fieldName = $(this).attr("data_field");
var value = prompt("Please type the name of the new " + fieldName + "you would like to add:");
if (value != null && $.trim(value) != '') {
var url = "/add" + fieldName + "/";
$.get(url, {
'name': value
}, function(response) {
addedId = response;
var html = '';
html += '<option value="' + addedId + '" selected="selected">' + value + "</option>";
selectId = "#id_" + fieldName;
$(selectId).append(html);
});
}
});
})
//TODO: Make this a function that actually validates.
function isValidIsbn(isbn) {
if ($.trim(isbn) != '') {
return true;
} else {
return false;
}
}
//Function fired on start loading ISBN Lookup
function doLoading() {
$('.autoisbn').css("background", "#f00").attr("disabled", "true");
$('#lookupIsbn').fadeOut();
$('#loading').fadeIn();
$('.loadingimage').fadeIn();
$('.button_loadingimage').fadeIn();
}
//Function fired when ISBN Lookup is loaded
function stopLoading() {
$('.autoisbn').removeAttr("disabled").css("background", "#fff");
$('#loading').hide();
$('.loadingimage').fadeOut();
$('.button_loadingimage').fadeOut();
$('#lookupIsbn').fadeIn();
}
//Fetch ISBN Lookup data and populate relevant fields.
function isbnLookup() {
isbn = $('#id_isbn').val();
if (isValidIsbn(isbn)) {
doLoading();
$.getJSON("/isbnlookup/", {
'isbn': isbn
}, function(data) {
if (data.errors && data.errors != '') {
$('#formErrors').html(data.errors);
stopLoading();
} else {
// alert(data.title);
$('#id_title').val(data.title);
$('#id_dimensions').val(data.dimensions);
var publishedStr = getStrFromDate(data.published);
$('#id_published').val(publishedStr);
$('#id_publisher').val(data.publisher);
if (data.weight != 0) {
$('#id_weight').val(data.weight);
}
if (data.pages != 0) {
$('#id_pages').val(data.pages);
}
if (data.tags.length > 0) {
var tString = '';
for (t in data.tags) {
tString += data.tags[t] + ", ";
}
$('#id_autotags').val(tString);
}
$('#id_first_sentence').val(data.first_sentence);
//TODO: Formsets should be referencable by prefixes.
formsets[0].populate(data.weblinks);
formsets[1].populate(data.netReviews);
formsets[2].populate(data.summaries);
formsets[3].populate(data.relatedBooks);
formsets[4].populate(data.imageUrls);
formsets[5].populate(data.chapters);
formsets[6].populate(data.sources);
var authorString = '';
for (author in data.authors) {
authorString += data.authors[author] + "; ";
}
// alert(authorString);
$('#id_authors').val(authorString);
stopLoading();
}
});
}
}
//Isbn Lookup passes date as a dict - convert to string.
function getStrFromDate(d) {
var str = '';
if (d.year) {
str += d.year
if (d.month) {
str += '-' + d.month
if (d.day) {
str += '-' + d.day
}
}
}
return str;
}
//#################################################
//BEGIN Formset functions
//#################################################
var prefixes = ['weblinks', 'webreviews', 'summaries', 'relatedbooks', 'imageurls', 'chapters', 'sources']
//TODO: more elegant way to calculate length of buttons
$(document).ready(function() {
formsets = [];
for (var p in prefixes) {
formsets[p] = new Formset(prefixes[p]);
}
$('.formset_title').hover(function() {
$(this).css('border-color', '#fff');
$(this).css('color', '#f00');
}, function() {
$(this).css('border-color', '#f00');
$(this).css('color', '#fff');
});
});

192
index/media/js/formset.js Normal file
View File

@ -0,0 +1,192 @@
/*
*@class Formset class
*@property {String} prefix same prefix as defined in django view while instantiating formset.
*@property {String} prefixStr prefix string to
*@property {String} jqPrefix just a helpful prefix with '#_id' prepended to use in jQuery selector strings
*@property {jQuery Object} totalFormsElem the Input field of the TOTAL_FORMS field for this formset. (Django: management_form)
*@property {jQuery Object} cloneObj a jQuery element that is a clone of a single form in the formset.
*@property {jQuery Object} titleElem The div that contains the title of the formset
*/
/*
*@constructor
*@param {String} Prefix for this formset (same as used in django during instantiation)
*/
var Formset = function(prefix) {
var that = this;
this.prefix = prefix;
this.jqPrefix = '#id_' + prefix;
this.prefixStr = 'id_' + this.prefix + '-';
this.totalFormsElem = $(that.jqPrefix + '-TOTAL_FORMS');
this.cloneObj = function() {
var that = this;
return $('#' + that.prefix + ' > .formset_forms').children('.formset_form:first').clone()
}
this.titleElem = $('#' + that.prefix + ' > .formset_title');
this.formsetElem = $('#' + that.prefix + ' > .formset_forms');
this.titleElem.click(function() {
that.formsetElem.toggle('slow', function() {
thisTop = that.titleElem.offset().top;
$(document).scrollTop(thisTop);
});
});
}
/*
*Toggles display of formset (except for title element), also scrolls the page to the top of the title element.
* TODO: fix the scroll being weird or get rid of it.
*/
Formset.prototype.toggleDisplay = function() {
var that = this;
that.formsetElem.toggle('slow', function() {
thisTop = that.titleElem.offset().top;
$(document).scrollTop(thisTop);
});
}
/*
* Get total no of forms in formset.
* @returns {Int} Total no of forms according to formset metadata.
*/
Formset.prototype.getTotal = function() {
return parseInt(this.totalFormsElem.val());
}
/*
* Set total no of forms in formset.
*@param {Int} No of forms to set formset metadata to.
*/
Formset.prototype.setTotal = function(newNo) {
this.totalFormsElem.val(newNo);
return true;
}
/*
* Increment formset metadata total by one
*/
Formset.prototype.increment = function() {
var currTotal = this.getTotal();
var newTotal = currTotal + 1;
this.setTotal(newTotal);
return true;
}
Formset.prototype.getFieldName = function(idStr) {
// var thisId = jqObj.attr('id');
// var thisPrefixStr = 'id_' + this.prefix + '-';
var that = this;
var prefixLength = this.prefixStr.length + 2;
var thisField = idStr.substring(prefixLength);
console.log(that.prefixStr);
return thisField;
}
/*
*@returns {Array} returns array of strings representing field names present in this formset.
*/
Formset.prototype.getFields = function() {
var that = this;
var fields = [];
that.cloneObj().find('input').each(function() {
if ($(this).attr('type') != 'hidden') {
var thisField = that.getFieldName($(this).attr('id'))
if (thisField != 'DELETE') {
fields.push(thisField);
}
}
});
that.cloneObj().find('textarea').each(function() {
if ($(this).attr('type') != 'hidden') {
var thisField = that.getFieldName($(this).attr('id'))
if (thisField != 'DELETE') {
fields.push(thisField);
}
}
});
return fields;
}
/*
* Add a form to the formset. Use this method from the outside.
* @param {Int} No of forms to add (Optional), defaults to 1.
*/
Formset.prototype.add = function(no) {
if (!no) {
var no = 1;
}
var that = this;
for (var i=0; i < no; i++) {
var e = this.cloneObj();
//TODO: Figure out how to deal with changing 'for' attribute on labels correctly.
/*
e.find('label').each(function() {
var newId = that.incrementId($(this).attr('for'));
// $(this).attr('id', newId);
$(this).attr('for', newId);
});
*/
e.find('input').each(function() {
$(this).attr('id', that.incrementId($(this).attr('id')));
$(this).attr('name', that.incrementName($(this).attr('id')));
});
e.find('textarea').each(function() {
$(this).attr('id', that.incrementId($(this).attr('id')));
$(this).attr('name', that.incrementName($(this).attr('id')));
});
this.increment();
this.formsetElem.append(e);
}
return true;
}
/*
*@param {String} id of element to increment
*@returns {String} incremented id
*/
Formset.prototype.incrementId = function(idStr) {
var newTotal = this.getTotal() + 1;
var newId = this.prefixStr + newTotal.toString() + '-' + this.getFieldName(idStr);
return newId;
}
/*
*@param {String} name of element to increment
*@returns {String} incremented name
*/
Formset.prototype.incrementName = function(idStr) {
var newTotal = this.getTotal() + 1;
var newId = this.prefix + '-' + newTotal.toString() + '-' + this.getFieldName(idStr);
return newId;
}
/*
* Populates a formset with an array of JSON data
*@param {JSON Array} like so - [{'field1': 'some value', 'field2': 'some more value'}, {'field1': 'foo', 'field2': 'bar'}]
*@returns true once done
*/
Formset.prototype.populate = function(data) {
var that = this;
var noOfEntries = data.length;
if (data.length > 0) {
// that.titleElem.html(that.titleElem.html() + " " + noOfEntries.toString());
that.titleElem.children('.formset_no_results').html(noOfEntries.toString());
var totalFormsToShow = noOfEntries + 3;
var newFormsToAdd = totalFormsToShow - this.getTotal();
this.add(newFormsToAdd);
var fields = this.getFields();
for (var f in fields) {
var fieldName = fields[f];
for (var d in data) {
if (data[d]) {
var val = data[d][fieldName];
$(that.jqPrefix + '-' + d.toString() + '-' + fieldName).val(val);
}
}
}
}
return true;
}

19
index/media/js/jquery.js vendored Executable file

File diff suppressed because one or more lines are too long

92
index/settings.py Normal file
View File

@ -0,0 +1,92 @@
# Django settings for index project.
import os
from os.path import join
DEBUG = True
TEMPLATE_DEBUG = DEBUG
PROJECT_PATH = os.path.dirname(__file__)
ADMINS = (
# ('Your Name', 'your_email@domain.com'),
)
MANAGERS = ADMINS
DATABASE_ENGINE = 'mysql' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = 'idx' # Or path to database file if using sqlite3.
DATABASE_USER = 'root' # Not used with sqlite3.
DATABASE_PASSWORD = '' # Not used with sqlite3.
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# If running in a Windows environment this must be set to the same as your
# system time zone.
TIME_ZONE = 'America/Chicago'
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = join(PROJECT_PATH, 'media')
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
MEDIA_URL = '/media/'
LOGIN_URL = '/login/'
LOGIN_REDIRECT_URL = '/'
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX = '/admin/media/'
# Make this unique, and don't share it with anybody.
SECRET_KEY = ')o1)c68=b9k5z@af=_@s31)pcp58yxo@5@$b5!^_vlijibziq1'
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
# 'django.template.loaders.eggs.load_template_source',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)
ROOT_URLCONF = 'urls'
TEMPLATE_DIRS = (
join(PROJECT_PATH, 'templates'),
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
)
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
'books',
'harddrives',
'tagging',
'south',
)

View File

@ -0,0 +1,171 @@
{% extends 'site_base.html' %}
{% block extra_head %}
<script type="text/javascript">
$(document).ready(function() {
{% if defaults.location %}
var locationId = {{ defaults.location }}
$('#id_location').children().each(function() {
if ($(this).attr("value") == locationId) {
$(this).attr("selected", "selected");
}
});
{% endif %}
{% if defaults.owner %}
var ownerId = {{ defaults.owner }}
$('#id_owner').children().each(function() {
if ($(this).attr("value") == ownerId) {
$(this).attr("selected", "selected");
}
});
{% endif %}
});
</script>
<script type="text/javascript" src="/media/js/formset.js"></script>
<script type="text/javascript" src="/media/js/bookform.js"></script>
<link rel="stylesheet" href="/media/css/form.css" />
{% endblock %}
{% block content %}
<form action="/add/" method="POST" enctype="multipart/formdata">
<div id="formErrors">
<div id="loading">Loading...</div>
{{ form.errors }}
<div id="messages">
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
<div id="userFields">
<div id="isbn">
<label class="fixedWidth" for="id_isbn">ISBN Code:</label><br /> {{ form.isbn}} &nbsp; <button id="lookupIsbn" onclick="return false;">Lookup ISBN</button>
</div>
<p>
<label class="fixedWidth" for="id_tags">Tags: </label><br />
{{ form.tags }}
</p>
<p>
<label class="fixedWidth" for="id_description">Description: </label><br />
{{ form.description }}
</p>
<p>
<label for="id_owner">Owner: </label><br />
{{ form.owner }} <span class="addButton" data_field="owner"><img src="/media/images/plus.gif"></a></span>
</p>
<p>
<label for="id_location">Location: </label><br />
{{ form.location }} <span class="addButton" data_field="location"><img src="/media/images/plus.gif"></span>
</p>
<p id="submitBtn">
<input type="submit" value="Submit" id="submitButton" />
</p>
</div>
<div id="autoFields">
<p>
<label for="id_title">Title: </label><br />
{{ form.title}}
<img class="loadingimage" src="/media/images/loading.gif">
</p>
<p>
<label for="id_authors">Authors: </label><br />
{{ form.authors}}
<img class="loadingimage" src="/media/images/loading.gif">
</p>
<p>
<label for="id_publisher">Publisher: </label><br />
{{ form.publisher }}
<img class="loadingimage" src="/media/images/loading.gif">
</p>
<p>
<label for="id_published">Published: </label><br />
{{ form.published }}
<img class="loadingimage" src="/media/images/loading.gif">
</p>
<p>
<label for="id_autotags">AutoTags: </label>
{{ form.autotags }}
<img class="loadingimage" src="/media/images/loading.gif">
</p>
<p>
<label for="id_pages">Pages: </label><br />
{{ form.pages }}
<img class="loadingimage" src="/media/images/loading.gif">
</p>
<p>
<label for="id_weight">Weight: </label><br />
{{ form.weight }}
<img class="loadingimage" src="/media/images/loading.gif">
</p>
<p>
<label for="id_dimensions">Dimensions: </label><br />
{{ form.dimensions }}
<img class="loadingimage" src="/media/images/loading.gif">
</p>
<p>
<label for="id_first_sentence">First Sentence: </label><br />
{{ form.first_sentence }}
<img class="loadingimage" src="/media/images/loading.gif">
</p>
</div>
<div id="formsets">
{% for formset in formsets %}
<div id="{{formset.prefix}}" class="formset">
<div class="formset_title">
{{formset.prefix}} <span class="formset_no_results"></span>
<!-- <img class="button_loadingimage" src="/media/images/loading.gif"> -->
</div>
<div class='formset_meta'>
{{ formset.management_form }}
</div>
<div class="formset_forms">
{% for form in formset.forms %}
<div class="formset_form">
{{ form.as_p }}
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
</form>
{% endblock %}

17
index/templates/base.html Executable file
View File

@ -0,0 +1,17 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script type="text/javascript" src="/media/js/jquery.js"></script>
{% block head %}
{% endblock %}
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>

20
index/templates/index.html Executable file
View File

@ -0,0 +1,20 @@
{% extends 'site_base.html' %}
{% block title %} Home {% endblock %}
{% block extra_head %}
<script type="text/javascript">
</script>
{% endblock %}
{% block content %}
<ul>
{% for book in books %}
<li data_id="{{ book.id }}"><a href="/book/{{ book.id }}">{{ book.title }}</a></li>
{% endfor %}
</ul>
{% endblock %}

30
index/templates/login.html Executable file
View File

@ -0,0 +1,30 @@
{% extends 'site_base.html' %}
{% block title %}
Login
{% endblock %}
{% block content %}
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
<form method="post" action="{% url django.contrib.auth.views.login %}">
<table>
<tr>
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
</tr>
</table>
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
{% endblock %}

6
index/templates/logout.html Executable file
View File

@ -0,0 +1,6 @@
{% extends 'site_base.html' %}
{% block title %} Logout {% endblock %}
{% block content %}
{{ title }}
{% endblock %}

46
index/templates/site_base.html Executable file
View File

@ -0,0 +1,46 @@
{% extends 'base.html' %}
{% block head %}
<link rel="stylesheet" href="/media/css/base.css" />
<title>{% block title %} {% endblock %} </title>
{% block extra_head %}
{% endblock %}
{% endblock %}
{% block body %}
<div id="header">
<div id="pageTitle">
The Index . ( ) .
</div>
<div id="buttons">
<div class="button">
{% if user.is_authenticated %}
<a href="/logout">Logout</a>
{% else %}
<a href="/login">Login</a>
{% endif %}
</div>
{% block extra_buttons %}
<div class="button">
<a href="/add/">Books</a>
</div>
<div class="button">
<a href="/admin/books/catalogue/add/">Catalogues</a>
</div>
<div class="button">
<a href="/admin/books/hardware/">Hardware</a>
</div>
<div class="button">
<a href="/admin/harddrives/file/">Hard-Drives</a>
</div>
{% endblock %}
</div>
</div>
{% block content %}
{% endblock %}
{% endblock %}

31
index/urls.py Executable file
View File

@ -0,0 +1,31 @@
from django.conf.urls.defaults import *
from settings import PROJECT_PATH
from os.path import join
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
# Example:
# (r'^index/', include('index.foo.urls')),
(r'^login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}),
(r'^logout/$', 'django.contrib.auth.views.logout', {'template_name': 'logout.html'}),
(r'^$', 'books.views.index'),
# (r'^addhardware/(.*)', 'hardware.views.addhardware'),
(r'^add/$', 'books.views.add'),
(r'^addlocation/$', 'books.views.addLocation'),
(r'addowner/$', 'books.views.addOwner'),
(r'^(\w+)/add/$', 'books.views.add'),
(r'^isbnlookup/$', 'books.views.isbnLookup'),
(r'^media/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': join(PROJECT_PATH, 'media')}),
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
# to INSTALLED_APPS to enable admin documentation:
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
# (r'^admin/', include('django.contrib.admin.urls')),
# Uncomment the next line to enable the admin:
(r'^admin/(.*)', admin.site.root),
)