Difference between revisions of "CSC111 Homework 5 2015"

From dftwiki3
Jump to: navigation, search
(Problem #2)
 
(28 intermediate revisions by the same user not shown)
Line 2: Line 2:
 
----
 
----
  
 +
<br />
 +
<br />
 +
<showafterdate after="20150226 16:00" before="20150606 00:00">
 +
<br />
 +
__TOC__
 +
<br />
 +
<bluebox>
 +
This homework assignment is due on Tuesday March 3rd, at 11:55 p.m.
 +
</bluebox>
 
=Problem #1=
 
=Problem #1=
 
<br />
 
<br />
Line 81: Line 90:
 
* Your program should now have the following organization, where a new parameter is being passed to the functions.  This parameter is the length of the line with the user name, inside the box.
 
* Your program should now have the following organization, where a new parameter is being passed to the functions.  This parameter is the length of the line with the user name, inside the box.
 
<br />
 
<br />
<source lang="python">
+
::<source lang="python">
 
# Hw5_2.py
 
# Hw5_2.py
 
# Your name
 
# Your name
Line 102: Line 111:
 
</source>
 
</source>
 
<br />
 
<br />
 +
 
==Submission==
 
==Submission==
 
<br />
 
<br />
Line 117: Line 127:
 
Who should we sing for?  
 
Who should we sing for?  
 
(you may enter several names separated by spaces)
 
(you may enter several names separated by spaces)
> Carl Jorge Gru Minions
+
> CARL Jorge GRU mINIONs
 
+--------------------------+
 
+--------------------------+
 
|  Happy birthday to you!  |
 
|  Happy birthday to you!  |
Line 149: Line 159:
 
==Requirements==
 
==Requirements==
 
<br />
 
<br />
* Your program should  have the following organization as for Problem 2.
+
* Your program should  have the same organization as that of your solution for Problem 2.
 
<br />
 
<br />
<source lang="python">
+
::<source lang="python">
 
# Hw5_3.py
 
# Hw5_3.py
 
# Your name
 
# Your name
Line 172: Line 182:
 
</source>
 
</source>
 
<br />
 
<br />
 +
 
==Submission==
 
==Submission==
 
<br />
 
<br />
 
* Name your program hw5_3.py and submit it to Moodle, Section HW 5 PB 3.
 
* Name your program hw5_3.py and submit it to Moodle, Section HW 5 PB 3.
 
<br />
 
<br />
 +
=Problem #4=
 +
<br />
 +
* Variation on the same scheme.  This time, your program will ask the user for the name of a text file that will always be located in the same directory where your program resides.  This file will contain the names of several people, one per line.  Your program will sing Happy Birthday to each one! 
 +
* You may assume that the file will always be there, and that the user always spells it correctly.  You may also assume that the file will always contain at least 1 name.
 +
* ''Note 1'': you can easily create a text file with Idle.  Just open a new window with Idle, enter 3 names on the top 3 lines, and save the file as "names.txt", for example.  And voilà!  You have a text file in your directory.
 +
* ''Note 2'': I recommend that you read the contents of the file as a '''string''', rather than a '''list of strings''', and then '''.strip()''' this string to remove possible extra blank lines that you might have inadvertently created at the end of your text file.
 +
<br />
 +
:;'''Contents of File "names.txt"'''
 +
<br />
 +
::<source lang="text">
 +
Gru
 +
Jorge
 +
</source>
 +
<br />
 +
:;'''Example Output'''
 +
<br />
 +
::<source lang="text">
 +
File name? names.txt
 +
+-------------------------+
 +
|  Happy birthday to you! |
 +
|  Happy birthday to you! |
 +
|Happy birthday, dear Gru!|
 +
|  Happy birthday to you! |
 +
+-------------------------+
 +
 +
+---------------------------+
 +
|  Happy birthday to you!  |
 +
|  Happy birthday to you!  |
 +
|Happy birthday, dear Jorge!|
 +
|  Happy birthday to you!  |
 +
+---------------------------+
 +
</source>
 +
<br />
 +
==Submission==
 +
* Call your program hw5_4.py and submit it to the HW 5 PB 4 section on Moodle.
 +
<br />
 +
 +
=Problem #5 =
 +
<br />
 +
* This time your program will ask for the names of two different text files.
 +
* The first one will contain some message, containing the string "zzzz".  For example:
 +
<br />
 +
::<source lang="text">
 +
Congrats, zzzz, you are the best!
 +
</source>
 +
<br />
 +
:or
 +
<br />
 +
::<source lang="text">
 +
Happy birthday to you,
 +
Happy birthday, dear zzzz!
 +
</source>
 +
<br />
 +
* The second file will contain a list of names, as in Problem 4.  One name per line. 
 +
* Your program will repeat the message stored in the first file for everybody whose name is in the name file.
 +
<br />
 +
==Output Example 1==
 +
<br />
 +
:;'''Example of Message File
 +
<br />
 +
::<source lang="text">
 +
Happy birthday to you,
 +
Happy birthday, dear zzzz!
 +
</source>
 +
<br />
 +
:; '''Example of Name File'''
 +
<br />
 +
::<source lang="text">
 +
Gru
 +
Dave
 +
</source>
 +
<br />
 +
:;'''Example of Output'''
 +
<br />
 +
::<source lang="text">
 +
Message file? message.txt
 +
Name file? names.txt
 +
 +
Happy birthday to you,
 +
Happy birthday, dear Gru!
 +
 +
Happy birthday to you,
 +
Happy birthday, dear Dave!
 +
 +
</source>
 +
<br />
 +
<br />
 +
==Output Example 2==
 +
<br />
 +
:;'''Example of Message File
 +
<br />
 +
::<source lang="text">
 +
Hip hip, Hurray!  zzzz is the best!
 +
</source>
 +
<br />
 +
:; '''Example of Name File'''
 +
<br />
 +
::<source lang="text">
 +
Gru
 +
Dave
 +
</source>
 +
<br />
 +
:;'''Example of Output'''
 +
<br />
 +
::<source lang="text">
 +
Message file? message.txt
 +
Name file? names.txt
 +
 +
Hip hip, Hurray!  Gru is the best!
 +
 +
Hip hip, Hurray!  Dave is the best!
 +
</source>
 +
<br />
 +
==Requirements==
 +
<br />
 +
* You are required to use a main() function, and you are free to create additional functions or not.
 +
<br />
 +
==Submission==
 +
<br />
 +
* Name your program hw5_5.py, and submit it to the Moodle HW 5 PB 5 section.
 +
<br />
 +
==Note==
 +
<br />
 +
* This program is a very simplified form of mail-merge, a technique where one creates a form letter with place holders for various strings, such as first name and last name, and an file containing the first names and last names of many people.  The mail-merge program creates many different copies of the form letter, replacing the place-holders with the different first and last names found in the name file.
 +
<br />
 +
=Problem 6=
 +
<br />
 +
This problem was suggested by Dave Marshall, and combines nicely many Python constructs we have seen recently.
 +
<br />
 +
==First Step==
 +
<br />
 +
* Write a program called hw5_6.py that asks the user for the names of two files, reads the contents of both text files, and then prints a series of messages on the screen.
 +
* The '''first file''' that your program should prompt the user for, will contain the information about several people, on several lines of text.  Each line contains 3 words separated by spaces.  The first word is the name of a person, the second is a thing related to that person (maybe something she ordered on line), and the third is a date (for example, a delivery date).
 +
<br />
 +
:;Example
 +
<br />
 +
::<source lang="text">
 +
Dave chocolate 02152015
 +
Carl toothpaste 02152020
 +
Gru superglue 01012100
 +
</source>
 +
<br />
 +
* The second file that your program will prompt the user for, contains one or several lines of text.  This text is a message that will could be a message sent to the user, or maybe some text  In the text, there will be three special keywords prefixed with a $-sign: "$name", "$stuff", and "$date".
 +
<br />
 +
:;Example
 +
<br />
 +
::<source lang="text">
 +
Dear $name, this is a coupon for
 +
a ton of $stuff, which you can pick up
 +
in Ford Hall 241 at any time before $date. 
 +
If you do not claim the $stuff before $date, it will be put on sale on eBay.
 +
</source>
 +
<br />
 +
* Your program will read each line of the file containing user information, and will divide each line into separate words.  This should be a list of 3 words.  It will take the first word and will replace the string "$name" by this word in the message.  Then it will take the second word and replace "$stuff" by this word in the message.  Finally it will take the third word and replace "$date" by it.  So, assuming that the first line of  the user file is <tt>Dave chocolate 02152015</tt>, your program will output:
 +
<br />
 +
::<source lang="text">
 +
Dear Dave, this is a coupon for
 +
a ton of chocolate, which you can pick up
 +
in Ford Hall 241 at any time before 02152015. 
 +
If you do not claim the chocolate before 02152015, it will be put on sale on eBay.
 +
</source>
 +
<br />
 +
* The full output of your program, if '''users.txt''' contains 3 lines of information for 3 different people (as shown above) would be:
 +
<br />
 +
::<source lang="text">
 +
Dear Dave, this is a coupon for
 +
a ton of chocolate, which you can pick up
 +
in Ford Hall 241 at any time before 02152015. 
 +
If you do not claim the chocolate before 02152015, it will be put on sale on eBay.
 +
 +
Dear Carl, this is a coupon for
 +
a ton of toothpaste, which you can pick up
 +
in Ford Hall 241 at any time before 02152020. 
 +
If you do not claim the toothpaste before 02152020, it will be put on sale on eBay.
 +
 +
Dear Gru, this is a coupon for
 +
a ton of superglue, which you can pick up
 +
in Ford Hall 241 at any time before 01012100. 
 +
If you do not claim the superglue before 01012100, it will be put on sale on eBay.
 +
 +
</source>
 +
<br />
 +
==Second Step==
 +
<br />
 +
* Modify your program so that it formats the date nicely, with the date formatted as dd mmm yyyy.  For example, '''02152020''' would be printed as '''15 Feb 2020'''.
 +
<br />
 +
:;Example of the output for Person Carl
 +
<br />
 +
::<source lang="text">
 +
Dear Carl, this is a coupon for
 +
a ton of toothpaste, which you can pick up
 +
in Ford Hall 241 at any time before 15 Feb 2020. 
 +
If you do not claim the toothpaste before 15 Feb 2020, it will be put on sale on eBay.
 +
 +
</source>
 +
<br />
 +
==Requirements==
 +
<br />
 +
* Your program should have at least a main() function that is the entry point of the program.  You may include additional functions as well.
 +
* If the message file contains 3 lines of text, your program should also print 3 lines of text.  Do not make your program
 +
wrap lines, or output all 3 lines as a single line. 
 +
* The number of blank lines printed along with the message is unimportant.  Extra blank lines will be skipped by the testing script.
 +
<br />
 +
==Submission==
 +
<br />
 +
* Submit your '''hw5_6.py''' program to the HW 5 PB 6 section on Moodle.
 +
<br />
 +
 +
 +
 +
<!-- ==================================================================== -->
 +
 +
</showafterdate>
 +
<br />
 +
<!-- =================================================================== -->
 +
<showafterdate after="20150304 00:00" before="20150601 00:00">
 +
=Solution Programs=
 +
<br />
 +
<source lang="python">
 +
# --------------------------------------------------------------------------------------
 +
# Solutions for Hw #5 PB #1
 +
# D. Thiebaut
 +
 +
 +
def happyBirthday():
 +
    length = 50
 +
    print( "|" + "Happy birthday to you!".center( length ) + "|" )
 +
 +
def happyBirthdayDear( name ):
 +
    length = 50
 +
    line = "Happy birthday, dear " + name +"!"
 +
    print( "|" + line.center( length ) + "|"  )
 +
 +
def singSong( name ):
 +
    length = 50
 +
    bar = "+" + "-"*length + "+"
 +
    print( bar )
 +
    happyBirthday()
 +
    happyBirthday()
 +
    happyBirthdayDear( name )
 +
    happyBirthday()
 +
    print( bar )
 +
 +
def main():
 +
    name = input( "Who should we sing for? " )
 +
    singSong( name )
 +
 +
main()
 +
 +
# --------------------------------------------------------------------------------------
 +
# Solutions for Hw #5 Problem 2
 +
# D. Thiebaut
 +
 +
 +
def happyBirthday( length ):
 +
    print( "|" + "Happy birthday to you!".center( length ) + "|" )
 +
 +
def happyBirthdayDear( name, length ):
 +
    line = "Happy birthday, dear " + name +"!"
 +
    print( "|" + line.center( length ) + "|"  )
 +
 +
def singSong( name, length ):
 +
    bar = "+" + "-"*length + "+"
 +
    print( bar )
 +
    happyBirthday( length )
 +
    happyBirthday( length )
 +
    happyBirthdayDear( name, length )
 +
    happyBirthday( length )
 +
    print( bar )
 +
 +
def main():
 +
    name = input( "Who should we sing for? " )
 +
    longLine = "Happy birthday, dear " + name + "!"
 +
    length  = len( longLine )
 +
    singSong( name, length )
 +
 +
main()
 +
 +
# --------------------------------------------------------------------------------------
 +
# Solutions for Hw #5 Problem #3
 +
# D. Thiebaut
 +
 +
 +
def happyBirthday( length ):
 +
    print( "|" + "Happy birthday to you!".center( length ) + "|" )
 +
 +
def happyBirthdayDear( name, length ):
 +
    line = "Happy birthday, dear " + name +"!"
 +
    print( "|" + line.center( length ) + "|"  )
 +
 +
def singSong( name, length ):
 +
    bar = "+" + "-"*length + "+"
 +
    print( bar )
 +
    happyBirthday( length )
 +
    happyBirthday( length )
 +
    happyBirthdayDear( name, length )
 +
    happyBirthday( length )
 +
    print( bar )
 +
 +
def main():
 +
    print( "Who should we sing for? " )
 +
    names = input( "(you may enter several names separated by spaces)\n> " )
 +
 +
    for name in names.split( " " ):
 +
        longLine = "Happy birthday, dear " + name + "!"
 +
        length  = len( longLine )
 +
        singSong( name, length )
 +
        print()
 +
 +
main()
 +
 +
# --------------------------------------------------------------------------------------
 +
# Solutions for Hw #5 Problem #4
 +
# D. Thiebaut
 +
 +
 +
def happyBirthday( length ):
 +
    print( "|" + "Happy birthday to you!".center( length ) + "|" )
 +
 +
def happyBirthdayDear( name, length ):
 +
    line = "Happy birthday, dear " + name +"!"
 +
    print( "|" + line.center( length ) + "|"  )
 +
 +
def singSong( name, length ):
 +
    bar = "+" + "-"*length + "+"
 +
    print( bar )
 +
    happyBirthday( length )
 +
    happyBirthday( length )
 +
    happyBirthdayDear( name, length )
 +
    happyBirthday( length )
 +
    print( bar )
 +
 +
def main():
 +
    fileName = input( "File name? " )
 +
 +
    # get the contents of the file as a string.
 +
    file = open( fileName, "r" )
 +
    text = file.read()
 +
    file.close
 +
 +
    # strip any blank lines that may be at the front
 +
    # or end of the text.
 +
    text = text.strip()
 +
 +
   
 +
    for name in text.split( "\n" ):
 +
        longLine = "Happy birthday, dear " + name + "!"
 +
        length  = len( longLine )
 +
        singSong( name, length )
 +
        print()
 +
 +
main()
 +
 +
# --------------------------------------------------------------------------------------
 +
# Solutions for Hw #5 Problem #5
 +
# D. Thiebaut
 +
 +
def createMessageAndNameFiles():
 +
    message1 = "Hip hip, Hurray! zzzz is the best!\n"
 +
    file = open( "message.txt", "w" )
 +
    file.write( message1 )
 +
    file.close()
 +
 +
    names = "Gru\nDave\n"
 +
    file = open( "names.txt", "w" )
 +
    file.write( names )
 +
    file.close()
 +
 +
def main():
 +
    # create the 2 different files.  This is not really what
 +
    # you had to do, but this way I don't have to create separate
 +
    # files by hand...
 +
    createMessageAndNameFiles()
 +
   
 +
    # get the names of the two files
 +
    messageFile= input( "Message file name? " )
 +
    namesFile  = input( "Name file?        " )
 +
   
 +
   
 +
    # get the message
 +
    file = open( messageFile, "r" )
 +
    text = file.read()
 +
    file.close
 +
 +
    # get the names
 +
    file = open( namesFile, "r" )
 +
    names = file.read()
 +
    file.close()
 +
 +
    # strip any blank lines that may be at the front
 +
    # or end of the text.
 +
    names = names.strip()
 +
    names = names.split( "\n" )
 +
 +
    # iterate through the names and print the message
 +
    # for each name found.
 +
    for name in names:
 +
        print( text.strip().replace( "zzzz", name ) )
 +
        print()
 +
 +
main()
 +
 +
# --------------------------------------------------------------------------------------
 +
# Solutions for Hw #5 Problem #5
 +
# hw5_6.py
 +
# D. Thiebaut
 +
 +
 +
def mailMerge():
 +
    """
 +
    open( "users.txt", "w" ).write(
 +
        "Dave chocolate 02152015\n"
 +
        +"Carl toothpaste 02152020\n"
 +
        +"Gru superglue 01012100\n" )
 +
    open( "message.txt", "w" ).write(
 +
        "Dear $name, this is a coupon for\n"
 +
        +"a ton of $stuff, which you can pick up\n"
 +
        +"in Ford Hall 241 at any time before $date.\n" )
 +
    """
 +
    months = ["dummy", "Jan", "Feb", "Mar", "Apr", "May",
 +
              "Jun", "Jul", "Aug", "Sep", "Oct",
 +
              "Nov", "Dec" ]
 +
    message = open( "message2.txt", "r" ).read()
 +
    users  = open( "users2.txt", "r" ).readlines()
 +
 +
    for user in users:
 +
        words = user.split( ' ' )
 +
        day  = int( words[2][2:4] )
 +
        month = int( words[2][0:2] )
 +
        month = months[ month ]
 +
        year  = words[2][4:]
 +
        date  = "{0:1} {1:1} {2:1}".format( day, month, year )
 +
        date  = date.strip()
 +
        userMessage = message.replace( "$name", words[0] )
 +
        userMessage = userMessage.replace( "$stuff", words[1] )
 +
        userMessage = userMessage.replace( "$date", date )
 +
        print( userMessage )
 +
 +
     
 +
def main():
 +
    mailMerge()
 +
 +
main()
 +
 +
 +
 +
 +
</source>
 +
 +
</showafterdate>
 +
<br />
 +
<br />
 +
<onlydft>
 +
=VPL Modules=
 +
==Problem 1==
 +
<br />
 +
<source lang="python">
 +
# evaluate.py
 +
# D. Thiebaut
 +
# This program is used to test a student's python program on Moodle.
 +
 +
import sys
 +
import random
 +
import subprocess
 +
 +
#--- define what the student program is called, and what the solution
 +
#--- program name is.
 +
module = "hw5_1"
 +
solutionModule = module + "sol"
 +
userOutSplitPattern = "+-"    # pattern used to start recording the user
 +
                              # output.  Useful when program does several
 +
                              # input() statements, and user output starts
 +
                              # after that.
 +
stripOutputsBeforeCompare = True
 +
                              # set to True if extra spaces at beginning or
 +
                              # end of user output is ok
 +
 +
#--- GLOBALS ---
 +
interpreter = sys.executable
 +
 +
def commentLong( line ):
 +
    print( "<|--\n" + line  + "\n --|>" )
 +
 +
def commentShort( text ):
 +
    print( "Comment :=>> " + text )
 +
 +
def printGrade( grade ):
 +
    print( "Grade :=>> ", grade )
 +
 +
# generateInputFileWithRandomInputs
 +
# generate a file with name "inputFileName" with some random input
 +
# for the program.
 +
# MAKE SURE TO EDIT THIS TO MATCH THE PROGRAM BEING TESTED
 +
def generateInputFileWithRandomInputs( inputFileName ):
 +
    #--- generate random inputs ---
 +
 +
    #list = []
 +
    #for i in range( random.choice( [2, 5, 3] ) ):
 +
    #    list.append( random.choice( [ "Dave", "Jorge", "Max", "Carl", "Tim" ] ) )
 +
   
 +
    #names = " ".join( list )
 +
   
 +
    names = random.choice( [ "Dave", "Jorge", "Katarina", "Carl", "Tim" ] )
 +
 +
    #--- create input file for stdin ---
 +
    file = open( inputFileName, "w" )
 +
    file.write( names + "\n" )
 +
    file.close()
 +
 +
    return names + "\n"
 +
 +
# checkForFunctionPresence
 +
# checks that "functionName" is defined and called in the program.
 +
# MAKE SURE TO EDIT TO MATCH PROGRAM BEING TESTED
 +
def checkForFunctionPresence( module, functionName ):
 +
    foundDef = False
 +
    foundCall = False
 +
 +
    for line in open( module+".py", "r" ).readlines():
 +
        # remove comments
 +
        idx = line.find( "#" )
 +
        if ( idx >=0 ): line = line[0:idx]
 +
 +
        if line.startswith( "def " + functionName + "(" ):
 +
            foundDef = True
 +
            continue
 +
        if line.startswith( "def " + functionName + " (" ):
 +
            foundDef = True
 +
            continue
 +
        if line.find( functionName+"(" ) != -1:
 +
            foundCall = True
 +
            continue
 +
 +
    return (foundDef, foundCall)
 +
 +
 +
 +
# ==================================================================
 +
# NO EDITS NEEDED BELOW!
 +
# ==================================================================
 +
 +
 +
 +
 +
# checkModuleRunsOK: runs the module as a shell subprocess and
 +
# look for errors in the output.  This is required, because otherwise
 +
# importing the module in this program will make this program crash.
 +
# It's not possible (as far as I can tell0 to catch exceptions from
 +
# the import or __module__ statements.
 +
# returns True, none if no errors, otherwise False, string if there's
 +
# an exception, and the error message (string) is in the returned 2nd
 +
# arg.
 +
# The module name is assumed to not include ".py"
 +
def checkModuleRunsOk( module, inputFileName ):
 +
    global interpreter
 +
    p = subprocess.Popen( [ interpreter, module+".py" ],
 +
                          stdout=subprocess.PIPE,
 +
                          stderr=subprocess.PIPE,
 +
                          stdin=subprocess.PIPE)
 +
 +
    #print( "inputFileName = ", inputFileName )
 +
    #print( "open( inputFileName, r).read() = ", open( inputFileName, "r" ).read() )
 +
 +
    p.stdin.write( bytes( open( inputFileName, "r" ).read(), 'UTF-8' ) )
 +
    data = p.communicate( )
 +
    p.stdin.close()
 +
 +
    error = data[1].decode( 'UTF-8' )
 +
    if len( error ) > 1:
 +
        return False, error
 +
    return True, None
 +
 +
                     
 +
# extractTextFromErrorMessage( sys_exc_info ):
 +
def extractTextFromErrorMessage( sys_exc_info ):
 +
    print( "sys_exec_info = ", sys_exc_info )
 +
    text = ""
 +
    for field in sys_exc_info:
 +
        if type( field )==type( " " ):
 +
            text += field + "\n"
 +
    return text
 +
 +
# runModule:
 +
# runs the module, passes it data from the input file on its stdin
 +
# and get its output on stdout captured in outputFileName.
 +
# We assume the module will not crash, because we already tested
 +
# it with checkModuleRunsOk().
 +
def runModule( module, inputFileName, outputFileName ):
 +
    global userOutSplitPattern
 +
 +
    error = False
 +
 +
    #--- make stdin read information from the text file
 +
    sys.stdin = open( inputFileName, "r" )
 +
 +
    #--- capture the stdout of the program to test into a file
 +
    saveStdOut = sys.stdout
 +
    saveStdErr = sys.stderr
 +
 +
    sys.stdout = open( outputFileName, "w" )
 +
    sys.stderr = open( "errorOut", "w" )
 +
 +
    #--- run the student program ---
 +
    try:
 +
        _module = __import__(  module  )
 +
    except:
 +
        error = True
 +
        sys.stderr.close()
 +
        sys.stderr = saveStdErr
 +
        sys.stdout.close()
 +
        sys.stdout = saveStdOut
 +
        text = sys.exc_info()[0]
 +
        text = extractTextFromErrorMessage( text )
 +
        print( "*** sys.exc_info() = ", text )
 +
        return error, text
 +
       
 +
    #--- filter out junk from output of program ---
 +
    sys.stdout.close()
 +
    sys.stdout = saveStdOut
 +
    file = open( outputFileName, "r" )
 +
    text = file.read()
 +
    index = text.find( userOutSplitPattern ) #"+-" )
 +
    text = text[index:]
 +
    text = text.strip( ).strip( "\n" ) + "\n"
 +
    #print( text, end="" )
 +
    file.close()
 +
    return False, text
 +
 +
def removeBlankLines( lines ):
 +
    newLines = []
 +
    for line in lines.split( "\n" ):
 +
        if len( line )==0:
 +
            continue
 +
        newLines.append( line )
 +
 +
    return ( "\n".join( newLines ) ) + "\n"
 +
 +
def compareUserExpected( inputLines, userOutText, expectedOutText ):
 +
    global stripOutputsBeforeCompare
 +
 +
    userOutText = removeBlankLines( userOutText )
 +
    expectedOutText = removeBlankLines( expectedOutText )
 +
    misMatchLineNumbers = []
 +
    userTextOutLines = userOutText.split( "\n" )
 +
    expectedOutTextLines = expectedOutText.split( "\n" )
 +
 +
    for i in range( len( userTextOutLines ) ):
 +
        lineNo = i+1
 +
        userLine = userTextOutLines[i]
 +
        expectedLine = expectedOutTextLines[i]
 +
        if stripOutputsBeforeCompare:
 +
            userLine = userLine.strip()
 +
            expectedLine = expectedLine.strip()
 +
        if userLine != expectedLine:
 +
            #print( "user    >" + userTextOutLines[i] + "<" )
 +
            #print( "expected >" + expectedOutTextLines[i] + "<" )
 +
            misMatchLineNumbers.append( lineNo )
 +
   
 +
    return misMatchLineNumbers
 +
           
 +
 +
 +
def main():
 +
    global module
 +
    global solutionModule
 +
 +
   
 +
    #--- check that the main module uses a main() function
 +
    foundDef, foundCall = checkForFunctionPresence( module, "main" )
 +
    if (not foundDef) or (not foundCall):
 +
        commentShort( "-Missing main() program" )
 +
        commentShort( "Your program must use a main() function." )
 +
        commentShort( "(make sure you spell it exactly \"main()\"!" )
 +
        printGrade( 40 )
 +
        return
 +
 +
 +
    #--- generate input file with random data ---
 +
    inputLines = generateInputFileWithRandomInputs( "input" )
 +
 +
 +
    Ok, errorMessage = checkModuleRunsOk( module, "input" )
 +
    if not Ok:
 +
        commentLong( "- Your program crashed...\n"
 +
                    + "Your program was tested with:\n"
 +
                    + inputLines  + "\n"
 +
                    + "Error message:\n"
 +
                    + errorMessage + "\n" )
 +
        printGrade( 50 )
 +
        return
 +
 +
                   
 +
    error, userOutText        = runModule( module, "input", "userOut" )
 +
    if error:
 +
        commentLong( "- Your program crashed...\n"
 +
                    + "Your program was tested with:\n"
 +
                    + inputLines  + "\n"
 +
                    + "Error message:\n"
 +
                    + userOutText + "\n" )
 +
        printGrade( 50 )
 +
        return
 +
 +
    dummy, expectedOutText    = runModule( solutionModule, "input", "expectedOut" )
 +
 +
    misMatchLineNumbers = compareUserExpected( inputLines,
 +
                                              userOutText,
 +
                                              expectedOutText )
 +
    if len( misMatchLineNumbers ) == 0:
 +
        commentLong( "- Congrats, correct output!\n"
 +
                    +"Your program was tested with:\n"
 +
                    +inputLines + "\n"
 +
                    +"Your output:\n"
 +
                    +userOutText + "\n" )
 +
        printGrade( 100 )
 +
    else:
 +
        s = "" if len( misMatchLineNumbers ) == 1 else "s"
 +
 +
        commentLong( "-Incorrect output.\n"
 +
                    +"Your program was tested with:\n"
 +
                    +inputLines + "\n"
 +
                    +"Your output:\n"
 +
                    +userOutText + "\n"
 +
                    +"Expected output:\n"
 +
                    +expectedOutText + "\n"
 +
                    +("There are errors in Line%s " % s) +
 +
                    ", ".join( [str(k) for k in misMatchLineNumbers] )
 +
                    +"\n" )
 +
        noLinesGood = len( expectedOutText.strip().split("\n") )
 +
        noErrors    = len( misMatchLineNumbers )
 +
        #print( "noLinesGood = ", noLinesGood, " noErrors = ", noErrors )
 +
 +
        grade      = round( 60 + 40 / noLinesGood *( noLinesGood- noErrors ) )
 +
        printGrade( grade )
 +
 +
main()
 +
 +
 +
</source>
 +
<br />
 +
 +
==Problem 2==
 +
<br />
 +
<source lang="python">
 +
# evaluate.py
 +
# D. Thiebaut
 +
# This program is used to test a student's python program on Moodle.
 +
 +
import sys
 +
import random
 +
import subprocess
 +
 +
#--- define what the student program is called, and what the solution
 +
#--- program name is.
 +
module = "hw5_2"
 +
solutionModule = module + "sol"
 +
userOutSplitPattern = "+-"    # pattern used to start recording the user
 +
                              # output.  Useful when program does several
 +
                              # input() statements, and user output starts
 +
                              # after that.
 +
stripOutputsBeforeCompare = True
 +
                              # set to True if extra spaces at beginning or
 +
                              # end of user output is ok
 +
 +
#--- GLOBALS ---
 +
interpreter = sys.executable
 +
 +
def commentLong( line ):
 +
    print( "<|--\n" + line  + "\n --|>" )
 +
 +
def commentShort( text ):
 +
    print( "Comment :=>> " + text )
 +
 +
def printGrade( grade ):
 +
    print( "Grade :=>> ", grade )
 +
 +
# generateInputFileWithRandomInputs
 +
# generate a file with name "inputFileName" with some random input
 +
# for the program.
 +
# MAKE SURE TO EDIT THIS TO MATCH THE PROGRAM BEING TESTED
 +
def generateInputFileWithRandomInputs( inputFileName ):
 +
    #--- generate random inputs ---
 +
 +
    #list = []
 +
    #for i in range( random.choice( [2, 5, 3] ) ):
 +
    #    list.append( random.choice( [ "Dave", "Jorge", "Max", "Carl", "Tim" ] ) )
 +
   
 +
    #names = " ".join( list )
 +
   
 +
    names = random.choice( [ "Dave", "Jorge", "Katarina", "Carl", "Tim" ] )
 +
 +
    #--- create input file for stdin ---
 +
    file = open( inputFileName, "w" )
 +
    file.write( names + "\n" )
 +
    file.close()
 +
 +
    return names + "\n"
 +
 +
# checkForFunctionPresence
 +
# checks that "functionName" is defined and called in the program.
 +
# MAKE SURE TO EDIT TO MATCH PROGRAM BEING TESTED
 +
def checkForFunctionPresence( module, functionName ):
 +
    foundDef = False
 +
    foundCall = False
 +
 +
    for line in open( module+".py", "r" ).readlines():
 +
        # remove comments
 +
        idx = line.find( "#" )
 +
        if ( idx >=0 ): line = line[0:idx]
 +
 +
        if line.startswith( "def " + functionName + "(" ):
 +
            foundDef = True
 +
            continue
 +
        if line.startswith( "def " + functionName + " (" ):
 +
            foundDef = True
 +
            continue
 +
        if line.find( functionName+"(" ) != -1:
 +
            foundCall = True
 +
            continue
 +
 +
    return (foundDef, foundCall)
 +
 +
 +
 +
# ==================================================================
 +
# NO EDITS NEEDED BELOW!
 +
# ==================================================================
 +
 +
 +
 +
 +
# checkModuleRunsOK: runs the module as a shell subprocess and
 +
# look for errors in the output.  This is required, because otherwise
 +
# importing the module in this program will make this program crash.
 +
# It's not possible (as far as I can tell0 to catch exceptions from
 +
# the import or __module__ statements.
 +
# returns True, none if no errors, otherwise False, string if there's
 +
# an exception, and the error message (string) is in the returned 2nd
 +
# arg.
 +
# The module name is assumed to not include ".py"
 +
def checkModuleRunsOk( module, inputFileName ):
 +
    global interpreter
 +
    p = subprocess.Popen( [ interpreter, module+".py" ],
 +
                          stdout=subprocess.PIPE,
 +
                          stderr=subprocess.PIPE,
 +
                          stdin=subprocess.PIPE)
 +
 +
    #print( "inputFileName = ", inputFileName )
 +
    #print( "open( inputFileName, r).read() = ", open( inputFileName, "r" ).read() )
 +
 +
    p.stdin.write( bytes( open( inputFileName, "r" ).read(), 'UTF-8' ) )
 +
    data = p.communicate( )
 +
    p.stdin.close()
 +
 +
    error = data[1].decode( 'UTF-8' )
 +
    if len( error ) > 1:
 +
        return False, error
 +
    return True, None
 +
 +
                     
 +
# extractTextFromErrorMessage( sys_exc_info ):
 +
def extractTextFromErrorMessage( sys_exc_info ):
 +
    print( "sys_exec_info = ", sys_exc_info )
 +
    text = ""
 +
    for field in sys_exc_info:
 +
        if type( field )==type( " " ):
 +
            text += field + "\n"
 +
    return text
 +
 +
# runModule:
 +
# runs the module, passes it data from the input file on its stdin
 +
# and get its output on stdout captured in outputFileName.
 +
# We assume the module will not crash, because we already tested
 +
# it with checkModuleRunsOk().
 +
def runModule( module, inputFileName, outputFileName ):
 +
    global userOutSplitPattern
 +
 +
    error = False
 +
 +
    #--- make stdin read information from the text file
 +
    sys.stdin = open( inputFileName, "r" )
 +
 +
    #--- capture the stdout of the program to test into a file
 +
    saveStdOut = sys.stdout
 +
    saveStdErr = sys.stderr
 +
 +
    sys.stdout = open( outputFileName, "w" )
 +
    sys.stderr = open( "errorOut", "w" )
 +
 +
    #--- run the student program ---
 +
    try:
 +
        _module = __import__(  module  )
 +
    except:
 +
        error = True
 +
        sys.stderr.close()
 +
        sys.stderr = saveStdErr
 +
        sys.stdout.close()
 +
        sys.stdout = saveStdOut
 +
        text = sys.exc_info()[0]
 +
        text = extractTextFromErrorMessage( text )
 +
        print( "*** sys.exc_info() = ", text )
 +
        return error, text
 +
       
 +
    #--- filter out junk from output of program ---
 +
    sys.stdout.close()
 +
    sys.stdout = saveStdOut
 +
    file = open( outputFileName, "r" )
 +
    text = file.read()
 +
    index = text.find( userOutSplitPattern ) #"+-" )
 +
    text = text[index:]
 +
    text = text.strip( ).strip( "\n" ) + "\n"
 +
    #print( text, end="" )
 +
    file.close()
 +
    return False, text
 +
 +
def removeBlankLines( lines ):
 +
    newLines = []
 +
    for line in lines.split( "\n" ):
 +
        if len( line )==0:
 +
            continue
 +
        newLines.append( line )
 +
 +
    return ( "\n".join( newLines ) ) + "\n"
 +
 +
def compareUserExpected( inputLines, userOutText, expectedOutText ):
 +
    global stripOutputsBeforeCompare
 +
 +
    userOutText = removeBlankLines( userOutText )
 +
    expectedOutText = removeBlankLines( expectedOutText )
 +
    misMatchLineNumbers = []
 +
    userTextOutLines = userOutText.split( "\n" )
 +
    expectedOutTextLines = expectedOutText.split( "\n" )
 +
 +
    for i in range( len( userTextOutLines ) ):
 +
        lineNo = i+1
 +
        userLine = userTextOutLines[i]
 +
        expectedLine = expectedOutTextLines[i]
 +
        if stripOutputsBeforeCompare:
 +
            userLine = userLine.strip()
 +
            expectedLine = expectedLine.strip()
 +
        if userLine != expectedLine:
 +
            #print( "user    >" + userTextOutLines[i] + "<" )
 +
            #print( "expected >" + expectedOutTextLines[i] + "<" )
 +
            misMatchLineNumbers.append( lineNo )
 +
   
 +
    return misMatchLineNumbers
 +
           
 +
 +
 +
def main():
 +
    global module
 +
    global solutionModule
 +
 +
   
 +
    #--- check that the main module uses a main() function
 +
    foundDef, foundCall = checkForFunctionPresence( module, "main" )
 +
    if (not foundDef) or (not foundCall):
 +
        commentShort( "-Missing main() program" )
 +
        commentShort( "Your program must use a main() function." )
 +
        commentShort( "(make sure you spell it exactly \"main()\"!" )
 +
        printGrade( 40 )
 +
        return
 +
 +
 +
    #--- generate input file with random data ---
 +
    inputLines = generateInputFileWithRandomInputs( "input" )
 +
 +
 +
    Ok, errorMessage = checkModuleRunsOk( module, "input" )
 +
    if not Ok:
 +
        commentLong( "- Your program crashed...\n"
 +
                    + "Your program was tested with:\n"
 +
                    + inputLines  + "\n"
 +
                    + "Error message:\n"
 +
                    + errorMessage + "\n" )
 +
        printGrade( 50 )
 +
        return
 +
 +
                   
 +
    error, userOutText        = runModule( module, "input", "userOut" )
 +
    if error:
 +
        commentLong( "- Your program crashed...\n"
 +
                    + "Your program was tested with:\n"
 +
                    + inputLines  + "\n"
 +
                    + "Error message:\n"
 +
                    + userOutText + "\n" )
 +
        printGrade( 50 )
 +
        return
 +
 +
    dummy, expectedOutText    = runModule( solutionModule, "input", "expectedOut" )
 +
 +
    misMatchLineNumbers = compareUserExpected( inputLines,
 +
                                              userOutText,
 +
                                              expectedOutText )
 +
    if len( misMatchLineNumbers ) == 0:
 +
        commentLong( "- Congrats, correct output!\n"
 +
                    +"Your program was tested with:\n"
 +
                    +inputLines + "\n"
 +
                    +"Your output:\n"
 +
                    +userOutText + "\n" )
 +
        printGrade( 100 )
 +
    else:
 +
        s = "" if len( misMatchLineNumbers ) == 1 else "s"
 +
 +
        commentLong( "-Incorrect output.\n"
 +
                    +"Your program was tested with:\n"
 +
                    +inputLines + "\n"
 +
                    +"Your output:\n"
 +
                    +userOutText + "\n"
 +
                    +"Expected output:\n"
 +
                    +expectedOutText + "\n"
 +
                    +("There are errors in Line%s " % s) +
 +
                    ", ".join( [str(k) for k in misMatchLineNumbers] )
 +
                    +"\n" )
 +
        noLinesGood = len( expectedOutText.strip().split("\n") )
 +
        noErrors    = len( misMatchLineNumbers )
 +
        #print( "noLinesGood = ", noLinesGood, " noErrors = ", noErrors )
 +
 +
        grade      = round( 60 + 40 / noLinesGood *( noLinesGood- noErrors ) )
 +
        printGrade( grade )
 +
 +
main()
 +
 +
 +
</source>
 +
<br />
 +
 +
==Problem 3==
 +
<br />
 +
<source lang="python">
 +
# evaluate.py
 +
# D. Thiebaut
 +
# This program is used to test a student's python program on Moodle.
 +
 +
import sys
 +
import random
 +
import subprocess
 +
 +
#--- define what the student program is called, and what the solution
 +
#--- program name is.
 +
module = "hw5_3"
 +
solutionModule = module + "sol"
 +
userOutSplitPattern = "+-"    # pattern used to start recording the user
 +
                              # output.  Useful when program does several
 +
                              # input() statements, and user output starts
 +
                              # after that.
 +
stripOutputsBeforeCompare = True
 +
                              # set to True if extra spaces at beginning or
 +
                              # end of user output is ok
 +
 +
#--- GLOBALS ---
 +
interpreter = sys.executable
 +
 +
def commentLong( line ):
 +
    print( "<|--\n" + line  + "\n --|>" )
 +
 +
def commentShort( text ):
 +
    print( "Comment :=>> " + text )
 +
 +
def printGrade( grade ):
 +
    print( "Grade :=>> ", grade )
 +
 +
# generateInputFileWithRandomInputs
 +
# generate a file with name "inputFileName" with some random input
 +
# for the program.
 +
# MAKE SURE TO EDIT THIS TO MATCH THE PROGRAM BEING TESTED
 +
def generateInputFileWithRandomInputs( inputFileName ):
 +
    #--- generate random inputs ---
 +
 +
    #--- create a list of random names and put them in a string ---
 +
    list = []
 +
    for i in range( random.choice( [2, 3] ) ):
 +
        list.append( random.choice( [ "Lu", "Jorge", "Katarina", "Gru", "Dave", "Karl", "Max" ] ) )
 +
 +
    names = " ".join( list )
 +
 +
    #--- create a single name   
 +
    #names = random.choice( [ "Dave", "Jorge", "Katarina", "Carl", "Tim" ] )
 +
 +
    #--- create input file for stdin ---
 +
    file = open( inputFileName, "w" )
 +
    file.write( names + "\n" )
 +
    file.close()
 +
 +
    return names + "\n"
 +
 +
# checkForFunctionPresence
 +
# checks that "functionName" is defined and called in the program.
 +
# MAKE SURE TO EDIT TO MATCH PROGRAM BEING TESTED
 +
def checkForFunctionPresence( module, functionName ):
 +
    foundDef = False
 +
    foundCall = False
 +
 +
    for line in open( module+".py", "r" ).readlines():
 +
        # remove comments
 +
        idx = line.find( "#" )
 +
        if ( idx >=0 ): line = line[0:idx]
 +
 +
        if line.startswith( "def " + functionName + "(" ):
 +
            foundDef = True
 +
            continue
 +
        if line.startswith( "def " + functionName + " (" ):
 +
            foundDef = True
 +
            continue
 +
        if line.find( functionName+"(" ) != -1:
 +
            foundCall = True
 +
            continue
 +
 +
    return (foundDef, foundCall)
 +
 +
 +
 +
# ==================================================================
 +
# NO EDITS NEEDED BELOW!
 +
# ==================================================================
 +
 +
 +
 +
 +
# checkModuleRunsOK: runs the module as a shell subprocess and
 +
# look for errors in the output.  This is required, because otherwise
 +
# importing the module in this program will make this program crash.
 +
# It's not possible (as far as I can tell0 to catch exceptions from
 +
# the import or __module__ statements.
 +
# returns True, none if no errors, otherwise False, string if there's
 +
# an exception, and the error message (string) is in the returned 2nd
 +
# arg.
 +
# The module name is assumed to not include ".py"
 +
def checkModuleRunsOk( module, inputFileName ):
 +
    global interpreter
 +
    p = subprocess.Popen( [ interpreter, module+".py" ],
 +
                          stdout=subprocess.PIPE,
 +
                          stderr=subprocess.PIPE,
 +
                          stdin=subprocess.PIPE)
 +
 +
    #print( "inputFileName = ", inputFileName )
 +
    #print( "open( inputFileName, r).read() = ", open( inputFileName, "r" ).read() )
 +
 +
    p.stdin.write( bytes( open( inputFileName, "r" ).read(), 'UTF-8' ) )
 +
    data = p.communicate( )
 +
    p.stdin.close()
 +
 +
    error = data[1].decode( 'UTF-8' )
 +
    if len( error ) > 1:
 +
        return False, error
 +
    return True, None
 +
 +
                     
 +
# extractTextFromErrorMessage( sys_exc_info ):
 +
def extractTextFromErrorMessage( sys_exc_info ):
 +
    print( "sys_exec_info = ", sys_exc_info )
 +
    text = ""
 +
    for field in sys_exc_info:
 +
        if type( field )==type( " " ):
 +
            text += field + "\n"
 +
    return text
 +
 +
# runModule:
 +
# runs the module, passes it data from the input file on its stdin
 +
# and get its output on stdout captured in outputFileName.
 +
# We assume the module will not crash, because we already tested
 +
# it with checkModuleRunsOk().
 +
def runModule( module, inputFileName, outputFileName ):
 +
    global userOutSplitPattern
 +
 +
    error = False
 +
 +
    #--- make stdin read information from the text file
 +
    sys.stdin = open( inputFileName, "r" )
 +
 +
    #--- capture the stdout of the program to test into a file
 +
    saveStdOut = sys.stdout
 +
    saveStdErr = sys.stderr
 +
 +
    sys.stdout = open( outputFileName, "w" )
 +
    sys.stderr = open( "errorOut", "w" )
 +
 +
    #--- run the student program ---
 +
    try:
 +
        _module = __import__(  module  )
 +
    except:
 +
        error = True
 +
        sys.stderr.close()
 +
        sys.stderr = saveStdErr
 +
        sys.stdout.close()
 +
        sys.stdout = saveStdOut
 +
        text = sys.exc_info()[0]
 +
        text = extractTextFromErrorMessage( text )
 +
        print( "*** sys.exc_info() = ", text )
 +
        return error, text
 +
       
 +
    #--- filter out junk from output of program ---
 +
    sys.stdout.close()
 +
    sys.stdout = saveStdOut
 +
    file = open( outputFileName, "r" )
 +
    text = file.read()
 +
    index = text.find( userOutSplitPattern ) #"+-" )
 +
    text = text[index:]
 +
    text = text.strip( ).strip( "\n" ) + "\n"
 +
    #print( text, end="" )
 +
    file.close()
 +
    return False, text
 +
 +
def removeBlankLines( lines ):
 +
    newLines = []
 +
    for line in lines.split( "\n" ):
 +
        if len( line )==0:
 +
            continue
 +
        newLines.append( line )
 +
 +
    return ( "\n".join( newLines ) ) + "\n"
 +
 +
def compareUserExpected( inputLines, userOutText, expectedOutText ):
 +
    global stripOutputsBeforeCompare
 +
 +
    userOutText = removeBlankLines( userOutText )
 +
    expectedOutText = removeBlankLines( expectedOutText )
 +
    misMatchLineNumbers = []
 +
    userTextOutLines = userOutText.split( "\n" )
 +
    expectedOutTextLines = expectedOutText.split( "\n" )
 +
 +
    for i in range( len( userTextOutLines ) ):
 +
        lineNo = i+1
 +
        userLine = userTextOutLines[i]
 +
        expectedLine = expectedOutTextLines[i]
 +
        if stripOutputsBeforeCompare:
 +
            userLine = userLine.strip()
 +
            expectedLine = expectedLine.strip()
 +
        if userLine != expectedLine:
 +
            #print( "user    >" + userTextOutLines[i] + "<" )
 +
            #print( "expected >" + expectedOutTextLines[i] + "<" )
 +
            misMatchLineNumbers.append( lineNo )
 +
   
 +
    return misMatchLineNumbers
 +
           
 +
 +
 +
def main():
 +
    global module
 +
    global solutionModule
 +
 +
   
 +
    #--- check that the main module uses a main() function
 +
    foundDef, foundCall = checkForFunctionPresence( module, "main" )
 +
    if (not foundDef) or (not foundCall):
 +
        commentShort( "-Missing main() program" )
 +
        commentShort( "Your program must use a main() function." )
 +
        commentShort( "(make sure you spell it exactly \"main()\"!" )
 +
        printGrade( 40 )
 +
        return
 +
 +
 +
    #--- generate input file with random data ---
 +
    inputLines = generateInputFileWithRandomInputs( "input" )
 +
 +
 +
    Ok, errorMessage = checkModuleRunsOk( module, "input" )
 +
    if not Ok:
 +
        commentLong( "- Your program crashed...\n"
 +
                    + "Your program was tested with:\n"
 +
                    + inputLines  + "\n"
 +
                    + "Error message:\n"
 +
                    + errorMessage + "\n" )
 +
        printGrade( 50 )
 +
        return
 +
 +
                   
 +
    error, userOutText        = runModule( module, "input", "userOut" )
 +
    if error:
 +
        commentLong( "- Your program crashed...\n"
 +
                    + "Your program was tested with:\n"
 +
                    + inputLines  + "\n"
 +
                    + "Error message:\n"
 +
                    + userOutText + "\n" )
 +
        printGrade( 50 )
 +
        return
 +
 +
    dummy, expectedOutText    = runModule( solutionModule, "input", "expectedOut" )
 +
 +
    misMatchLineNumbers = compareUserExpected( inputLines,
 +
                                              userOutText,
 +
                                              expectedOutText )
 +
    if len( misMatchLineNumbers ) == 0:
 +
        commentLong( "- Congrats, correct output!\n"
 +
                    +"Your program was tested with:\n"
 +
                    +inputLines + "\n"
 +
                    +"Your output:\n"
 +
                    +userOutText + "\n" )
 +
        printGrade( 100 )
 +
    else:
 +
        s = "" if len( misMatchLineNumbers ) == 1 else "s"
 +
 +
        commentLong( "-Incorrect output.\n"
 +
                    +"Your program was tested with:\n"
 +
                    +inputLines + "\n"
 +
                    +"Your output:\n"
 +
                    +userOutText + "\n"
 +
                    +"Expected output:\n"
 +
                    +expectedOutText + "\n"
 +
                    +("There are errors in Line%s " % s) +
 +
                    ", ".join( [str(k) for k in misMatchLineNumbers] )
 +
                    +"\n" )
 +
        noLinesGood = len( expectedOutText.strip().split("\n") )
 +
        noErrors    = len( misMatchLineNumbers )
 +
        #print( "noLinesGood = ", noLinesGood, " noErrors = ", noErrors )
 +
 +
        grade      = round( 60 + 40 / noLinesGood *( noLinesGood- noErrors ) )
 +
        printGrade( grade )
 +
 +
main()
 +
 +
 +
</source>
 +
<br />
 +
 +
==Problem 4==
 +
<br />
 +
<source lang="python">
 +
# evaluate.py
 +
# D. Thiebaut
 +
# This program is used to test a student's python program on Moodle.
 +
 +
import sys
 +
import random
 +
import subprocess
 +
 +
#--- define what the student program is called, and what the solution
 +
#--- program name is.
 +
module = "hw5_4"
 +
solutionModule = module + "sol"
 +
userOutSplitPattern = "+-"    # pattern used to start recording the user
 +
                              # output.  Useful when program does several
 +
                              # input() statements, and user output starts
 +
                              # after that.
 +
stripOutputsBeforeCompare = True
 +
                              # set to True if extra spaces at beginning or
 +
                              # end of user output is ok
 +
 +
#--- GLOBALS ---
 +
interpreter = sys.executable
 +
 +
def commentLong( line ):
 +
    print( "<|--\n" + line  + "\n --|>" )
 +
 +
def commentShort( text ):
 +
    print( "Comment :=>> " + text )
 +
 +
def printGrade( grade ):
 +
    print( "Grade :=>> ", grade )
 +
 +
# generateInputFileWithRandomInputs
 +
# generate a file with name "inputFileName" with some random input
 +
# for the program.
 +
# MAKE SURE TO EDIT THIS TO MATCH THE PROGRAM BEING TESTED
 +
def generateInputFileWithRandomInputs( inputFileName ):
 +
    #--- generate random inputs ---
 +
 +
    #--- create a list of random names and put them in a string ---
 +
    list = []
 +
    for i in range( random.choice( [2, 3] ) ):
 +
        list.append( random.choice( [ "Lu", "Jorge", "Katarina", "Gru", "Dave", "Karl", "Max" ] ) )
 +
 +
    names = "\n".join( list )
 +
 +
    #--- create a single name   
 +
    #names = random.choice( [ "Dave", "Jorge", "Katarina", "Carl", "Tim" ] )
 +
 +
    #--- create input file for stdin ---
 +
    file = open( "names.txt", "w" )
 +
    file.write( names + "\n" )
 +
    file.close()
 +
 +
    file = open( inputFileName, "w" )
 +
    file.write( "names.txt\n" )
 +
    file.close()
 +
 +
    return names + "\n"
 +
 +
# checkForFunctionPresence
 +
# checks that "functionName" is defined and called in the program.
 +
# MAKE SURE TO EDIT TO MATCH PROGRAM BEING TESTED
 +
def checkForFunctionPresence( module, functionName ):
 +
    foundDef = False
 +
    foundCall = False
 +
 +
    for line in open( module+".py", "r" ).readlines():
 +
        # remove comments
 +
        idx = line.find( "#" )
 +
        if ( idx >=0 ): line = line[0:idx]
 +
 +
        if line.startswith( "def " + functionName + "(" ):
 +
            foundDef = True
 +
            continue
 +
        if line.startswith( "def " + functionName + " (" ):
 +
            foundDef = True
 +
            continue
 +
        if line.find( functionName+"(" ) != -1:
 +
            foundCall = True
 +
            continue
 +
 +
    return (foundDef, foundCall)
 +
 +
 +
 +
# ==================================================================
 +
# NO EDITS NEEDED BELOW!
 +
# ==================================================================
 +
 +
 +
 +
 +
# checkModuleRunsOK: runs the module as a shell subprocess and
 +
# look for errors in the output.  This is required, because otherwise
 +
# importing the module in this program will make this program crash.
 +
# It's not possible (as far as I can tell0 to catch exceptions from
 +
# the import or __module__ statements.
 +
# returns True, none if no errors, otherwise False, string if there's
 +
# an exception, and the error message (string) is in the returned 2nd
 +
# arg.
 +
# The module name is assumed to not include ".py"
 +
def checkModuleRunsOk( module, inputFileName ):
 +
    global interpreter
 +
    p = subprocess.Popen( [ interpreter, module+".py" ],
 +
                          stdout=subprocess.PIPE,
 +
                          stderr=subprocess.PIPE,
 +
                          stdin=subprocess.PIPE)
 +
 +
    #print( "inputFileName = ", inputFileName )
 +
    #print( "open( inputFileName, r).read() = ", open( inputFileName, "r" ).read() )
 +
 +
    p.stdin.write( bytes( open( inputFileName, "r" ).read(), 'UTF-8' ) )
 +
    data = p.communicate( )
 +
    p.stdin.close()
 +
 +
    error = data[1].decode( 'UTF-8' )
 +
    if len( error ) > 1:
 +
        return False, error
 +
    return True, None
 +
 +
                     
 +
# extractTextFromErrorMessage( sys_exc_info ):
 +
def extractTextFromErrorMessage( sys_exc_info ):
 +
    print( "sys_exec_info = ", sys_exc_info )
 +
    text = ""
 +
    for field in sys_exc_info:
 +
        if type( field )==type( " " ):
 +
            text += field + "\n"
 +
    return text
 +
 +
# runModule:
 +
# runs the module, passes it data from the input file on its stdin
 +
# and get its output on stdout captured in outputFileName.
 +
# We assume the module will not crash, because we already tested
 +
# it with checkModuleRunsOk().
 +
def runModule( module, inputFileName, outputFileName ):
 +
    global userOutSplitPattern
 +
 +
    error = False
 +
 +
    #--- make stdin read information from the text file
 +
    sys.stdin = open( inputFileName, "r" )
 +
 +
    #--- capture the stdout of the program to test into a file
 +
    saveStdOut = sys.stdout
 +
    saveStdErr = sys.stderr
 +
 +
    sys.stdout = open( outputFileName, "w" )
 +
    sys.stderr = open( "errorOut", "w" )
 +
 +
    #--- run the student program ---
 +
    try:
 +
        _module = __import__(  module  )
 +
    except:
 +
        error = True
 +
        sys.stderr.close()
 +
        sys.stderr = saveStdErr
 +
        sys.stdout.close()
 +
        sys.stdout = saveStdOut
 +
        text = sys.exc_info()[0]
 +
        text = extractTextFromErrorMessage( text )
 +
        print( "*** sys.exc_info() = ", text )
 +
        return error, text
 +
       
 +
    #--- filter out junk from output of program ---
 +
    sys.stdout.close()
 +
    sys.stdout = saveStdOut
 +
    file = open( outputFileName, "r" )
 +
    text = file.read()
 +
    index = text.find( userOutSplitPattern ) #"+-" )
 +
    text = text[index:]
 +
    text = text.strip( ).strip( "\n" ) + "\n"
 +
    #print( text, end="" )
 +
    file.close()
 +
    return False, text
 +
 +
def removeBlankLines( lines ):
 +
    newLines = []
 +
    for line in lines.split( "\n" ):
 +
        if len( line )==0:
 +
            continue
 +
        newLines.append( line )
 +
 +
    return ( "\n".join( newLines ) ) + "\n"
 +
 +
def compareUserExpected( inputLines, userOutText, expectedOutText ):
 +
    global stripOutputsBeforeCompare
 +
 +
    userOutText = removeBlankLines( userOutText )
 +
    expectedOutText = removeBlankLines( expectedOutText )
 +
    misMatchLineNumbers = []
 +
    userTextOutLines = userOutText.split( "\n" )
 +
    expectedOutTextLines = expectedOutText.split( "\n" )
 +
 +
    for i in range( len( userTextOutLines ) ):
 +
        lineNo = i+1
 +
        userLine = userTextOutLines[i]
 +
        expectedLine = expectedOutTextLines[i]
 +
        if stripOutputsBeforeCompare:
 +
            userLine = userLine.strip()
 +
            expectedLine = expectedLine.strip()
 +
        if userLine != expectedLine:
 +
            #print( "user    >" + userTextOutLines[i] + "<" )
 +
            #print( "expected >" + expectedOutTextLines[i] + "<" )
 +
            misMatchLineNumbers.append( lineNo )
 +
   
 +
    return misMatchLineNumbers
 +
           
 +
 +
 +
def main():
 +
    global module
 +
    global solutionModule
 +
 +
   
 +
    #--- check that the main module uses a main() function
 +
    foundDef, foundCall = checkForFunctionPresence( module, "main" )
 +
    if (not foundDef) or (not foundCall):
 +
        commentShort( "-Missing main() program" )
 +
        commentShort( "Your program must use a main() function." )
 +
        commentShort( "(make sure you spell it exactly \"main()\"!" )
 +
        printGrade( 40 )
 +
        return
 +
 +
 +
    #--- generate input file with random data ---
 +
    inputLines = generateInputFileWithRandomInputs( "input" )
 +
 +
 +
    Ok, errorMessage = checkModuleRunsOk( module, "input" )
 +
    if not Ok:
 +
        commentLong( "- Your program crashed...\n"
 +
                    + "Your program was tested with a file containing:\n"
 +
                    + inputLines  + "\n"
 +
                    + "Error message:\n"
 +
                    + errorMessage + "\n" )
 +
        printGrade( 50 )
 +
        return
 +
 +
                   
 +
    error, userOutText        = runModule( module, "input", "userOut" )
 +
    if error:
 +
        commentLong( "- Your program crashed...\n"
 +
                    + "Your program was tested with a file containing:\n"
 +
                    + inputLines  + "\n"
 +
                    + "Error message:\n"
 +
                    + userOutText + "\n" )
 +
        printGrade( 50 )
 +
        return
 +
 +
    dummy, expectedOutText    = runModule( solutionModule, "input", "expectedOut" )
 +
 +
    misMatchLineNumbers = compareUserExpected( inputLines,
 +
                                              userOutText,
 +
                                              expectedOutText )
 +
    if len( misMatchLineNumbers ) == 0:
 +
        commentLong( "- Congrats, correct output!\n"
 +
                    +"Your program was tested with a file containing:\n"
 +
                    +inputLines + "\n"
 +
                    +"Your output:\n"
 +
                    +userOutText + "\n" )
 +
        printGrade( 100 )
 +
    else:
 +
        s = "" if len( misMatchLineNumbers ) == 1 else "s"
 +
 +
        commentLong( "-Incorrect output.\n"
 +
                    +"Your program was tested with a file containing:\n"
 +
                    +inputLines + "\n"
 +
                    +"Your output:\n"
 +
                    +userOutText + "\n"
 +
                    +"Expected output:\n"
 +
                    +expectedOutText + "\n"
 +
                    +("There are errors in Line%s " % s) +
 +
                    ", ".join( [str(k) for k in misMatchLineNumbers] )
 +
                    +"\n" )
 +
        noLinesGood = len( expectedOutText.strip().split("\n") )
 +
        noErrors    = len( misMatchLineNumbers )
 +
        #print( "noLinesGood = ", noLinesGood, " noErrors = ", noErrors )
 +
 +
        grade      = round( 60 + 40 / noLinesGood *( noLinesGood- noErrors ) )
 +
        printGrade( grade )
 +
 +
main()
 +
 +
 +
 +
</source>
 +
<br />
 +
 +
==Problem 5==
 +
<br />
 +
<source lang="python">
 +
# evaluate.py
 +
# D. Thiebaut
 +
# This program is used to test a student's python program on Moodle.
 +
 +
import sys
 +
import random
 +
import subprocess
 +
 +
#--- define what the student program is called, and what the solution
 +
#--- program name is.
 +
module = "hw5_5"
 +
solutionModule = module + "sol"
 +
userOutSplitPattern = "H"    # pattern used to start recording the user
 +
                              # output.  Useful when program does several
 +
                              # input() statements, and user output starts
 +
                              # after that.
 +
stripOutputsBeforeCompare = True
 +
                              # set to True if extra spaces at beginning or
 +
                              # end of user output is ok
 +
 +
#--- GLOBALS ---
 +
interpreter = sys.executable
 +
 +
def commentLong( line ):
 +
    print( "<|--\n" + line  + "\n --|>" )
 +
 +
def commentShort( text ):
 +
    print( "Comment :=>> " + text )
 +
 +
def printGrade( grade ):
 +
    print( "Grade :=>> ", grade )
 +
 +
def abort( message, grade ):
 +
    commentLong( message )
 +
    printGrade( grade )
 +
    sys.exit()
 +
 +
# generateInputFileWithRandomInputs
 +
# generate a file with name "inputFileName" with some random input
 +
# for the program.
 +
# MAKE SURE TO EDIT THIS TO MATCH THE PROGRAM BEING TESTED
 +
def generateInputFileWithRandomInputs( inputFileName ):
 +
    #--- generate random inputs ---
 +
 +
    #--- create a list of random names and put them in a string ---
 +
    list = []
 +
    for i in range( random.choice( [2, 3] ) ):
 +
        list.append( random.choice( [ "Lu", "Jorge", "Katarina", "Gru", "Dave", "Karl", "Max" ] ) )
 +
 +
    names = "\n".join( list )
 +
 +
    #--- create a single name   
 +
    #names = random.choice( [ "Dave", "Jorge", "Katarina", "Carl", "Tim" ] )
 +
 +
    #--- create input file for stdin ---
 +
    file = open( "names.txt", "w" )
 +
    file.write( names + "\n" )
 +
    file.close()
 +
 +
    message = random.choice( [ "Hello, zzzz!\n", "Hey, Are you the famous zzzz?\nAmazing!\n",
 +
                              "Happy birthday to you,\nHappy birthday, dear zzzz!\n" ] )
 +
    file = open( "message.txt", "w" )
 +
    file.write( message )
 +
    file.close()
 +
                           
 +
    file = open( inputFileName, "w" )
 +
    file.write( "message.txt\nnames.txt\n" )
 +
    file.close()
 +
 +
    return names + "\n"
 +
 +
# checkForFunctionPresence
 +
# checks that "functionName" is defined and called in the program.
 +
# MAKE SURE TO EDIT TO MATCH PROGRAM BEING TESTED
 +
def checkForFunctionPresence( module, functionName ):
 +
    foundDef = False
 +
    foundCall = False
 +
 +
    for line in open( module+".py", "r" ).readlines():
 +
        # remove comments
 +
        idx = line.find( "#" )
 +
        if ( idx >=0 ): line = line[0:idx]
 +
 +
        if line.startswith( "def " + functionName + "(" ):
 +
            foundDef = True
 +
            continue
 +
        if line.startswith( "def " + functionName + " (" ):
 +
            foundDef = True
 +
            continue
 +
        if line.find( functionName+"(" ) != -1:
 +
            foundCall = True
 +
            continue
 +
 +
    return (foundDef, foundCall)
 +
 +
 +
 +
# ==================================================================
 +
# NO EDITS NEEDED BELOW!
 +
# ==================================================================
 +
 +
 +
 +
 +
# checkModuleRunsOK: runs the module as a shell subprocess and
 +
# look for errors in the output.  This is required, because otherwise
 +
# importing the module in this program will make this program crash.
 +
# It's not possible (as far as I can tell0 to catch exceptions from
 +
# the import or __module__ statements.
 +
# returns True, none if no errors, otherwise False, string if there's
 +
# an exception, and the error message (string) is in the returned 2nd
 +
# arg.
 +
# The module name is assumed to not include ".py"
 +
def checkModuleRunsOk( module, inputFileName ):
 +
    global interpreter
 +
    p = subprocess.Popen( [ interpreter, module+".py" ],
 +
                          stdout=subprocess.PIPE,
 +
                          stderr=subprocess.PIPE,
 +
                          stdin=subprocess.PIPE)
 +
 +
    #print( "inputFileName = ", inputFileName )
 +
    #print( "open( inputFileName, r).read() = ", open( inputFileName, "r" ).read() )
 +
 +
    p.stdin.write( bytes( open( inputFileName, "r" ).read(), 'UTF-8' ) )
 +
    data = p.communicate( )
 +
    p.stdin.close()
 +
 +
    error = data[1].decode( 'UTF-8' )
 +
    if len( error ) > 1:
 +
        return False, error
 +
    return True, None
 +
 +
                     
 +
# extractTextFromErrorMessage( sys_exc_info ):
 +
def extractTextFromErrorMessage( sys_exc_info ):
 +
    print( "sys_exec_info = ", sys_exc_info )
 +
    text = ""
 +
    for field in sys_exc_info:
 +
        if type( field )==type( " " ):
 +
            text += field + "\n"
 +
    return text
 +
 +
# runModule:
 +
# runs the module, passes it data from the input file on its stdin
 +
# and get its output on stdout captured in outputFileName.
 +
# We assume the module will not crash, because we already tested
 +
# it with checkModuleRunsOk().
 +
def runModule( module, inputFileName, outputFileName ):
 +
    global userOutSplitPattern
 +
 +
    error = False
 +
 +
    #--- make stdin read information from the text file
 +
    sys.stdin = open( inputFileName, "r" )
 +
 +
    #--- capture the stdout of the program to test into a file
 +
    saveStdOut = sys.stdout
 +
    saveStdErr = sys.stderr
 +
 +
    sys.stdout = open( outputFileName, "w" )
 +
    sys.stderr = open( "errorOut", "w" )
 +
 +
    #--- run the student program ---
 +
    try:
 +
        _module = __import__(  module  )
 +
    except:
 +
        error = True
 +
        sys.stderr.close()
 +
        sys.stderr = saveStdErr
 +
        sys.stdout.close()
 +
        sys.stdout = saveStdOut
 +
        text = sys.exc_info()[0]
 +
        text = extractTextFromErrorMessage( text )
 +
        print( "*** sys.exc_info() = ", text )
 +
        return error, text
 +
       
 +
    #--- filter out junk from output of program ---
 +
    sys.stdout.close()
 +
    sys.stdout = saveStdOut
 +
    file = open( outputFileName, "r" )
 +
    text = file.read()
 +
    index = text.find( userOutSplitPattern ) #"+-" )
 +
    text = text[index:]
 +
    text = text.strip( ).strip( "\n" ) + "\n"
 +
    #print( text, end="" )
 +
    file.close()
 +
    return False, text
 +
 +
def removeBlankLines( lines ):
 +
    newLines = []
 +
    for line in lines.split( "\n" ):
 +
        if len( line )==0:
 +
            continue
 +
        newLines.append( line )
 +
 +
    return ( "\n".join( newLines ) ) + "\n"
 +
 +
def compareUserExpected( inputLines, userOutText, expectedOutText ):
 +
    global stripOutputsBeforeCompare
 +
 +
    userOutText = removeBlankLines( userOutText )
 +
    expectedOutText = removeBlankLines( expectedOutText )
 +
    misMatchLineNumbers = []
 +
    userTextOutLines = userOutText.split( "\n" )
 +
    expectedOutTextLines = expectedOutText.split( "\n" )
 +
 +
    if len( userTextOutLines ) != len( expectedOutTextLines ):
 +
        abort( ("Your output contains {0:1} lines,\nwhile {1:1} lines are expected.\n"
 +
              + "Please fix this problem first,\nthen your output will be compared\n"
 +
              + "to the solution output.\n\nYour output:\n\n{2:1}\n\n"
 +
              + "Expected output:\n\n{3:1}\n" ).format( len( userTextOutLines ),
 +
                                                    len( expectedOutTextLines ),
 +
                                                    userOutText,expectedOutText ),
 +
              70 )
 +
 +
    for i in range( min( len( expectedOutTextLines), len( userTextOutLines ) ) ):
 +
        lineNo = i+1
 +
        userLine = userTextOutLines[i]
 +
        expectedLine = expectedOutTextLines[i]
 +
        if stripOutputsBeforeCompare:
 +
            userLine = userLine.strip()
 +
            expectedLine = expectedLine.strip()
 +
        if userLine != expectedLine:
 +
            #print( "user    >" + userTextOutLines[i] + "<" )
 +
            #print( "expected >" + expectedOutTextLines[i] + "<" )
 +
            misMatchLineNumbers.append( lineNo )
 +
 +
    print
 +
    return misMatchLineNumbers
 +
           
 +
 +
 +
def main():
 +
    global module
 +
    global solutionModule
 +
 +
   
 +
    #--- check that the main module uses a main() function
 +
    foundDef, foundCall = checkForFunctionPresence( module, "main" )
 +
    if (not foundDef) or (not foundCall):
 +
        commentShort( "-Missing main() program" )
 +
        commentShort( "Your program must use a main() function." )
 +
        commentShort( "(make sure you spell it exactly \"main()\"!" )
 +
        printGrade( 40 )
 +
        return
 +
 +
 +
    #--- generate input file with random data ---
 +
    inputLines = generateInputFileWithRandomInputs( "input" )
 +
 +
 +
    Ok, errorMessage = checkModuleRunsOk( module, "input" )
 +
    if not Ok:
 +
        commentLong( "- Your program crashed...\n"
 +
                    + "Your program was tested with a file containing:\n"
 +
                    + inputLines  + "\n"
 +
                    + "Error message:\n"
 +
                    + errorMessage + "\n" )
 +
        printGrade( 50 )
 +
        return
 +
 +
                   
 +
    error, userOutText        = runModule( module, "input", "userOut" )
 +
    if error:
 +
        commentLong( "- Your program crashed...\n"
 +
                    + "Your program was tested with a file containing:\n"
 +
                    + inputLines  + "\n"
 +
                    + "Error message:\n"
 +
                    + userOutText + "\n" )
 +
        printGrade( 50 )
 +
        return
 +
 +
    dummy, expectedOutText    = runModule( solutionModule, "input", "expectedOut" )
 +
 +
    misMatchLineNumbers = compareUserExpected( inputLines,
 +
                                              userOutText,
 +
                                              expectedOutText )
 +
    if len( misMatchLineNumbers ) == 0:
 +
        commentLong( "- Congrats, correct output!\n"
 +
                    +"Your program was tested with a file containing:\n"
 +
                    +inputLines + "\n"
 +
                    +"Your output:\n"
 +
                    +userOutText + "\n" )
 +
        printGrade( 100 )
 +
    else:
 +
        s = "" if len( misMatchLineNumbers ) == 1 else "s"
 +
 +
        commentLong( "-Incorrect output.\n"
 +
                    +"Your program was tested with a file containing:\n"
 +
                    +inputLines + "\n"
 +
                    +"Your output:\n"
 +
                    +userOutText + "\n"
 +
                    +"Expected output:\n"
 +
                    +expectedOutText + "\n"
 +
                    +("There are errors in Line%s " % s) +
 +
                    ", ".join( [str(k) for k in misMatchLineNumbers] )
 +
                    +"\n" )
 +
        noLinesGood = len( expectedOutText.strip().split("\n") )
 +
        noErrors    = len( misMatchLineNumbers )
 +
        #print( "noLinesGood = ", noLinesGood, " noErrors = ", noErrors )
 +
 +
        grade      = round( 60 + 40 / noLinesGood *( noLinesGood- noErrors ) )
 +
        printGrade( grade )
 +
 +
main()
 +
 +
</source>
 +
<br />
 +
==Problem #6==
 +
<br />
 +
===evaluate.py===
 +
<br />
 +
<source lang="python">
 +
# evaluate.py
 +
# D. Thiebaut
 +
# This program is used to test a student's python program on Moodle.
 +
 +
import sys
 +
import random
 +
import subprocess
 +
 +
#--- GLOBALS ---
 +
#--- define what the student program is called, and what the solution
 +
#--- program name is.
 +
module = "hw5_6"
 +
solutionModule = module + "sol"
 +
userOutSplitPattern = ""      # pattern used to start recording the user
 +
                              # output.  Useful when program does several
 +
                              # input() statements, and user output starts
 +
                              # after that.
 +
stripOutputsBeforeCompare = True
 +
                              # set to True if extra spaces at beginning or
 +
                              # end of user output is ok
 +
 +
interpreter = sys.executable
 +
 +
def commentLong( line ):
 +
    print( "<|--\n" + line  + "\n --|>" )
 +
 +
def commentShort( text ):
 +
    print( "Comment :=>> " + text )
 +
 +
def printGrade( grade ):
 +
    print( "Grade :=>> ", grade )
 +
 +
# generateInputFileWithRandomInputs
 +
# generate a file with name "inputFileName" with some random input
 +
# for the program.
 +
# MAKE SURE TO EDIT THIS TO MATCH THE PROGRAM BEING TESTED
 +
def generateInputFileWithRandomInputs( inputFileName ):
 +
    #--- we don't need an input file for stdin, but we'll generate a
 +
    #--- dummy one nonetheless
 +
    open( inputFileName, "w" ).write( "\n\n\n\n\n\n\n" )
 +
 +
    #--- generate random inputs ---
 +
    names = [ "Lei", "Mei", "Lisa", "Katarina" ]
 +
    stuff = [ "nutella", "strawberries", "snow", "sand", "candies" ]
 +
    dates = [ "01012100", "02152020", "12012015" ]
 +
 +
    text = ""
 +
    for i in range( random.choice( [2,3] ) ):
 +
        text = text + "{0:1} {1:1} {2:1}\n".format(
 +
                      random.choice( names ),
 +
                      random.choice( stuff ),
 +
                      random.choice( dates ) )
 +
    open( "users.txt", "w" ).write( text )
 +
    open( "users2.txt", "w" ).write( text )
 +
 +
    #--- generate random messages ---
 +
    messages = [ "$name, you won a barrel of $stuff,\nwhich you have to eat before $date.\n",
 +
                "On $date, $name jumped\non a pile of $stuff.\n",
 +
                "We all like $name.\n$name is great.\n$name graduates in $date.\n" ]
 +
   
 +
    message = random.choice( messages )
 +
    open( "message.txt", "w").write( message )
 +
    open( "message2.txt", "w" ).write( message )
 +
 +
    return text +"\n===========================\n"+ message + "\n"
 +
 +
# checkForFunctionPresence
 +
# checks that "functionName" is defined and called in the program.
 +
# MAKE SURE TO EDIT TO MATCH PROGRAM BEING TESTED
 +
def checkForFunctionPresence( module, functionName ):
 +
    foundDef = False
 +
    foundCall = False
 +
 +
    for line in open( module+".py", "r" ).readlines():
 +
        # remove comments
 +
        idx = line.find( "#" )
 +
        if ( idx >=0 ): line = line[0:idx]
 +
 +
        if line.startswith( "def " + functionName + "(" ):
 +
            foundDef = True
 +
            continue
 +
        if line.startswith( "def " + functionName + " (" ):
 +
            foundDef = True
 +
            continue
 +
        if line.find( functionName+"(" ) != -1:
 +
            foundCall = True
 +
            continue
 +
 +
    return (foundDef, foundCall)
 +
 +
 +
 +
# ==================================================================
 +
# NO EDITS NEEDED BELOW!
 +
# ==================================================================
 +
 +
def clearLog():
 +
    open( "log.txt", "w" ).write( "" )
 +
 +
def log( message ):
 +
    file = open( "log.txt", "a" )
 +
    file.write( message + "\n" )
 +
    file.flush()
 +
    file.close()
 +
 +
 +
# checkModuleRunsOK: runs the module as a shell subprocess and
 +
# look for errors in the output.  This is required, because otherwise
 +
# importing the module in this program will make this program crash.
 +
# It's not possible (as far as I can tell0 to catch exceptions from
 +
# the import or __module__ statements.
 +
# returns True, none if no errors, otherwise False, string if there's
 +
# an exception, and the error message (string) is in the returned 2nd
 +
# arg.
 +
# The module name is assumed to not include ".py"
 +
def checkModuleRunsOk( module, inputFileName ):
 +
    global interpreter
 +
    p = subprocess.Popen( [ interpreter, module+".py" ],
 +
                          stdout=subprocess.PIPE,
 +
                          stderr=subprocess.PIPE,
 +
                          stdin=subprocess.PIPE)
 +
 +
    #print( "inputFileName = ", inputFileName )
 +
    #print( "open( inputFileName, r).read() = ", open( inputFileName, "r" ).read() )
 +
 +
    p.stdin.write( bytes( open( inputFileName, "r" ).read(), 'UTF-8' ) )
 +
    data = p.communicate( )
 +
    p.stdin.close()
 +
 +
    error = data[1].decode( 'UTF-8' )
 +
    if len( error ) > 1:
 +
        return False, error
 +
    return True, None
 +
 +
                     
 +
# extractTextFromErrorMessage( sys_exc_info ):
 +
def extractTextFromErrorMessage( sys_exc_info ):
 +
    print( "sys_exec_info = ", sys_exc_info )
 +
    text = ""
 +
    for field in sys_exc_info:
 +
        if type( field )==type( " " ):
 +
            text += field + "\n"
 +
    return text
 +
 +
# runModule:
 +
# runs the module, passes it data from the input file on its stdin
 +
# and get its output on stdout captured in outputFileName.
 +
# We assume the module will not crash, because we already tested
 +
# it with checkModuleRunsOk().
 +
def runModule( module, inputFileName, outputFileName ):
 +
    global userOutSplitPattern
 +
 +
    error = False
 +
 +
    #--- make stdin read information from the text file
 +
    sys.stdin = open( inputFileName, "r" )
 +
 +
    #--- capture the stdout of the program to test into a file
 +
    saveStdOut = sys.stdout
 +
    saveStdErr = sys.stderr
 +
 +
    sys.stdout = open( outputFileName, "w" )
 +
    sys.stderr = open( "errorOut", "w" )
 +
 +
    #--- run the student program ---
 +
    try:
 +
        _module = __import__(  module  )
 +
    except:
 +
        error = True
 +
        sys.stderr.close()
 +
        sys.stderr = saveStdErr
 +
        sys.stdout.close()
 +
        sys.stdout = saveStdOut
 +
        text = sys.exc_info()[0]
 +
        text = extractTextFromErrorMessage( text )
 +
        print( "*** sys.exc_info() = ", text )
 +
        return error, text
 +
       
 +
    #--- filter out junk from output of program ---
 +
    sys.stdout.close()
 +
    sys.stdout = saveStdOut
 +
    sys.stderr.close()
 +
    sys.stderr = saveStdErr
 +
 +
    file = open( outputFileName, "r" )
 +
    text = file.read()
 +
    index = text.find( userOutSplitPattern ) #"+-" )
 +
    text = text[index:]
 +
    text = text.strip( ).strip( "\n" ) + "\n"
 +
    #print( text, end="" )
 +
    file.close()
 +
    return False, text
 +
 +
def removeBlankLines( lines ):
 +
    newLines = []
 +
    log( "removeBlankLines: lines = " + str( lines ) )
 +
    for line in lines.split( "\n" ):
 +
        if len( line )==0:
 +
            continue
 +
        newLines.append( line )
 +
 +
    return ( "\n".join( newLines ) ) + "\n"
 +
 +
def compareUserExpected( inputLines, userOutText, expectedOutText ):
 +
    global stripOutputsBeforeCompare
 +
 +
    log( "userOutText = " + userOutText )
 +
    log( "expectedOutText = " + expectedOutText )
 +
 +
    userOutText = removeBlankLines( userOutText )
 +
    expectedOutText = removeBlankLines( expectedOutText )
 +
    misMatchLineNumbers = []
 +
    userTextOutLines = userOutText.split( "\n" )
 +
    expectedOutTextLines = expectedOutText.split( "\n" )
 +
 +
    log( "-" * 60 )
 +
    log( "userTextOutLines = " + str( userTextOutLines ) )
 +
    log( "expectedOutTextLines = " + str( expectedOutTextLines ) )
 +
    log( "-" * 60 )
 +
 +
    for i in range( len( userTextOutLines ) ):
 +
        lineNo = i+1
 +
        userLine = userTextOutLines[i]
 +
        if i >= len( expectedOutTextLines ):
 +
            misMatchLineNumbers.append( lineNo )
 +
            break
 +
        expectedLine = expectedOutTextLines[i]
 +
        log( "comparing:\n  "+userLine+"\n  "+expectedLine )
 +
        if stripOutputsBeforeCompare:
 +
            userLine = userLine.strip()
 +
            expectedLine = expectedLine.strip()
 +
        if userLine != expectedLine:
 +
            log( "\ndifference: user    >" + userTextOutLines[i] + "<" )
 +
            log( "expected >" + expectedOutTextLines[i] + "<" )
 +
            misMatchLineNumbers.append( lineNo )
 +
   
 +
    return misMatchLineNumbers
 +
           
 +
 +
 +
def main():
 +
    global module
 +
    global solutionModule
 +
 +
    #--- clear debug log ---
 +
    clearLog()
 +
   
 +
    #--- check that the main module uses a main() function
 +
    foundDef, foundCall = checkForFunctionPresence( module, "main" )
 +
    if (not foundDef) or (not foundCall):
 +
        commentShort( "-Missing main() program" )
 +
        commentShort( "Your program must use a main() function." )
 +
        commentShort( "(make sure you spell it exactly \"main()\"!" )
 +
        printGrade( 40 )
 +
        return
 +
 +
 +
    #--- generate input file with random data ---
 +
    inputLines = generateInputFileWithRandomInputs( "input" )
 +
 +
   
 +
    Ok, errorMessage = checkModuleRunsOk( module, "input" )
 +
    if not Ok:
 +
        commentLong( "- Your program crashed...\n"
 +
                    + "Your program was tested with files containing:\n"
 +
                    + inputLines  + "\n"
 +
                    + "Error message:\n"
 +
                    + errorMessage + "\n" )
 +
        printGrade( 50 )
 +
        return
 +
 +
                   
 +
    error, userOutText        = runModule( module, "input", "userOut" )
 +
    if error:
 +
        commentLong( "- Your program crashed...\n"
 +
                    + "Your program was tested with files containing:\n"
 +
                    + inputLines  + "\n"
 +
                    + "Error message:\n"
 +
                    + userOutText + "\n" )
 +
        printGrade( 50 )
 +
        return
 +
 +
    dummy, expectedOutText    = runModule( solutionModule, "input", "expectedOut" )
 +
 +
    misMatchLineNumbers = compareUserExpected( inputLines,
 +
                                              userOutText,
 +
                                              expectedOutText )
 +
    if len( misMatchLineNumbers ) == 0:
 +
        commentLong( "- Congrats, correct output!\n"
 +
                    +"Your program was tested with files containing:\n"
 +
                    +inputLines + "\n"
 +
                    +"Your output:\n"
 +
                    +userOutText + "\n" )
 +
        printGrade( 100 )
 +
    else:
 +
        s = "" if len( misMatchLineNumbers ) == 1 else "s"
 +
 +
        commentLong( "-Incorrect output.\n"
 +
                    +"Your program was tested with files containing:\n"
 +
                    +inputLines + "\n"
 +
                    +"Your output:\n"
 +
                    +userOutText + "\n"
 +
                    +"Expected output:\n"
 +
                    +expectedOutText + "\n"
 +
                    +("There are errors in Line%s " % s) +
 +
                    ", ".join( [str(k) for k in misMatchLineNumbers] )
 +
                    +"\n" )
 +
        noLinesGood = len( expectedOutText.strip().split("\n") )
 +
        noErrors    = len( misMatchLineNumbers )
 +
        #print( "noLinesGood = ", noLinesGood, " noErrors = ", noErrors )
 +
 +
        grade      = round( 50 + 50 / noLinesGood *( noLinesGood- noErrors ) )
 +
        printGrade( grade )
 +
 +
main()
 +
 +
 +
</source>
 +
</onlydft>
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
<br />
 +
[[Category:CSC111]][[Category:Python]][[Category:Homework]]

Latest revision as of 10:53, 15 June 2015

--D. Thiebaut (talk) 09:55, 24 February 2015 (EST)




<showafterdate after="20150226 16:00" before="20150606 00:00">


This homework assignment is due on Tuesday March 3rd, at 11:55 p.m.

Problem #1


  • Write a program called hw5_1.py that asks the user for the name of a person to sing "Happy Birthday" to, and then displays the song in a box 52-characters wide.


Example output


Who should we sing for? Carl
+--------------------------------------------------+
|              Happy birthday to you!              |
|              Happy birthday to you!              |
|            Happy birthday, dear Carl!            |
|              Happy birthday to you!              |
+--------------------------------------------------+


Requirements


  • This should be the outline of your program:


# hw5_1.py
# your name
# short description
def happyBirthday():
    ...

def happyBirthdayDear( name ):
    ...

def singSong( name ):
    ...

def main():
    ...

main()


  • Make sure you use the same function names as shown above, with the same parameter name. The test script will look for the same format.


Submission to Moodle


  • Submit your program in the HW5 PB 1 section on Moodle.


Problem #2


Modify your program, while still keeping the same organization (with some modifications, see below), so that the box is tight around the song, as illustrated below:

Example output 1


Who should we sing for? Lu
+------------------------+
| Happy birthday to you! |
| Happy birthday to you! |
|Happy birthday, dear Lu!|
| Happy birthday to you! |
+------------------------+


Example output 2


Who should we sing for? Alexandra-Katarina
+----------------------------------------+
|         Happy birthday to you!         |
|         Happy birthday to you!         |
|Happy birthday, dear Alexandra-Katarina!|
|         Happy birthday to you!         |
+----------------------------------------+


Requirements


  • Your program should now have the following organization, where a new parameter is being passed to the functions. This parameter is the length of the line with the user name, inside the box.


# Hw5_2.py
# Your name
# short description

def happyBirthday( length ):
    ...

def happyBirthdayDear( name, length ):
    ...

def singSong( name, length ):
    ...

def main():
    ...

main()


Submission


  • Name your program hw5_2.py and submit it to Moodle, Section HW 5 PB 2.


Problem #3


Modify your previous program and make it accept several names as an input:

Example output


Who should we sing for? 
(you may enter several names separated by spaces)
> CARL Jorge GRU mINIONs
+--------------------------+
|  Happy birthday to you!  |
|  Happy birthday to you!  |
|Happy birthday, dear Carl!|
|  Happy birthday to you!  |
+--------------------------+

+---------------------------+
|   Happy birthday to you!  |
|   Happy birthday to you!  |
|Happy birthday, dear Jorge!|
|   Happy birthday to you!  |
+---------------------------+

+-------------------------+
|  Happy birthday to you! |
|  Happy birthday to you! |
|Happy birthday, dear Gru!|
|  Happy birthday to you! |
+-------------------------+

+-----------------------------+
|    Happy birthday to you!   |
|    Happy birthday to you!   |
|Happy birthday, dear Minions!|
|    Happy birthday to you!   |
+-----------------------------+


Requirements


  • Your program should have the same organization as that of your solution for Problem 2.


# Hw5_3.py
# Your name
# short description

def happyBirthday( length ):
    ...

def happyBirthdayDear( name, length ):
    ...

def singSong( name, length ):
    ...

def main():
    ...

main()


Submission


  • Name your program hw5_3.py and submit it to Moodle, Section HW 5 PB 3.


Problem #4


  • Variation on the same scheme. This time, your program will ask the user for the name of a text file that will always be located in the same directory where your program resides. This file will contain the names of several people, one per line. Your program will sing Happy Birthday to each one!
  • You may assume that the file will always be there, and that the user always spells it correctly. You may also assume that the file will always contain at least 1 name.
  • Note 1: you can easily create a text file with Idle. Just open a new window with Idle, enter 3 names on the top 3 lines, and save the file as "names.txt", for example. And voilà! You have a text file in your directory.
  • Note 2: I recommend that you read the contents of the file as a string, rather than a list of strings, and then .strip() this string to remove possible extra blank lines that you might have inadvertently created at the end of your text file.


Contents of File "names.txt"


Gru
Jorge


Example Output


File name? names.txt
+-------------------------+
|  Happy birthday to you! |
|  Happy birthday to you! |
|Happy birthday, dear Gru!|
|  Happy birthday to you! |
+-------------------------+

+---------------------------+
|   Happy birthday to you!  |
|   Happy birthday to you!  |
|Happy birthday, dear Jorge!|
|   Happy birthday to you!  |
+---------------------------+


Submission

  • Call your program hw5_4.py and submit it to the HW 5 PB 4 section on Moodle.


Problem #5


  • This time your program will ask for the names of two different text files.
  • The first one will contain some message, containing the string "zzzz". For example:


Congrats, zzzz, you are the best!


or


Happy birthday to you,
Happy birthday, dear zzzz!


  • The second file will contain a list of names, as in Problem 4. One name per line.
  • Your program will repeat the message stored in the first file for everybody whose name is in the name file.


Output Example 1


Example of Message File


Happy birthday to you,
Happy birthday, dear zzzz!


Example of Name File


Gru
Dave


Example of Output


Message file? message.txt
Name file? names.txt

Happy birthday to you,
Happy birthday, dear Gru!

Happy birthday to you,
Happy birthday, dear Dave!



Output Example 2


Example of Message File


Hip hip, Hurray!  zzzz is the best!


Example of Name File


Gru
Dave


Example of Output


Message file? message.txt
Name file? names.txt

Hip hip, Hurray!  Gru is the best!

Hip hip, Hurray!  Dave is the best!


Requirements


  • You are required to use a main() function, and you are free to create additional functions or not.


Submission


  • Name your program hw5_5.py, and submit it to the Moodle HW 5 PB 5 section.


Note


  • This program is a very simplified form of mail-merge, a technique where one creates a form letter with place holders for various strings, such as first name and last name, and an file containing the first names and last names of many people. The mail-merge program creates many different copies of the form letter, replacing the place-holders with the different first and last names found in the name file.


Problem 6


This problem was suggested by Dave Marshall, and combines nicely many Python constructs we have seen recently.

First Step


  • Write a program called hw5_6.py that asks the user for the names of two files, reads the contents of both text files, and then prints a series of messages on the screen.
  • The first file that your program should prompt the user for, will contain the information about several people, on several lines of text. Each line contains 3 words separated by spaces. The first word is the name of a person, the second is a thing related to that person (maybe something she ordered on line), and the third is a date (for example, a delivery date).


Example


Dave chocolate 02152015
Carl toothpaste 02152020
Gru superglue 01012100


  • The second file that your program will prompt the user for, contains one or several lines of text. This text is a message that will could be a message sent to the user, or maybe some text In the text, there will be three special keywords prefixed with a $-sign: "$name", "$stuff", and "$date".


Example


Dear $name, this is a coupon for
a ton of $stuff, which you can pick up
in Ford Hall 241 at any time before $date.  
If you do not claim the $stuff before $date, it will be put on sale on eBay.


  • Your program will read each line of the file containing user information, and will divide each line into separate words. This should be a list of 3 words. It will take the first word and will replace the string "$name" by this word in the message. Then it will take the second word and replace "$stuff" by this word in the message. Finally it will take the third word and replace "$date" by it. So, assuming that the first line of the user file is Dave chocolate 02152015, your program will output:


Dear Dave, this is a coupon for
a ton of chocolate, which you can pick up
in Ford Hall 241 at any time before 02152015.  
If you do not claim the chocolate before 02152015, it will be put on sale on eBay.


  • The full output of your program, if users.txt contains 3 lines of information for 3 different people (as shown above) would be:


Dear Dave, this is a coupon for
a ton of chocolate, which you can pick up
in Ford Hall 241 at any time before 02152015.  
If you do not claim the chocolate before 02152015, it will be put on sale on eBay.

Dear Carl, this is a coupon for
a ton of toothpaste, which you can pick up
in Ford Hall 241 at any time before 02152020.  
If you do not claim the toothpaste before 02152020, it will be put on sale on eBay.

Dear Gru, this is a coupon for
a ton of superglue, which you can pick up
in Ford Hall 241 at any time before 01012100.  
If you do not claim the superglue before 01012100, it will be put on sale on eBay.


Second Step


  • Modify your program so that it formats the date nicely, with the date formatted as dd mmm yyyy. For example, 02152020 would be printed as 15 Feb 2020.


Example of the output for Person Carl


Dear Carl, this is a coupon for
a ton of toothpaste, which you can pick up
in Ford Hall 241 at any time before 15 Feb 2020.  
If you do not claim the toothpaste before 15 Feb 2020, it will be put on sale on eBay.


Requirements


  • Your program should have at least a main() function that is the entry point of the program. You may include additional functions as well.
  • If the message file contains 3 lines of text, your program should also print 3 lines of text. Do not make your program

wrap lines, or output all 3 lines as a single line.

  • The number of blank lines printed along with the message is unimportant. Extra blank lines will be skipped by the testing script.


Submission


  • Submit your hw5_6.py program to the HW 5 PB 6 section on Moodle.




</showafterdate>
<showafterdate after="20150304 00:00" before="20150601 00:00">

Solution Programs


# --------------------------------------------------------------------------------------
# Solutions for Hw #5 PB #1
# D. Thiebaut


def happyBirthday():
    length = 50
    print( "|" + "Happy birthday to you!".center( length ) + "|" )

def happyBirthdayDear( name ):
    length = 50
    line = "Happy birthday, dear " + name +"!"
    print( "|" + line.center( length ) + "|"  )

def singSong( name ):
    length = 50
    bar = "+" + "-"*length + "+"
    print( bar )
    happyBirthday()
    happyBirthday()
    happyBirthdayDear( name )
    happyBirthday()
    print( bar )

def main():
    name = input( "Who should we sing for? " )
    singSong( name )

main()

# --------------------------------------------------------------------------------------
# Solutions for Hw #5 Problem 2
# D. Thiebaut


def happyBirthday( length ):
    print( "|" + "Happy birthday to you!".center( length ) + "|" )

def happyBirthdayDear( name, length ):
    line = "Happy birthday, dear " + name +"!"
    print( "|" + line.center( length ) + "|"  )

def singSong( name, length ):
    bar = "+" + "-"*length + "+"
    print( bar )
    happyBirthday( length )
    happyBirthday( length )
    happyBirthdayDear( name, length )
    happyBirthday( length )
    print( bar )

def main():
    name = input( "Who should we sing for? " )
    longLine = "Happy birthday, dear " + name + "!"
    length   = len( longLine )
    singSong( name, length )

main()

# --------------------------------------------------------------------------------------
# Solutions for Hw #5 Problem #3
# D. Thiebaut


def happyBirthday( length ):
    print( "|" + "Happy birthday to you!".center( length ) + "|" )

def happyBirthdayDear( name, length ):
    line = "Happy birthday, dear " + name +"!"
    print( "|" + line.center( length ) + "|"  )

def singSong( name, length ):
    bar = "+" + "-"*length + "+"
    print( bar )
    happyBirthday( length )
    happyBirthday( length )
    happyBirthdayDear( name, length )
    happyBirthday( length )
    print( bar )

def main():
    print( "Who should we sing for? " )
    names = input( "(you may enter several names separated by spaces)\n> " )

    for name in names.split( " " ):
        longLine = "Happy birthday, dear " + name + "!"
        length   = len( longLine )
        singSong( name, length )
        print()

main()

# --------------------------------------------------------------------------------------
# Solutions for Hw #5 Problem #4
# D. Thiebaut


def happyBirthday( length ):
    print( "|" + "Happy birthday to you!".center( length ) + "|" )

def happyBirthdayDear( name, length ):
    line = "Happy birthday, dear " + name +"!"
    print( "|" + line.center( length ) + "|"  )

def singSong( name, length ):
    bar = "+" + "-"*length + "+"
    print( bar )
    happyBirthday( length )
    happyBirthday( length )
    happyBirthdayDear( name, length )
    happyBirthday( length )
    print( bar )

def main():
    fileName = input( "File name? " )

    # get the contents of the file as a string.
    file = open( fileName, "r" )
    text = file.read()
    file.close

    # strip any blank lines that may be at the front
    # or end of the text.
    text = text.strip()

    
    for name in text.split( "\n" ):
        longLine = "Happy birthday, dear " + name + "!"
        length   = len( longLine )
        singSong( name, length )
        print()

main()

# --------------------------------------------------------------------------------------
# Solutions for Hw #5 Problem #5
# D. Thiebaut

def createMessageAndNameFiles():
    message1 = "Hip hip, Hurray! zzzz is the best!\n"
    file = open( "message.txt", "w" )
    file.write( message1 )
    file.close()

    names = "Gru\nDave\n"
    file = open( "names.txt", "w" )
    file.write( names )
    file.close()

def main():
    # create the 2 different files.  This is not really what
    # you had to do, but this way I don't have to create separate
    # files by hand...
    createMessageAndNameFiles()
    
    # get the names of the two files
    messageFile= input( "Message file name? " )
    namesFile  = input( "Name file?         " )
    
    
    # get the message 
    file = open( messageFile, "r" )
    text = file.read()
    file.close

    # get the names
    file = open( namesFile, "r" )
    names = file.read()
    file.close()

    # strip any blank lines that may be at the front
    # or end of the text.
    names = names.strip()
    names = names.split( "\n" )

    # iterate through the names and print the message
    # for each name found.
    for name in names:
        print( text.strip().replace( "zzzz", name ) )
        print()

main()

# --------------------------------------------------------------------------------------
# Solutions for Hw #5 Problem #5
# hw5_6.py
# D. Thiebaut


def mailMerge():
    """
    open( "users.txt", "w" ).write(
         "Dave chocolate 02152015\n"
        +"Carl toothpaste 02152020\n"
        +"Gru superglue 01012100\n" )
    open( "message.txt", "w" ).write(
         "Dear $name, this is a coupon for\n"
         +"a ton of $stuff, which you can pick up\n"
         +"in Ford Hall 241 at any time before $date.\n" )
    """
    months = ["dummy", "Jan", "Feb", "Mar", "Apr", "May",
              "Jun", "Jul", "Aug", "Sep", "Oct",
              "Nov", "Dec" ]
    message = open( "message2.txt", "r" ).read()
    users   = open( "users2.txt", "r" ).readlines()

    for user in users:
        words = user.split( ' ' )
        day   = int( words[2][2:4] )
        month = int( words[2][0:2] )
        month = months[ month ]
        year  = words[2][4:]
        date  = "{0:1} {1:1} {2:1}".format( day, month, year )
        date  = date.strip()
        userMessage = message.replace( "$name", words[0] )
        userMessage = userMessage.replace( "$stuff", words[1] )
        userMessage = userMessage.replace( "$date", date )
        print( userMessage )

       
def main():
    mailMerge()

main()

</showafterdate>


...