# Ryan Martin
# macid: abcd123
# student number: #######
# January 11, 2012
#
#
# This is a solution for Major Assignment 4 (Lab 10).

#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.

TIME_INDEX = 0
TEMP_INDEX = 1
HUMID_INDEX = 2
CLOUD_INDEX = 3
WIND_INDEX = 4

def isTempBetween(row, minTemp, maxTemp):
    '''Returns True if temperature of row is between the given temps, false otherwise.'''

    if minTemp < row[TEMP_INDEX] < maxTemp:
        return True
    else:
        return False

def isHumidityOver(row, percent):
    '''Returns True if the relative humidity of row is over the given amount, false otherwise.'''

    if row[HUMID_INDEX] > percent:
        return True
    else:
        return False

def isWindSpeedOver(row, speed):
    '''Returns True if the minimum wind speed of row is over the given speed, false otherwise.'''
    
    windData = row[WIND_INDEX]
    
    #Min speed is the first element in windData
    if windData[0] > speed:
        return True
    else:
        return False

def hailChance(data):
    '''Returns a list [chance, time] of the weather being hail, based on the simulation data.'''

    trueSims = 0
    hours = []
    cloud = 0.9
    minTemp = -10
    maxTemp = 0
    humidity = 0.9
    wind = 100

    for row in data:
        if isHumidityOver(row, humidity) and isTempBetween(row, minTemp, maxTemp) and isWindSpeedOver(row, wind):
            trueSims = trueSims + 1
            hours.append(row[TIME_INDEX])

    chance = float(trueSims) / len(data)
    time = float(sum(hours)) / len(hours)
    return [chance, time]

'''
Design Question

If the data set contains no rows, the hailChance function
will throw a ZeroDivisionError when we divide by len(data).

Since we know the specific name of the error, we could use a try-except
statement as follows:

try:
    ...
except ZeroDivisionError:
    ...

The code that follows the except statement would depend on the desired behaviour.
We could return 0, return None, or return a string containing a message, for example. 

'''
##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. AutoMarker uses the data that you see here
##Format: [funName, [arg1, arg2, ...]]
'''

Test Plan

data = [ [0, 1.5, 0.4, [0.3, 42], [5.1, 10.0] ], #avg=7.55	
		[10, 1.4, 0.3, [0.2, 41], [6.2, 8.6] ],	#avg=7.4
		[3, -5.1, 0.91, [0.83, 21], [107.1, 108.1] ], #Fog, snow, hail
		[12, -6.3, 0.94, [0.91, 30], [106.5, 109.4] ], #Fog, snow, hail
		[18, 20.5, 0.4, [0.3, 12000], [60.1, 162.4] ], #thunderstorm, avg=111.25
		[2, 20.4, 0.3, [0.2, 11000], [105.6, 118.7] ], #thunderstorm, avg=112.15
		[5, -9.2, 0.2, [0.93, 1001], [6.2, 6.3] ], #Freezing Rain
		[15, -2.4, 0.1, [0.95, 1981], [7.4, 8.5] ] ] #Freezing Rain	
data2 = []

     {'test1': 
       {'inputs': ['isHumidityOver',[data[0], 0.3]],
       'outputs': [True]},
       'test2':
       {'inputs': ['isHumidityOver',[data[1], 0.3]],
       'outputs': [False]},
       'test3':
       {'inputs': ['isWindSpeedOver',[data[1], 5.1]],
       'outputs': [True]},
       'test4':
       {'inputs': ['isWindSpeedOver',[data[0], 5.1]],
       'outputs': [False]},
       'test5':
       {'inputs': ['isTempBetween',[data[0], 1.4, 1.6]],
       'outputs': [True]},
       'test6':
       {'inputs': ['isTempBetween',[data[1], 1.5, 1.6]],
       'outputs': [False]},
       'test7':
       {'inputs': ['hailChance',[data]],
       'outputs': [[2.0/len(data), 7.5]]}
       'test8':
       {'inputs': ['hailChance',[data2]],
       'outputs': [ZeroDivisionError: float division by zero]}}

'''
#When an empty list is passed to our function (test8) a float division by zero
#error is generated.  
