#!/usr/bin/python

__version__='$Rev: 552 $'
__release__='1.0'

program = 'greendex'

import sys, optparse, logging, traceback, os
import mitsfs

from datetime import date, datetime
from dateutil.relativedelta import relativedelta
dex = None
if 'dex' in locals():
    del dex

parser = optparse.OptionParser(usage = 'usage: %prog [options]',
                              version ='%prog '+'$Id$')

member = None

def main(args):
    global dex,  membook

    dex = mitsfs.dexdb(client=program)

    membook = dex.membook()

    options, args = parser.parse_args(args)

    if len(args) != 1:
        mitsfs.banner(program, __release__, __version__)
        parser.print_usage()
        sys.exit(1)

    production = os.environ.get('MITSFS_DSN') == None

    try:
        while True:
            if member != None:
                main_menu(None, False)
            mitsfs.banner(program, __release__, __version__)
            if production:
                print "This is a production version of Greendex, changes affect the dex. Do not use for testing."
            else:
                print "This is a development version of Greendex, changes do not affect the dex"
            print "No Member Selected"

            mitsfs.menu([('S', 'Select Patron', main_menu),
                         ('I', 'Check In Books by Author or Barcode', checkin),
                         ('N', 'New Patron', newmem),
                         ('D', 'Display Book', display),
                         ('A', 'Book Drop/Fancy Check In', advanced),
                         ('Q', 'Quit', quit),
                         ],once = True, cleanup = dex.db.rollback)
    except EOFError:
        pass



def main_menu(line, select_mem = True):
    global member
    try:
        if select_mem:
            selmem(None)
            if member == None:
                return 

        while True:
            print

            print "Member Selected: ", member, " Membership:", member.membership
            if len(member.checkouts) > 0:
                print mitsfs.tabulate([mitsfs.checkout_header_str()] + 
                                      [c.str_list() for c in member.checkouts])

            mitsfs.menu([('O', 'Check Out Books by Author or Barcode', checkout),
                         ('I', 'Check In Books by Patron/Member', checkin_member),
                         ('V', 'View Patron', viewmem),
                         ('E', 'Edit Patron and Membership', editmem),
                         ('P', 'Pay Outstanding Fines', lambda x: check_balance(member, print_notices = True)),
                         ('F', 'Financial Transaction', financial),
                         ('A', 'Book Drop/Fancy Check In/Check Out', advanced_member),
                         ('Q', 'Unselect Patron', quit),
                         ], once = True, cleanup = dex.db.rollback, title = "Main Menu:")
    except EOFError:
        member = None


def quit(line):
    raise EOFError()


def selmem(line):
    global member
    member = mitsfs.specify_member(membook)


def checkin(line, advanced = False, bookdrop = False):

    while True:
        checkedout = set(dex.cursor.execute('select title_id from checkout natural join book'
                                            ' where checkin_stamp is null',
                                            ()))

        authors = list(dex.cursor.execute('select entity_name'
                                          ' from entity'
                                          '  natural join title_responsibility'
                                          '  natural join book'
                                          '  natural join checkout'
                                          ' where checkin_stamp is null',
                                          ()))

        def author_completer(text, author=None):
            for i in authors:
                if i.upper().startswith(text.upper()):
                    yield i

        book = mitsfs.specify_book(dex,
                                   authorcomplete = author_completer,
                                   titlecomplete = dex.indices.titles.complete_checkedout,
                                   title_predicate = lambda title: title.title_id in checkedout,
                                   book_predicate = lambda book: book.outto != None)

        if not book:
            break

        checkin_date = None
        if advanced or bookdrop:
            print "Specify Checkin Date:"
            checkin_date = mitsfs.readdate(date.today(), False)

        print 'Checking In: '

        # mark it as checked in:
        #  get checkout_id
        try:
            for (mem, checkout) in book.checkin(checkin_date):
                print checkout
                delta = (checkin_date or date.today()) - checkout.due_date.date()
                if delta.days > 0 and not mem.pseudo:
                    fine = min(delta.days, 40) * .25
                    print "Book is overdue", delta.days, "days"
                    if advanced:
                        fine = -mitsfs.readmoney(fine, "Fine to charge to patron's account: ")
                    else:
                        fine = -fine
                    print 'FINE: %s added to balance' % (mitsfs.money_str(fine),)

                    desc = 'Book %s overdue %d days.' % (book, delta.days)
                    mem.fine_transaction(fine, desc, checkout.checkout_id)
                print 'Book checked out to', mem, 'has been checked in'
                global member 
                member = mem
        except mitsfs.CirculationException, exc:
            print exc
        print

def checkin_member(line, advanced = False, bookdrop = False):

    while True:
        if len(member.checkouts) == 0:
            print "All books have been returned"
        
        table = [[""] + mitsfs.checkout_header_str()]

        for n, c in enumerate(member.checkouts):
            table += [[mitsfs.COLOR.select('%d.' % (n + 1,))] + c.str_list()]
        table += [(mitsfs.COLOR.select('Q.'), 'Back to Main Menu')]
        print mitsfs.tabulate(table)
        print

        num = mitsfs.readnumber("Select book to checkin: ", 1 , len(member.checkouts) + 1, escape = 'Q')

        print

        if num == None:
            return

        book = member.checkouts[num - 1].book

        checkin_date = None
        if advanced or bookdrop:
            print "Specify Checkin Date:"
            checkin_date = mitsfs.readdate(date.today(), False)

        print 'Checking In: '

        # mark it as checked in:
        #  get checkout_id
        try:
            for (mem, checkout) in book.checkin(checkin_date):
                print checkout
                delta = (checkin_date or date.today()) - checkout.due_date.date()
                if delta.days > 0 and not mem.pseudo:
                    fine = min(delta.days, 40) * .25
                    print "Book is overdue", delta.days, "days"
                    if advanced:
                        fine = -mitsfs.readmoney(fine, "Fine to charge to patron's account: ")
                    else:
                        fine = -fine
                    print 'FINE: %s added to balance' % (mitsfs.money_str(fine),)

                    desc = 'Book %s overdue %d days.' % (book, delta.days)
                    mem.fine_transaction(fine, desc, checkout.checkout_id)
                print 'Book checked out to', mem, 'has been checked in'

        except mitsfs.CirculationException, exc:
            print exc
        print


def checkout(line, advanced=False):
    
    #Various Checks go here
    if not member.pseudo:
        if not check_balance(member):
            print "WARNING Member", member, "has negative Balance"
            if not advanced:
                print "Correct Balance or use Fancy Checkout"
                return

        if member.membership is None or member.membership.isexpired:
            print "WARNING Member", member, "has an expired or non-existing membership"
            if not advanced:
                print "Get new Membership or use Fancy Checkout"
                return

        overdue = False
        for out in member.checkouts:
            if datetime.now() > out.due_date.replace(tzinfo=None):
                overdue = True

        if overdue:
            print "WARNING Member", member, "has overdue books"
            if not advanced:
                for out in member.checkouts:
                    if datetime.now() > out.due_date.replace(tzinfo=None):
                         print out

                print "Return Books use Fancy Checkout"
                return

    while True:
        (count, ) = dex.cursor.execute('select count(*)'
                                       ' from checkout_member natural join checkout'
                                       ' where member_id=%s and checkin_stamp is null',
                                       (member.member_id,))
        if not member.pseudo and count >= 8:
            print "Member", member, "has", count, "books out."
            print "Only 8 books are allowed out at a time"
            if not advanced:
                print "Checkin Books or use Fancy Checkout to Checkout More"
                return
            
        #Only Circulating books on non fancy checkoout
        if advanced:
            title_predicate = lambda title: any(book for book in title.books if not book.outto)
            book_predicate = lambda book: not book.outto
        
        else:
            title_predicate = lambda title: any(book for book in title.books 
                                                if (not book.outto and book.circulating))
            book_predicate = lambda book: not book.outto and book.circulating

        print "Check Out books for member", str(member)
        print
        book = mitsfs.specify_book(dex, # predicate for not in select book_id in checkout where checkin_stamp is not null
                                        # is too much cpu for not enough benefit
                                   title_predicate = title_predicate,
                                   book_predicate =  book_predicate)

        if not book:
            break

        outto = book.outto
        if outto is not None:
            print book
            print 'is already checked out to', outto
            return

        checkout_date = None
        if advanced:
            print "Specify Checkout Date:"
            checkout_date = mitsfs.readdate(date.today(),False)


        checkout = book.checkout(member, checkout_date)
        print 'Checking Out:'
        print checkout
        
        # Uncomment when we start barcoding
        # barcodebook(book)

