Last active
December 12, 2015 05:28
-
-
Save lawlesst/4722068 to your computer and use it in GitHub Desktop.
Flask app for stackview.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
from flask import Flask, url_for | |
from flask import request, current_app | |
from flask import jsonify | |
from flask import render_template | |
from functools import wraps | |
from fetch_data import stackview_data, get_by_bib | |
app = Flask(__name__) | |
def jsonp(func): | |
""" | |
Taken from: https://gist.github.com/1094140 | |
Wraps JSONified output for JSONP requests. | |
""" | |
@wraps(func) | |
def decorated_function(*args, **kwargs): | |
callback = request.args.get('callback', False) | |
if callback: | |
data = str(func(*args, **kwargs).data) | |
content = str(callback) + '(' + data + ')' | |
mimetype = 'application/javascript' | |
return current_app.response_class(content, mimetype=mimetype) | |
else: | |
return func(*args, **kwargs) | |
return decorated_function | |
@app.route('/', methods=['GET']) | |
@app.route('/<bib>', methods=['GET']) | |
def stack(bib=None): | |
if bib is not None: | |
record = get_by_bib(str(bib)) | |
callnumber = record.get('callnumber') | |
#Add shelf location. | |
this_title = "%s -- %s" % (record.get('title'), callnumber) | |
else: | |
callnumber = str(request.args.get('cn', None)) | |
return render_template( | |
'stackview.html', | |
callnumber=callnumber, | |
title=this_title | |
) | |
@app.route('/data', methods=['GET']) | |
@jsonp | |
def stack_view(): | |
callnumber = str(request.args.get('query', None)) | |
#import pdb; pdb.set_trace() | |
out = stackview_data(callnumber) | |
return jsonify(out) | |
if __name__ == '__main__': | |
if os.getenv('DEVBOX') == 'true': | |
app.run( | |
host='0.0.0.0', | |
debug=True | |
) | |
else: | |
app.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Run Z3950 searches to get the items sorted by call number. | |
Example Stackview data objects. | |
{ | |
"title": "Blankets", | |
"creator": [ | |
"Craig Thompson" | |
], | |
"measurement_page_numeric": 582, | |
"measurement_height_numeric": 25, | |
"shelfrank": 13, | |
"pub_date": "2003", | |
"link": "http://holliscatalog.harvard.edu/?itemid=|library/m/aleph|009189638" | |
}, | |
{ | |
"title": "Persepolis", | |
"creator": [ | |
"Marjane Satrapi" | |
], | |
"measurement_page_numeric": 153, | |
"measurement_height_numeric": 24, | |
"shelfrank": 64, | |
"pub_date": "2003", | |
"link": "http://holliscatalog.harvard.edu/?itemid=|library/m/aleph|009098946" | |
} | |
""" | |
import os | |
import sys | |
import re | |
from PyZ3950 import zoom | |
from PyZ3950.z3950 import ConnectionError | |
import pymarc | |
Z39_SERVER = { | |
'host': os.getenv('ZHOST'), | |
'port': int(os.getenv('ZPORT')), | |
'db': os.getenv('ZDB'), | |
'qsyntax': 'PQF', | |
'rsyntax': 'OPAC', | |
'element_set': 'F', | |
'charset': 'utf-8', | |
} | |
PUB_YEAR = re.compile('(?:2[0-9]0u|\?)?[0-9]{3}u|\?') | |
class Z39Result(object): | |
""" | |
Converts the Z39.50 result to a stackview response. | |
""" | |
def __init__(self, result_set): | |
self.result = result_set | |
def callnumber(self, record): | |
""" | |
Call number routine. Brown specific. | |
""" | |
call_number = None | |
if record['090']: | |
call_number = record['090'].format_field() | |
elif record['050']: | |
call_number = record['050'].format_field() | |
return {'callnumber': call_number} | |
def get_publication_date(self, record): | |
""" | |
Try to find pub date and convert to Python date object. | |
""" | |
from datetime import datetime, date | |
#=008 100315s2010\\\\it\a\\\\\b\\\\001\0\ita\\ | |
o08 = record['008'].data | |
date1 = o08[7:11] | |
if PUB_YEAR.search(date1): | |
date1 = date1.replace('u', '0') | |
#Will convert to date object even though we will report | |
#just the year string. This will return null for 19uu | |
#fields which we don't want to appear in facets. | |
try: | |
date_obj = date(year=int(date1), month=1, day=1) | |
#Throw out bad data for years greater than two years in | |
#advance. | |
if date_obj.year > (date.today().year + 2): | |
return | |
except ValueError: | |
return | |
return str(date_obj.year) | |
def physical(self, bib): | |
""" | |
Try to assign a numeric page size. | |
Default to 250 pages. | |
""" | |
phys = bib['300'] | |
try: | |
pages = phys['a'] | |
if pages is not None: | |
try: | |
pages = int(re.findall(r'[0-9]+', pages)[0]) | |
except IndexError: | |
pages = 250 | |
else: | |
pages = 250 | |
try: | |
height = int(re.findall(r'[0-9]+', phys['c'])[0]) | |
except IndexError: | |
height = 10 | |
if phys is None: | |
return {} | |
except TypeError: | |
pages = 250 | |
height = 10 | |
return { | |
"measurement_page_numeric": pages, | |
"measurement_height_numeric": height | |
} | |
def response(self): | |
results = {} | |
holdings_set = [1] | |
for rec in self.result: | |
#Read details into pymarc Record object. | |
try: | |
bib = pymarc.Record(data=rec.data.bibliographicRecord.encoding[1]) | |
except AttributeError: | |
continue | |
results['title'] = bib.title() | |
#Assuming the record id is in 907 and follows III convention | |
results['id'] = bib['907']['a'].lstrip('.')[:8] | |
results['link'] = os.getenv('OPAC') + results['id'] | |
results['pub_date'] = self.get_publication_date(bib) | |
results['callnumber'] = self.callnumber(bib) | |
au = bib.author() | |
if au: | |
creator = [au] | |
else: | |
creator = [] | |
results['creator'] = creator | |
#Get physical description | |
results.update(self.physical(bib)) | |
results.update(self.callnumber(bib)) | |
#Get item count for shelfrank. | |
try: | |
holdings_set = rec.data.holdingsData | |
except AttributeError: | |
pass | |
results['items'] = len(holdings_set) | |
#Computer a shelfrank based on the number of items in | |
#the catalog. | |
results['shelfrank'] = self.shelfrank(results['items']) | |
return results | |
def shelfrank(self, item_count): | |
""" | |
Make a shelf rank based off the number | |
of items/copies held of a given title. | |
""" | |
if item_count == 1: | |
sr = 10 | |
elif (item_count > 1) and (item_count < 3): | |
sr = 50 | |
elif (item_count >= 3) and (item_count < 4): | |
sr = 70 | |
else: | |
sr = 100 | |
return sr | |
def stackview_data(call_number): | |
""" | |
Run a Z39.50 scan for a particular call number. | |
""" | |
server = Z39_SERVER | |
#Open Z39.50 connection | |
conn = zoom.Connection( | |
server['host'], | |
server['port'], | |
preferredRecordSyntax=server['rsyntax'], | |
elementSetName=server['element_set'], | |
databaseName=server['db'], | |
charset='utf-8', | |
numberOfEntries=int(os.getenv('STACK_SIZE')), | |
responsePosition=int(os.getenv('ITEM_POS')) | |
) | |
params = '@attr 1=16 "%s"' % call_number | |
query = zoom.Query(server['qsyntax'], params) | |
#Do a scan by call number | |
docs = [] | |
results = conn.scan(query) | |
for count, bib in enumerate(results): | |
call = bib.get('term')[1] | |
csearch = '@attr 1=13 "%s"' % call | |
lookup = zoom.Query(server['qsyntax'], csearch) | |
result = conn.search(lookup) | |
r = Z39Result(result).response() | |
docs.append(r) | |
conn.close() | |
out = {"start": "-1", "limit": 50, "num_found": count} | |
out["docs"] = docs | |
return out | |
def get_by_bib(bib_number): | |
""" | |
Get the bibliographic data for a given id number. | |
""" | |
server = Z39_SERVER | |
#Open Z39.50 connection | |
conn = zoom.Connection(server['host'], | |
server['port'], | |
preferredRecordSyntax=server['rsyntax'], | |
elementSetName=server['element_set'], | |
databaseName=server['db'], | |
charset='utf-8', | |
) | |
params = '@attr 1=12 "%s"' % bib_number | |
query = zoom.Query(server['qsyntax'], params) | |
result = conn.search(query) | |
r = Z39Result(result).response() | |
conn.close() | |
return r |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<!-- saved from url=(0068)http://librarylab.law.harvard.edu/projects/stackview/demo/basic.html --> | |
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | |
<title>StackView Basic</title> | |
<link rel="icon" href="http://librarylab.law.harvard.edu/projects/stackview/demo/favicon.ico" type="image/x-icon"> | |
<!-- stackview.css to style the stack --> | |
<link rel="stylesheet" href="http://librarylab.law.harvard.edu/projects/stackview/demo/lib/jquery.stackview.css" type="text/css"> | |
<!-- stackview.js and all js dependencies --> | |
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script> | |
<script type="text/javascript" src="http://librarylab.law.harvard.edu/projects/stackview/demo/lib/jquery.stackview.min.js"></script> | |
</head> | |
<body> | |
<div id="stackview"></div> | |
<script type="text/javascript"> | |
$(function () { | |
$('#stackview').stackView( | |
{ | |
'url': './data', | |
'books_per_page': "1", | |
'start': "30", | |
'ribbon': "Stackview -- {{title}}", | |
'jsonp': true, | |
'search_type': 'cn', | |
'query': "{{callnumber}}", | |
//'search_type': 'loc_sort_order' | |
} | |
); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment