home | list info | list archive | date index | thread index

Re: [OCLUG-Tech] symbolic links

Guess not attachments on oclug list so it is here.

On Friday 17 April 2009 3:11:16 pm Bruce Harding wrote:
>
> No not to much of a hurry.  Right all images prior to mid-2008 are named
> with there 10 digit ISBN.
>
> I found this attached script as well.

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
#
# isbn.py
#
# Code for messing with ISBN numbers
# Especially stuff for converting between ISBN-10 and ISBN-13
#
# Copyright (C) 2007 Darren J Wilkinson
#               2008 Osvaldo Santana Neto <osantana [ at ] gmail [ dot ] com>
#
# Free GPL code
# Last updated: 14/8/2007

"""
Code for messing with ISBN numbers. Stuff for validating ISBN-10 and
ISBN-13 numbers, computing check digits and converting from one format
to the other.

This code doesn't know anything about proper hyphenation of ISBNs. Nor does
it know anything about the real "validity" of ISBNs - it just validates on
the basis of the check-digit.

Some examples::

    >>> valid("3-79081-945-X")
    True
    >>> valid("1-58488-540-8")
    True
    >>> valid("1-58488-540-5")
    False
    >>> valid("978-1@#$5848ZZZYYY8-540-5")
    True
    >>> check("1-58488-540")
    '8'
    >>> check("3-79081-945")
    'X'
    >>> check("978-158488-540")
    '5'
    >>> valid_isbn13("978-158488-540-5")
    True
    >>> valid_isbn10("978-158488-540-5")
    False
    >>> strip("978-1ZYUI58488-540-5")
    '9781584885405'
    >>> convert("1-58488-540-8")
    '9781584885405'
    >>> convert("978-158488-540-5")
    '1584885408'
    >>> convert("1-8758488-540-8")
    Traceback (most recent call last):
        ...
    ValueError: Invalid ISBN
    >>> to_isbn13("1-58488-540-8")
    '9781584885405'
    >>> to_isbn13("978-158488-540-5")
    '9781584885405'
    >>> to_isbn10("978-158488-540-5")
    '1584885408'
    >>> to_isbn10("1584885408")
    '1584885408'
    >>> format("1584885408", "")
    '1584885408'
    >>> format("1584885408", "-")
    '1-58488-540-8'
    >>> format("1584885408", " ")
    '1 58488 540 8'
    >>> format("9781584885405")
    '9781584885405'
    >>> format("9781584885405", "-")
    '978-158488-540-5'
    >>> format("97815ASDQWE!@#84885405", "-")
    '978-158488-540-5'
    >>> format("9781584883ASDF@#$l5405", "-")
    '9781584883ASDF@#$l5405'
    >>> url("amazon", "978-158488-540-5")
    'http://www.amazon.com/o/ASIN/1584885408'

The code is very simple pure python code in a single source file. Please
read the source code file (isbn.py) for further information about how
it works.

Please send bug reports, bug fixes, etc. to:
darrenjwilkinson [ at ] btinternet [ dot ] com
Free GPL code, Copyright (C) 2007 Darren J Wilkinson
http://www.staff.ncl.ac.uk/d.j.wilkinson/

Or, if you are using the isbn.py utility version send your bug reports, fixes,
etc. to:
Osvaldo Santana Neto <osantana [ at ] gmail [ dot ] com>
http://www.pythonologia.org/
"""

import sys

(OK,
 INVALID,
 ERROR,
) = range(3)

def strip(isbn):
    """Strip whitespace, hyphens, etc. from an ISBN number and return the 
result."""
    return filter(lambda x: str.isdigit(x) or x.upper() == "X", isbn)

def convert(isbn):
    """Convert an ISBN-10 to ISBN-13 or vice-versa."""

    short = strip(isbn)
    if not valid(short):
        raise ValueError("Invalid ISBN")

    if len(short) == 10:
        stem = "978" + short[:-1]
        return stem + check(stem)
    else:
        if short.startswith("978"):
            stem = short[3:-1]
            return stem + check(stem)
        else:
            raise ValueError("ISBN not convertible")

def valid(isbn):
    """Check the validity of an ISBN. Works for either ISBN-10 or ISBN-13."""

    short = strip(isbn)
    if len(short) == 10:
        return valid_isbn10(short)
    if len(short) == 13:
        return valid_isbn13(short)
    return False

def check(stem):
    """Compute the check digit for the stem of an ISBN. Works with either the
    first 9 digits of an ISBN-10 or the first 12 digits of an ISBN-13."""

    short = strip(stem)
    if len(short) == 9:
        return check_isbn10(short)
    if len(short) == 12:
        return check_isbn13(short)
    return False

def check_isbn10(stem):
    """Computes the ISBN-10 check digit based on the first 9 digits of a
    stripped ISBN-10 number."""

    check = 11 - sum( (x+2) * int(y) for x,y in enumerate(reversed(stem)) ) % 
11
    if check == 10:
        return "X"
    elif check == 11:
        return "0"

    return str(check)

