#JES- Jython Environment for Students
#Copyright (C) 2002  Jason Ergle, Claire Bailey, David Raines
#See JESCopyright.txt for full licensing information
#This class, Copyright 2003  Adam Wilson, Yu Cheung Ho, Larry Olson, Eric Mickley

import smtplib
import JESConstants
import JESAddressFinder
import JESURLFinder
import MimeWriter
import base64
import quopri
import StringIO
import mimetypes
import httplib
import os
import string
import java.io as io
import java.net as net


####################################################################
####################################################################
# Class: JESHomeworkSubmission
# Parameters:
#     -hwTitle: The title of the homework being turned in
#     -fileName: The name of the .py file the homework is in
#     -zipFile: The local path to the zip archive of the homework
# Description:
#     Holds and provides functionality for homeworks.  Holds & sends  
#     a zip file with all the submission materials.  Lets the user 
#     turnin the submission via the preferred method (email or coweb
#     posting).
####################################################################
class JESHomeworkSubmission:
    def __init__(self, hwTitle, fileName, zipFile):
	config = self.readFromConfigFile()
	self.studentName =  config[JESConstants.CONFIG_NAME]
	self.gtNumber = config[JESConstants.CONFIG_GT]
	self.mail = config[JESConstants.CONFIG_MAIL]
	self.studentEmail = config[JESConstants.CONFIG_EMAIL_ADDR]
	self.mailServer = string.strip(self.mail[(self.mail.find("@") + 1):])
        self.cowebPort = 80
	self.hwTitle = hwTitle
	self.fileName = fileName
	self.zipFile = zipFile



####################################################################
# Method: turnin
# Description
#     Decides which method(s) to use to turnin the homework.  Looks
#     At the constants file EMAIL_TURNIN sends the hw as an email to 
#     The TA's email address.  COWEB_TURNIN posts the hw zip file to 
#     a coweb page.  The turnin information and definitions should
#     be on a web page defined in JESConstants.
####################################################################
    def turnin(self):
        emailError = 0
        cowebError = 0
        try:
            if JESConstants.EMAIL_TURNIN:
                self.emailTurnin()
        except Exception:
            emailError = 1
	try:
            if JESConstants.COWEB_TURNIN:
                self.cowebTurnin()
        except Exception:
            cowebError = 1
        if emailError and cowebError:
            raise StandardError, "Error emailing and uploading submission."
        elif emailError:
            raise StandardError, "Error emailing submission."
        elif cowebError:
            raise StandardError, "Error uploading submission to coweb."


####################################################################
# Method: emailTurnin
# Description:
#     Constructs an email message for the turnin.  Attaches the zip
#     file of the homework as a base64 encoded attachment.  Finally,
#     it sends the email through the defined SMTP server.
####################################################################
    def emailTurnin(self):
	#Get email information
	addr = JESAddressFinder.JESAddressFinder()
	taEmail = addr.getTargetAddress(self.gtNumber, self.hwTitle)
	try:
            filehandle = open(self.zipFile,"rb")
	   #CONSTRUCT EMAIL
	   #Build the email from all the parts of information:
            subject = "%s : %s : %s : %s" % \
		      (self.hwTitle, self.studentName, self.gtNumber, self.fileName)  
            msgBody = 'From: %s\n' % self.studentEmail
            msgBody += 'Subject: %s\n' % subject
            file = StringIO.StringIO()
            mime = MimeWriter.MimeWriter(file)
            mime.addheader("Mime-Version","1.0")
            mime.startmultipartbody("mixed")
            part=mime.nextpart()
            part.addheader("Content-Transfer-Encoding","quoted-printable")
            part.startbody("text/plain")
            quopri.encode(StringIO.StringIO("An Assignment Submission from "+self.studentName),file,0)
            quopri.encode(StringIO.StringIO("Notes to TA: "),file,0)
            #quopri.encode(StringIO.StringIO(notes),file,0)
            part = mime.nextpart()
            part.addheader("Content-Transfer-Encoding","base64")
            part.startbody('application/x-zip-compressed; name='+self.zipFile) 
            base64.encode(filehandle, file)
            mime.lastpart()
            msgBody += file.getvalue()
            filehandle.close()
	   #END CONSTRUCT EMAIL
	    #SEND EMAIL:
	    servObj = smtplib.SMTP(self.mailServer)
            servObj.sendmail(self.studentEmail, taEmail, msgBody)
        except:
            raise StandardError, "Error emailing assignment."
	