def barcodebook(book):

    if len(book.barcodes) == 0:
        print
        print "Book has no Barcode. Please attach and scan new Barcode."
        while True:
            barcode = mitsfs.readbarcode()
            if barcode is None:
                break
            if book.addbarcode(barcode):
                if len(book.barcodes) > 1:
                    print "WARNING: book has acquired two barcodes when it had zero"
                    print "moments ago; please look to your left or right and see if"
                    print "someone is checking out a similar book and role-play"
                    print "accordingly; otherwise please let libcomm know that they"
                    print "need to go meditate on the database logs."
                break
            print "Error adding barcode; perhaps it is already in use."


def viewmem(line):

    def fin(line):
        print 'Transactions of ', member
        print mitsfs.tabulate(
            [('Amount', 'Keyholder', 'Date', 'Type', 'Description')]
            + [(mitsfs.money_str(amount), by, when.date(),
                membook.txn_types[txn_type], desc)
               for (amount, desc, txn_type, by, when) in member.transactions])

    def history(line):
        print "History of: ", str(member)
        print mitsfs.tabulate([mitsfs.checkout_header_str_long()] + 
                                      [c.str_list() for c in member.checkout_history])



    def mem(line): 
        print str(member), " Membership History:"
        print mitsfs.tabulate([("Membership History", "Keyholder", "Bought")]+
                              [(str(m), str(m.created_by), str(m.created.date())) 
                               for m in member.memberships])

    print
    print member.info()

    try:
        mitsfs.menu([('C', 'Checkout History', history),
                     ('F', 'Financial History', fin),
                     ('M', 'Membership History', mem),
                     ('Q', 'Main Menu', quit),
                     ], cleanup = dex.db.rollback, title = "View User/Patron:")
    except EOFError:
        pass


def membership(line):
    def val(line):
        ok = line.strip().upper() in membook.memberships
        if not ok:
            print "Not a valid membership type"
        return ok

    membership_types = mitsfs.tabulate([
            [mitsfs.COLOR.select(k + '.'), v]
            for (k, v) in sorted(membook.memberships.items())])

    print "Select membership type:"
    print membership_types

    member_type = mitsfs.readvalidate('Select Membership Type: ', validate = val).upper()

    if member_type == 'Y':
        print "Enter Number of Years:"
        for i, x in enumerate(mitsfs.rates['Y']):
            if i == 0:
                continue
            print "  %s Year Membership: $%s" % (i, x)
        num_years = mitsfs.readnumber('Years: ',1,5)
        money = mitsfs.readmoney(mitsfs.rates[member_type][num_years])
        exp = date.today() + relativedelta(years=+num_years)
    else:
        money = mitsfs.readmoney(mitsfs.rates[member_type])
        if member_type == 'T':
            exp = date.today()  + relativedelta(months=+3)
        else:
            exp = None
    if exp is not None:
        print 'Projected expiration', exp
        exp = mitsfs.readdate(exp)
    #TODO get this to keep track of renewals
    c = dex.getcursor()
    description = 'New Membership - %s - Expires: %s' % (member_type, str(exp))
    transaction_id = member.transaction(-money, 'M', description, commit = False)
    c.execute('insert into membership(membership_type, member_id, membership_expires, membership_payment)'
              ' values (%s,%s,%s,%s)', (member_type, member.member_id, exp, transaction_id))

    dex.db.commit()
    check_balance(member, "Membership Payment")