def valid_isbn10(isbn):
    """Checks the validity of an ISBN-10 number."""

    short = strip(isbn)
    if len(short) != 10:
        return False

    digits = [ (10 if x.upper() == "X" else int(x)) for x in short ]
    return (sum( (x+1)*y for x,y in enumerate(reversed(digits)) ) % 11) == 0

def check_isbn13(stem):
    """Compute the ISBN-13 check digit based on the first 12 digits of a
    stripped ISBN-13 number. """

    check = 10 - sum( (x%2*2+1) * int(y) for x,y in enumerate(stem) ) % 10
    if check == 10:
        return "0"

    return str(check)

def valid_isbn13(isbn):
    """Checks the validity of an ISBN-13 number."""

    short = strip(isbn)
    if len(short) != 13:
        return False

    digits = [ (10 if x.upper() == "X" else int(x)) for x in short ]
    return (sum( (x%2*2+1) * y for x,y in enumerate(digits) ) % 10) == 0

def to_isbn10(isbn):
    """Converts supplied ISBN (either ISBN-10 or ISBN-13) to a stripped 
ISBN-10."""

    if not valid(isbn):
        raise ValueError("Invalid ISBN")

    if valid_isbn10(isbn):
        return strip(isbn)
    else:
        return convert(isbn)

def to_isbn13(isbn):
    """Converts supplied ISBN (either ISBN-10 or ISBN-13) to a stripped 
ISBN-13."""

    if not valid(isbn):
        raise ValueError("Invalid ISBN")

    if valid_isbn13(isbn):
        return strip(isbn)
    else:
        return convert(isbn)

def url(type_, isbn):
    """Returns a URL for a book, corresponding to the "type" and the "isbn"
    provided. This function is likely to go out-of-date quickly, and is
    provided mainly as an example of a potential use-case for the module.
    Currently allowed types are "google-books" (the default if the type is not
    recognised), "amazon", "amazon-uk", "blackwells"."""

    short = to_isbn10(isbn)

    providers = {
        'amazon':     'http://www.amazon.com/o/ASIN/%s',
        'amazon-uk':  'http://www.amazon.co.uk/o/ASIN/%s',
        'blackwells': 'http://bookshop.blackwell.co.uk/jsp/welcome.jsp?action=search&type=isbn&term=%s',
    }

    return providers.get(type_, "http://books.google.com/books?vid=%s";) % 
(short,)

def format(isbn, sep=""):
    s = strip(isbn)

    if len(s) == 10:
        return s[0] + sep + s[1:6] + sep + s[6:9] + sep + s[9]

    if len(s) == 13:
        return s[0:3] + sep + s[3:9] + sep + s[9:12] + sep + s[12]

    return isbn

def main():
    from optparse import OptionParser

    usage = """usage: %prog [options] ISBN

    If ISBN arg is an ISBN-13:
        --isbn13 will check the ISBN number (and return an error code with the 
result)
        --isbn10 will convert the ISBN-13 to ISBN-10

    If ISBN arg is an ISBN-10:
        --isbn10 will check the ISBN number (and return an error code with the 
result)
        --isbn13 will convert the ISBN-10 to ISBN-13
    """

    parser = OptionParser(usage=usage)
    parser.add_option("-3", "--isbn13", action="store_true", dest="isbn13", 
default=True,
        help="check or convert a ISBN-13 value (default)")
    parser.add_option("-0", "--isbn10", action="store_false", dest="isbn13",
        help="check or convert a ISBN-10 value")
    parser.add_option("-s", "--separator", dest="separator", metavar="SEP", 
default="",
        help="select a ISBN separator character. (default: '%default')")
    parser.add_option("-q", "--quiet", action="store_false", dest="verbose", 
default=True,
        help="quiet (no output)")
    parser.add_option("-t", "--test", action="store_true", dest="test",
        help="test internal functions (useful for developers).")

    (options, args) = parser.parse_args()

    if options.test:
        import doctest
        doctest.testmod()
        sys.exit(0)

    if not args:
        parser.error("incorrect number of arguments.")

    verbose = options.verbose
    separator = options.separator
    isbn = strip(args[0])

    if len(isbn) == 13:
        valid_func = valid_isbn13
        only_check = options.isbn13
    elif len(isbn) == 10:
        valid_func = valid_isbn10
        only_check = not options.isbn13
    else: # invalid input
        if verbose:
            print "INVALID: %s" % (isbn,)
        return INVALID


    if only_check:         # check it
        if valid_func(isbn): # OK!
            if verbose:
                print format(isbn, separator)
            return OK
        else:                  # NOT OK!
            if verbose:
                print "INVALID: %s" % (isbn,)
            return INVALID
    else:                      # convert it
        try:
            n_isbn = convert(isbn)
        except ValueError:     # CANNOT CONVERT!
            if verbose:
                print "INVALID: %s" % (isbn,)
            return INVALID
        if verbose:            # CONVERT!
            print format(n_isbn, separator)
        return OK


if __name__=='__main__':
    sys.exit(main())

# vim:ts=4:sw=4:et:si:ai:sm


-- 
Bruce Harding, Member: IEEE, SPIE, IACR
Manager,
Computer Books for Less
210 Bank St.
Ottawa ON  K2P 1W2
Phone: 613-233-7357
Fax: 613-233-6823

references

message navigation