diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d1f0145
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,139 @@
+*pycache*
+*solution*
+*.idea*
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+pip-wheel-metadata/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+.python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# USER SPECIFIC/IDE FILES
+.idea/
+
+# GENERAL PYTHON FILES
+__pycache__/
diff --git a/calculator.py b/calculator.py
index a46affd..da13b9c 100644
--- a/calculator.py
+++ b/calculator.py
@@ -1,84 +1,126 @@
+#!/usr/bin/env python
+"""
+# ------------------------------------------------------------------------ #
+# Title: Lesson 4 - WSGI (The Web Server Gateway Interface) example. This script
+# is a set of simple calculator web pages allowing the user to pass values in and get
+# calculated values back.
+# Description: This is the main "calculator.py" script.
+# ChangeLog (Who,When,What):
+# ASimpson, 10/21/2020, Modified code to complete lesson4 - Assignment04
+# ------------------------------------------------------------------------ #
"""
-For your homework this week, you'll be creating a wsgi application of
-your own.
-You'll create an online calculator that can perform several operations.
+import re
+import functools
-You'll need to support:
+DEFAULT = "No Value Set"
- * Addition
- * Subtractions
- * Multiplication
- * Division
-Your users should be able to send appropriate requests and get back
-proper responses. For example, if I open a browser to your wsgi
-application at `http://localhost:8080/multiple/3/5' then the response
-body in my browser should be `15`.
+def index():
+ funcs = ['add', 'subtract', 'multiply', 'divide']
-Consider the following URL/Response body pairs as tests:
+ body = ['
Calculator!!
', 'To use this calculator, select the
'
+ ' operation below and add a "/" between each
'
+ ' number in the URL address bar.
Ex: /multiply/3/5
', '
']
-```
- http://localhost:8080/multiply/3/5 => 15
- http://localhost:8080/add/23/42 => 65
- http://localhost:8080/subtract/23/42 => -19
- http://localhost:8080/divide/22/11 => 2
- http://localhost:8080/ => Here's how to use this page...
-```
+ item_template = '- {0} numbers
'
+ for func in funcs:
+ body.append(item_template.format(func))
+ body.append('
')
+ return '\n'.join(body)
-To submit your homework:
- * Fork this repository (Session03).
- * Edit this file to meet the homework requirements.
- * Your script should be runnable using `$ python calculator.py`
- * When the script is running, I should be able to view your
- application in my browser.
- * I should also be able to see a home page (http://localhost:8080/)
- that explains how to perform calculations.
- * Commit and push your changes to your fork.
- * Submit a link to your Session03 fork repository!
+def add(*args):
+ """ Returns a STRING with the sum of the arguments """
+ response_body = 'Your total is: {0}
Back to the ' \
+ 'list of operations'.format(str(sum(map(int, args))))
+ return response_body
-"""
+def subtract(*args):
+ """ Returns a STRING with the sum of the arguments """
+ if len(args) == 0:
+ sum = 0
+ else:
+ sum = functools.reduce(lambda a, b: a - b, map(int, args))
+ response_body = 'Your total is: {0}
Back to the list of operations'.format(str(sum))
+ return response_body
-def add(*args):
+def multiply(*args):
""" Returns a STRING with the sum of the arguments """
+ if len(args) == 0:
+ sum = 0
+ else:
+ sum = functools.reduce(lambda a, b: a * b, map(int, args))
+ response_body = 'Your total is: {0}
Back to the list of operations'.format(str(sum))
+ return response_body
- # TODO: Fill sum with the correct value, based on the
- # args provided.
- sum = "0"
- return sum
+def divide(*args):
+ """ Returns a STRING with the sum of the arguments """
+ try:
+ sum = 0
+ if "0" in args:
+ raise ZeroDivisionError
+ if 0 in args:
+ raise ZeroDivisionError
+ if len(args) == 0:
+ raise ValueError
+ sum = functools.reduce(lambda a, b: a / b, map(int, args))
+ response_body = 'Your total is: {0}
Back to the list of operations'.format(str(sum))
+ return response_body
+ except ZeroDivisionError:
+ response_body = 'Cannot divide by zero. Try Again!
Back to the list of operations'
+ return response_body
+ except ValueError:
+ response_body = 'No values were provided for Division Operation.
Please provide values and Try Again!' \
+ '
Back to the list of operations'
+ return response_body
+
+
+def application(environ, start_response):
+ """Application function used to create a response in bytes bak to the client"""
+ try:
+ headers = [("Content-type", "text/html")]
+ func, args = resolve_path(environ.get('PATH_INFO', DEFAULT))
+ body = func(*args)
+ status = "200 OK"
+ except NameError:
+ status = "404 Not Found"
+ body = "Not Found
"
+ except Exception:
+ status = "500 Internal Server Error"
+ body = "Internal Server Error
"
+ except ZeroDivisionError:
+ status = "Can not divide by zero"
+ body = "Zero Division Error. Please Try again!!
"
+ finally:
+ headers.append(('Content-length', str(len(body))))
+ start_response(status, headers)
+ return [body.encode('utf8')]
-# TODO: Add functions for handling more arithmetic operations.
def resolve_path(path):
"""
Should return two values: a callable and an iterable of
arguments.
"""
+ funcs = {"": index, "add": add, "subtract": subtract,
+ "multiply": multiply, "divide": divide}
- # TODO: Provide correct values for func and args. The
- # examples provide the correct *syntax*, but you should
- # determine the actual values of func and args using the
- # path.
- func = add
- args = ['25', '32']
+ path = path.strip('/').split('/')
+ func_name = path[0]
+ args = path[1:]
+ try:
+ func = funcs[func_name]
+ except KeyError:
+ raise NameError
return func, args
-def application(environ, start_response):
- # TODO: Your application code from the book database
- # work here as well! Remember that your application must
- # invoke start_response(status, headers) and also return
- # the body of the response in BYTE encoding.
- #
- # TODO (bonus): Add error handling for a user attempting
- # to divide by zero.
- pass
if __name__ == '__main__':
- # TODO: Insert the same boilerplate wsgiref simple
- # server creation that you used in the book database.
- pass
+ from wsgiref.simple_server import make_server
+ srv = make_server('localhost', 8080, application)
+ srv.serve_forever()
diff --git a/tests.py b/tests.py
index c2a8dcb..9733b6c 100644
--- a/tests.py
+++ b/tests.py
@@ -2,6 +2,7 @@
import subprocess
import http.client
import random
+import time
class WebTestCase(unittest.TestCase):
@@ -16,6 +17,9 @@ def setUp(self):
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
+ # Had to pause the code here for a second to let the sun processes finish
+ # otherwise if we don't all the tests will fail.
+ time.sleep(1)
def tearDown(self):
self.server_process.kill()
@@ -25,9 +29,10 @@ def get_response(self, url):
"""
Helper function to get a response from a given url, using http.client
"""
-
conn = http.client.HTTPConnection('localhost:8080')
+
conn.request('GET', url)
+ #time.sleep(1)
response = conn.getresponse()
self.assertEqual(200, response.getcode())
@@ -40,7 +45,6 @@ def test_add(self):
"""
A call to /add/a/b yields a + b
"""
-
a = random.randint(100, 10000)
b = random.randint(100, 10000)
@@ -48,14 +52,12 @@ def test_add(self):
response = self.get_response(path)
self.assertEqual(200, response.getcode())
-
self.assertIn(str(a + b).encode(), response.read())
def test_multiply(self):
"""
A call to /multiply/a/b yields a*b
"""
-
a = random.randint(100, 10000)
b = random.randint(100, 10000)
@@ -63,14 +65,12 @@ def test_multiply(self):
response = self.get_response(path)
self.assertEqual(200, response.getcode())
-
self.assertIn(str(a*b).encode(), response.read())
def test_subtract_positive_result(self):
"""
A call to /subtract/a/b yields a - b, for a > b
"""
-
a = random.randint(10000, 100000)
b = random.randint(100, 1000)
@@ -78,14 +78,12 @@ def test_subtract_positive_result(self):
response = self.get_response(path)
self.assertEqual(200, response.getcode())
-
self.assertIn(str(a - b).encode(), response.read())
def test_subtract_negative_result(self):
"""
A call to /subtract/a/b yields a - b, for a < b
"""
-
a = random.randint(100, 1000)
b = random.randint(10000, 100000)
@@ -93,16 +91,13 @@ def test_subtract_negative_result(self):
response = self.get_response(path)
self.assertEqual(200, response.getcode())
-
self.assertIn(str(a - b).encode(), response.read())
def test_divide(self):
"""
A call to /divide/a/b yields a/b, for a % b = 0
"""
-
result = random.randint(2, 10)
-
b = random.randint(100, 1000)
a = result * b
@@ -110,7 +105,6 @@ def test_divide(self):
response = self.get_response(path)
self.assertEqual(200, response.getcode())
-
self.assertIn(str(result).encode(), response.read())
def test_index_instructions(self):