def editmem(line):
    c = dex.getcursor()
    if member.pseudo:
        print "WARNING editing pseudo account: %s is disallowed." % (member,)
        print "Email libcomm if you need to modify information in a pseudo user account."
        return

    
    def addname(line):
        print member, 'existing names:'
        for x in member.names:
             print member.pretty_name(x)
        new = mitsfs.readvalidate("Name to add: ").strip()
        c.execute("INSERT INTO member_name(member_id,member_name)"
                  " VALUES (%s,%s) RETURNING member_name_id", (member.member_id, new))
        if mitsfs.readyes('Set name to default? [' + mitsfs.COLOR.yN + '] '):
            member_name_id = c.fetchone()[0]
            member.member_name_default = member_name_id
        dex.db.commit()

    def addemail(line):
        print member, 'existing emails:'
        for x in member.emails:
            print member.pretty_email(x)
        new = mitsfs.reademail("Email to add: ")
        c.execute("INSERT into member_email(member_id,member_email)"
                  " VALUES (%s,%s) RETURNING member_email_id", (member.member_id, new))
        if mitsfs.readyes('Set email to default? [' + mitsfs.COLOR.yN + '] '):
            member_email_id = c.fetchone()[0]
            member.member_email_default = member_email_id
        dex.db.commit()

    def addaddress(line):
        print member, "Existing addressess:"

        for x in member.addresses:
             print member.pretty_address(x)

        (addr_type, new) = mitsfs.readaddress(membook.address_types)
        
        print 'Adding', membook.address_types[addr_type]
        new = '\n'.join(new).strip()

        c.execute('INSERT INTO member_address'
                  ' (member_id,member_address, address_type)'
                  ' VALUES (%s,%s, %s) RETURNING member_address_id',
                  (member.member_id, new, addr_type))
        if mitsfs.readyes('Set address to default? [' + mitsfs.COLOR.yN + '] '):
            member_address_id = c.fetchone()[0]
            member.member_address_default = member_address_id
        dex.db.commit()

    def remove(line, title, info):
        if len(info) == 0:
            print "No non-default", title, "to remove"
            return
        print "Remove a non-default", title + ":"
        table = []
        for n, x in enumerate(info):
            lines = str(x).split("\n")
            table += [(mitsfs.COLOR.select('%d.' % (n + 1,)), lines[0])]
            table += [('', line) for line in lines[1:]]
        table += [(mitsfs.COLOR.select('Q.'), 'Back to Remove Menu')]
        print mitsfs.tabulate(table)
        print

        delete = mitsfs.readnumber("Select %s to delete: " % (title,), 0 , len(info) + 1, escape = 'Q')

        if delete == None:
            print "Nothing removed"
            return
        else:
            c.execute("DELETE FROM member_" + title + " WHERE member_" + title + "_id=%s", (info[delete-1].id,))
            dex.db.commit()

    def default(line, title, info, field):
        if len(info) == 0:
            print "No", title, "to set as default"
            return
        print "Set Default", title + ":"
        table = []
        for n, x in enumerate(info):
            lines = str(x).split("\n")
            table += [(mitsfs.COLOR.select('%d.' % (n + 1,)), lines[0])]
            table += [('', line) for line in lines[1:]]
        table += [(mitsfs.COLOR.select('Q.'), 'Back to Set Default Menu')]
        print mitsfs.tabulate(table)
        print

        select = mitsfs.readnumber("Select %s to set as default: " % (title,), 0 , len(info) + 1, escape = 'Q')

        if select == None:
            print "Nothing selected"
            return
        else:
            field(info[select-1].id)


    def set_default_name(name):
        member.member_name_default = name

    def set_default_email(email):
        member.member_email_default = email

    def set_default_address(address):
        member.member_address_default = address

    def add_info(line):
         try:
             mitsfs.menu([('N', 'Add Name', addname),
                          ('E', 'Add Email', addemail),
                          ('A', 'Add Address', addaddress),
                          ('Q', 'Back to Edit Membership', quit)
                          ], cleanup = dex.db.rollback, title = "Add User/Patron Information:")
         except EOFError:
             pass

    def remove_info(line):
         try:
             mitsfs.menu([('N', 'Remove Name', lambda x:remove(x,"name",member.other_names)),
                          ('E', 'Remove Email', lambda x:remove(x,"email",member.other_emails)),
                          ('A', 'Remove Address', lambda x:remove(x,"address",member.other_addresses)),
                          ('Q', 'Back to Edit Membership', quit)
                          ], cleanup = dex.db.rollback, title = "Remove User/Patron Information:")
         except EOFError:
             pass

    def set_default_info(line):
         try:
             mitsfs.menu([('N', 'Set Default Name', lambda x:default(x,"name",member.names, set_default_name)),
                          ('E', 'Set Default Email', lambda x:default(x,"email",member.emails, set_default_email)),
                          ('A', 'Set Default Address', lambda x:default(x,"address",member.addresses, set_default_address)),
                          ('Q', 'Back to Edit Membership', quit)
                          ], cleanup = dex.db.rollback, title = "Set Default User/Patron Information:")
         except EOFError:
             pass

    print
    print member.info()
    try:
        mitsfs.menu([('M', 'New/Renew Membership', membership),
                     ('A', 'Add Info', add_info),
                     ('R', 'Remove Info', remove_info),
                     ('D', 'Set Default Info', set_default_info),
                     ('Q', 'Main Menu', quit),
                     ], cleanup = dex.db.rollback, title = "Renew Membership/Edit User/Patron Information:")
    except EOFError:
        pass


