#!/usr/bin/env python
######################################################
#   script: gc2attend.py
#
# synopsis: Python script produce nicely formatted attendance 
#           sheet from Blackboard Grade Centre file.
#
#   author: Claas Roever, Sep 2012
# homepage: http://www.maths.nuigalway.ie/~chew/software/gc2attend/
######################################################

######################################################
## Change these if you don't like them. Units are cm
#
topMargin = 0.5
bottomMargin = 0.5
leftMargin = 1.5
rightMargin = 1.5
font = "Helvetica" # or font="Courier" work
# these two will be appended to the module code if possible
default_header="Attendance List"
default_outfile="attend"
#######################################################

import sys
import os

try:
    from pango import FontDescription
    fd = FontDescription(font+" 11")
except: 
    fd = None

me = os.path.basename(sys.argv[0])
ERROR_MSG = "%s: Error Message" % me
INFO_MSG = "%s: Information" % me

# handle flagged optional arguments
viewer = False
if '-v' in sys.argv:
    i = sys.argv.index('-v')
    viewer = sys.argv[i+1]
    sys.argv.remove('-v')
    sys.argv.remove(viewer)
head = ''
if '-h' in sys.argv:   
    i = sys.argv.index('-h')
    head = sys.argv[i+1]
    sys.argv.remove('-h')
    sys.argv.remove(head)

# some functions for communication with user
def errGUI(msg,msg_type=ERROR_MSG,fatal=True):
    d = gtk.Dialog(msg_type, None, 0, (gtk.STOCK_OK, gtk.RESPONSE_OK))
    l = gtk.Label(msg)
    if msg_type == ERROR_MSG:
        l.modify_fg(gtk.STATE_NORMAL,l.get_colormap().alloc_color("red"))
    l.set_padding(10,10)
    l.set_max_width_chars(60)
    l.set_line_wrap(True)
    if fd:
        l.modify_font(fd)
    d.vbox.pack_start(l, True, True, 0)
    l.show()
    d.run()
    if fatal:
        sys.exit()

def errTTY(msg,msg_type=ERROR_MSG,fatal=True):
    if msg_type:
        msg = msg_type + ': ' + msg
    sys.stderr.write(msg+'\n')
    if fatal:
        sys.exit()

# returns up a file input dialog
def get_dialog():
    dialog = gtk.FileChooserDialog("Select Blackboard Grade Centre File",
                                   None,
                                   gtk.FILE_CHOOSER_ACTION_OPEN,
                                   (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                    gtk.STOCK_OPEN, gtk.RESPONSE_OK))
    dialog.set_default_response(gtk.RESPONSE_OK)    
    filtr = gtk.FileFilter()
    filtr.set_name("CSV files")
    filtr.add_pattern("*.csv")
    dialog.add_filter(filtr)
    filtr = gtk.FileFilter()
    filtr.set_name("All files")
    filtr.add_pattern("*")
    dialog.add_filter(filtr)
    return dialog

# try to decide if we are invoked from the command line or otherwise
if sys.stdin.isatty() and not sys.platform.startswith("win"):
    gui = False
else:
    import pygtk
    pygtk.require('2.0')
    import gtk
    if gtk.pygtk_version < (2,3,90):
        sys.stderr.writeline("PyGtk 2.3.90 or later required for this example")
        sys.exit()
    gui = True

if not gui:
    err = errTTY
    if len(sys.argv) < 2:
        err("""
Usage: %s infile [outfile[.pdf]] [-h header] [-v PDFviewer]

     -h header 
          user specified header/watermark

     -v PDFviewer  
          fires up the specified PDF viewer showing the output""" % sys.argv[0],'')
else:
    err = errGUI


# figure out what the input file is
if len(sys.argv) < 2:   # we are running GUI here
    fc = get_dialog()
    response = fc.run()
    if response == gtk.RESPONSE_OK:
        infile_name = fc.get_filename()
        fc.destroy()
    else:
        sys.exit()
else:                   # or have an argument, otherwise the usage messsage was printed
    infile_name = sys.argv[1]


# check that we can read the infile and that it 
# has the magic characters at the start
try:
    infile = open(infile_name, 'r')
except:
    err("Could not open file: %s" % infile_name)
if infile.readline()[:3] != '\xef\xbb\xbf':
    err("Not a Blackboard Grade Centre CSV file:\n%s" % infile_name)

# find the module code
course = ''  # default
fn = os.path.basename(infile_name)
dirn = os.path.dirname(infile_name)
if fn.startswith('gc_'):  # from Grade Centre file name
    course = fn.split('_')[1].split('-')[1]

# set the header for all pages
if not head: 
    head = ' '.join([course,default_header])

# sanitise the output filename
if len(sys.argv) > 2:
    outf = sys.argv[2]
    out_given = True
else:
    out_given = False
    outf = course + 'attend'
    outf = os.path.join(dirn, outf)
if outf[-4:] not in ['.PDF', '.pdf']:
    outf += '.pdf'

# collect the entries for the table from infile
data = [2*['Surname','Name','Signature']]
odd = 1
row = []
for line in infile:
    line = line.split(',')
    line = list(x.strip('"').upper() for x in line)
    if odd:
        row = line[:2] + [' ']
    else:
        row += line[:2] + [' ']
        data.append(row)
    odd = (odd + 1) % 2
if len(row) < 6:
    row += (6-len(row))*[' ']
    data.append(row)
for i in range(3):
    data.append(6*[' '])
infile.close()

# build the PDF document
from reportlab.lib import colors
from reportlab.lib.pagesizes import cm, landscape, A4
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Spacer 
from math import sin, cos, pi, degrees, atan2

doc = SimpleDocTemplate(outf, pagesize=landscape(A4))
doc.bottomMargin = bottomMargin * cm
doc.topMargin = topMargin * cm
doc.leftMargin = leftMargin * cm
doc.rightMargin = rightMargin * cm

def firstPage(canvas,doc):
    canvas.saveState()
    canvas.setFont(font+'-Bold', 72)
    canvas.setFillColor(colors.lightgrey)
    canvas.setAuthor("gc2attend.py http://www.maths.nuigalway.ie/~chew/software/gc2attend/")
    canvas.setTitle(head)
    canvas.setSubject("Attendance List")
    height = A4[0]/2.0
    width = A4[1]/2.0
    rads = atan2(height,width)
    canvas.rotate(degrees(rads))
    canvas.translate(100, -width+110)
    canvas.drawCentredString(width, height, head)
    canvas.restoreState()

# container for the 'Flowable' objects
elements = []
 
t=Table(data,[None,None,7*cm]*2,0.75*cm,None,1) #,5*[0.4*inch], 4*[0.4*inch])
t.setStyle(TableStyle([('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
                       ('BOX', (0,0), (-1,-1), 0.25, colors.black),
                       ('FONT',(0,0),(-1,-1),font,9),
                       ('FONT',(0,0),(-1,0),font+'-Bold',10),
                       ]))

elements.append(t)
# write the document to disk
doc.build(elements,firstPage,firstPage)

# and possibly open it in a PDF viewer
if viewer:
    import subprocess
    subprocess.call([viewer, outf])

# report where the ouput was saved
if not out_given:
    err("Output left in %s." % outf, INFO_MSG, False)
