#! /usr/bin/env python # # BookList # Copyright 2010, Adriaan de Groot # # This program implements functions for the "BBC Book List" meme # which is going around; it can present your book list in a # variety of different formats. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # import re import sys # These are some constants used in the representation of # book statuses. Pick something nice to read. READ="*" WANT="+" NONE="." # The book database. The database is a list of either pairs or triples; # if it is a pair, then it is a (title,author) pair; if a triple, then # (status, title, author). Code later on handles the normalization to # triples based on user input. To obtain your code number, add # READ and WANT as the first element of those triples you've read # or want to read, and then run "booklist.py short" to obtain a number. # To view other people's databases, get their number and run # "booklist.py text number". books = [ ("Pride and Prejudice","Jane Austen"), ("The Lord of the Rings","JRR Tolkien"), ("Jane Eyre","Charlotte Bronte"), ("Harry Potter series","JK Rowling"), ("To Kill a Mockingbird","Harper Lee"), ("The Bible",":"), ("Wuthering Heights","Emily Bronte"), ("Nineteen Eighty Four","George Orwell"), ("His Dark Materials","Philip Pullman"), ("Great Expectations","Charles Dickens"), ("Little Women","Louisa M Alcott"), ("Tess of the D'Urbervilles","Thomas Hardy"), ("Catch 22","Joseph Heller"), ("Complete Works of Shakespeare","Shakespeare"), ("Rebecca","Daphne Du Maurier"), ("The Hobbit","JRR Tolkien"), ("Birdsong","Sebastian Faulk"), ("Catcher in the Rye","JD Salinger"), ("The Time Traveller's Wife","Audrey Niffenegger"), ("Middlemarch","George Eliot"), ("Gone With The Wind","Margaret Mitchell"), ("The Great Gatsby","F Scott Fitzgerald"), ("Bleak House","Charles Dickens"), ("War and Peace","Leo Tolstoy"), ("The Hitch Hiker's Guide to the Galaxy","Douglas Adams"), ("Brideshead Revisited","Evelyn Waugh"), ("Crime and Punishment","Fyodor Dostoyevsky"), ("Grapes of Wrath","John Steinbeck"), ("Alice in Wonderland","Lewis Carroll"), ("The Wind in the Willows","Kenneth Grahame"), ("Anna Karenina","Leo Tolstoy"), ("David Copperfield","Charles Dickens"), ("Chronicles of Narnia","CS Lewis"), ("Emma","Jane Austen"), ("Persuasion","Jane Austen"), ("The Lion, The Witch and The Wardrobe","CS Lewis"), ("The Kite Runner","Khaled Hosseini"), ("Captain Corelli's Mandolin","Louis De Bernieres"), ("Memoirs of a Geisha","Arthur Golden"), ("Winnie the Pooh","AA Milne"), ("Animal Farm","George Orwell"), ("The Da Vinci Code","Dan Brown"), ("One Hundred Years of Solitude","Gabriel Garcia Marquez"), ("A Prayer for Owen Meaney","John Irving"), ("The Woman in White","Wilkie Collins"), ("Anne of Green Gables","LM Montgomery"), ("Far From The Madding Crowd","Thomas Hardy"), ("The Handmaid's Tale","Margaret Atwood"), ("Lord of the Flies","William Golding"), ("Atonement","Ian McEwan"), ("Life of Pi","Yann Martel"), ("Dune","Frank Herbert"), ("Cold Comfort Farm","Stella Gibbons"), ("Sense and Sensibility","Jane Austen"), ("A Suitable Boy","Vikram Seth."), ("The Shadow of the Wind","Carlos Ruiz Zafon"), ("A Tale Of Two Cities","Charles Dickens"), ("Brave New World","Aldous Huxley"), ("The Curious Incident of the Dog in the Night-time","Mark Haddon"), ("Love In The Time Of Cholera","Gabriel Garcia Marquez"), ("Of Mice and Men","John Steinbeck"), ("Lolita","Vladimir Nabokov"), ("The Secret History","Donna Tartt"), ("The Lovely Bones","Alice Sebold"), ("Count of Monte Cristo","Alexandre Dumas"), ("On The Road","Jack Kerouac"), ("Jude the Obscure","Thomas Hardy"), ("Bridget Jones's Diary","Helen Fielding"), ("Midnight's Children","Salman Rushdie"), ("Moby Dick","Herman Melville "), ("Oliver Twist","Charles Dickens"), ("Dracula","Bram Stoker"), ("The Secret Garden","Frances Hodgson Burnett"), ("Notes From A Small Island","Bill Bryson"), ("Ulysses","James Joyce"), ("The Bell Jar","Sylvia Plath"), ("Swallows and Amazons","Arthur Ransome"), ("Germinal","Emile Zola"), ("Vanity Fair","William Makepeace Thackeray"), ("Possession","AS Byatt"), ("A Christmas Carol","Charles Dickens"), ("Cloud Atlas","David Mitchell"), ("The Color Purple","Alice Walker"), ("The Remains of the Day","Kazuo Ishiguro"), ("Madame Bovary","Gustave Flaubert"), ("A Fine Balance","Rohinton Mistry"), ("Charlotte's Web","EB White"), ("The Five People You Meet In Heaven","Mitch Albom"), ("Adventures of Sherlock Holmes","Sir Arthur Conan Doyle"), ("The Faraway Tree Collection","Enid Blyton"), ("Heart of Darkness","Joseph Conrad"), ("The Little Prince","Antoine De Saint-Exupery"), ("The Wasp Factory","Iain Banks"), ("Watership Down","Richard Adams"), ("A Confederacy of Dunces","John Kennedy Toole"), ("A Town Like Alice","Nevil Shute"), ("The Three Musketeers","Alexandre Dumas"), ("Hamlet","William Shakespeare"), ("Charlie and the Chocolate Factory","Roald Dahl"), ("Les Miserables","Victor Hugo"), ] def represent_text(): """Represent the book database as a plain text list.""" count = 1 for b in books: # Double string interpolation is generally a bad idea print ("%s %%d %s - %s" % b) % count count = count + 1 def represent_html(): """Represent the book database in a HTML
    with bolding and italics according to the status of each book.""" print "
      " for b in books: status, title, author = b tag="" endtag="" if status == READ: tag = "" endtag = "" if status == WANT: tag = "" endtag = "" print "
    1. %s%s - %s%s
    2. " % (tag, title, author, endtag) print "
    " def represent_integer(): """Represent the book database as a single integer, based on a base-3 interpretation of the book statuses (READ=2, WANT=1).""" r = 0 for b in books: r = r * 3 if b[0] == READ: r = r + 2 elif b[0] == WANT: r = r + 1 print r def interpret_integer(v): """Given a number v (presumably the output of 'short' at some point), create a list of values by interpreting it in base-three. Returns the list of values. This is the inverse of represent_integer.""" n = 0 try: n = int(v) except: print usage print "Argument [number] does not represent a number." sys.exit(1) values = 100 * [ NONE ] place = 99 while n > 0: digit = n % 3 n = n / 3 if digit == 2: values[place] = READ if digit == 1: values[place] = WANT place = place - 1 return values def abbr(s): """Abbreviate a (multi-word) string to just the first letters of each word, preserving capitalization.""" return re.sub(r"([A-Za-z])['A-Za-z]*",lambda mo : mo.group(1),s).replace(" ","") def represent_code(): """Represent the books in a geek-code style with status and abbreviated title and author -- this is surprisingly recognizable.""" for b in books: if (b[0] != NONE): print "%s%s-%s" % (b[0], abbr(b[1]), abbr(b[2])), def normalize_database(library,values,keep=True): """Go through the library (e.g. list of books, which might be triples or pairs, depending on their status, and merge the library statuses with the given values (a list of status constants); if keep is true, the status of any *triple* is preserved, otherwise the status from values is used. Return a new library.""" new_books = [] for b,v in zip(library,values): if len(b)==3: if keep: t = b else: t = (v,b[1],b[2]) else: t = (v,b[0],b[1]) new_books.append(t) return new_books def update_database(v): """Given a number v (presumably the output of 'short' at some point), return a new library of books with their statuses updated according to the given value.""" return normalize_database(books,interpret_integer(v),False) # Main part of the program: map program arguments to functions to be called. actions = { "text" : represent_text, "html" : represent_html, "code" : represent_code, "short" : represent_integer, # Pretend to have switches, too "-t" : represent_text, "-h" : represent_html, "-c" : represent_code, "-d" : represent_integer, } usage = """Usage: booklist.py (text|html|code|short) [number]""" if len(sys.argv)==1 or len(sys.argv)>3: print usage sys.exit(1) # 472899411102988434671899921134218056756239761136 is my value. if len(sys.argv)==3: books = update_database(sys.argv[2]) else: books = normalize_database(books,100 * [ NONE ]) # Execute the representation action according to the if sys.argv[1] in actions: actions[sys.argv[1]]() else: print usage sys.exit(1)