def newmem(line):
    
    print "Please transfer the patrons information from the sheet."

    full_name = mitsfs.readvalidate("Legal Name (required): ").strip()
    
    names = membook.search(full_name)
    if len(names) > 0:
        print "The following people are already in greendex:"
        for n in names:
            print "    " + str(n)
        print "Are your sure you want to continure instead of adding a membership in the edit menu?"
        if not mitsfs.readyes('Continue? [' + mitsfs.COLOR.yN + '] '):
            return
    nickname = mitsfs.read("Nickname: ").strip()
    email = mitsfs.reademail("Email (required): ")
    
    print
    print "Please enter a real address which will be valid for the longest term:"
    print

    (addr_type, addr) = mitsfs.readaddress(membook.address_types)

    if not mitsfs.readyes('Add this patron? [' + mitsfs.COLOR.yN + '] '):
        return

    dex.cursor.execute("INSERT INTO member DEFAULT VALUES RETURNING member_id", ())
    member_id = dex.cursor.fetchone()[0]

    dex.cursor.execute("INSERT INTO member_name(member_id,member_name)"
                       " VALUES (%s,%s) RETURNING member_name_id",
                       (member_id,full_name,))
    if(nickname is not ""):
        dex.cursor.execute("INSERT INTO member_name(member_id,member_name)"
                           " VALUES (%s,%s) RETURNING member_name_id",
                           (member_id, nickname,))

    member_name_id = dex.cursor.fetchone()[0]

    dex.cursor.execute("INSERT INTO member_email(member_id,member_email)"
                       " VALUES (%s,%s) RETURNING member_email_id",
                       (member_id,email,))
    
    member_email_id = dex.cursor.fetchone()[0]

    addr = "\n".join(addr).strip()
    dex.cursor.execute("INSERT INTO member_address(member_id,member_address,address_type)"
                       " VALUES (%s,%s,%s) RETURNING member_address_id",
                       (member_id,addr,'P',))
    
    member_address_id = dex.cursor.fetchone()[0]

    dex.cursor.execute("UPDATE member"
                       " SET member_name_default=%s,"
                       " member_email_default=%s,"
                       " member_address_default=%s"
                       " WHERE member_id=%s",
                       (member_name_id, member_email_id, member_address_id, member_id))

    dex.db.commit()
    global member
    member = mitsfs.member(dex, member_id)

    print
    print "Member Added"
    print 

    if mitsfs.readyes('Add a membership to new patron? [' + mitsfs.COLOR.yN + '] '):
        membership(None)



def financial(line):
    
    other_item = ('A', 'Advanced Transaction Options', financial_other)
    quit_item = ('Q', 'Back to Main Menu', quit)

    menu = [(k, v, lambda x, k = k: do_transaction(k)) for (k, v) in membook.basic_transactions.items()]

    menu.sort(key = lambda x: x[0])
    menu.append(other_item)
    menu.append(quit_item)

    try:
        mitsfs.menu(menu,once = False, cleanup = dex.db.rollback, title = "Finanical Transactions Menu:")
    except EOFError:
        pass

def financial_other(line):
    
    quit_item = ('Q', 'Back to Main Menu', quit)

    menu = [(k, v, lambda x, k = k: do_transaction(k)) for (k, v) in membook.fancy_transactions.items()]
    menu.sort(key = lambda x: x[0])
    menu.append(quit_item)

    try:
        mitsfs.menu(menu,once = False, cleanup = dex.db.rollback, title = "Advanced Financial Transactions Menu:")
    except EOFError:
        pass