####################################################################
# Method: cowebTurnin
# Description:
#     Constructs a zip file with the assignment submission materials
#     inside.  It then posts it to the coweb page defined in the
#     turnin definitions via the .attach script.
####################################################################
    def cowebTurnin(self):
	try:
	    filehandle = open(self.zipFile,"rb")
            url = net.URL(JESConstants.HW_COWEB_ADDRESS_URL)
            host = url.getHost()
            port = url.getPort()
	    finder = JESURLFinder.JESURLFinder()
            turninURL = finder.getTargetURL(self.gtNumber, string.strip(self.hwTitle))
	    selector = string.strip(turninURL[turninURL.find("/"):]) + '.attach'
	    fields = [['specific', 'true'], ['reference', 'false']]
	    files = [['filestuff', os.path.basename(self.zipFile), filehandle.read()]]
	    response = self.post_multipart(host, port, selector, fields, files)
	    return response
	except:
	    raise StandardError, "Error turning in to the Coweb."
	

################################################################################
# Function name: readFromConfigFile
# Parameters: self
# Description: Attempts to open the Configfile.  If it exists, it is opened and
#       read into an array.  Each line of the file will get its spot in the array
#       and newline characters will be removed.  The array is returned.  The 
#       configfile should exist before this function is called.  If an IO Error 
#       occurs, a message will be printed to the transcript. 
#
################################################################################
    def readFromConfigFile(self):
        try:
            homedir=os.path.expanduser("~")
            f=open(homedir+io.File.separator+JESConstants.JES_CONFIG_FILE_NAME,'r')
            text=f.read()
            f.close()
            array=text.splitlines()
            return array
        except:
            raise StandardError, "Error reading configuration file."

#All of the following code is:
#Written by: Wade Leftwich
#Date: 8/23/2002
#URL: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306
    def post_multipart(self, host, port, selector, fields, files):
        #Post fields and files to an http host as multipart/form-data.
        #fields is a sequence of (name, value) elements for regular form fields.
        #files is a sequence of (name, filename, value) elements for data to be uploaded as files
        #Return the server's response page.
        array = self.encode_multipart_formdata(fields, files)
        h = httplib.HTTPConnection(host, port)
        h.putrequest('POST', selector)
        h.putheader('content-type', array[0])
        h.putheader('content-length', str(len(array[1])))
        h.endheaders()
        h.send(array[1])
        response = h.getresponse()
        h.close()
        return response

    def encode_multipart_formdata(self, fields, files):
        #fields is a sequence of (name, value) elements for regular form fields.
        #files is a sequence of (name, filename, value) elements for data to be uploaded as files
        #Return (content_type, body) ready for httplib.HTTP instance
        BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
        CRLF = '\r\n'
        L = []
        for (key, value) in fields:
            L.append('--' + BOUNDARY)
            L.append('Content-Disposition: form-data; name="%s"' % key)
            L.append('')
            L.append(value)
        for (key, filename, value) in files:
            L.append('--' + BOUNDARY)
            L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
            L.append('Content-Type: %s' % self.get_content_type(filename))
            L.append('')
            L.append(value)
        L.append('--' + BOUNDARY + '--')
        L.append('')
        body = CRLF.join(L)
        content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
        return [content_type, body]

    def get_content_type(self, filename):
        return mimetypes.guess_type(filename)[0] or 'application/octet-stream'

      