# Ryan Martin
# macid: abcd123
# student number: #######
# November 7, 2011
#
#
# This is a solution for Major Assignment 4 (Lab 09) on Tuesday.

import time


#Constants for Database Entries.
#Not required in the assignment specification,
#but improves module flexibility by allowing
#the user to configure the module in the event
#that the database specification changes.

#Extra Study Topic: This set of values could be 
#elegantly implemented using an Enumeration type.

GENDER_INDEX = 0
BIRTH_YEAR_INDEX = 1
GRAD_YEAR_INDEX = 2
SESSIONAL_AVG_INDEX = 3

def isGraduated(row):
        '''Returns True if the student has graduated, False otherwise'''

        gradYear = row[GRAD_YEAR_INDEX]
        currentYear = time.localtime().tm_year

        #If the grad year is before or equal to this year,
        #the student has graduated.
        if gradYear <= currentYear:
                return True
        else:
                return False

def isBornInYear(row, year):
        '''Returns True if the student was born in the specified year, False otherwise'''

        birthYear = row[BIRTH_YEAR_INDEX]

        if birthYear == year:
                return True
        else:
                return False

def isCumulativeAvgAbove(row, grade):
        '''Returns True if the student's cumulative average is above grade, False otherwise'''

        sessionalAvgList = row[SESSIONAL_AVG_INDEX]
        cumulativeAvg = float(sum(sessionalAvgList)) / len(sessionalAvgList)

        if cumulativeAvg > grade:
                return True
        else:
                return False

def query(data, grade, year):
        '''Returns the percentage of graduated students born in year that have a cumulative average over grade.'''

        numerator = 0
        denominator = 0
        
        #The first row of data contains headings.
        #Only loop through the following rows.
        #Can also use: for i in range(1, len(data))
        for row in data[1:]:
                if isGraduated(row) and isBornInYear(row, year):
                        denominator = denominator + 1

                        if isCumulativeAvgAbove(row, grade):
                                numerator = numerator + 1

        result = 100 * float(numerator) / denominator
        return result

'''
Design Question

It is poor practice to directly compare two floats (if float a == float b).
Instead of directly comparing them, we should check that the relative
rifference of the two is within some error tolerance or accuracy:

if (float a - float b) < E

where E is the error tolerance.

We can set this E value either by storing it as a constant,
or by asking the user though input() or as a function argument.

'''
##Note: This format is used by our automated marking system.
##You will see that, for inputs, the function name is
##given with the corresponding inputs to that function
##listed right after it. Our automated marking system uses
##the variable data you see here.
'''

Testing Report

data = [ ["GENDER","BIRTH_YEAR","GRAD_YEAR","SESSIONAL_AVERAGES"],
		['F', 1988, 2010, [1.5,2.4,3.3,4.2,5.1] ],	#3.3
		['F', 1989, 2011, [1.4,2.3,3.2,4.1] ],		#2.75
		['F', 1990, 2012, [1.3,2.2,3.1] ],			#2.2
		['F', 1991, 2013, [1.2,2.1] ],				#1.65
		['M', 1988, 2010, [2.5,3.4,4.3,5.2,6.1] ],	#4.3
		['M', 1989, 2011, [2.4,3.3,4.2,5.1] ],		#3.75
		['M', 1990, 2012, [2.3,3.2,4.1] ],			#3.2
		['M', 1991, 2013, [2.2,3.1] ] ]				#2.65

TUE = {'test1': 
       {'inputs': ['isGraduated',[data[1]]],
       'outputs': [True]},
       'test2':
       {'inputs': ['isGraduated',[data[3]]],
       'outputs': [False]},
       'test3':
       {'inputs': ['isBornInYear',[data[1], 1988]],
       'outputs': [True]},
       'test4':
       {'inputs': ['isBornInYear',[data[1], 1989]],
       'outputs': [False]},
       'test5':
       {'inputs': ['isCumulativeAvgAbove',[data[1], 3]],
       'outputs': [True]},
       'test6':
       {'inputs': ['isCumulativeAvgAbove',[data[1], 3.5]],
       'outputs': [False]},
       'test7':
       {'inputs': ['query',[data, 4.0, 1988]],
       'outputs': [50.0]}}

'''