def do_transaction(txntype):

    print
    print 'Transaction for', member
    print

    c = dex.getcursor()

    if txntype == 'V':
        txns = member.non_void_transactions
        
        if len(txns) == 0:
            print "No transactions to VOID"
            return

        quit_item = (mitsfs.COLOR.select('Q.'), 'Back to Main Menu')

        print 'Non Void Transactions of ', member
        print mitsfs.tabulate(
            [('#', 'Amount', 'Keyholder', 'Date', 'Type', 'Description')]
            + [(mitsfs.COLOR.select(str(i + 1) + '.'), mitsfs.money_str(tx[1]), tx[4], 
                tx[5].date(), membook.txn_types[tx[3]], tx[2])
               for (i, tx) in enumerate(txns)] + [quit_item])

        num = mitsfs.readnumber("Select transaction to Void: ", 1 , len(txns) + 1, escape = 'Q')

        if num != None:
            print
            voided = member.void_transaction(txns[num-1][0])
            print "Voided transactions:"
            print mitsfs.tabulate(
                [('Member', 'Amount', 'Keyholder', 'Date', 'Type', 'Description')]
                + [(mitsfs.member(dex, mem_id).name, mitsfs.money_str(amount), by, when.date(),
                    membook.txn_types[txn_type], desc)
                   for (amount, desc, txn_type, by, when, mem_id) in voided])
        return
    
    if txntype in ['D', 'P']:
        if txntype == 'D':
            print "Enter amount of donation, this will increase the patron's balance"
        else:
            print "Enter the amount the patron is paying, this will increase the patron's balance"
        amount = mitsfs.readmoney(prompt = 'Amount: ').copy_abs()
        print amount
    elif txntype in ['K', 'F', 'R', 'M']:
        if txntype in ['K', 'F']:
            print "Enter the fine amount, this will decrease the patron's balance"
        elif txntype == 'M':
            print "Warning, this does not update the patrons membership"
            print "All this does is create a transaction with the type 'membership'"
            print "If you want to update a membership, go to 'Edit Member' and add"
            print "a new membership, a transaction will be created with that"
            print
            print "Enter amount, this will decrease the patron's balance"
        else:
            print "Enter the amount the patron is being reimbursed, this will decrease the patron's balance"
        amount = -mitsfs.readmoney(prompt = 'Amount: ').copy_abs()
    else:
        print 'Enter Amount (negative for fines, positive for credit)'
        amount = mitsfs.readmoney(prompt = "Amount: ")

    desc = mitsfs.read('Enter Description: ', history='description')


    print 'Adding %s to account of %s' % (mitsfs.money_str(amount), member)
    
    if txntype  in ['P', 'R']:
        print 'Adding %s to Cash Drawer' % (mitsfs.money_str(amount),)
    
    if not mitsfs.readyes('Commit the transaction? [' + mitsfs.COLOR.yN + '] '):
        return

    if txntype not in ['P', 'R']:
        member.transaction(amount, txntype, desc)
    else:
        cash_desc = "Cash transaction for %s: %s" % (member.normal_str, desc)
        member.cash_transaction(amount, txntype, cash_desc)
        

def check_balance(member, desc = "Payment", print_notices = False):
    if member.pseudo:
        if print_notices:
            print "Member pseudo, can't change balances"
        return True
    amount = -member.balance

    if amount > 0:
        print 'Member', member, 'has a negative balance'
        if mitsfs.readyes('Pay balance? [' + mitsfs.COLOR.yN + '] '):
            amount = mitsfs.readmoney(amount,
                                      prompt2 = 'Is member paying %s? [' + mitsfs.COLOR.yN + '] ',
                                      prompt = 'Amount they are paying: ')

            desc = desc + ' by ' + member.normal_str
            member.cash_transaction(amount, 'P', desc)
    elif print_notices:
        print "Member doesn't have a negative balance"

    return member.balance >= 0

def advanced(line):
    try:
        mitsfs.menu([
                ('B', 'Book Drop Check In by Author or Barcode', lambda line : checkin(line, bookdrop = True)),
                ('I', 'Fancy Check In by Author or Barcode', lambda line : checkin(line, advanced = True)),
                ('Q', 'Main Menu', quit),
                ], cleanup = dex.db.rollback, title = "Fancy/Book Drop Check In:")
    except EOFError:
        pass

def advanced_member(line):
    try:
        mitsfs.menu([
                ('B', 'Book Drop Checkin by Patron/Member', lambda line : checkin_member(line, bookdrop = True)),
                ('I', 'Fancy Check In Books by Patron/Member', lambda line : checkin_member(line, advanced = True)),
                ('O', 'Fancy Check Out by Author or Barcode', lambda line: checkout(line, advanced = True)),
                ('Q', 'Main Menu', quit),
                ], cleanup = dex.db.rollback, title = "Fancy/Book Drop Check In/Check Out:")
    except EOFError:
        pass


def display(line):
    title = mitsfs.specify(dex)
    if not title:
        return

    print title
    print
    print 'HOLDINGS - If book is checked out, the member it is checked out to'
    print 'will be on the next line'
    for book in title.books:
        print book
        outto = book.outto
        if outto:
            print "    " + str(outto)

    print

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