#!/usr/bin/python3 # -*- coding: UTF-8 -*- # GET me using a web browser, # executing my code with a Python interpreter called by a CGI-compliant webserver! # Example URI: # http://www.example.org/path/main.py?title=title&author=author # where: # title: # title of the book to filter (optional) # author: # author of the book to filter (optional) # # Every parameter is optional. # Please note that not providing filters results in all books in the library. # Useful libraries (no pun intended) import xml.etree.ElementTree as ET import json import sys import cgitb, cgi import zipfile from io import BytesIO import shutil as sh import os import time import logging # Connection to remote library file import requests # Parsing of HTTP RFC 1123 datetime format from email.utils import parsedate_to_datetime # Our custom library (again no pun intended) import tcparser # Global variables and configurations import glob try: # Start the logging library (to avoid printing on stdout) # TODO logging.basicConfig(filename='conf/tpdf.log', level=logging.DEBUG) # Start CGI handling for webserver cgitb.enable() inputvars = cgi.FieldStorage() logging.debug("Started CGI") print('Content-Type: text/json; charset=utf-8') print('Access-Control-Allow-Origin: *') print() ### End of HTTP headers: it is now safe to output things ########################################################## # Create output directory and temporary files if they do not exist if not os.path.exists(glob.conf['default']['outdir']): logging.debug("missing outdir, creating...") os.mkdir(glob.conf['default']['outdir']) if not os.path.exists(glob.conf['default']['outdir'] + '/lastupdate.txt'): logging.debug("missing lastupdate.txt, creating...") luh = open(glob.conf['default']['outdir'] + '/lastupdate.txt', 'w') luh.write('0') luh.close() # Retrieve last database update timestamp luh = open(glob.conf['default']['outdir'] + '/lastupdate.txt', 'r') try: lu = int(float(luh.read())) except ValueError: lu = 0 luh.close() logging.info("last database update timestamp is %d" % lu) # Fetch last modified from HTTP header path = glob.conf['default']['path'] user = glob.conf['default']['user'] pswd = glob.conf['default']['pswd'] req = requests.head(path, auth=(user, pswd)) logging.debug("fetched header from %s, returned code %d" % (path, req.status_code)) cachefile = glob.conf['default']['outdir'] + "/tellico.xml" # If header fetch fails I can't update cache. # Try with current one, if exists if req.status_code == 200 and 'Last-modified' in req.headers: mtime = int(parsedate_to_datetime(req.headers['Last-modified']).timestamp()) logging.info("Tellico last modified timestamp is %d" % mtime) # If local xml is out-of-date or missing, try download it if int(lu) < int(mtime) or not os.path.isfile(cachefile): logging.info("Out-of-date, updating") # Download Tellico .tc database req = requests.get(path, auth=(user, pswd)) if req.status_code == 200 and req.content != None: # Unzip Tellico .tc database and "cache it" locally zipHandler = zipfile.ZipFile(BytesIO(req.content), 'r') zipHandler.extractall(glob.conf['default']['outdir']) zipHandler.close() luh = open(glob.conf['default']['outdir'] + '/lastupdate.txt', 'w') luh.write(str(mtime)) luh.close() else: logging.error("Update failed") # Get a Python-friendly library struct from XML file library = tcparser.getLibrary(cachefile, lu) ### Get filters to search for books ### try: title = inputvars['title'].value except KeyError: title = '' try: author = inputvars['author'].value except KeyError: author = '' result = tcparser.filter(library, title=title, author=author) # Wanna get a pretty JSON encoded library to do your nasty things offline at home? ;-) print(json.dumps(result, indent=4)) # Avoid printing on str{out,err} the unexpected exception traces. Log it instead. except Exception as e: logging.exception(e)