diff --git a/students/Lucas_See/Week 10/Asignment10.py b/students/Lucas_See/Week 10/Asignment10.py new file mode 100644 index 0000000..67d82d3 --- /dev/null +++ b/students/Lucas_See/Week 10/Asignment10.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jun 22 13:40:49 2018 + +@author: seelc +""" + +'''Exploring option 1 in activity 10: Using memoization to improve code performance, +will try both an object oriented and non object oriented memoization approach and +compare performance between versions''' + + +from timeit import default_timer as timer +from great_circle_v0 import great_circle +import logging + +logging.basicConfig(level=logging.INFO) + +def memoize(input_func): + + '''memoize function to keep track of of values returned from input_func''' + + func_list = {} + def helper(i): + + '''helper func to check if call of input_func returns a value currently + in func_list, if not returns''' + + if i not in func_list: + func_list[i] = input_func(i) + return func_list[i] + + return helper + + +def exponential_series(n): + + '''Recrusive power series that takes the intial n value and calls the + function recursively n-1 times''' + + if n == 1: + return 1 + else: + return exponential_series(n - 1) * 2 + + +class oop_memoize: + + '''Object oriented memoization approach''' + + def __init__(self, function): + self.function = function + self.call_storage = {} + + def __call__(self, *args): + + '''Using same approach as in "helper" function, adding function values + to storage if not already in it''' + + if args not in self.call_storage: + self.call_storage[args] = self.function(*args) + return self.call_storage[args] + +@oop_memoize +def oop_exponential_series(n): + + '''Same as "exponential_series", just adding decorator''' + + if n == 1: + return 1 + else: + return oop_exponential_series(n - 1) * 2 + + +if __name__ == "__main__": + iterations = 10 + + #calling and timing exponential function without a helper + start1 = timer() + my_func = exponential_series(iterations) + end1 = timer() + run_time_base = end1 - start1 + + start2 = timer() + my_func = memoize(exponential_series(iterations)) + end2 = timer() + run_time_memoize = end2 - start2 + + difference = run_time_memoize/run_time_base + print("Percentage run time reduction, memoization: ", difference) + #Strategy results in almost a halving of the total run time with iterations = 20 + + #Now testing object oriented memoiation method + start3 = timer() + my_oop_func = oop_exponential_series(iterations) + end3 = timer() + run_time_oop_memoize = end3 - start3 + difference2 = run_time_oop_memoize/run_time_base + print("Percentage run time reduction oop memoize: ", difference2) + + '''For some reasone the object oriented memoization method is taking significantly + longer than the base version, not sure if this is because theres some overhead + associated with setting up a class or if I made an error in my code''' + \ No newline at end of file diff --git a/students/Lucas_See/Week 6/.gitignore b/students/Lucas_See/Week 6/.gitignore new file mode 100644 index 0000000..a5bd672 --- /dev/null +++ b/students/Lucas_See/Week 6/.gitignore @@ -0,0 +1,5 @@ +.idea/ +*pychache* +*.pyc +.coverage +htmlcov \ No newline at end of file diff --git a/students/Lucas_See/Week 6/Activity/.gitignore b/students/Lucas_See/Week 6/Activity/.gitignore new file mode 100644 index 0000000..260e662 --- /dev/null +++ b/students/Lucas_See/Week 6/Activity/.gitignore @@ -0,0 +1,3 @@ +*pycache* +htmlcov/ +.coverage diff --git a/students/Lucas_See/Week 6/Activity/Pylint output.txt b/students/Lucas_See/Week 6/Activity/Pylint output.txt new file mode 100644 index 0000000..504ef0a --- /dev/null +++ b/students/Lucas_See/Week 6/Activity/Pylint output.txt @@ -0,0 +1,158 @@ +Using config file C:\Users\seelc\OneDrive\Desktop\Python\Advanced Python\calculator-master-619cb7598cd1df17c3c52d254c52ace08e0b3312\.pylintrc +************* Module integration-test +C0303: 11,0: : Trailing whitespace +C0303: 19,0: : Trailing whitespace +C0303: 31,0: : Trailing whitespace +C0103: 1,0: : Module name "integration-test" doesn't conform to '(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$' pattern +C0111: 1,0: : Missing module docstring +R0201: 15,4: ModuleTests.test_module: Method could be a function +C0103: 33,4: : Constant name "my_module" doesn't conform to '(([A-Z_][A-Z0-9_]*)|(__.*__))$' pattern + + + +Report + +====== + +22 statements analysed. + + + +Statistics by type + +------------------ + + ++---------+-------+-----------+-----------+------------+---------+ +|type |number |old number |difference |%documented |%badname | ++=========+=======+===========+===========+============+=========+ +|module |1 |1 |= |0.00 |100.00 | ++---------+-------+-----------+-----------+------------+---------+ +|class |1 |1 |= |100.00 |0.00 | ++---------+-------+-----------+-----------+------------+---------+ +|method |1 |1 |= |100.00 |0.00 | ++---------+-------+-----------+-----------+------------+---------+ +|function |0 |0 |= |0 |0 | ++---------+-------+-----------+-----------+------------+---------+ + + + + + + +External dependencies + +--------------------- + +:: + + + calculator + + \-adder (integration-test) + + \-calculator (integration-test) + + \-divider (integration-test) + + \-multiplier (integration-test) + + \-subtracter (integration-test) + + + + + + + +Raw metrics + +----------- + + ++----------+-------+------+---------+-----------+ +|type |number |% |previous |difference | ++==========+=======+======+=========+===========+ +|code |24 |66.67 |24 |= | ++----------+-------+------+---------+-----------+ +|docstring |4 |11.11 |4 |= | ++----------+-------+------+---------+-----------+ +|comment |0 |0.00 |0 |= | ++----------+-------+------+---------+-----------+ +|empty |8 |22.22 |8 |= | ++----------+-------+------+---------+-----------+ + + + + + + +Duplication + +----------- + + ++-------------------------+------+---------+-----------+ +| |now |previous |difference | ++=========================+======+=========+===========+ +|nb duplicated lines |0 |0 |= | ++-------------------------+------+---------+-----------+ +|percent duplicated lines |0.000 |0.000 |= | ++-------------------------+------+---------+-----------+ + + + + + + +Messages by category + +-------------------- + + ++-----------+-------+---------+-----------+ +|type |number |previous |difference | ++===========+=======+=========+===========+ +|convention |6 |6 |= | ++-----------+-------+---------+-----------+ +|refactor |1 |1 |= | ++-----------+-------+---------+-----------+ +|warning |0 |0 |= | ++-----------+-------+---------+-----------+ +|error |0 |0 |= | ++-----------+-------+---------+-----------+ + + + + + + +Messages + +-------- + + ++--------------------+------------+ +|message id |occurrences | ++====================+============+ +|trailing-whitespace |3 | ++--------------------+------------+ +|invalid-name |2 | ++--------------------+------------+ +|no-self-use |1 | ++--------------------+------------+ +|missing-docstring |1 | ++--------------------+------------+ + + + + + + + +------------------------------------------------------------------ + +Your code has been rated at 6.82/10 (previous run: 6.82/10, +0.00) + + + diff --git a/students/Lucas_See/Week 6/Activity/README.md b/students/Lucas_See/Week 6/Activity/README.md new file mode 100644 index 0000000..af9b79d --- /dev/null +++ b/students/Lucas_See/Week 6/Activity/README.md @@ -0,0 +1,32 @@ +# Calculator + +## Instructions + +Your assignment is to complete testing and linting on the calculator from the lesson videos. + +There's one new addition since the videos: I've separated the unit tests and the integration tests into two separate test files. + +## Your Goals + +1. `python -m unittest integration-test` should have no failures. Don't edit integration-test.py, edit your code to make it pass. +2. Add unit tests to unit-test.py such that `coverage run --source=calculator -m unittest unit-test; coverage report` shows 100% coverage. +3. All of the tests in unit-test.py should pass. +4. Satisfy the linter such that `pylint calculator` gives no errors and `flake8 calculator` gives no errors. See (PEP257)[https://www.python.org/dev/peps/pep-0257/] if you'd like more information about docstrings. There are quite a few docstrings to add, but for this exercise you don't need to get too creative: """ this method adds two numbers """ is sufficient. + +## Bonus goal +One of our specs for calculator says the following: + +``` +The add, subtract, multiply, and divide methods shall both: +Return the result of the operation +Enter the result of the operation back into the calculator +This makes it possible to perform the following sequences of operations: + calculator.enter_number(2) + calculator.enter_number(3) + calculator.add() # Returns 5, and now 5 is now 'in the calculator' + calculator.enter_number(1) + calculator.subtract() # Returns 4 because 5 - 1 = 4 +``` + +This feature is tested by our integration test, but it is not tested in our unit tests. Because this sequencing of operations is a defined feature of calculator, it would definitely be appropriate to test in the unit-test.CalculatorTests. Your bonus goal is to use *MagicMock* to test this method sequencing feature in isolation. Ie: without relying on the correctness of any particular operator we use to initialize our calculator. + diff --git a/students/Lucas_See/Week 6/Activity/ads b/students/Lucas_See/Week 6/Activity/ads new file mode 100644 index 0000000..8bd6648 --- /dev/null +++ b/students/Lucas_See/Week 6/Activity/ads @@ -0,0 +1 @@ +asdf diff --git a/students/Lucas_See/Week 6/Activity/calc unit-test.py b/students/Lucas_See/Week 6/Activity/calc unit-test.py new file mode 100644 index 0000000..b5fa20b --- /dev/null +++ b/students/Lucas_See/Week 6/Activity/calc unit-test.py @@ -0,0 +1,89 @@ +import unittest +from unittest.mock import MagicMock + +from calculator.adder import Adder +from calculator.subtracter import Subtracter +from calculator.multiplier import Multiplier +from calculator.divider import Divider +from calculator.calculator import Calculator +from calculator.exceptions import InsufficientOperands + +class AdderTests(unittest.TestCase): + + def test_adding(self): + adder = Adder() + + for i in range(-10, 10): + for j in range(-10, 10): + self.assertEqual(i + j, adder.calc(i, j)) + + +class SubtracterTests(unittest.TestCase): + + def test_subtracting(self): + subtracter = Subtracter() + + for i in range(-10, 10): + for j in range(-10, 10): + self.assertEqual(i - j, subtracter.calc(i, j)) + +class MultiplierTests(unittest.TestCase): + + def test_multiplying(self): + multiplier = Multiplier() + for i in range(-10, 10): + for j in range(-10, 10): + self.assertEqual(i * j, multiplier.calc(i, j)) + + +class DividerTests(unittest.TestCase): + + def test_divider(self): + divider = Divider() + for i in range(-10, 10): + for j in range(-10, 10): + if i != 0 and j != 0: + self.assertEqual(i / j, divider.calc(i, j)) + + +class CalculatorTests(unittest.TestCase): + + def setUp(self): + self.adder = Adder() + self.subtracter = Subtracter() + self.multiplier = Multiplier() + self.divider = Divider() + + self.calculator = Calculator(self.adder, self.subtracter, self.multiplier, self.divider) + + def test_insufficient_operands(self): + self.calculator.enter_number(0) + + with self.assertRaises(InsufficientOperands): + self.calculator.add() + + def test_adder_call(self): + self.adder.calc = MagicMock(return_value=0) + + self.calculator.enter_number(1) + self.calculator.enter_number(2) + self.calculator.add() + + self.adder.calc.assert_called_with(1, 2) + + def test_subtracter_call(self): + self.subtracter.calc = MagicMock(return_value=0) + + self.calculator.enter_number(1) + self.calculator.enter_number(2) + self.calculator.subtract() + + self.subtracter.calc.assert_called_with(1, 2) + + +if __name__ == "__main__": + unittest.main() + + + + diff --git a/students/Lucas_See/Week 6/Activity/calculator/adder.py b/students/Lucas_See/Week 6/Activity/calculator/adder.py new file mode 100644 index 0000000..a8c3bcf --- /dev/null +++ b/students/Lucas_See/Week 6/Activity/calculator/adder.py @@ -0,0 +1,5 @@ +class Adder(object): + + @staticmethod + def calc(operand_1, operand_2): + return operand_1 + operand_2 diff --git a/students/Lucas_See/Week 6/Activity/calculator/calculator.py b/students/Lucas_See/Week 6/Activity/calculator/calculator.py new file mode 100644 index 0000000..1434d8a --- /dev/null +++ b/students/Lucas_See/Week 6/Activity/calculator/calculator.py @@ -0,0 +1,35 @@ +from .exceptions import InsufficientOperands + +class Calculator(object): + + def __init__(self, adder, subtracter, multiplier, divider): + self.adder = adder + self.subtracter = subtracter + self.multiplier = multiplier + self.divider = divider + + self.stack = [] + + def enter_number(self, number): + self.stack.insert(0, number) + + def _do_calc(self, operator): + try: + result = operator.calc(self.stack[0], self.stack[1]) + except IndexError: + raise InsufficientOperands + + self.stack = [result] + return result + + def add(self): + return self._do_calc(self.adder) + + def subtract(self): + return self._do_calc(self.subtracter) + + def multiply(self): + return self._do_calc(self.multiplier) + + def divide(self): + return self._do_calc(self.divider) diff --git a/students/Lucas_See/Week 6/Activity/calculator/divider.py b/students/Lucas_See/Week 6/Activity/calculator/divider.py new file mode 100644 index 0000000..9700aac --- /dev/null +++ b/students/Lucas_See/Week 6/Activity/calculator/divider.py @@ -0,0 +1,5 @@ +class Divider(object): + + @staticmethod + def calc(operand_1, operand_2): + return operand_2/operand_1 diff --git a/students/Lucas_See/Week 6/Activity/calculator/exceptions.py b/students/Lucas_See/Week 6/Activity/calculator/exceptions.py new file mode 100644 index 0000000..f1602cf --- /dev/null +++ b/students/Lucas_See/Week 6/Activity/calculator/exceptions.py @@ -0,0 +1,2 @@ +class InsufficientOperands(Exception): + pass diff --git a/students/Lucas_See/Week 6/Activity/calculator/multiplier.py b/students/Lucas_See/Week 6/Activity/calculator/multiplier.py new file mode 100644 index 0000000..9aa566e --- /dev/null +++ b/students/Lucas_See/Week 6/Activity/calculator/multiplier.py @@ -0,0 +1,5 @@ +class Multiplier(object): + + @staticmethod + def calc(operand_1, operand_2): + return operand_1*operand_2 diff --git a/students/Lucas_See/Week 6/Activity/calculator/subtracter.py b/students/Lucas_See/Week 6/Activity/calculator/subtracter.py new file mode 100644 index 0000000..7e05c5b --- /dev/null +++ b/students/Lucas_See/Week 6/Activity/calculator/subtracter.py @@ -0,0 +1,10 @@ +""" +This module provides a subtraction operator +""" + + +class Subtracter(object): + + @staticmethod + def calc(operand_1, operand_2): + return operand_2 - operand_1 diff --git a/students/Lucas_See/Week 6/Activity/integration-test.py b/students/Lucas_See/Week 6/Activity/integration-test.py new file mode 100644 index 0000000..7d335c8 --- /dev/null +++ b/students/Lucas_See/Week 6/Activity/integration-test.py @@ -0,0 +1,34 @@ +import unittest +from calculator.adder import Adder +from calculator.subtracter import Subtracter +from calculator.multiplier import Multiplier +from calculator.divider import Divider +from calculator.calculator import Calculator + + + +class ModuleTests(unittest.TestCase): + + '''Integration test: In order to get it to pass, had to swap places of operand_1 + and operand_2 in divider and subrtractor module''' + + def test_module(self): + + '''Runs the actual integration test, going through all calculator calls + and testing the end result''' + + calculator = Calculator(Adder(), Subtracter(), Multiplier(), Divider()) + calculator.enter_number(5) + calculator.enter_number(2) + calculator.multiply() + calculator.enter_number(46) + calculator.add() + calculator.enter_number(8) + calculator.divide() + calculator.enter_number(1) + result = calculator.subtract() + assert result == 6 + +if __name__ == "__main__": + my_module = ModuleTests() + my_module.test_module() diff --git a/students/Lucas_See/Week 6/Activity/unit-test.py b/students/Lucas_See/Week 6/Activity/unit-test.py new file mode 100644 index 0000000..059f66e --- /dev/null +++ b/students/Lucas_See/Week 6/Activity/unit-test.py @@ -0,0 +1,65 @@ +from unittest import TestCase +from unittest.mock import MagicMock + +from calculator.adder import Adder +from calculator.subtracter import Subtracter +from calculator.multiplier import Multiplier +from calculator.divider import Divider +from calculator.calculator import Calculator +from calculator.exceptions import InsufficientOperands + +class AdderTests(TestCase): + + def test_adding(self): + adder = Adder() + + for i in range(-10, 10): + for j in range(-10, 10): + self.assertEqual(i + j, adder.calc(i, j)) + + +class SubtracterTests(TestCase): + + def test_subtracting(self): + subtracter = Subtracter() + + for i in range(-10, 10): + for j in range(-10, 10): + self.assertEqual(i - j, subtracter.calc(i, j)) + + +class CalculatorTests(TestCase): + + def setUp(self): + self.adder = Adder() + self.subtracter = Subtracter() + self.multiplier = Multiplier() + self.divider = Divider() + + self.calculator = Calculator(self.adder, self.subtracter, self.multiplier, self.divider) + + def test_insufficient_operands(self): + self.calculator.enter_number(0) + + with self.assertRaises(InsufficientOperands): + self.calculator.add() + + def test_adder_call(self): + self.adder.calc = MagicMock(return_value=0) + + self.calculator.enter_number(1) + self.calculator.enter_number(2) + self.calculator.add() + + self.adder.calc.assert_called_with(1, 2) + + def test_subtracter_call(self): + self.subtracter.calc = MagicMock(return_value=0) + + self.calculator.enter_number(1) + self.calculator.enter_number(2) + self.calculator.subtract() + + self.subtracter.calc.assert_called_with(1, 2) + + diff --git a/students/Lucas_See/Week 6/Pump/__init__.py b/students/Lucas_See/Week 6/Pump/__init__.py new file mode 100644 index 0000000..5e2ea93 --- /dev/null +++ b/students/Lucas_See/Week 6/Pump/__init__.py @@ -0,0 +1 @@ +from .pump import Pump \ No newline at end of file diff --git a/students/Lucas_See/Week 6/Pump/__pycache__/__init__.cpython-36.pyc b/students/Lucas_See/Week 6/Pump/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..31c83fb Binary files /dev/null and b/students/Lucas_See/Week 6/Pump/__pycache__/__init__.cpython-36.pyc differ diff --git a/students/Lucas_See/Week 6/Pump/__pycache__/pump.cpython-36.pyc b/students/Lucas_See/Week 6/Pump/__pycache__/pump.cpython-36.pyc new file mode 100644 index 0000000..7de425d Binary files /dev/null and b/students/Lucas_See/Week 6/Pump/__pycache__/pump.cpython-36.pyc differ diff --git a/students/Lucas_See/Week 6/Pump/pump.py b/students/Lucas_See/Week 6/Pump/pump.py new file mode 100644 index 0000000..c869007 --- /dev/null +++ b/students/Lucas_See/Week 6/Pump/pump.py @@ -0,0 +1,53 @@ +""" +Encapsulates the connection to an HTTP pump controller. +""" + +import urllib.request +import urllib.error + + +class Pump(object): + """ + Encapsulates the connection to an HTTP pump controller + """ + + PUMP_IN = 1 + PUMP_OFF = 0 + PUMP_OUT = -1 + + def __init__(self, address, port): + """ + Create a connection to a pump controller. + + :param address: the address of the pump controller + :param port: the port number of the pump controller + """ + + self.address = address + self.port = port + + def set_state(self, state): + """ + Set the state of the remote pump. + + :param state: One of PUMP_IN, PUMP_OFF, PUMP_OUT + :return: True if the remote pump controller acknowledges the request, otherwise False + """ + + request = urllib.request.Request(self.address + ':' + self.port, state) + + try: + urllib.request.urlopen(request) + except urllib.error.HTTPError: + return False + + return True + + def get_state(self): + """ + Get the state of the remote pump. + + :return: One of PUMP_IN, PUMP_OFF, PUMP_OUT + """ + response = urllib.request.urlopen(self.address + ":" + self.port) + return int(response.read) diff --git a/students/Lucas_See/Week 6/README.md b/students/Lucas_See/Week 6/README.md new file mode 100644 index 0000000..ffed363 --- /dev/null +++ b/students/Lucas_See/Week 6/README.md @@ -0,0 +1,16 @@ +# Water Regulation + +## Instructions + +Your assignment is to complete, test, and lint this project. Begin by forking a copy of this project to yourself, and then cloning down to your computer. + +## Your Goals + +Note that all of the command examples below should be run from the project root which contains the directories waterregulation, sensor, and pump. + +1. Complete the TODOs in *waterregulation/controller.py* and *waterregulation/decider.py*. +2. Complete the TODOs in *waterregulation/test.py* and *waterregulation/integrationtest.py*. A single integration test may be sufficient. However, your unit tests in *test.py* should include at least one test for each specified behavior. +3. `python -m unittest waterregulation/test.py` and `python -m unittest waterregulation/integrationtest.py` should have no failures. +4. Running `coverage run --source=waterregulation/controller.py,waterregulation/decider.py -m unittest waterregulation/test.py; coverage report` shows 90%+ coverage.. +5. Satisfy the linter such that `pylint waterregulation` gives no errors and and `flake8 waterregulation` gives no errors. You may have to add docstrings to your test files. + diff --git a/students/Lucas_See/Week 6/Sensor/__init__.py b/students/Lucas_See/Week 6/Sensor/__init__.py new file mode 100644 index 0000000..cc5bac5 --- /dev/null +++ b/students/Lucas_See/Week 6/Sensor/__init__.py @@ -0,0 +1 @@ +from .sensor import Sensor \ No newline at end of file diff --git a/students/Lucas_See/Week 6/Sensor/__pycache__/__init__.cpython-36.pyc b/students/Lucas_See/Week 6/Sensor/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..baf757a Binary files /dev/null and b/students/Lucas_See/Week 6/Sensor/__pycache__/__init__.cpython-36.pyc differ diff --git a/students/Lucas_See/Week 6/Sensor/__pycache__/sensor.cpython-36.pyc b/students/Lucas_See/Week 6/Sensor/__pycache__/sensor.cpython-36.pyc new file mode 100644 index 0000000..56d3d4d Binary files /dev/null and b/students/Lucas_See/Week 6/Sensor/__pycache__/sensor.cpython-36.pyc differ diff --git a/students/Lucas_See/Week 6/Sensor/sensor.py b/students/Lucas_See/Week 6/Sensor/sensor.py new file mode 100644 index 0000000..eade898 --- /dev/null +++ b/students/Lucas_See/Week 6/Sensor/sensor.py @@ -0,0 +1,31 @@ +""" +Encapsulates the connection to an HTTP liquid height sensor controller. +""" + +import urllib.request + + +class Sensor(object): + """ + Encapsulates the connection to an HTTP liquid height sensor controller + """ + + def __init__(self, address, port): + """ + Create a connection to a liquid height sensor controller. + + :param address: the address of the liquid height sensor controller + :param port: the port number of the liquid height sensor controller + """ + + self.address = address + self.port = port + + def measure(self): + """ + Set the state of the remote liquid height sensor. + + :return: True if the remote liquid height sensor controller acknowledges the request, otherwise False + """ + response = urllib.request.urlopen(self.address + ":" + self.port) + return float(response.read) diff --git a/students/Lucas_See/Week 6/__pycache__/controller.cpython-36.pyc b/students/Lucas_See/Week 6/__pycache__/controller.cpython-36.pyc new file mode 100644 index 0000000..165a849 Binary files /dev/null and b/students/Lucas_See/Week 6/__pycache__/controller.cpython-36.pyc differ diff --git a/students/Lucas_See/Week 6/__pycache__/decider.cpython-36.pyc b/students/Lucas_See/Week 6/__pycache__/decider.cpython-36.pyc new file mode 100644 index 0000000..8184215 Binary files /dev/null and b/students/Lucas_See/Week 6/__pycache__/decider.cpython-36.pyc differ diff --git a/students/Lucas_See/Week 6/waterregulation/controller.py b/students/Lucas_See/Week 6/waterregulation/controller.py new file mode 100644 index 0000000..79453b0 --- /dev/null +++ b/students/Lucas_See/Week 6/waterregulation/controller.py @@ -0,0 +1,59 @@ +""" +Encapsulates command and coordination for the water-regulation module +""" + + +class Controller(object): + """ + Encapsulates command and coordination for the water-regulation module + """ + + def __init__(self, sensor, pump, decider): + """ + Create a new controller + + :param sensor: Typically an instance of sensor.Sensor + :param pump: Typically an instance of pump.Pump + :param decider: Typically an instance of decider.Decider + """ + + self.sensor = sensor + self.pump = pump + self.decider = decider + + self.actions = { + 'PUMP_IN': pump.PUMP_IN, + 'PUMP_OUT': pump.PUMP_OUT, + 'PUMP_OFF': pump.PUMP_OFF, + } + + def tick(self): + """ + On each call to tick, the controller shall: + + 1. query the sensor for the current height of liquid in the tank + 2. query the pump for its current state (pumping in, pumping out, or at rest) + 3. query the decider for the next appropriate state of the pump, given the above + 4. set the pump to that new state + + :return: True if the pump has acknowledged its new state, else False + """ + + #Task (1), query sensor + current_height = self.sensor.measure() + + #Task (2), query pump for current state + current_state = self.pump.get_state() + + #Task (3), query decider + next_state = self.decider.decide(current_height, current_state, self.actions) + + #Task (4), using decider to set pump to appropriate state + self.pump.set_state(next_state) + + + + + # TODO: Implement the above-defined behaviors + + return None diff --git a/students/Lucas_See/Week 6/waterregulation/decider.py b/students/Lucas_See/Week 6/waterregulation/decider.py new file mode 100644 index 0000000..3be253d --- /dev/null +++ b/students/Lucas_See/Week 6/waterregulation/decider.py @@ -0,0 +1,92 @@ +""" +Encapsulates decision making in the water-regulation module +""" + + +class Decider(object): + """ + Encapsulates decision making in the water-regulation module + """ + + def __init__(self, target_height, margin): + """ + Create a new decider instance for this tank. + + :param target_height: the target height for liquid in this tank + :param margin: the margin of liquid above and below the target height for + which the pump should not turn on. Ex: .05 represents a + 5% margin above and below the target_height. + """ + self.target_height = target_height + self.margin = margin + + def decide(self, current_height, current_action, actions): + """ + Decide a new action for the pump, given the current height of liquid in the + tank and the current action of the pump. + + Note that the new action for the pump MAY be the same as the current action + of the pump. + + The *decide* method shall obey the following behaviors: + + 1. If the pump is off and the height is below the margin region, then the + pump should be turned to PUMP_IN. + 2. If the pump is off and the height is above the margin region, then the + pump should be turned to PUMP_OUT. + 3. If the pump is off and the height is within the margin region or on + the exact boundary of the margin region, then the pump shall remain at + PUMP_OFF. + 4. If the pump is performing PUMP_IN and the height is above the target + height, then the pump shall be turned to PUMP_OFF, otherwise the pump + shall remain at PUMP_IN. + 5. If the pump is performing PUMP_OUT and the height is below the target + height, then the pump shall be turned to PUMP_OFF, otherwise, the pump + shall remain at PUMP_OUT. + + + + :param current_height: the current height of liquid in the tank + :param current_action: the current action of the pump + :param actions: a dictionary containing the keys 'PUMP_IN', 'PUMP_OFF', + and 'PUMP_OUT' + :return: The new action for the pump: one of actions['PUMP_IN'], actions['PUMP_OUT'], actions['PUMP_OFF'] + """ + print('current action ', current_action) + print('current_height ', current_height) + print('target_height ', self.target_height) + #Task (1) + if current_action == 0 and current_height < self.target_height - self.margin: + return actions['PUMP_IN'] + print(1) + + #Task (2) + if current_action == 0 and current_height > self.target_height + self.margin: + return actions['PUMP_OUT'] + print(2) + + #task (3) + if current_action == 0 and self.target_height - self.margin <= current_height and current_height<= self.target_height + self.margin: + return actions['PUMP_OFF'] + print(3) + + #task (4) + if current_action == 1 and current_height >= self.target_height: + return actions['PUMP_OFF'] + print(4) + elif current_action == 1 and current_height < self.target_height: + return actions['PUMP_IN'] + print(5) + + + #Task 5 + if current_action == -1 and current_height < self.target_height: + return actions['PUMP_OFF'] + print(6) + elif current_action == -1: + return actions['PUMP_OUT'] + print(7) + + + #return actions['PUMP_IN'] + diff --git a/students/Lucas_See/Week 6/waterregulation/integrationtest.py b/students/Lucas_See/Week 6/waterregulation/integrationtest.py new file mode 100644 index 0000000..57b564f --- /dev/null +++ b/students/Lucas_See/Week 6/waterregulation/integrationtest.py @@ -0,0 +1,81 @@ +""" +Module tests for the water-regulation module +""" + +import logging +import os +import unittest +from unittest.mock import MagicMock +os.chdir('C:\\Users\\seelc\\OneDrive\\Desktop\\Python\\Advanced Python\\water-regulation-master') +#from pump import Pump +#from sensor import Sensor + +os.chdir('C:\\Users\\seelc\\OneDrive\\Desktop\\Python\\Advanced Python\\water-regulation-master//waterregulation') +from controller import Controller +from decider import Decider + +os.chdir('C:\\Users\\seelc\\OneDrive\\Desktop\\Python\\Advanced Python\\water-regulation-master') +logger = logging.getLogger('test_application') +logging.basicConfig(level=logging.DEBUG, + format=('%(filename)s: ' + '%(levelname)s: ' + '%(funcName)s(): ' + '%(lineno)d:\t' + '%(message)s') + ) + +class ModuleTests(unittest.TestCase): + """ + Module tests for the water-regulation module + """ + + # TODO: write an integration test that combines controller and decider, + # using a MOCKED sensor and pump. + + + Pump = MagicMock() + Pump_methods = {'set_state.return_value': True, 'get_state.return_value': 1} + Pump.configure_mock(**Pump_methods) + Pump = Pump + logger.info(Pump.set_State(), Pump.get_State()) + print(Pump.set_state()) + print(Pump.get_state()) + + + logger.info("Creating mocked sensor") + Sensor = MagicMock() + Sensor_methods = {'measure.return_value': 4.0} + Sensor.configure_mock(**Sensor_methods) + Sensor = Sensor + print(Sensor.measure()) + + def integration_test(self): + + '''Uses mocked Sensor and Pump as inputs into a decider and controller, + tests controller output for given inputs''' + + '''For the integration check we are only checking the end result of our function calls, + in this case the ability to correctly return true from teh set_state function with + the previous functions as inputs''' + + actions = {'PUMP_IN' : 1, + 'PUMP_OFF' : 0, + 'PUMP_OUT' : -1 + } + logger.info("Begininning integration_test") + my_decider = Decider(5.0,1) + my_controller = Controller(self.Sensor, self.Pump, my_decider) + print(my_decider.decide(self.Sensor.measure(), self.Pump.get_state, actions)) + + #Tick Behavior 4 + assert my_controller.Pump.set_state(my_decider.decide(self.Sensor.measure(), + self.Pump.get_state, actions)) == True + #Final check + assert my_controller.tick() == None + + + #pass + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/students/Lucas_See/Week 6/waterregulation/test.py b/students/Lucas_See/Week 6/waterregulation/test.py new file mode 100644 index 0000000..d3c73aa --- /dev/null +++ b/students/Lucas_See/Week 6/waterregulation/test.py @@ -0,0 +1,245 @@ +""" +Unit tests for the water-regulation module +""" +import os +os.chdir('C:\\Users\\seelc\\OneDrive\\Desktop\\Python\\Advanced Python\\water-regulation-master') +import logging + +import unittest +from unittest.mock import MagicMock + +from pump import Pump +from sensor import Sensor +os.chdir('C:\\Users\\seelc\\OneDrive\\Desktop\\Python\\Advanced Python\\water-regulation-master//waterregulation') +import logging +from controller import Controller +from decider import Decider + +os.chdir('C:\\Users\\seelc\\OneDrive\\Desktop\\Python\\Advanced Python\\water-regulation-master') +logger = logging.getLogger('test_application') +logging.basicConfig(level=logging.DEBUG, + format=('%(filename)s: ' + '%(levelname)s: ' + '%(funcName)s(): ' + '%(lineno)d:\t' + '%(message)s') + ) + + + +class DeciderTests(unittest.TestCase): + """ + Unit tests for the Decider class + """ + + # TODO: write a test or tests for each of the behaviors defined for + # Decider.decide + + + def first_decider_test(self, address, port): + + '''Behavior Test: If the pump is off and the height is below the margin region, then the + pump should be turned to PUMP_IN.''' + + logger.info("running first decider test") + my_pump = Pump(address, address) + my_sensor = Sensor(address, port) + my_decider = Decider(10,1) + my_controller = Controller(my_sensor, my_pump, my_decider) + try: + self.assertequals(my_decider.decide(5, 0, my_controller.actions), 1) + except Exception as exception: + logger.critical(exception) + + + def second_decider_test(self, address, port): + + '''Behavior Test: If the pump is off and the height is above the margin region, then the + pump should be turned to PUMP_OUT.''' + + logger.info("running second decider test") + my_pump = Pump(address, port) + my_sensor = Sensor(address, port) + my_decider = Decider(10,1) + my_controller = Controller(my_sensor, my_pump, my_decider) + try: + self.assertequals(my_decider.decide(12, 0, my_controller.actions), -1) + except Exception as exception: + logger.critical(exception) + + + def third_decider_test(self, address, port): + + '''Behavior Test: If the pump is off and the height is within the margin region or on + the exact boundary of the margin region, then the pump shall remain at + PUMP_OFF.''' + + logger.info("running third decider test") + my_pump = Pump(address, port) + my_sensor = Sensor(address, port) + my_decider = Decider(10,1) + my_controller = Controller(my_sensor, my_pump, my_decider) + try: + self.assertequals( my_decider.decide(my_decider.target_height + my_decider.margin, 0,my_controller.actions), 0) + self.assertequals( my_decider.decide(my_decider.target_height - my_decider.margin, 0, my_controller.actions), 0) + except Exception as exception: + logger.critical(exception) + + + def fourth_decider_test(self, address, port): + + '''Behavior Test: If the pump is performing PUMP_IN and the height is above the target + height, then the pump shall be turned to PUMP_OFF, otherwise the pump + shall remain at PUMP_IN.''' + + logger.info("running fourth decider test") + my_pump = Pump(address, port) + my_sensor = Sensor(address, port) + my_decider = Decider(10,1) + my_controller = Controller(my_sensor, my_pump, my_decider) + try: + self.assertequals( my_decider.decide(my_decider.target_height + 1, 1, my_controller.actions), 0) + except Exception as exception: + logger.critical(exception) + + + def fifth_decider_test(self, address, port): + + '''Behavior Test: If the pump is performing PUMP_OUT and the height is below the target + height, then the pump shall be turned to PUMP_OFF, otherwise, the pump + shall remain at PUMP_OUT.''' + + logger.info("running fifth decider test") + my_pump = Pump(address, port) + my_sensor = Sensor(address, port) + my_decider = Decider(10,1) + my_controller = Controller(my_sensor, my_pump, my_decider) + try: + self.assertequals( my_decider.decide(my_decider.target_height - 1, -1, my_controller.actions), 0) + except Exception as exception: + logger.critical(exception) + + +class ControllerTests(unittest.TestCase): + """ + Unit tests for the Controller class + """ + + + # TODO: write a test or tests for each of the behaviors defined for + # Controller.tick + + def first_controller_test(self, address, port): + + '''Testing ability to correctly query sensor for height of tank''' + + logger.info("Starting first controller test") + try: + #Creating pump, sensor, and decider for test case + my_pump = Pump(address, port) + my_sensor = Sensor(address, port) + my_decider = Decider(10,1) + #Passing pump, sensor, and decider to controller + my_controller = Controller(my_sensor, my_pump, my_decider) + logger.info("Done with first controller test") + self.asserttrue( isinstance(my_sensor.measure(), float)) + + except Exception as exception: + logger.critical(exception) + + def second_controller_test(self, address, port): + + '''Tests the ability to query the pump for its current state''' + + logger.info("Starting second controller test") + try: + #Creating pump, sensor, and decider for test case + my_pump = Pump(address, port) + my_sensor = Sensor(address, port) + my_decider = Decider(10,1) + #Passing pump, sensor, and decider to controller + my_controller = Controller(my_sensor, my_pump, my_decider) + logger.info("Done with second controller test") + self.assertTrue( my_pump.get_state() in [1, -1, 0]) + + except Exception as exception: + logger.critical(exception) + + + def third_controller_test(self, address, port): + + '''Tests the ability to query the decider for the next appropriate state + given a set of starting conditions. Note, this will only test one set of starting + conditions. Exhaustive testing of the deciders logic is performed in DeciderTests''' + + if True: + logger.info("Starting third controller test") + #Creating pump, sensor, and decider for test case + my_pump = Pump(address, port) + my_sensor = Sensor(address, port) + my_decider = Decider(10,1) + #Passing pump, sensor, and decider to controller + my_controller = Controller(my_sensor, my_pump, my_decider) + logger.info("Done with third controller test") + + my_decider.decide(500, my_decider.target_height, my_controller.actions) + #except Exception as exception: + #logger.critical(exception) + + def fourth_controller_test(self, address, port): + + '''Tests the ability to set the pump to a new state''' + try: + logger.info("Starting fourth controller test") + print("here") + #Creating pump, sensor, and decider for test case + my_pump = Pump(address, port) + my_sensor = Sensor(address, port) + my_decider = Decider(10,1) + #Passing pump, sensor, and decider to controller + my_controller = Controller(my_sensor, my_pump, my_decider) + logger.info("Done with fourth controller test") + my_pump.set_state(0) + self.assertequal( my_pump.get_state(), 0) + + except Exception as exception: + logger.critical(exception) + + def create_instance(address, port, target_height, margin): + + '''helper method to create new pump, sensor, decider, and controller + for each test''' + + my_pump = Pump(address, port) + my_sensor = Sensor(address, port) + my_decider = Decider(target_height,margin) + #Passing pump, sensor, and decider to controller + my_controller = Controller(my_sensor, my_pump, my_decider) + + logger.info("succesfully created controller instance") + + pass + +if __name__ == "__main__": + + '''Creates new controller test objects and runs through all the given controller + test methods''' + + unittest.main() + address = '127.0.0.1' + port = '8000' + my_controller_tests = ControllerTests() + my_controller_tests.first_controller_test(address, port) + my_controller_tests.second_controller_test(address, port) + my_controller_tests.third_controller_test(address, port) + my_controller_tests.fourth_controller_test(address, port) + + my_decider_test = DeciderTests() + my_decider_test.first_decider_test(address, port) + my_decider_test.second_decider_test(address, port) + my_decider_test.third_decider_test(address, port) + my_decider_test.fourth_decider_test(address, port) + my_decider_test.fifth_decider_test(address, port) + + + diff --git a/students/Lucas_See/Week 6/waterregulation/test2.py b/students/Lucas_See/Week 6/waterregulation/test2.py new file mode 100644 index 0000000..872e257 --- /dev/null +++ b/students/Lucas_See/Week 6/waterregulation/test2.py @@ -0,0 +1,196 @@ +# -*- coding: utf-8 -*- +""" +Created on Sat Jun 9 14:23:20 2018 + +@author: seelc +""" + +""" +Unit tests for the water-regulation module +""" +import os +os.chdir('C:\\Users\\seelc\\OneDrive\\Desktop\\Python\\Advanced Python\\water-regulation-master') +import logging + +import unittest +from unittest.mock import MagicMock + +from pump import Pump +from sensor import Sensor +os.chdir('C:\\Users\\seelc\\OneDrive\\Desktop\\Python\\Advanced Python\\water-regulation-master//waterregulation') +import logging +from controller import Controller +from decider import Decider + +os.chdir('C:\\Users\\seelc\\OneDrive\\Desktop\\Python\\Advanced Python\\water-regulation-master') +logger = logging.getLogger('test_application') +logging.basicConfig(level=logging.DEBUG, + format=('%(filename)s: ' + '%(levelname)s: ' + '%(funcName)s(): ' + '%(lineno)d:\t' + '%(message)s') + ) + + + +class DeciderTests(unittest.TestCase): + """ + Unit tests for the Decider class, declaring pre-requisite test objects + outside __init__ class because I ahd difficulting using __init__ + with unittest.Testcase + """ + + # TODO: write a test or tests for each of the behaviors defined for + # Decider.decide + + my_pump = Pump(address, port) + my_sensor = Sensor(address, port) + my_decider = Decider(10,1) + my_controller = Controller(my_sensor, my_pump, my_decider) + address = '127.0.0.1' + port = '8000' + + + def test_first_decider(self): + + '''Behavior Test: If the pump is off and the height is below the margin region, then the + pump should be turned to PUMP_IN.''' + + logger.info("running first decider test") + + try: + assert(self.my_decider.decide(5, 0, self.my_controller.actions) == 1) + except Exception as exception: + logger.critical(exception) + + + def test_second_decider(self): + + '''Behavior Test: If the pump is off and the height is above the margin region, then the + pump should be turned to PUMP_OUT.''' + + logger.info("running second decider test") + try: + assert(self.my_decider.decide(12, 0, self.my_controller.actions) == -1) + except Exception as exception: + logger.critical(exception) + + + def test_third_decider(self): + + '''Behavior Test: If the pump is off and the height is within the margin region or on + the exact boundary of the margin region, then the pump shall remain at + PUMP_OFF.''' + + logger.info("running third decider test") + + try: + assert( (self.my_decider.decide(self.my_decider.target_height + + self.my_decider.margin, 0,self.my_controller.actions))== 0) + assert( (self.my_decider.decide(self.my_decider.target_height + - self.my_decider.margin, 0, self.my_controller.actions))== 0) + except Exception as exception: + logger.critical(exception) + + + def test_fourth_decider(self): + + '''Behavior Test: If the pump is performing PUMP_IN and the height is above the target + height, then the pump shall be turned to PUMP_OFF, otherwise the pump + shall remain at PUMP_IN.''' + + logger.info("running fourth decider test") + try: + assert( (self.my_decider.decide(self.my_decider.target_height + + 1, 1, self.my_controller.actions)) == 0) + except Exception as exception: + logger.critical(exception) + + + def test_fifth_decider(self): + + '''Behavior Test: If the pump is performing PUMP_OUT and the height is below the target + height, then the pump shall be turned to PUMP_OFF, otherwise, the pump + shall remain at PUMP_OUT.''' + + logger.info("running fifth decider test") + try: + assert( self.my_decider.decide(self.my_decider.target_height - 1, + -1, self.my_controller.actions)== 0) + except Exception as exception: + logger.critical(exception) + + +class ControllerTests(unittest.TestCase): + """ + Unit tests for the Controller class, declaring pump, address, etc. outside + an __init__ method because I had some difficulty implementing __init__ while + inhereting from unittest.Testcase + """ + + address = '127.0.0.1' + port = '8000' + my_pump = Pump(address, port) + my_sensor = Sensor(address, port) + my_decider = Decider(10,1) + my_controller = Controller(my_sensor, my_pump, my_decider) + + def test_first_controller(self): + + '''Testing ability to correctly query sensor for height of tank''' + + logger.info("Starting first controller test") + try: + + logger.info("Done with first controller test") + assert( isinstance(self.my_sensor.measure(), float)) + + except Exception as exception: + logger.critical(exception) + + def test_second_controller(self): + + '''Tests the ability to query the pump for its current state''' + + logger.info("Starting second controller test") + try: + + logger.info("Done with second controller test") + assert( self.my_pump.get_state() in [1, -1, 0]) + + except Exception as exception: + logger.critical(exception) + + + def test_third_controller(selft): + + '''Tests the ability to query the decider for the next appropriate state + given a set of starting conditions. Note, this will only test one set of starting + conditions. Exhaustive testing of the deciders logic is performed in DeciderTests''' + + try: + + logger.info("Done with third controller test") + + self.my_decider.decide(500, self.my_decider.target_height, self.my_controller.actions) + except Exception as exception: + logger.critical(exception) + + + def test_fourth_controller(self): + + '''Tests the ability to set the pump to a new state''' + try: + logger.info("Starting fourth controller test") + self.my_pump.set_state(0) + assert( self.my_pump.get_state() == 0) + + except Exception as exception: + logger.critical(exception) + + pass + +if __name__ == "__main__": + unittest.main() +