-
-
Save bwiernik/45f514da37af92491893f2f7212710f9 to your computer and use it in GitHub Desktop.
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
#! /usr/bin/env python3 | |
# | |
# zotxform | |
# BW custom Zotero locale transform. | |
# | |
# AUTHORS: Ian-Mathew Hornburg ‹imhornburg@gmail.com› | |
# Optima Language Analytics LLC | |
# LICENSE: [TBD] | |
# | |
# Copyright (c) 2014–2015, the authors. | |
# All rights reserved. | |
# --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- # | |
from shutil import copytree, rmtree | |
from sys import exit | |
import argparse | |
import json | |
import logging | |
import os | |
import os.path as path | |
import zipfile as z | |
CONFIG_PATH = 'zotxform.json' | |
TEMPDIR_PATH = 'zotxform_temp' | |
FIREFOX_MANIFEST_PATH = 'chrome.manifest' | |
FIREFOX_MANIFEST_LOCALE = '\n\nlocale zotero bw-US chrome/locale/bw-US/zotero/' | |
STANDALONE_WIN_DFLT_ARCHIVE_PATH = "C:\\Program Files (x86)\\Zotero Standalone\\zotero.jar" | |
STANDALONE_WIN_DFLT_MANIFEST_PATH = "C:\\Program Files (x86)\\Zotero Standalone\\chrome.manifest" | |
STANDALONE_MANIFEST_LOCALE = '\n\nlocale zotero bw-US jar:zotero.jar!/chrome/locale/bw-US/zotero/' | |
replacement_table = { | |
'itemTypes.patent = Patent': 'itemTypes.patent = Class', | |
'itemFields.series = Series': 'itemFields.series = Series/Type', | |
'itemFields.assignee = Assignee': 'itemFields.assignee = Level', | |
'itemFields.patentNumber = Patent Number': 'itemFields.patentNumber = Lecture Number', | |
'itemFields.priorityNumbers = Priority Numbers': 'itemFields.priorityNumbers = Course Title', | |
'itemFields.issueDate = Issue Date': 'itemFields.issueDate = Class Date', | |
'itemFields.references = References': 'itemFields.references = Primary Instructor', | |
'itemFields.legalStatus = Legal Status': 'itemFields.legalStatus = Program', | |
'itemFields.country = Country': 'itemFields.country = Department', | |
'itemFields.applicationNumber = Application Number': 'itemFields.applicationNumber = Course Number', | |
'itemFields.proceedingsTitle = Proceedings Title': 'itemFields.proceedingsTitle = Session/Proceedings', | |
'itemFields.issuingAuthority = Issuing Authority': 'itemFields.issuingAuthority = Material Type', | |
'itemFields.filingDate = Filing Date': 'itemFields.filingDate = Term', | |
'creatorTypes.editor = Editor': 'creatorTypes.editor = Editor/Chair', | |
'creatorTypes.inventor = Inventor': 'creatorTypes.inventor = Instructor', | |
'creatorTypes.attorneyAgent = Attorney/Agent': 'creatorTypes.attorneyAgent = Notes By', | |
} | |
def transform(args): | |
def check_file(fn): | |
fn_exists = path.exists(fn) and path.isfile(fn) | |
if not fn_exists: | |
raise OSError("The specified file does not exist or cannot be accessed.") | |
sys.exit(1) | |
if args['firefox']: | |
archive_location = {} | |
if args['file']: | |
# Ensure that we have a full absolute path on the passed filename | |
fn_archive = path.abspath(args['file']) | |
check_file(fn_archive) | |
# Overwrite any existing configuration | |
with open(CONFIG_PATH, encoding='utf_8', mode='w') as config_file: | |
archive_location['firefox'] = fn_archive | |
json.dump(archive_location, config_file) | |
else: | |
try: | |
# Check for an existing config file | |
with open(CONFIG_PATH, encoding='utf_8', mode='r') as config_file: | |
archive_location = json.load(config_file) | |
except: | |
raise FileNotFoundError("Could not find zotxform.json. Please specify a Zotero Firefox archive to be patched.") | |
if args['firefox']: | |
fn_archive = path.abspath(archive_location.get('firefox')) | |
fn_manifest = FIREFOX_MANIFEST_PATH | |
if args['standalone']: | |
# If we're Standalone, just use the hardcoded defaults | |
fn_archive = path.abspath(STANDALONE_WIN_DFLT_ARCHIVE_PATH) | |
fn_manifest = path.abspath(STANDALONE_WIN_DFLT_MANIFEST_PATH) | |
try: | |
with z.ZipFile(fn_archive, mode='r') as archive: | |
os.mkdir(TEMPDIR_PATH) | |
os.chdir(TEMPDIR_PATH) | |
# We wanna preserve the exact order of the files in the archive, | |
# since for some applications, order matters (e.g., needing to have | |
# the chrome.manifest first). So everything that’s going into the | |
# archive needs to be explicitly enumerated in this list, starting | |
# with the existing contents. | |
file_list = archive.namelist() | |
if args['firefox']: | |
if FIREFOX_MANIFEST_PATH in archive.namelist(): | |
logger.info("Manifest found.") | |
else: | |
raise FileNotFoundError("chrome.manifest file not found.") | |
if args['standalone']: | |
if os.path.isfile(fn_manifest): | |
logger.info("Manifest found.") | |
else: | |
raise FileNotFoundError("chrome.manifest file not found.") | |
# Extract and end scope, closing archive | |
archive.extractall() | |
# Append custom locale line to manifest | |
with open(fn_manifest, encoding='utf_8', mode='r') as manifest: | |
s = manifest.read() | |
if args['firefox']: | |
if FIREFOX_MANIFEST_LOCALE in s: | |
raise ValueError("Manifest already patched.") | |
else: | |
s = s.join([s, FIREFOX_MANIFEST_LOCALE]) | |
if args['standalone']: | |
if STANDALONE_MANIFEST_LOCALE in s: | |
raise ValueError("Manifest already patched.") | |
s = s.join([s, STANDALONE_MANIFEST_LOCALE]) | |
logger.info("Writing patched manifest.") | |
with open(fn_manifest, encoding='utf_8', mode='w') as manifest: | |
manifest.write(s) | |
# Copy the contents of en-US locale to our new custom locale directory | |
new_locale_dir = path.join('chrome', 'locale', 'bw-US') | |
copytree(path.join('chrome', 'locale', 'en-US'), new_locale_dir) | |
file_list.append(new_locale_dir) | |
# [TO-DO] Make this cleaner, more clear; better comprehensions | |
# Walk the directory tree, grabbing all nodes along the way, and | |
# append this list to our archive list. | |
for e in [path.join(dirpath, f) for dirpath, dirname, filename in os.walk(new_locale_dir) for f in filename]: | |
file_list.append(e) | |
# Read in properties file, patch, write | |
with open(path.join(new_locale_dir, 'zotero', 'zotero.properties'), encoding='utf_8', mode='r') as f: | |
properties = f.read() | |
for target in replacement_table.keys(): | |
properties = properties.replace(target, replacement_table.get(target)) | |
with open(path.join(new_locale_dir, 'zotero', 'zotero.properties'), encoding='utf_8', mode='w') as f: | |
f.write(properties) | |
# Truncate and write new archive | |
with z.ZipFile(fn_archive, compression=z.ZIP_DEFLATED, mode='w') as archive: | |
for e in file_list: | |
archive.write(e) | |
if 'bw-US' in e or e == FIREFOX_MANIFEST_PATH: | |
print(e) | |
logger.info("Done.") | |
finally: | |
os.chdir(os.pardir) | |
rmtree(TEMPDIR_PATH) | |
# Command-line argument parsing - --- --- --- --- --- --- --- --- --- --- --- # | |
parser = argparse.ArgumentParser( | |
add_help=True, | |
description="Custom Zotero locale transform.", | |
epilog="Copyright (c) 2014–2015, Optima Language Analytics LLC. All rights reserved." | |
) | |
parser.add_argument('-v', '--verbose', action='store_true', help='increase verbosity') | |
parser.add_argument('-f', '--firefox', action='store_true', help='patch Zotero Firefox') | |
parser.add_argument('-s', '--standalone', action='store_true', help='patch Zotero Standalone') | |
parser.add_argument('file', nargs='?', default=False, help="A valid Zotero Firefox settings file to be patched. If none given, zotxform will attempt to use the last valid archive location.") | |
# “main()” -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- # | |
if __name__ == '__main__': | |
logging.basicConfig(format="[zotxform] {levelname} :: {message}", style='{') | |
logger = logging.getLogger('zotxform') | |
args = vars(parser.parse_args()) | |
if args['verbose']: | |
logger.setLevel('INFO') | |
del args['verbose'] | |
logger.info("zotxform v0.612") | |
# If no action flag is given, warn and bail. | |
if not any(args.values()): | |
logger.warn("At least one argument is required.") | |
exit(1) | |
if args['firefox'] and args['standalone']: | |
logger.warn("Specify only one type of Zotero settings file to patch.") | |
exit(1) | |
if args['file'] and args['standalone']: | |
logger.warn("Cannot specify file. Zotero Standalone patch uses default files located at " + STANDALONE_WIN_DFLT_ARCHIVE_PATH + " and " + STANDALONE_WIN_DFLT_MANIFEST_PATH + ".") | |
exit(1) | |
transform(args) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment