diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 3b911062..00000000 --- a/.gitignore +++ /dev/null @@ -1,108 +0,0 @@ -.DS_Store -.git-completion.bash - - -# 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/ -*.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/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# 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/ diff --git a/students/FyfyNguyen/session06/mailroom4.py b/students/FyfyNguyen/session06/mailroom4.py new file mode 100644 index 00000000..1b2a0d8e --- /dev/null +++ b/students/FyfyNguyen/session06/mailroom4.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 + +""" +Mailroom 4 +""" + +import sys +import tempfile +import pathlib +from textwrap import dedent + +donor_db = {"Robyn Rihanna": [100, 200, 300], + "Ariana Grande": [2250, 4000, 1000], + "Beyonce Carter-Knowles": [150000, 3500, 25000], + "Aubrey Drake Graham": [15000, 5500.25, 1200], + "Justin Bieber": [2500, 250, 750.50] + } + +file_dir = pathlib.Path(tempfile.gettempdir()) + + +def print_donors(): + """ + Prints a list of exiting donors in donor_db + """ + for donor in donor_db: + print(donor) + + +def find_donor(name_entered): + """ + Lookup donor in donor_db + + :param: the name of the donor + :returns: the donor data structure -- None if donor not found in donor_db + """ + donor_key = name_entered.strip().title() + return donor_db.get(donor_key) + + +def add_new_donor(name_entered): + """ + Add a new donor to donor_db + + :param: the name of the donor + :returns: the new donor data structure + """ + donor = (name_entered, []) + donor_db[name_entered.title()] = [] + print("Successfully added new donor to database.") + ask_donation(name_entered) + + +def update_donation(name_entered, donation): + donor_db.setdefault(name_entered.title(), []).append(donation) + print("Successfully updated donation amount.") + send_thank_you(name_entered, donation) + + +def ask_donation(name_entered): + """ + Ask the user for donation amount + + :param: the name of the donor + """ + donation = float(input("Enter donation amount >>> ")) + update_donation(name_entered, donation) + + +def confirm_donor(name_entered): + """ + Confirm the user wants to add a new donor to donor_db + + :param: the name of the donor + """ + response = input(f"Donor does not exist. Add {name_entered.title()} to " + "database? [y/n?] >>> ") + if response.strip() == 'y': + add_new_donor(name_entered) + else: + return + + +def send_thank_you(name_entered, donation): + """ + Generate a thank you letter to the donor + + :param: the name of the donor + :param: the donation amount + :returns:string with letter + """ + return dedent("""Dear {}, + + Your generous ${:,.2f} donation just made our day! + + Thank you! + -Our Charity + """.format(name_entered.title(), donation)) + + +def thank_you(): + """ + Execute the logic to record a donation and generate a thank you message + """ + while True: + name_entered = input("Enter the name of a donor or 'list' to view " + "donors >>> ").lower().strip() + if name_entered.strip() == "list": + print_donors() + else: + break + + donor = find_donor(name_entered) + if donor is None: + confirm_donor(name_entered) + else: + ask_donation(name_entered) + + +def thank_you_all(): + """ + Generate a letter for each donor and save it to temporary directory + """ + for donor in donor_db: + letters = send_thank_you(donor, donor_db[donor][-1]) + file_path = file_dir / (donor.replace(" ", "_") + ".txt") + with open(file_path, 'w') as f: + f.write(letters) + print(f'Files downloaded to: {file_dir}') + + +def sort_key(donor): + return donor + + +def create_report(): + """ + Generate report of donors and the amounts donated + + :returns: the donor report as a string + """ + report_rows = [] + for name, donations in donor_db.items(): + total_given = sum(donations) + num_gifts = len(donations) + avg_gifts = total_given / num_gifts + report_rows.append((name, num_gifts, num_gifts, avg_gifts)) + + report_rows.sort(key=sort_key) + report = [] + report.append("{:30s} | {:11s} | {:9s} | {:12s}".format("Donor Name", + "Total Given", + "Num Gifts", + "Average Gift" + )) + report.append("-" * 72) + for row in report_rows: + report.append("{:30s} | {:11d} | {:9d} | ${:12,.2f}".format(*row)) + return "\n".join(report) + + +def print_report(): + print(create_report()) + + +def exit_program(): + """ + Quit the program + """ + print("Exiting Program. Goodbye!") + sys.exit() + + +def main_menu(): + selection = input(dedent(""" + Welcome to Mailroom! + + Select an option: + + 1 - Create a Report + 2 - Send Thank You to a single donor + 3 - Send Thank You to all donors + 4 - Quit + + >>> """)) + return selection.strip() + + +if __name__ == "__main__": + menu_selections = {"1": print_report, + "2": thank_you, + "3": thank_you_all, + "4": exit_program} + + while True: + user_selection = main_menu() + try: + menu_selections[user_selection]() + except KeyError: + print("Error: Invalid Selection. Select from the main menu.") diff --git a/students/FyfyNguyen/session06/test_mailroom4.py b/students/FyfyNguyen/session06/test_mailroom4.py new file mode 100644 index 00000000..74c3e9f5 --- /dev/null +++ b/students/FyfyNguyen/session06/test_mailroom4.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 + +""" +Mailroom 4 tests +""" + +import pytest +import os +import pathlib +import mailroom4 as mr + + +def test_print_donors(): + listing = mr.print_donors() + assert "Robyn Rihanna" in listing + assert "Aubrey Drake Graham" in listing + + +def test_find_donor(): + donor = mr.find_donor("beyonce caRter Knowles ") + assert donor[0] == "Beyonce Carter Knowles" + + +def test_if_not_find_donor(): + donor = mr.find_donor("Beyonce Carter Knowles") + assert donor is None + + +def test_add_new_donor(): + name = " Jennifer Lopez" + donor = mr.add_new_donor(name) + donor[1].append(750) + assert donor[0] == "Jennifer Lopez" + assert donor[1] == [750] + assert mailroom.find_donor(name) == donor + + +def test_create_report(): + result = mr.create_report() + assert result.startswith("Donor Name | Total Given | Num Gifts | Average Gift") + assert "------------------------------------------------------------------------" in result + assert "Ariana Grande | 3 | 3 | $ 2,416.67" in result + + +def test_thank_you(): + result = mr.thank_you("Chrissy Teigen", 4500) + assert "Dear Chrissy Teigen," in result + assert "$4,500.00" in result + assert "4500" not in result + + +def test_thank_you_all(): + result = mr.thank_you_all("Fyfy Nguyen") + assert result.name == "Fyfy_Nguyen.txt" + + +def test_thank_you_all_files(): + mr.thank_you_all() + for donor in mr.donor_db.items(): + file_path = mr.thank_you_all diff --git a/students/FyfyNguyen/session07/__pycache__/html_render.cpython-37.pyc b/students/FyfyNguyen/session07/__pycache__/html_render.cpython-37.pyc new file mode 100644 index 00000000..58d44c43 Binary files /dev/null and b/students/FyfyNguyen/session07/__pycache__/html_render.cpython-37.pyc differ diff --git a/students/FyfyNguyen/session07/__pycache__/test_html_render.cpython-37-PYTEST.pyc b/students/FyfyNguyen/session07/__pycache__/test_html_render.cpython-37-PYTEST.pyc new file mode 100644 index 00000000..14deac4c Binary files /dev/null and b/students/FyfyNguyen/session07/__pycache__/test_html_render.cpython-37-PYTEST.pyc differ diff --git a/students/FyfyNguyen/session07/html_render.py b/students/FyfyNguyen/session07/html_render.py new file mode 100644 index 00000000..1449b6ab --- /dev/null +++ b/students/FyfyNguyen/session07/html_render.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 + +""" +A class-based system for rendering html. +""" + + +class TextWrapper: + """ + A simple wrapper that creates a class with a render method + for simple text + """ + def __init__(self, text): + self.text = text + + def render(self, file_out, cur_ind=""): + file_out.write(cur_ind + self.text) + + +# This is the framework for the base class +class Element: + tag = "html" + indent = " " + + def __init__(self, content=None, **kwargs): + self.attributes = kwargs + self.contents = [] + if content is not None: + self.append(content) + + def append(self, new_content): + self.contents.append(new_content) + + def _make_tags(self): + attributes = " ".join(['{}="{}"'.format(key, value) for key, value in self.attributes.items()]) + if attributes.strip(): + open_tag = "<{} {}>".format(self.tag, attributes.strip()) + else: + open_tag = "<{}>".format(self.tag) + close_tag = "".format(self._make_tags) + + return open_tag, close_tag + + def render(self, out_file, cur_ind=""): + # loop through the list of contents: + open_tag, close_tag = self._make_tags() + out_file.write(cur_ind + open_tag + "\n") + for content in self.contents: + try: + content.render(out_file, cur_ind + self.indent) + out_file.write("\n") + out_file.write(cur_ind + close_tag) + except AttributeError: + out_file.write(content) + + + # def tag_open(self, out_file): + # outfile.write(f"<{self.tag}{self.attributes_text()}>{self.content_separator}) + + # def tag_close(self, out_file): + # out_file.write(f"") + + # def tag_content(self, out_file): + # for line in self.comtent: + # line.render(out_file) + # out_file.write(self.content_separator) + + +class Html(Element): + tag = "html" + + def render(self, file_out, cur_ind=""): + file_out.write(cur_ind + "\n") + super().render(file_out, cur_ind=cur_ind) + + +class Body(Element): + tag = "body" + + +class P(Element): + tag = "p" + + +class Head(Element): + tag = "head" + + +class OneLineTag(Element): + def render(self, out_file, cur_ind=""): + open_tag, close_tag = self._make_tags() + out_file.write(cur.ind + open_tag) + for content in self.content: + content.render(out_file) + out_file.write(close_tag) + + +class Title(OneLineTag): + tag = "title" + + +class SelfClosingTag(Element): + def __init__(self, content=None, **kwargs): + if content is not None: + raise TypeError("SelfClosingTag can not contain any content") + super().__init__(self, content=content, **kwargs) + + def append(self, *args, **kwargs): + raise TypeError("SelfClosingTag can not contain any content") + + def render(self, out_file, cur_ind=""): + open_tag, _ = self._make_tags() + out_file.write(ind + open_tag.replace(">", " />")) + # # loop through the list of contents: + # out_file.write(self._open_tag()) + # # out_file.write("\n") + # for content in self.contents: + # try: + # content.render(out_file) + # except AttributeError: + # out_file.write(content) + # out_file.write("\n") + # out_file.write(self._close_tag()) + # out_file.write("\n") + + +class Hr(SelfClosingTag): + tag = "hr" + + +class Br(SelfClosingTag): + tag = "br" + + +class A(OneLineTag): + tag = "a" + + def __init__(self, link, *args, **kwargs): + kwargs['href'] = link + super().__init__(self, *args, **kwargs) + + +class Ul(Element): + tag = "ul" + + +class Li(Element): + tag = "li" + + +class H(OneLineTag): + tag = "H" + + def __init__(self, level, *args, **kwargs): + self.tag = "h" + str(int(level)) + super().__init__(*args, **kwargs) + + +class Meta(SelfClosingTag): + tag = "meta" diff --git a/students/FyfyNguyen/session07/run_html_render.py b/students/FyfyNguyen/session07/run_html_render.py new file mode 100644 index 00000000..9608a65f --- /dev/null +++ b/students/FyfyNguyen/session07/run_html_render.py @@ -0,0 +1,231 @@ +#!/usr/bin/env python3 + +""" +a simple script can run and test your html rendering classes. + +Uncomment the steps as you add to your rendering. + +""" + +from io import StringIO + +# importing the html_rendering code with a short name for easy typing. +import html_render as hr + + +# writing the file out: +def render_page(page, filename, indent=None): + """ + render the tree of elements + + This uses StringIO to render to memory, then dump to console and + write to file -- very handy! + """ + + f = StringIO() + if indent is None: + page.render(f) + else: + page.render(f, indent) + + print(f.getvalue()) + with open(filename, 'w') as outfile: + outfile.write(f.getvalue()) + + +# Step 1 +######### + +page = hr.Element() + +page.append("Here is a paragraph of text -- there could be more of them, " + "but this is enough to show that we can do some text") + +page.append("And here is another piece of text -- you should be able to add any number") + +render_page(page, "test_html_output1.html") + +# The rest of the steps have been commented out. +# Uncomment them as you move along with the assignment. + +# ## Step 2 +# ########## + +# page = hr.Html() + +# body = hr.Body() + +# body.append(hr.P("Here is a paragraph of text -- there could be more of them, " +# "but this is enough to show that we can do some text")) + +# body.append(hr.P("And here is another piece of text -- you should be able to add any number")) + +# page.append(body) + +# render_page(page, "test_html_output2.html") + +# # Step 3 +# ########## + +# page = hr.Html() + +# head = hr.Head() +# head.append(hr.Title("PythonClass = Revision 1087:")) + +# page.append(head) + +# body = hr.Body() + +# body.append(hr.P("Here is a paragraph of text -- there could be more of them, " +# "but this is enough to show that we can do some text")) +# body.append(hr.P("And here is another piece of text -- you should be able to add any number")) + +# page.append(body) + +# render_page(page, "test_html_output3.html") + +# # Step 4 +# ########## + +# page = hr.Html() + +# head = hr.Head() +# head.append(hr.Title("PythonClass = Revision 1087:")) + +# page.append(head) + +# body = hr.Body() + +# body.append(hr.P("Here is a paragraph of text -- there could be more of them, " +# "but this is enough to show that we can do some text", +# style="text-align: center; font-style: oblique;")) + +# page.append(body) + +# render_page(page, "test_html_output4.html") + +# # Step 5 +# ######### + +# page = hr.Html() + +# head = hr.Head() +# head.append(hr.Title("PythonClass = Revision 1087:")) + +# page.append(head) + +# body = hr.Body() + +# body.append(hr.P("Here is a paragraph of text -- there could be more of them, " +# "but this is enough to show that we can do some text", +# style="text-align: center; font-style: oblique;")) + +# body.append(hr.Hr()) + +# page.append(body) + +# render_page(page, "test_html_output5.html") + +# # Step 6 +# ######### + +# page = hr.Html() + +# head = hr.Head() +# head.append(hr.Title("PythonClass = Revision 1087:")) + +# page.append(head) + +# body = hr.Body() + +# body.append(hr.P("Here is a paragraph of text -- there could be more of them, " +# "but this is enough to show that we can do some text", +# style="text-align: center; font-style: oblique;")) + +# body.append(hr.Hr()) + +# body.append("And this is a ") +# body.append( hr.A("http://google.com", "link") ) +# body.append("to google") + +# page.append(body) + +# render_page(page, "test_html_output6.html") + +# # Step 7 +# ######### + +# page = hr.Html() + +# head = hr.Head() +# head.append(hr.Title("PythonClass = Revision 1087:")) + +# page.append(head) + +# body = hr.Body() + +# body.append( hr.H(2, "PythonClass - Class 6 example") ) + +# body.append(hr.P("Here is a paragraph of text -- there could be more of them, " +# "but this is enough to show that we can do some text", +# style="text-align: center; font-style: oblique;")) + +# body.append(hr.Hr()) + +# list = hr.Ul(id="TheList", style="line-height:200%") + +# list.append( hr.Li("The first item in a list") ) +# list.append( hr.Li("This is the second item", style="color: red") ) + +# item = hr.Li() +# item.append("And this is a ") +# item.append( hr.A("http://google.com", "link") ) +# item.append("to google") + +# list.append(item) + +# body.append(list) + +# page.append(body) + +# render_page(page, "test_html_output7.html") + +# # Step 8 and 9 +# ############## + +# page = hr.Html() + + +# head = hr.Head() +# head.append( hr.Meta(charset="UTF-8") ) +# head.append(hr.Title("PythonClass = Revision 1087:")) + +# page.append(head) + +# body = hr.Body() + +# body.append( hr.H(2, "PythonClass - Example") ) + +# body.append(hr.P("Here is a paragraph of text -- there could be more of them, " +# "but this is enough to show that we can do some text", +# style="text-align: center; font-style: oblique;")) + +# body.append(hr.Hr()) + +# list = hr.Ul(id="TheList", style="line-height:200%") + +# list.append( hr.Li("The first item in a list") ) +# list.append( hr.Li("This is the second item", style="color: red") ) + +# item = hr.Li() +# item.append("And this is a ") +# item.append( hr.A("http://google.com", "link") ) +# item.append("to google") + +# list.append(item) + +# body.append(list) + +# page.append(body) + +# render_page(page, "test_html_output8.html") diff --git a/students/FyfyNguyen/session07/sample_html.html b/students/FyfyNguyen/session07/sample_html.html new file mode 100644 index 00000000..9c2e675d --- /dev/null +++ b/students/FyfyNguyen/session07/sample_html.html @@ -0,0 +1,27 @@ + + + + + Python Class Sample page + + +

Python Class - Html rendering example

+

+ Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text +

+
+ + + \ No newline at end of file diff --git a/students/FyfyNguyen/session07/test_html_render.py b/students/FyfyNguyen/session07/test_html_render.py new file mode 100644 index 00000000..d85762a4 --- /dev/null +++ b/students/FyfyNguyen/session07/test_html_render.py @@ -0,0 +1,433 @@ +""" +test code for html_render.py + +This is just a start -- you will need more tests! +""" + +import io +import pytest + +# import * is often bad form, but makes it easier to test everything in a module. +from html_render import * + + +# utility function for testing render methods +# needs to be used in multiple tests, so we write it once here. +def render_result(element, ind=""): + """ + calls the element's render method, and returns what got rendered as a + string + """ + # the StringIO object is a "file-like" object -- something that + # provides the methods of a file, but keeps everything in memory + # so it can be used to test code that writes to a file, without + # having to actually write to disk. + outfile = io.StringIO() + # this so the tests will work before we tackle indentation + if ind: + element.render(outfile, ind) + else: + element.render(outfile) + return outfile.getvalue() + +######## +# Step 1 +######## + + +def test_init(): + """ + This only tests that it can be initialized with and without + some content -- but it's a start + """ + e = Element() + + e = Element("this is some text") + + +def test_append(): + """ + This tests that you can append text + + It doesn't test if it works -- + that will be covered by the render test later + """ + e = Element("this is some text") + e.append("some more text") + + +def test_render_element(): + """ + Tests whether the Element can render two pieces of text + So it is also testing that the append method works correctly. + + It is not testing whether indentation or line feeds are correct. + """ + e = Element("this is some text") + e.append("and this is some more text") + + # This uses the render_results utility above + file_contents = render_result(e) + + # making sure the content got in there. + assert("this is some text") in file_contents + assert("and this is some more text") in file_contents + + # make sure it's in the right order + assert file_contents.index("this is") < file_contents.index("and this") + + # making sure the opening and closing tags are right. + assert file_contents.startswith("") + assert file_contents.endswith("") + +# Uncomment this one after you get the one above to pass +# Does it pass right away? + + +def test_render_element2(): + """ + Tests whether the Element can render two pieces of text + So it is also testing that the append method works correctly. + + It is not testing whether indentation or line feeds are correct. + """ + e = Element() + e.append("this is some text") + e.append("and this is some more text") + + # This uses the render_results utility above + file_contents = render_result(e).strip() + + # making sure the content got in there. + assert("this is some text") in file_contents + assert("and this is some more text") in file_contents + + # make sure it's in the right order + assert file_contents.index("this is") < file_contents.index("and this") + + # making sure the opening and closing tags are right. + assert file_contents.startswith("") + assert file_contents.endswith("") + + +# # ######## +# # # Step 2 +# # ######## + +# tests for the new tags +# def test_html(): +# e = Html("this is some text") +# e.append("and this is some more text") + +# file_contents = render_result(e).strip() + +# assert("this is some text") in file_contents +# assert("and this is some more text") in file_contents +# print(file_contents) +# assert file_contents.endswith("") + + +# def test_body(): + e = Body("this is some text") + e.append("and this is some more text") + + file_contents = render_result(e).strip() + + assert("this is some text") in file_contents + assert("and this is some more text") in file_contents + + assert file_contents.startswith("") + assert file_contents.endswith("") + + +# def test_p(): + e = P("this is some text") + e.append("and this is some more text") + + file_contents = render_result(e).strip() + + assert("this is some text") in file_contents + assert("and this is some more text") in file_contents + + assert file_contents.startswith("

") + assert file_contents.endswith("

") + + +def test_sub_element(): + """ + tests that you can add another element and still render properly + """ + page = Html() + page.append("some plain text.") + page.append(P("A simple paragraph of text")) + page.append("Some more plain text.") + + file_contents = render_result(page) + print(file_contents) # so we can see it if the test fails + + # note: The previous tests should make sure that the tags are getting + # properly rendered, so we don't need to test that here. + assert "some plain text" in file_contents + assert "A simple paragraph of text" in file_contents + assert "Some more plain text." in file_contents + assert "some plain text" in file_contents + # but make sure the embedded element's tags get rendered! + assert "

" in file_contents + assert "

" in file_contents + + +######## +# Step 3 +######## + +def test_head(): + h = Head() + h.append(Title("PythonClass - title example")) + + +def test_title(): + page = Html() + head = Head() + head.append(head) + body = Body() + body.append(P("Paragraph of text and words")) + body.append(P("Some more text")) + page.append(body) + file_contents = render_result(page) + print(file_contents) + # assert False + + +######## +# Step 4 +######## + +def test_attributes(): + p = P("A paragraph of text", style="text-align: center", id="intro") + p_contents = render_result(p) + print(p_contents) # so we can see it if the test fails + + # note: The previous tests should make sure that the tags are getting + # properly rendered, so we don't need to test that here. + # so using only a "P" tag is fine + assert "A paragraph of text" in p_contents + # but make sure the embedded element's tags get rendered! + # first test the end tag is there -- same as always: + assert p_contents.endswith("

") + + # but now the opening tag is far more complex + # but it starts the same: + assert p_contents.startswith("") > p_contents.index('id="intro"') + + +######## +# Step 5 +######## + +def test_hr(): + """a simple horizontal rule with no attributes""" + hr = Hr() + file_contents = render_result(hr) + print(file_contents) + assert file_contents == '
\n' + + +def test_hr_attr(): + """a horizontal rule with an attribute""" + hr = Hr(width=400) + file_contents = render_result(hr) + print(file_contents) + assert file_contents == '
\n' + + +def test_br(): + br = Br() + file_contents = render_result(br) + print(file_contents) + assert file_contents == "
\n" + + +def test_content_in_br(): + with pytest.raises(TypeError): + br = Br("some content") + + +def test_append_content_in_br(): + with pytest.raises(TypeError): + br = Br() + br.append("some content") + +# def test_one_line_tag_append(): +# """ +# You should not be able to append content to a OneLineTag +# """ +# e = OneLineTag("the initial content") +# e.append("some more content") + +# file_contents = render_result(e).strip() +# print(file_contents) + +# def test_one_line_tag_append(): +# title = OneLineTag("the intitial content") +# with pytest.raises(NotImplementedError): +# title.append("some more content") + +######## +# Step 6 +######## + + +def test_anchor_tag(): + a = A("http://google.com", "link to google") + a_text = render_result(a) + print(a_text) + assert a_text.startswith('') + assert 'href="http://google.com"' in a_text + assert 'link to google' in a_text + +######## +# Step 7 +######## + + +def test_ul(): + ul = Ul() + ul.append(Li("item one in a list")) + ul.append(Li("item two in a list")) + text = render_result(ul) + print(file_contents) + + assert text.startswith('') + assert "item one in a list" in text + assert "item two in a list" in text + assert file_contents.count("
  • ") == 2 + assert file_contents.count("
  • ") == 2 + + +def test_header(): + h = H(3, "A nice header line") + file_contents = render_result(h) + print(file_contents) + assert file_contents.startswith('

    ') + assert file_contents.endswith('

    ') + assert "A nice header line" in file_contents + + +def test_header2(): + """ + adding an attribute to a header + """ + h = H(2, "A nice header line", align="center") + file_contents = render_result(h) + print(file_contents) + assert file_contents.startswith('') + assert "A nice header line" in file_contents + assert ' align="center"' in file_contents + + +######## +# Step 8 +######## + +def test_html_doctype(): + e = Html("this is some text") + file_contents = render_result(e) + + assert "this is some text" in file_contents + assert file_contents.startswith("\n") + + +def test_meta(): + e = Meta(charset="UTF-8") + + file_contents = render_result(e) + + assert file_contents == '\n' + + +# ##################### +# # indentation testing +# # Uncomment for Step 9 -- adding indentation +# ##################### + + +# def test_indent(): +# """ +# Tests that the indentation gets passed through to the renderer +# """ +# html = Html("some content") +# file_contents = render_result(html, ind=" ").rstrip() #remove the end newline + +# print(file_contents) +# lines = file_contents.split("\n") +# assert lines[0].startswith(" <") +# print(repr(lines[-1])) +# assert lines[-1].startswith(" <") + + +# def test_indent_contents(): +# """ +# The contents in a element should be indented more than the tag +# by the amount in the indent class attribute +# """ +# html = Element("some content") +# file_contents = render_result(html, ind="") + +# print(file_contents) +# lines = file_contents.split("\n") +# assert lines[1].startswith(Element.indent) + + +# def test_multiple_indent(): +# """ +# make sure multiple levels get indented fully +# """ +# body = Body() +# body.append(P("some text")) +# html = Html(body) + +# file_contents = render_result(html) + +# print(file_contents) +# lines = file_contents.split("\n") +# for i in range(3): # this needed to be adapted to the tag +# assert lines[i + 1].startswith(i * Element.indent + "<") + +# assert lines[4].startswith(3 * Element.indent + "some") + + +# def test_element_indent1(): +# """ +# Tests whether the Element indents at least simple content + +# we are expecting to to look like this: + +# +# this is some text +# <\html> + +# More complex indentation should be tested later. +# """ +# e = Element("this is some text") + +# # This uses the render_results utility above +# file_contents = render_result(e).strip() + +# # making sure the content got in there. +# assert("this is some text") in file_contents + +# # break into lines to check indentation +# lines = file_contents.split('\n') +# # making sure the opening and closing tags are right. +# assert lines[0] == "" +# # this line should be indented by the amount specified +# # by the class attribute: "indent" +# assert lines[1].startswith(Element.indent + "thi") +# assert lines[2] == "" +# assert file_contents.endswith("") diff --git a/students/FyfyNguyen/session08/circle.py b/students/FyfyNguyen/session08/circle.py new file mode 100644 index 00000000..3dc5db5c --- /dev/null +++ b/students/FyfyNguyen/session08/circle.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 + +""" +Class that represents a circle +""" +from math import pi +import functools + + +@functools.total_ordering +class Circle(object): + + @classmethod + def from_diameter(cls, diameter): + return cls(diameter / 2) + + def __init__(self, radius): + if radius is None: + raise TypeError("Radius cannot be none") + self.radius = radius + + @property + def diameter(self): + return self.radius * 2 + + @diameter.setter + def diameter(self, val): + self.radius = val/2 + + @property + def area(self): + return pi * (self.radius ** 2) + + def __repr__(self): + return f'Circle({self.radius!r}' + + def __str__(self): + return f'Circle with radius: {self.radius:.6f}' + + @staticmethod + def sort_key(a_circle): + return a_circle.radius + + def __add__(self, other): + return Circle(self.radius + other.radius) + + def __iadd__(self, other): + self.radius += other.radius + return self + + def __mul__(self, other): + return Circle(self.radius * other) + + def __rmul__(self, other): + return Circle(self.radius * other) + + def __imul__(self, other): + self.radius *= other + return self + + def __eq__(self, other): + return self.radius == other.radius + + def __lt__(self, other): + return self.radius < other.radius + + def __ge__(self, other): + return self.radius >= other.radius + + def __notequal__(self, other): + return self.radius != other.radius + + +""" +Class that represents a sphere +""" + + +class Sphere(Circle): + + def volume(self): + return (4 / 3) * pi * (self.radius ** 3) + + @property + def area(self): + return 4 * pi * (self.radius ** 2) + + def __str__(self): + return "Sphere with radius: {}".format(self.radius) + + def __repr__(self): + return "Sphere({})".format(self.radius) diff --git a/students/FyfyNguyen/session08/test_circle.py b/students/FyfyNguyen/session08/test_circle.py new file mode 100644 index 00000000..a1504c60 --- /dev/null +++ b/students/FyfyNguyen/session08/test_circle.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 + +from circle import Circle, Sphere +import math +import pytest + +""" +Circle Tests +""" + + +def test_cir_radius(): + c = Circle(5) + assert c.radius == 5 + + +def test_cir_radius_invalid(): + with pytest.raises(TypeError): + c = Circle(None) + + +def test_cir_diameter(): + c = Circle(5) + assert c.diameter == 10 + + +def test_cir_diameter_change(): + c = Circle(5) + assert c.diameter == 10 + c.diameter = 50 + assert c.diameter == 50 + assert c.radius == 25 + + +def test_cir_area(): + c = Circle(10) + assert round(c.area, 2) == 314.16 + + +def test_cir_from_diameter(): + c = Circle.from_diameter(10) + assert c.radius == 5 + + +def test_cir_str(): + c = Circle(15) + assert str(c) == "Circle with radius: 15.000000" + + +def test_cir_sum(): + c1 = Circle(10) + c2 = Circle(15) + c3 = c1 + c2 + assert c3.radius == 25 + + +def test_cir_multiply(): + c1 = Circle(2) + c3 = c1 * 4 + assert c3.radius == 8 + + +def test_cir_equal(): + c1 = Circle(5) + c2 = Circle(5) + assert c1 == c2 + assert c1 <= c2 + + +def test_cir_greater_than(): + c1 = Circle(2) + c2 = Circle(5) + assert c2 > c1 + assert c2 >= c1 + + +def test_cir_less_than(): + c1 = Circle(2) + c2 = Circle(5) + assert c1 < c2 + assert c1 <= c2 + + +def test_cir_sort(): + cir_list = [Circle(10), Circle(5), Circle(25), Circle(20), Circle(15)] + circles = cir_list.sort(key=Circle.sort_key) + print(circles) + assert cir_list[0] == Circle(5) + assert cir_list[4] == Circle(25) + assert cir_list[4] > cir_list[0] + + +""" +Spheres Test +""" + + +def test_sph_str(): + c = Sphere(10) + assert str(c) == "Sphere with radius: 10" + + +def test_sph_repr(): + c = Sphere(15) + assert repr(c) == "Sphere(15)" + + +def test_sph_diameter(): + c = Sphere(5) + assert c.diameter == 10 + + +def test_sph_diameter_change(): + c = Sphere(10) + assert c.diameter == 20 + c.diameter = 20 + assert c.diameter == 20 + assert c.radius == 10 + + +def test_sph_area(): + c = Sphere(2) + assert round(c.area, 2) == 50.27 + + +def test_sph_vol(): + c = Sphere(4) + assert c.volume() == 268.082573106329