diff --git a/students/visokoo/00-README.md b/students/visokoo/00-README.md new file mode 100644 index 0000000..d4b3c73 --- /dev/null +++ b/students/visokoo/00-README.md @@ -0,0 +1,25 @@ +### About Me + +### Intro +I started out in the tech world as a Helpdesk Specialist and was given +the opportunity move into the DevOps space about 2 years ago. It was a +rollercoaster ride going from full Windows to Linux but I loved every +moment of it. + +I've done some basic coding with ruby for some day to day tasks but I'm +really hoping to be able to do more with python like writing full-fledged +web apps or just something better than your standard script. + +In my freetime I generally enjoy: + +- Playing boardgames... +- Playing console/PC games... +- Thinking about solutions for work... +- Eating too much... +- Outdoor activities such as hiking, snowboarding, etc. + +### Goal + +I've done some basic coding with ruby for some day to day tasks but I'm +really hoping to be able to do more with python like writing full-fledged +web apps/cli tools or just something better than your standard sc diff --git a/students/visokoo/Final/.gitignore b/students/visokoo/Final/.gitignore new file mode 100644 index 0000000..98e6ef6 --- /dev/null +++ b/students/visokoo/Final/.gitignore @@ -0,0 +1 @@ +*.db diff --git a/students/visokoo/Final/DBProcessor.py b/students/visokoo/Final/DBProcessor.py new file mode 100644 index 0000000..76c7df1 --- /dev/null +++ b/students/visokoo/Final/DBProcessor.py @@ -0,0 +1,136 @@ +""" +DB Processor Base class +""" + +import sqlite3 + + +class DBProcessor(object): + table_name = "" + columns = {} + + def __init__(self, db_con, db_name=":memory:"): + self.__db_name = db_name + self.__db_con = self.create_db(self.db_name) + + @property + def db_name(self): + return self.__db_name + + @property + def db_con(self): + return self.__db_con + + def create_db(self, db_name): + conn = sqlite3.connect(db_name) + return conn + + def execute_sql(self, statement): + db_con = self.db_con + try: + with db_con: + conn = db_con.cursor() + conn.execute(statement) + except Exception as e: + raise Exception(e) + return conn + + def create_table(self): + table_name = self.table_name + columns = self.columns + col_collection = "" + p_key = "" + if columns is None: + raise Exception("Must provide at least one column.") + for column, field_def in columns.items(): + if column == "Primary Key": + p_key = f", {column}({field_def})" + else: + col_collection += f"{column} {field_def}, " + sql_str = f"create table {table_name.capitalize()}({col_collection[:-2]}{p_key});" + return sql_str + + @staticmethod + def create_select_statement(table_name, columns=[], filters={}): + statement = "" + col_collection = "" + filter_statement = "" + if columns is None: + raise Exception("Must provide at least one column.") + for column in columns: + col_collection += f"{column}, " + col_collection = col_collection[:-2] + if filters != {}: + for f, v in filters.items(): + filter_statement += f"{f}='{v}'" + statement = f"select {col_collection} from {table_name} where {filter_statement};" + else: + statement = f"select {col_collection} from {table_name};" + return statement + + def create_insert_statement(self, table_name, columns={}): + table_name = self.table_name + statement = "" + col_collection = "" + val_collection = "" + if columns is None: + raise Exception("Must provide at least one column.") + for column, value in columns.items(): + col_collection += f"{column}, " + val_collection += f"'{value}', " + col_collection = col_collection[:-2] + val_collection = val_collection[:-2] + statement = f"insert into {table_name} ({col_collection}) values ({val_collection});" + return statement + + def create_delete_statement(self, table_name, filters={}): + statement = "" + filter_statement = "" + if filters is None: + raise Exception("You must provide a filter condition statement.") + for f, v in filters.items(): + filter_statement += f"{f}='{v}'" + statement = f"delete from {table_name} where {filter_statement};" + return statement + + def create_update_statement(self, table_name, columns={}, filters={}): + statement = "" + col_collection = "" + filter_statement = "" + if columns is None: + raise Exception("Must provide at least one column.") + for column, value in columns.items(): + col_collection += f"{column} = '{value}', " + col_collection = col_collection[:-2] + if filters is None: + raise Exception("You must provide a filter condition statement.") + for f, v in filters.items(): + filter_statement += f"{f}='{v}'" + statement = f"update {table_name} set {col_collection} where {filter_statement};" + return statement + + +class InventoryCountProcessor(DBProcessor): + table_name = "inventorycounts" + columns = { + "InventoryID": "int not null", + "ProductID": "int not null", + "InventoryCount": "int not null", + "Primary Key": "InventoryID, ProductID" + } + + +class ProductProcessor(DBProcessor): + table_name = "products" + columns = { + "ProductID": "primary key not null", + "ProductName": "varchar(100) not null" + } + + +class InventoryProcessor(DBProcessor): + table_name = "inventories" + columns = { + "InventoryID": "primary key not null", + "InventoryDate": "date not null" + } diff --git a/students/visokoo/Final/Inventory.py b/students/visokoo/Final/Inventory.py new file mode 100644 index 0000000..dcec2b9 --- /dev/null +++ b/students/visokoo/Final/Inventory.py @@ -0,0 +1,73 @@ +""" +Inventory class +""" +from datetime import datetime +from InventoryCount import InventoryCount +from Product import Product + + +class Inventory(object): + def __init__( + self, + inventory_id: int, + inventory_date: datetime.date, + inventory_count: InventoryCount = [None], + ): + self.__inventory_id = inventory_id + if inventory_date != datetime.strptime( + inventory_date, '%Y-%m-%d').strftime('%Y-%m-%d'): + raise ValueError("Not a proper date! E.g. '2020-01-01'") + else: + self.__inventory_date = inventory_date + if inventory_count is not [None]: + self.__inventory_count = inventory_count + + @property + def inventory_date(self): + return self.__inventory_date + + @inventory_date.setter + def inventory_date(self, inventory_date): + if inventory_date != datetime.strptime( + inventory_date, '%Y-%m-%d').strftime('%Y-%m-%d'): + raise TypeError("Not a proper date! E.g. '2020-01-01'") + else: + self.__inventory_date = inventory_date + + @property + def inventory_id(self): + return self.__inventory_id + + @property + def inventory_count(self): + return self.__inventory_count + + def __str__(self): + return f"Inventory ID: {self.__inventory_id} | Inventory Date: {self.__inventory_date} | Inventory Count [{self.__inventory_count}]" + + def __dict__(self): + return { + "Inventory ID": self.__inventory_id, + "Inventory Date": self.__inventory_date, + "Product Name": self.__inventory_count.product.product_name, + "Product ID": self.__inventory_count.product.product_id, + "Inventory Count": self.__inventory_count.product_inventory_count + } + + def __repr__(self): + return f"Inventory:[{self.__dict__()}]" + + +if __name__ == '__main__': + p1 = Product(1, "Mouse") + p2 = Product(2, "Keyboard") + p3 = Product(3, "Laptop") + ic1 = InventoryCount(p1, 2) + ic2 = InventoryCount(p2, 3) + ic3 = InventoryCount(p3, 5) + i1 = Inventory(1, "2020-01-02", ic1) + i2 = Inventory(2, "2018-12-31", ic2) + i3 = Inventory(3, "2017-06-22", ic3) + print(i1) + print(i2) + print(i3) \ No newline at end of file diff --git a/students/visokoo/Final/InventoryCount.py b/students/visokoo/Final/InventoryCount.py new file mode 100644 index 0000000..691ab07 --- /dev/null +++ b/students/visokoo/Final/InventoryCount.py @@ -0,0 +1,52 @@ +""" +InventoryCount class +""" + +from Product import Product + + +class InventoryCount(object): + def __init__(self, product: Product, product_inventory_count: int): + self.__product = product + self.__product_inventory_count = product_inventory_count + + @property + def product(self): + return self.__product + + @product.setter + def product(self, product: str): + self.__product = self.__product.strip() + + @property + def product_inventory_count(self): + return self.__product_inventory_count + + @product_inventory_count.setter + def product_inventory_count(self, product_inventory_count: int): + if type(product_inventory_count) is not int: + raise Exception("Product Inventory count must be an int.") + else: + self.__product_inventory_count = product_inventory_count + + def __str__(self): + return f"Product [{self.__product}] | Inventory Count: {self.__product_inventory_count}" + + def __dict__(self): + return { + "Product ID": self.__product.product_id, + "Product Name": self.__product.product_name, + "Inventory Count": self.__product_inventory_count + } + + def __repr__(self): + return f"Inventory Count:[{self.__dict__()}]" + + +if __name__ == '__main__': + p1 = Product(1, "Mouse") + p2 = Product(2, "Keyboard") + i1 = InventoryCount(p1, 2) + i2 = InventoryCount(p2, 40) + print(i1) + print(i2) diff --git a/students/visokoo/Final/Product.py b/students/visokoo/Final/Product.py new file mode 100644 index 0000000..dbfcdee --- /dev/null +++ b/students/visokoo/Final/Product.py @@ -0,0 +1,48 @@ +""" +Product class +""" + + +class Product(object): + def __init__(self, product_id: int, product_name: str): + self.__product_id = product_id + self.__product_name = product_name + + @property + def product_id(self): + return self.__product_id + + @product_id.setter + def product_id(self, product_id: int): + if type(product_id) is not int: raise TypeError("Requires integer!") + if product_id <= 0: + raise ValueError("Requires value greater than zero!") + else: + self.__product_id = product_id + + @property + def product_name(self): + return self.__product_name + + @product_name.setter + def product_name(self, product_name: str): + self.__product_name = product_name.strip() + + def __str__(self): + return f"Product ID: {self.__product_id} | Product Name: {self.__product_name}" + + def __dict__(self): + return { + "Product ID": self.__product_id, + "Product Name": self.__product_name + } + + def __repr__(self): + return f"Product:[{self.__dict__()}]" + + +if __name__ == '__main__': + p1 = Product(100, "Mouse") + p2 = Product(200, "Keyboard") + print(p1) + print(p2) diff --git a/students/visokoo/Final/main.py b/students/visokoo/Final/main.py new file mode 100644 index 0000000..07574c1 --- /dev/null +++ b/students/visokoo/Final/main.py @@ -0,0 +1,464 @@ +""" +main module to run the app +""" +import DBProcessor as dp +import Product as pr +import Inventory as ir +import InventoryCount as ic +from tkinter import ttk +import logging +import tkinter as tk + + +class MainWindow(): + def __init__(self): + self.window = tk.Tk() + self.window.title = "Inventory Manager" + self.window['padx'] = 10 + self.window['pady'] = 10 + self.notebook = ttk.Notebook(self.window) + self.configure_notebook_tabs(self.notebook) + + @staticmethod + def redirector(text_box, output): + text_box.insert(tk.END, output) + + def configure_notebook_tabs(self, notebook_tabs): + notebook_tabs.grid(row=4, column=2, padx=20, pady=10) + self.products_tabs(notebook_tabs) + self.inventory_tabs(notebook_tabs) + self.inventory_count_tabs(notebook_tabs) + self.quit_button() + return notebook_tabs + + def quit_button(self): + btn_quit = ttk.Button( + self.window, text="Quit", command=self.window.quit) + btn_quit.grid(row=7, column=2) + + def products_tabs(self, notebook_tabs): + products_tab = tk.Frame(notebook_tabs) + notebook_tabs.add(products_tab, text="Products", compound=tk.TOP) + + mtx_instructions = tk.Text( + products_tab, width=50, height=10, borderwidth=2, relief='ridge') + mtx_instructions.grid(row=6, column=2, sticky=tk.W) + mtx_instructions.insert( + tk.END, """Instructions:\n +Select: Returns all data in textbox\n +Insert: Fill in all values except Filter\n +Update: Fill in all values and type in the row ID for Filter\n +Delete: Fill in the row ID for Filter + """) + + lbl_product_id = ttk.Label( + products_tab, text="Product ID", width=20, anchor=tk.E) + lbl_product_id.grid(row=2, column=2, sticky=tk.E) + txt_product_id = ttk.Entry(products_tab, width=20) + txt_product_id.grid(row=2, column=3, columnspan=2, sticky=tk.E) + txt_product_id.insert(0, "") + + lbl_product_name = ttk.Label( + products_tab, text="Product Name", width=20, anchor=tk.E) + lbl_product_name.grid(row=3, column=2, sticky=tk.E) + txt_product_name = ttk.Entry(products_tab, width=20) + txt_product_name.grid(row=3, column=3, columnspan=2, sticky=tk.E) + txt_product_name.insert(0, "") + + lbl_product_filter = ttk.Label( + products_tab, text="Primary ID Filter", width=20, anchor=tk.E) + lbl_product_filter.grid(row=4, column=2, sticky=tk.E) + txt_product_filter = ttk.Entry(products_tab, width=20) + txt_product_filter.grid(row=4, column=3, columnspan=2, sticky=tk.E) + txt_product_filter.insert(0, "") + + mtx = tk.Text( + products_tab, width=40, height=10, borderwidth=2, relief='ridge') + mtx.grid(row=6, column=0, sticky=tk.W) + + table = "products" + + btn_sel_product_info = ttk.Button( + products_tab, + text="Select Product Info", + command=lambda: IOProcessor.sel(table, mtx, "products")) + btn_sel_product_info.grid(row=1, column=0) + + btn_ins_product_info = ttk.Button( + products_tab, + text="Insert Product Info", + command=lambda: IOProcessor.ins_product( + table, mtx, ttk.Entry.get(txt_product_id), + ttk.Entry.get(txt_product_name))) + btn_ins_product_info.grid(row=2, column=0) + + btn_upd_product_info = ttk.Button( + products_tab, + text="Update Product Info", + command=lambda: IOProcessor.upd_product( + table, mtx, ttk.Entry.get(txt_product_id), + ttk.Entry.get(txt_product_name), + ttk.Entry.get(txt_product_filter))) + btn_upd_product_info.grid(row=3, column=0) + + btn_del_product_info = ttk.Button( + products_tab, + text="Delete Product Info", + command=lambda: IOProcessor.del_product( + table, mtx, ttk.Entry.get(txt_product_id), + ttk.Entry.get(txt_product_name), + ttk.Entry.get(txt_product_filter))) + btn_del_product_info.grid(row=4, column=0) + + def inventory_tabs(self, notebook_tabs): + inventory_tab = tk.Frame(notebook_tabs) + notebook_tabs.add(inventory_tab, text="Inventory", compound=tk.TOP) + + mtx_instructions = tk.Text( + inventory_tab, width=50, height=10, borderwidth=2, relief='ridge') + mtx_instructions.grid(row=6, column=2, sticky=tk.W) + mtx_instructions.insert( + tk.END, """Instructions:\n +Select: Returns all data in textbox\n +Insert: Fill in all values except Filter\n +Update: Fill in all values and type in the row ID for Filter\n +Delete: Fill in the row ID for Filter + """) + + lbl_inventory_id = ttk.Label( + inventory_tab, text="Inventory ID", width=20, anchor=tk.E) + lbl_inventory_id.grid(row=2, column=2, sticky=tk.E) + txt_inventory_id = ttk.Entry(inventory_tab, width=20) + txt_inventory_id.grid(row=2, column=3, columnspan=2, sticky=tk.E) + txt_inventory_id.insert(0, "") + + lbl_inventory_date = ttk.Label( + inventory_tab, text="Inventory Date", width=20, anchor=tk.E) + lbl_inventory_date.grid(row=3, column=2, sticky=tk.E) + txt_inventory_date = ttk.Entry(inventory_tab, width=20) + txt_inventory_date.grid(row=3, column=3, columnspan=2, sticky=tk.E) + txt_inventory_date.insert(0, "") + + lbl_inventory_filter = ttk.Label( + inventory_tab, text="Primary ID Filter", width=20, anchor=tk.E) + lbl_inventory_filter.grid(row=4, column=2, sticky=tk.E) + txt_inventory_filter = ttk.Entry(inventory_tab, width=20) + txt_inventory_filter.grid(row=4, column=3, columnspan=2, sticky=tk.E) + txt_inventory_filter.insert(0, "") + + mtx = tk.Text( + inventory_tab, width=40, height=10, borderwidth=2, relief='ridge') + mtx.grid(row=6, column=0, sticky=tk.W) + table = "inventories" + + btn_sel_inventory_info = ttk.Button( + inventory_tab, + text="Select Inventory Info", + command=lambda: IOProcessor.sel(table, mtx, "inventory")) + btn_sel_inventory_info.grid(row=1, column=0) + + btn_ins_inventory_info = ttk.Button( + inventory_tab, + text="Insert Inventory Info", + command=lambda: IOProcessor.ins_inventory( + table, mtx, ttk.Entry.get(txt_inventory_id), + ttk.Entry.get(txt_inventory_date))) + btn_ins_inventory_info.grid(row=2, column=0) + + btn_upd_inventory_info = ttk.Button( + inventory_tab, + text="Update Inventory Info", + command=lambda: IOProcessor.upd_inventory( + table, mtx, ttk.Entry.get(txt_inventory_id), + ttk.Entry.get(txt_inventory_date), + ttk.Entry.get(txt_inventory_filter))) + btn_upd_inventory_info.grid(row=3, column=0) + + btn_del_inventory_info = ttk.Button( + inventory_tab, + text="Delete Inventory Info", + command=lambda: IOProcessor.del_inventory( + table, mtx, ttk.Entry.get(txt_inventory_id), + ttk.Entry.get(txt_inventory_date), + ttk.Entry.get(txt_inventory_filter))) + btn_del_inventory_info.grid(row=4, column=0) + + def inventory_count_tabs(self, notebook_tabs): + inventory_count_tab = tk.Frame(notebook_tabs) + notebook_tabs.add( + inventory_count_tab, text="Inventory Count", compound=tk.TOP) + + mtx_instructions = tk.Text( + inventory_count_tab, + width=50, + height=10, + borderwidth=2, + relief='ridge') + mtx_instructions.grid(row=6, column=2, sticky=tk.W) + mtx_instructions.insert( + tk.END, """Instructions:\n +Select: Returns all data in textbox\n +Insert: Fill in all values except Filter\n +Update: Fill in all values and type in the row ID for Filter\n +Delete: Fill in the row ID for Filter + """) + + lbl_inventory_id = ttk.Label( + inventory_count_tab, text="Inventory ID", width=20, anchor=tk.E) + lbl_inventory_id.grid(row=2, column=2, sticky=tk.E) + txt_inventory_id = ttk.Entry(inventory_count_tab, width=20) + txt_inventory_id.grid(row=2, column=3, columnspan=2, sticky=tk.E) + txt_inventory_id.insert(0, "") + + lbl_inventory_count = ttk.Label( + inventory_count_tab, text="Inventory Count", width=20, anchor=tk.E) + lbl_inventory_count.grid(row=3, column=2, sticky=tk.E) + txt_inventory_count = ttk.Entry(inventory_count_tab, width=20) + txt_inventory_count.grid(row=3, column=3, columnspan=2, sticky=tk.E) + txt_inventory_count.insert(0, "") + + lbl_product_id = ttk.Label( + inventory_count_tab, text="Product ID", width=20, anchor=tk.E) + lbl_product_id.grid(row=4, column=2, sticky=tk.E) + txt_product_id = ttk.Entry(inventory_count_tab, width=20) + txt_product_id.grid(row=4, column=3, columnspan=2, sticky=tk.E) + txt_product_id.insert(0, "") + + lbl_inventory_count_filter = ttk.Label( + inventory_count_tab, + text="Primary ID Filter", + width=20, + anchor=tk.E) + lbl_inventory_count_filter.grid(row=5, column=2, sticky=tk.E) + txt_inventory_count_filter = ttk.Entry(inventory_count_tab, width=20) + txt_inventory_count_filter.grid( + row=5, column=3, columnspan=2, sticky=tk.E) + txt_inventory_count_filter.insert(0, "") + + mtx = tk.Text( + inventory_count_tab, + width=40, + height=10, + borderwidth=2, + relief='ridge') + mtx.grid(row=7, column=0, sticky=tk.W) + table = "inventorycounts" + + btn_sel_inventory_count_info = ttk.Button( + inventory_count_tab, + text="Select Inventory Count Info", + command=lambda: IOProcessor.sel(table, mtx, "inventorycounts")) + btn_sel_inventory_count_info.grid(row=1, column=0) + + btn_ins_inventory_count_info = ttk.Button( + inventory_count_tab, + text="Insert Inventory Count Info", + command=lambda: IOProcessor.ins_inventory_count( + table, mtx, ttk.Entry.get(txt_inventory_id), + ttk.Entry.get(txt_product_id), + ttk.Entry.get(txt_inventory_count))) + btn_ins_inventory_count_info.grid(row=2, column=0) + + btn_upd_inventory_count_info = ttk.Button( + inventory_count_tab, + text="Update Inventory Count Info", + command=lambda: IOProcessor.upd_inventory_count( + table, mtx, ttk.Entry.get(txt_inventory_id), + ttk.Entry.get(txt_product_id), + ttk.Entry.get(txt_inventory_count), + ttk.Entry.get(txt_inventory_count_filter))) + btn_upd_inventory_count_info.grid(row=3, column=0) + + btn_del_inventory_count_info = ttk.Button( + inventory_count_tab, + text="Delete Inventory Count Info", + command=lambda: IOProcessor.del_inventory_count( + table, mtx, ttk.Entry.get(txt_inventory_id), + ttk.Entry.get(txt_inventory_id), ttk.Entry.get(txt_product_id), + ttk.Entry.get(txt_inventory_count_filter))) + btn_del_inventory_count_info.grid(row=4, column=0) + + +class IOProcessor(): + @staticmethod + def sel(table, text_box, processor): + try: + select_statement = dp.DBProcessor.create_select_statement( + table, "*") + output = db.execute_sql(select_statement) + MainWindow.redirector(text_box, select_statement + "\n") + obj = [] + for data in output: + if processor == "products": + obj.append(pr.Product(data[0], data[1])) + elif processor == "inventory": + obj.append(ir.Inventory(data[0], data[1])) + elif processor == "inventorycounts": + obj.append(ir.InventoryCount(data[0], data[1])) + MainWindow.redirector(text_box, data) + MainWindow.redirector(text_box, "\n") + MainWindow.redirector(text_box, obj) + MainWindow.redirector(text_box, "\n") + + except Exception as e: + MainWindow.redirector(text_box, e) + + @staticmethod + def ins_product(table, text_box, id, name): + try: + data_dict = dict(productid=id, productname=name) + insert_statement = pp.create_insert_statement(table, data_dict) + output = pp.execute_sql(insert_statement) + MainWindow.redirector(text_box, insert_statement + "\n") + + for data in output: + MainWindow.redirector(text_box, data) + MainWindow.redirector(text_box, "\n") + except Exception as e: + MainWindow.redirector(text_box, e) + + @staticmethod + def upd_product(table, text_box, id, name, filters): + try: + data_dict = dict(productname=name) + filter_dict = dict(productid=filters) + update_statement = pp.create_update_statement( + table, data_dict, filter_dict) + output = pp.execute_sql(update_statement) + MainWindow.redirector(text_box, update_statement + "\n") + + for data in output: + MainWindow.redirector(text_box, data) + MainWindow.redirector(text_box, "\n") + except Exception as e: + MainWindow.redirector(text_box, e) + + @staticmethod + def del_product(table, text_box, id, name, filters): + try: + filter_dict = dict(productid=filters) + delete_statement = pp.create_delete_statement(table, filter_dict) + output = pp.execute_sql(delete_statement) + MainWindow.redirector(text_box, delete_statement + "\n") + + for data in output: + MainWindow.redirector(text_box, data) + MainWindow.redirector(text_box, "\n") + except Exception as e: + MainWindow.redirector(text_box, e) + + @staticmethod + def ins_inventory(table, text_box, id, date): + try: + data_dict = dict(inventoryid=id, inventorydate=date) + insert_statement = ip.create_insert_statement(table, data_dict) + output = ip.execute_sql(insert_statement) + MainWindow.redirector(text_box, insert_statement + "\n") + + for data in output: + MainWindow.redirector(text_box, data) + MainWindow.redirector(text_box, "\n") + except Exception as e: + MainWindow.redirector(text_box, e) + + @staticmethod + def upd_inventory(table, text_box, id, date, filters): + try: + data_dict = dict(inventoryid=id, inventorydate=date) + filter_dict = dict(inventoryid=filters) + + update_statement = ip.create_update_statement( + table, data_dict, filter_dict) + output = ip.execute_sql(update_statement) + MainWindow.redirector(text_box, update_statement + "\n") + + for data in output: + MainWindow.redirector(text_box, data) + MainWindow.redirector(text_box, "\n") + except Exception as e: + MainWindow.redirector(text_box, e) + + @staticmethod + def del_inventory(table, text_box, id, date, filters): + try: + filter_dict = dict(inventoryid=filters) + + delete_statement = ip.create_delete_statement(table, filter_dict) + output = ip.execute_sql(delete_statement) + MainWindow.redirector(text_box, delete_statement + "\n") + + for data in output: + MainWindow.redirector(text_box, data) + MainWindow.redirector(text_box, "\n") + except Exception as e: + MainWindow.redirector(text_box, e) + + @staticmethod + def ins_inventory_count(table, text_box, inventory_id, product_id, count): + try: + data_dict = dict( + inventoryid=inventory_id, productid=product_id, inventorycount=count) + insert_statement = ic.create_insert_statement(table, data_dict) + output = ic.execute_sql(insert_statement) + MainWindow.redirector(text_box, insert_statement + "\n") + + for data in output: + MainWindow.redirector(text_box, data) + MainWindow.redirector(text_box, "\n") + except Exception as e: + MainWindow.redirector(text_box, e) + + @staticmethod + def upd_inventory_count(table, text_box, inventory_id, product_id, count, + filters): + try: + data_dict = dict( + inventoryid=inventory_id, + productid=product_id, + inventorycount=count) + filter_dict = dict(inventoryid=filters) + + update_statement = ic.create_update_statement( + table, data_dict, filter_dict) + output = ip.execute_sql(update_statement) + MainWindow.redirector(text_box, update_statement + "\n") + + for data in output: + MainWindow.redirector(text_box, data) + MainWindow.redirector(text_box, "\n") + except Exception as e: + MainWindow.redirector(text_box, e) + + @staticmethod + def del_inventory_count(table, text_box, inventory_id, product_id, count, + filters): + try: + filter_dict = dict(inventoryid=filters) + + delete_statement = ic.create_delete_statement(table, filter_dict) + output = ic.execute_sql(delete_statement) + MainWindow.redirector(text_box, delete_statement + "\n") + + for data in output: + MainWindow.redirector(text_box, data) + MainWindow.redirector(text_box, "\n") + except Exception as e: + MainWindow.redirector(text_box, e) + + +if __name__ == '__main__': + db = dp.DBProcessor(db_con="visokoo", db_name="visokoo.db") + pp = dp.ProductProcessor(db_con="visokoo", db_name="visokoo.db") + ip = dp.InventoryProcessor(db_con="visokoo", db_name="visokoo.db") + ic = dp.InventoryCountProcessor(db_con="visokoo", db_name="visokoo.db") + product_tbl = pp.create_table() + inventory_tbl = ip.create_table() + inventory_count_tbl = ic.create_table() + try: + for tbl in product_tbl, inventory_tbl, inventory_count_tbl: + db.execute_sql(tbl) + except Exception: + print("Tables already created, opening the GUI.") + + window = MainWindow() + window.window.mainloop() diff --git a/students/visokoo/Final/test_DBProcessor.py b/students/visokoo/Final/test_DBProcessor.py new file mode 100644 index 0000000..1b4960f --- /dev/null +++ b/students/visokoo/Final/test_DBProcessor.py @@ -0,0 +1,107 @@ +""" +test code for DBProcessor.py +""" + +from DBProcessor import DBProcessor, ProductProcessor, InventoryCountProcessor, InventoryProcessor +import pytest +import os + + +def test_init(): + DBProcessor(db_con="visokoo", db_name="visokoo_test.db") + assert os.listdir(".").count('visokoo_test.db') == 1 + + +def test_create_product_table(): + pp = ProductProcessor(db_con="visokoo", db_name="visokoo.db") + pp.table_name = "blah_test" + pp.columns = { + "ProductID": "primary key not null", + "ProductName": "varchar(100) not null" + } + assert pp.create_table( + ) == "create table Blah_test(ProductID primary key not null, ProductName varchar(100) not null);" + + +def test_create_inventory_table(): + ip = InventoryProcessor(db_con="visokoo", db_name="visokoo.db") + ip.table_name = "blah_test_inv" + ip.columns = { + "InventoryID": "primary key not null", + "InventoryDate": "date not null" + } + assert ip.create_table( + ) == "create table Blah_test_inv(InventoryID primary key not null, InventoryDate date not null);" + + +def test_create_inventorycounts_table(): + ic = InventoryCountProcessor(db_con="visokoo", db_name="visokoo.db") + ic.table_name = "blah_test_invcount" + ic.columns = { + "InventoryID": "int not null", + "ProductID": "int not null", + "InventoryCount": "int not null", + "Primary Key": "InventoryID, ProductID" + } + assert ic.create_table( + ) == "create table Blah_test_invcount(InventoryID int not null, ProductID int not null, InventoryCount int not null, Primary Key(InventoryID, ProductID));" + + +def test_select_statement_generator_with_columns(): + pp = ProductProcessor(db_con="visokoo") + assert pp.create_select_statement( + pp.table_name, + pp.columns) == "select ProductID, ProductName from products;" + + +def test_select_statement_generator_all(): + pp = ProductProcessor(db_con="visokoo") + assert pp.create_select_statement(pp.table_name, + "*") == "select * from products;" + + +def test_insert_statement(): + pp = ProductProcessor(db_con="visokoo") + assert pp.create_insert_statement( + pp.table_name, + {"productname": "arya" + }) == "insert into products (productname) values ('arya');" + + +def test_insert_statement_without_columns(): + pp = ProductProcessor(db_con="visokoo") + with pytest.raises(Exception): + assert pp.create_insert_statement( + pp.table_name, + None) == "insert into products (productname) values ('arya');" + + +def test_delete_statement(): + pp = ProductProcessor(db_con="visokoo") + assert pp.create_delete_statement( + pp.table_name, + {"productid": 1}) == "delete from products where productid='1';" + + +def test_delete_statement_without_filters(): + pp = ProductProcessor(db_con="visokoo") + with pytest.raises(Exception): + assert pp.create_delete_statement( + pp.table_name, + None) == "insert into products (productname) values ('arya');" + + +def test_update_statement(): + pp = ProductProcessor(db_con="visokoo") + assert pp.create_update_statement( + pp.table_name, {"productname": "arya"}, + {"productid": 1 + }) == "update products set productname = 'arya' where productid='1';" + + +def test_update_statement_without_filters(): + pp = ProductProcessor(db_con="visokoo") + with pytest.raises(Exception): + assert pp.create_update_statement( + pp.table_name, + None) == "insert into products (productname) values ('arya');" diff --git a/students/visokoo/lesson1_assignments/break_me.py b/students/visokoo/lesson1_assignments/break_me.py new file mode 100644 index 0000000..9b86981 --- /dev/null +++ b/students/visokoo/lesson1_assignments/break_me.py @@ -0,0 +1,28 @@ +''' +---------------------------------------------------- +Assignment: Explore Errors +Author: visokoo | Created: 1/13/2019 +ChangeLog: 1/13/2019, Created file + +Create a script with four exceptions +---------------------------------------------------- + ''' + +def name_error(x): + print(y) + +#name_error(x) + +def value_error(x): + int(x) + print(x, "blah") + +#value_error("duh") + +def zero_division(x): + return x / 0 + +#zero_division(2) + +#def syntax_error(x) +# print(x) diff --git a/students/visokoo/lesson1_assignments/diff21.py b/students/visokoo/lesson1_assignments/diff21.py new file mode 100644 index 0000000..514de27 --- /dev/null +++ b/students/visokoo/lesson1_assignments/diff21.py @@ -0,0 +1,20 @@ +''' +---------------------------------------------------- +Assignment: Python Push Ups - Warmup Part 1 - diff21.py +Author: visokoo | Created: 1/9/2019 +ChangeLog: 1/9/2019, Created file + +Given an int n, return the absolute difference between n and 21, +except return double the absolute difference if n is over 21. + +diff21(19) → 2 +diff21(10) → 11 +diff21(21) → 0 +---------------------------------------------------- + ''' + +def diff21(n): + if n > 21: + return abs(21-n)*2 + else: + return abs(21-n) diff --git a/students/visokoo/lesson1_assignments/makes10.py b/students/visokoo/lesson1_assignments/makes10.py new file mode 100644 index 0000000..7e5e16a --- /dev/null +++ b/students/visokoo/lesson1_assignments/makes10.py @@ -0,0 +1,21 @@ +''' +---------------------------------------------------- +Assignment: Python Push Ups - Warmup Part 1 - makes10.py +Author: visokoo | Created: 1/9/2019 +ChangeLog: 1/9/2019, Created file + +Given 2 ints, a and b, return True if one if them is 10 or if their sum is 10. + +makes10(9, 10) → True +makes10(9, 9) → False +makes10(1, 9) → True +---------------------------------------------------- + ''' + +def makes10(a,b): + if a == 10 or b == 10: + return True + elif a + b == 10: + return True + else: + return False diff --git a/students/visokoo/lesson1_assignments/near_hundred.py b/students/visokoo/lesson1_assignments/near_hundred.py new file mode 100644 index 0000000..aadcbeb --- /dev/null +++ b/students/visokoo/lesson1_assignments/near_hundred.py @@ -0,0 +1,17 @@ +''' +---------------------------------------------------- +Assignment: Python Push Ups - Warmup Part 1 - near_hundred.py +Author: visokoo | Created: 1/9/2019 +ChangeLog: 1/9/2019, Created file + +Given an int n, return True if it is within 10 of 100 or 200. + +Note: abs(num) computes the absolute value of a number. +near_hundred(93) → True +near_hundred(90) → True +near_hundred(89) → False +---------------------------------------------------- + ''' + +def near_hundred(n): + return abs(100 - n) <= 10 or abs(200-n) <= 10 diff --git a/students/visokoo/lesson1_assignments/parrot_trouble.py b/students/visokoo/lesson1_assignments/parrot_trouble.py new file mode 100644 index 0000000..a02c683 --- /dev/null +++ b/students/visokoo/lesson1_assignments/parrot_trouble.py @@ -0,0 +1,22 @@ +''' +---------------------------------------------------- +Assignment: Python Push Ups - Warmup Part 1 - parrot_trouble.py +Author: visokoo | Created: 1/9/2019 +ChangeLog: 1/9/2019, Created file + +We have a loud talking parrot. The "hour" parameter is the current +hour time in the range 0..23. We are in trouble if the parrot is +talking and the hour is before 7 or after 20. Return True if we are +in trouble. + +parrot_trouble(True, 6) → True +parrot_trouble(True, 7) → False +parrot_trouble(False, 6) → False +---------------------------------------------------- + ''' + +def parrot_trouble(talking, hour): + if talking == True and (hour < 7 or hour > 20): + return True + else: + return False diff --git a/students/visokoo/lesson1_assignments/pos_neg.py b/students/visokoo/lesson1_assignments/pos_neg.py new file mode 100644 index 0000000..1043e8c --- /dev/null +++ b/students/visokoo/lesson1_assignments/pos_neg.py @@ -0,0 +1,22 @@ +''' +---------------------------------------------------- +Assignment: Python Push Ups - Warmup Part 1 - pos_neg.py +Author: visokoo | Created: 1/9/2019 +ChangeLog: 1/9/2019, Created file + +Given 2 int values, return True if one is negative and one is +positive. Except if the parameter "negative" is True, then +return True only if both are negative. + +pos_neg(1, -1, False) → True +pos_neg(-1, 1, False) → True +pos_neg(-4, -5, True) → True +---------------------------------------------------- + ''' + +def pos_neg(a, b, negative): + if negative == True: + return (a < 0 and b < 0) + else: + return (a < 0 and b > 0) or (a > 0 and b < 0) + diff --git a/students/visokoo/lesson1_assignments/sum_double.py b/students/visokoo/lesson1_assignments/sum_double.py new file mode 100644 index 0000000..f659396 --- /dev/null +++ b/students/visokoo/lesson1_assignments/sum_double.py @@ -0,0 +1,19 @@ +''' +---------------------------------------------------- +Assignment: Python Push Ups - Warmup Part 1 - sum_double.py +Author: visokoo | Created: 1/9/2019 +ChangeLog: 1/9/2019, Created file + +Given two int values, return their sum. Unless the two values +are the same, then return double their sum. + +sum_double(1, 2) → 3 +sum_double(3, 2) → 5 +sum_double(2, 2) → 8 +---------------------------------------------------- + ''' + +def sum_double(a, b): + if a == b: + return ((a + b) * 2) + return a + b diff --git a/students/visokoo/lesson2_assignments/fizz_buzz.py b/students/visokoo/lesson2_assignments/fizz_buzz.py new file mode 100644 index 0000000..74c4888 --- /dev/null +++ b/students/visokoo/lesson2_assignments/fizz_buzz.py @@ -0,0 +1,24 @@ +''' +---------------------------------------------------- +Assignment: fizz_buzz.py +Author: visokoo | Created: 1/20/2019 +ChangeLog: 1/20/2019, Created file + +Write a program that prints the numbers from 1 - 100 inclusive. +For multiples of three, print "Fizz", multiples of five, print "Buzz." +Numbers that are multiples of both, print "FizzBuzz." +''' + +def fizz_buzz(n): + for num in range(n): + if num % 3 == 0 and num % 5 == 0: + msg = "FizzBuzz" + elif num % 3 == 0: + msg = "Fizz" + elif num % 5 == 0: + msg = "Buzz" + else: + msg = num + print(msg) + +fizz_buzz(101) \ No newline at end of file diff --git a/students/visokoo/lesson2_assignments/front_times.py b/students/visokoo/lesson2_assignments/front_times.py new file mode 100644 index 0000000..1aa16da --- /dev/null +++ b/students/visokoo/lesson2_assignments/front_times.py @@ -0,0 +1,17 @@ +''' +---------------------------------------------------- +Assignment: Python Push Ups - Warmup Part 2 - front_times.py +Author: visokoo | Created: 1/16/2019 +ChangeLog: 1/16/2019, Created file + +Given a string and a non-negative int n, we'll say that the front of the string is the first 3 chars, or whatever is there if the string is less than length 3. Return n copies of the front; + +front_times('Chocolate', 2) → 'ChoCho' +front_times('Chocolate', 3) → 'ChoChoCho' +front_times('Abc', 3) → 'AbcAbcAbc' +---------------------------------------------------- +''' + +def front_times(str, n): + front = str[:3] + return front * n diff --git a/students/visokoo/lesson2_assignments/grid_printer.py b/students/visokoo/lesson2_assignments/grid_printer.py new file mode 100644 index 0000000..5940c5a --- /dev/null +++ b/students/visokoo/lesson2_assignments/grid_printer.py @@ -0,0 +1,55 @@ +''' +---------------------------------------------------- +Assignment: grid_printer.py +Author: visokoo | Created: 1/16/2019 +ChangeLog: 1/16/2019, Created file + +Write a function that draws a grid like the following: ++ - - - - + - - - - + +| | | +| | | +| | | +| | | ++ - - - - + - - - - + +| | | +| | | +| | | +| | | ++ - - - - + - - - - + +---------------------------------------------------- +''' + +def borders(n): + return " + " + " - " * n + +def pillars(n): + return " | " + " " * (n * 3) + +def grid_printer(n): + print(borders(n) * 2, "+") + for i in range(n): + print(pillars(n) * 2, "|", sep=" ") + print(borders(n) * 2, "+") + for i in range(n): + print(pillars(n) * 2, "|", sep=" ") + print(borders(n) * 2, "+") + +def fancy_grid_printer(table_size, grid_size): + print(borders(grid_size) * table_size, "+") + # print borders + for i in range(table_size): + # print pillars + for j in range(grid_size): + print(pillars(grid_size) * table_size, "|", sep=" ") + print(borders(grid_size) * table_size, "+") + +while True: + prompt = input("Standard 2x2 table or fancy table?" + "\n" + "Options: standard/fancy/exit ") + if prompt.lower() == "standard": + size = input("Pick a size for your table, larger the number, larger the table. 1 - infinity ") + grid_printer(int(size)) + elif prompt.lower() == "fancy": + total_table_size = input("Number of rows and columns? 1 - infinity ") + width = input("Width of each grid? 1 - infinity ") + fancy_grid_printer(int(total_table_size), int(width)) + else: break diff --git a/students/visokoo/lesson2_assignments/series.py b/students/visokoo/lesson2_assignments/series.py new file mode 100644 index 0000000..c607d09 --- /dev/null +++ b/students/visokoo/lesson2_assignments/series.py @@ -0,0 +1,86 @@ +''' +---------------------------------------------------- +Assignment: series.py +Author: visokoo | Created: 1/17/2019 +ChangeLog: 1/17/2019, Created file + +Write 3 functions: fibonacci, lucas, and sum_series and some tests +for the functionality. + +Fibonacci: +The Fibonacci Series is a numeric series starting with the integers 0 and 1. +In this series, the next integer is determined by summing the previous two. +This gives us: +0, 1, 1, 2, 3, 5, 8, 13, ... + +Lucas: +The Lucas Numbers are a related series of integers that start with the values +2 and 1 rather than 0 and 1. The resulting series looks like this: +2, 1, 3, 4, 7, 11, 18, 29, ... + +sum_series: +Create a function with one required parameter and two optional parameters. The required +parameter will determine which element in the series to print. The two optional parameters +will have default values of 0 and 1 and will determine the first two values for the series +to be produced. +''' + +def fibonacci(n): + ''' + fib(n) = fib(n-2) + fib(n-1) + Given the value n, call the sum_series function with that value + and return the fibonacci sequence at the nth index + ''' + return sum_series(n) + +def lucas(n): + ''' + Formula is the same as fib, but starting values are 2 and 1 respectively + Given the value n, call the sum_series function with that value and + return the lucas sequence at the nth index + ''' + return sum_series(n, v1=2, v2=0) + +def sum_series(n, v1=0, v2=1): + ''' + compute the nth value of a summation series. + + :param v1=0: value of zeroth element in the series + :param v2=1: value of first element in the series + + This function should generalize the fibonacci() and the lucas(), + so that this function works for any first two numbers for a sum series. + Once generalized that way, sum_series(n, 0, 1) should be equivalent to + fibonacci(n). And sum_series(n, 2, 1) should be equivalent to lucas(n). + ''' + if v1 is 2: + if n == 0: + return 2 + elif n == 1: + return 1 + else: + return sum_series(n - 2, v1=v1, v2=v2) + sum_series(n - 1, v1=v1, v2=v2) + else: + if n > 1: + return sum_series(n - 2) + sum_series(n - 1) + return n + +if __name__ == "__main__": + # run some tests to make sure the fibonacci() is returning expected values. + assert fibonacci(0) == 0 + assert fibonacci(1) == 1 + assert fibonacci(2) == 1 + assert fibonacci(3) == 2 + assert fibonacci(4) == 3 + assert fibonacci(5) == 5 + assert fibonacci(6) == 8 + assert fibonacci(7) == 13 + # run some tests to make sure the lucas() is returning expected values. + assert lucas(0) == 2 + assert lucas(1) == 1 + assert lucas(4) == 7 + # make sure sum_series outputs match fibonacci() and lucas() outputs. + assert sum_series(5) == fibonacci(5) + assert sum_series(5, 2, 1) == lucas(5) + + print("Tests passed.") \ No newline at end of file diff --git a/students/visokoo/lesson2_assignments/string_bits.py b/students/visokoo/lesson2_assignments/string_bits.py new file mode 100644 index 0000000..634a52f --- /dev/null +++ b/students/visokoo/lesson2_assignments/string_bits.py @@ -0,0 +1,21 @@ +''' +---------------------------------------------------- +Assignment: Python Push Ups - Warmup Part 2 - string_bits.py +Author: visokoo | Created: 1/16/2019 +ChangeLog: 1/16/2019, Created file + +Given a string, return a new string made of every other char starting with the first, so "Hello" yields "Hlo". + +string_bits('Hello') → 'Hlo' +string_bits('Hi') → 'H' +string_bits('Heeololeo') → 'Hello' + +---------------------------------------------------- + +''' + +def string_bits(str): + new_str = "" + for position in range(0, len(str), 2): + new_str += str[position] + return new_str diff --git a/students/visokoo/lesson2_assignments/string_times.py b/students/visokoo/lesson2_assignments/string_times.py new file mode 100644 index 0000000..afdc46e --- /dev/null +++ b/students/visokoo/lesson2_assignments/string_times.py @@ -0,0 +1,17 @@ +''' +---------------------------------------------------- +Assignment: Python Push Ups - Warmup Part 2 - string_times.py +Author: visokoo | Created: 1/16/2019 +ChangeLog: 1/16/2019, Created file + +Given a string and a non-negative int n, return a larger string that is n copies of the original string. + +string_times('Hi', 2) → 'HiHi' +string_times('Hi', 3) → 'HiHiHi' +string_times('Hi', 1) → 'Hi' +---------------------------------------------------- + +''' + +def string_times(str, n): + return str * n diff --git a/students/visokoo/lesson3_assignments/list_lab.py b/students/visokoo/lesson3_assignments/list_lab.py new file mode 100755 index 0000000..0f1f483 --- /dev/null +++ b/students/visokoo/lesson3_assignments/list_lab.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +''' +---------------------------------------------------- +Assignment: list_lab.py +Author: visokoo | Created: 1/25/2019 +ChangeLog: 1/25/2019, Created file + +Fun with lists, complete four series. +---------------------------------------------------- +''' + +# Series 1 +print("Series 1") +lstfruits = ["Apples", "Pears", "Oranges", "Peaches"] +print("Current fruits: ", lstfruits, sep="\n") + +# Add a to the fruit the list +fruit = input("Enter in another fruit to add to the list: ") +lstfruits.append(fruit.capitalize()) +print(lstfruits) + +# Print the fruit at the given index number +number = input(f"Give me a number, 1 - {len(lstfruits)}: ") +print(f"The fruit at index {number} is {lstfruits[int(number)-1]}.") + +# Add a fruit to the beginning of the list using the + operator +fruit2 = input("Enter another fruit to add to the front of the list: ") +lstfruits = [fruit2.capitalize()] + lstfruits +print(lstfruits) + +# Add a fruit to the beginning of the list using .insert() +fruit3 = input("Enter another fruit to add to the front of the list: ") +lstfruits.insert(0, fruit3.capitalize()) +print(lstfruits) + +# Display all fruits that begin with the letter 'P' +print("Fruits that begin with the letter 'P':") +for fruit in lstfruits: + if "P" in fruit: + print(fruit) + else: continue + +# Series 2 +print("Series 2") +print("Displaying the list:", lstfruits) +# removing the last item +lstfruits.pop() +print("Removed the last item.", lstfruits) + +# Keep asking user to delete a fruit until a match is found and delete all occurrences +lstdupe = lstfruits * 2 +print("Duplicated list:", lstdupe) +while True: + fruit_to_delete = input("Which fruit do you want to delete? Type the name: ").capitalize() + if fruit_to_delete in lstdupe: + occurrence = lstdupe.count(fruit_to_delete) + while occurrence > 0: + lstdupe.remove(fruit_to_delete) + occurrence -= 1 + print(f"Any occurrence of {fruit_to_delete} has been deleted.") + print(lstdupe) + break + else: + print("Given fruit not in list. Enter another fruit.") + continue + +# Series 3 +print("Series 3") +while True: + lst_of_fruits_to_remove = [] + for fruit in lstfruits: + prompt = input(f"Do you like {fruit.lower()}? ").lower() + if prompt == "yes": + continue + elif prompt == "no": + lst_of_fruits_to_remove += [fruit] + else: + confirm = input("Please answer with 'yes' or 'no': ").lower() + if confirm == "yes": + continue + else: + lst_of_fruits_to_remove += [fruit] + # had to remove them at the end of the loop because fruits were getting + # skipped over if I removed them in the middle of the for loop + for fruit in lst_of_fruits_to_remove: + lstfruits.remove(fruit) + break +print(lstfruits) + +# Series 4 +print("Series 4") +# Create a new empty list +newlstfruits = [] +for fruit in lstfruits: + # For each item in the old list, flip the letters and add the new item into the new list + flipped = fruit[::-1] + newlstfruits += [flipped] +print("Original list:", lstfruits) +print("Copied list:", newlstfruits) diff --git a/students/visokoo/lesson3_assignments/mailroom-p1.py b/students/visokoo/lesson3_assignments/mailroom-p1.py new file mode 100644 index 0000000..d47d620 --- /dev/null +++ b/students/visokoo/lesson3_assignments/mailroom-p1.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +''' +---------------------------------------------------- +Assignment: mailroom-p1.py +Author: visokoo | Created: 1/25/2019 +ChangeLog: 1/25/2019, Created file + +You work in the mail room at a local charity. Part of your job is to write incredibly boring, +repetitive emails thanking your donors for their generous gifts. You are tired of doing this +over and over again, so you’ve decided to let Python help you out of a jam and do your work for you. +---------------------------------------------------- +''' + +import sys + +donors = [ + ["William Gates, III", 458978.00, 3], + ["Mark Zuckerberg", 54687.00, 2], + ["Jeff Bezos", 87485.50, 2], + ["Paul Allen", 55000.00, 1], + ["Arya Stark", 60000.00, 1] +] + +def display_donors(donordb): + for row in donordb: + print(row[0]) + +def find_donor(donor_name): + for row in donors: + if row[0].lower() == donor_name.lower(): + return donor_name + return donor_name + +def add_donation(donor_name, donation): + donor_count = donor_history(donor_name) + for row in donors: + if row[0].lower() == donor_name.lower(): + new_amount = row[1] + donation + # remove old donor amount from list & insert new_amount + row.pop(1) + row.insert(1, new_amount) + # remove old donor acount from list and insert new + row.pop(2) + row.insert(2, donor_count) + return new_amount + else: + donors.append([donor_name, donation, donor_count]) + return donation + +def donor_history(donor_name): + for row in donors: + if row[0].lower() == donor_name.lower(): + return row[2] + 1 + return 1 + +def generate_email(donor_name, donation, total_donation): + print(f"\nDear {donor_name.split(' ')[0].capitalize()},\n\n" + f"Thank you so much for your generous donation amount of ${donation:.2f}.\n" + f"This donation brings your total donation amount to ${total_donation:.2f}.\n" + f"Your help will directly benefit the thousands of starving families this winter.\n" + f"Thank you again for making a difference in their lives.\n\n" + f"Cheers,\n" + f"A grateful volunteer\n") + +def generate_report(donordb): + sorted_donors = sorted(donordb, key=lambda r: r[1], reverse=True) + print("{:25}| {:^16} | {:^11} | {}".format("Donor Name", + "Total Given", + "Num Gifts", + "Average Gift"), + "-" * 72, sep="\n") + for row in sorted_donors: + print("{:25}| ${:>15.2f} | {:>11} | ${:11.2f}".format(row[0], row[1], row[2], row[1] / row[2])) + print() + +def main(): + while True: + option = int(input("Select an option:" + "\n" + + "1. Send a Thank You" + "\n" + + "2. Create a Report" + "\n" + + "3. Quit" + "\n" + + "Choice: ")) + if option == 1: + full_name = input("Name of the giftee (Enter 'list' if you want to see existing donors): ").title() + if full_name == "List": + display_donors(donors) + continue + else: + donor_name = find_donor(full_name) + donation_amount = float(input("Donation Amount: ")) + total_donation_amount = add_donation(donor_name, donation_amount) + generate_email(donor_name, donation_amount, total_donation_amount) + elif option == 2: + generate_report(donors) + else: + print("Quitting...") + break + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/students/visokoo/lesson3_assignments/slicing.py b/students/visokoo/lesson3_assignments/slicing.py new file mode 100644 index 0000000..ed25187 --- /dev/null +++ b/students/visokoo/lesson3_assignments/slicing.py @@ -0,0 +1,72 @@ +''' +---------------------------------------------------- +Assignment: slicing.py +Author: visokoo | Created: 1/23/2019 +ChangeLog: 1/23/2019, Created file + +Write some functions that take a sequence as an argument, and return a copy of that sequence: + +- with the first and last items exchanged. +- with every other item removed. +- with the first 4 and the last 4 items removed, and then every other item in the remaining sequence. +- with the elements reversed (just with slicing). +- with the last third, then first third, then the middle third in the new order. +---------------------------------------------------- +''' + +def flipped(seq): + ''' + first = first item of the parameter seq + last = last item of the parameter seq + middle = the rest of the items besides above 2 + + return a string with last_char and first_char flipped + ''' + first = seq[0:1] + last = seq[-1:] + middle = seq[1:-1] + return last + middle + first + +def every_other(seq): + ''' + utilizing stepping in slicing of sequences to avoid every other + ''' + return seq[0:len(seq):2] + +def first_last_four_removed(seq): + ''' + remove the first and last four items and iterate every other + ''' + return seq[4:-4:2] + +def reversed(seq): + ''' + reverse the items in sequence + ''' + return seq[::-1] + +def thirds(seq): + ''' + with the last third, then first third, then the middle third in the new order + ''' + last_third = seq[-3:] + first_third = seq[0:3] + middle_third = seq[3:-3] + return last_third + first_third + middle_third + +# Tests +str = "supercalifragilistic" +tuple = ('blah', 1, 'blah2', 'blah3', 'blah4', 2, 54, 13, 12, 5, 32, 31, 42, 3) + +assert flipped(str) == "cupercalifragilistis" +assert flipped(tuple) == (3, 1, 'blah2', 'blah3', 'blah4', 2, 54, 13, 12, 5, 32, 31, 42, 'blah') +assert every_other(str) == "sprairglsi" +assert every_other(tuple) == ('blah', 'blah2', 'blah4', 54, 12, 32, 42) +assert first_last_four_removed(str) == "rairgl" +assert first_last_four_removed(tuple) == ('blah4', 54, 12) +assert reversed(str) == "citsiligarfilacrepus" +assert reversed(tuple) == (3, 42, 31, 32, 5, 12, 13, 54, 2, 'blah4', 'blah3', 'blah2', 1, 'blah') +assert thirds(str) == "ticsupercalifragilis" +assert thirds(tuple) == (31, 42, 3, 'blah', 1, 'blah2', 'blah3', 'blah4', 2, 54, 13, 12, 5, 32) + +print("All Tests passed!") \ No newline at end of file diff --git a/students/visokoo/lesson3_assignments/strformat_lab.py b/students/visokoo/lesson3_assignments/strformat_lab.py new file mode 100644 index 0000000..a39ec6d --- /dev/null +++ b/students/visokoo/lesson3_assignments/strformat_lab.py @@ -0,0 +1,99 @@ +''' +---------------------------------------------------- +Assignment: strformat_lab.py +Author: visokoo | Created: 1/24/2019 +ChangeLog: 1/24/2019, Created file + +Task One: Write a format string that will take this tuple: +( 2, 123.4567, 10000, 12345.67) and produce: +'file_002 : 123.46, 1.00e+04, 1.23e+04' + +Task Two: Use results from task one and try diff ways to format + +Task Three: Rewrite: "the 3 numbers are: {:d}, {:d}, {:d}".format(1,2,3) +to take an arbitrary number of values + +Task Four: Given a 5 element tuple: ( 4, 30, 2017, 2, 27), use string +formatting to print: '02 27 2017 04 30' + +Task Five: Given this list: ['oranges', 1.3, 'lemons', 1.1], write a fstring +that will display: "The weight of an orange is 1.3 and the weight of a lemon is 1.1" + +Task Six: Write some Python code to print a table of several rows, each with a name, +an age and a cost. Make sure some of the costs are in the hundreds and thousands to +test your alignment specifiers. +---------------------------------------------------- +''' + +def str_format_t1(object): + ''' + Good old string formatting specifying each item in the given object + ''' + formatted_string = "file_00{:d} : {:.2f}, {:.2e}, {:3.2e}".format(object[0], object[1], object[2], object[3]) + return formatted_string + +def str_format_t2_kwargs(object): + ''' + String formatting w/ kwargs + ''' + formatted_string = "file_00{:d} : {:.2f}, {:.2e}, {:3.2e}".format(*object) + return formatted_string + +def str_format_t2_fstring(object): + ''' + String formatting with fstrings. Looks like it doesn't support unpacking like + .format does so you'll have to specify each item in the tuple. + ''' + formatted_string = f"file_00{object[0]} : {object[1]:.2f}, {object[2]:.2e}, {object[3]:3.2e}" + return formatted_string + +def arbitrary(object): + ''' + Making it so it doesn't matter how many args are provided it'll still work + ''' + tuple_length = len(object) + formatted_string = ("the {} numbers are: " + ", ".join(["{}"] * tuple_length)).format(tuple_length, *object) + return formatted_string + +def padding(object): + ''' + Always return 2 digits if it isn't already and rearrange via index + ''' + formatted_string = "{:02d} {:02d} {:02d} {:02d} {:02d}".format(object[3], object[4], object[2], object[0], object[1]) + return formatted_string + +def fstring(object): + ''' + Using fstring and some slicing to get rid of the extra 's' in both fruits + ''' + fruit1, weight1, fruit2, weight2 = object + return f"The weight of an {fruit1[0:len(fruit1)-1]} is {weight1} and the weight of a {fruit2[0:len(fruit2)-1]} is {weight2}" + +def fstring_modified(object): + ''' + Using fstring to do some simple string functions and math + ''' + fruit1, weight1, fruit2, weight2 = object + return f"The weight of an {fruit1[0:len(fruit1)-1].upper()} is {weight1} and the weight of a {fruit2[0:len(fruit2)-1].upper()} is {weight2}" + +def alignments(object): + ''' + Printing out a nice table using alignment specifiers + ''' + print("{:20}{:20}{:20}".format("Name", "Age", "Cost")) + for listing in object: + print("{:20}{:20}{:20}".format(*listing)) + +tuple = ( 2, 123.4567, 10000, 12345.67) +simple_tuple = (12, 31, 88, 6, 22, 89) +five_element_tuple = (4, 30, 2017, 2, 27) +fruit_list = ['oranges', 1.3, 'lemons', 1.1] +dogs = (('Sparky', '2', '$17000'), ('Popcorn', '1', '$2500'), ('Peanut', '3', '$1000')) +# print(str_format_t1(tuple)) +# print(str_format_t2_kwargs(tuple)) +# print(str_format_t2_fstring(tuple)) +# print(arbitrary(simple_tuple)) +# print(padding(five_element_tuple)) +# print(fstring(fruit_list)) +# print(fstring_modified(fruit_list)) +# alignments(dogs) \ No newline at end of file diff --git a/students/visokoo/lesson4_assignments/dict_lab.py b/students/visokoo/lesson4_assignments/dict_lab.py new file mode 100755 index 0000000..bf8810d --- /dev/null +++ b/students/visokoo/lesson4_assignments/dict_lab.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +''' +---------------------------------------------------- +Assignment: dict_lab.py +Author: visokoo | Created: 2/1/2019 +ChangeLog: 2/2/2019, Created file + +Learn the basic ins and outs of Python dictionaries and sets +---------------------------------------------------- +''' + +# Dictionaries 1 +dict1 = {"name": "Chris", "city": "Seattle", "cake": "Chocolate"} +print(dict1) +dict1.pop("cake") +print(dict1) +dict1.update({"fruit": "Mango"}) +print(dict1.keys()) +print(dict1.values()) +print("cake" in dict1.keys()) +print("Mango" in dict1.values()) + +# Dictionaries 2 +dict2 = {} +for key, value in dict1.items(): + occurrences = value.count("t") + dict2.update({key: occurrences}) +print(dict2) + +# Sets 1 +s2 = set(i for i in list(range(20)) if i % 2 == 0) +s3 = set(i for i in list(range(20)) if i % 3 == 0) +s4 = set(i for i in list(range(20)) if i % 4 == 0) +combined_sets = (s2, s3, s4) +for sets in combined_sets: + print(sets) + +print(s3 < s2) +print(s4 < s2) + +# Sets 2 +s1 = {'p', 'y', 't', 'h', 'o', 'n'} +s1.update("i") +print(s1) +fs = frozenset(('m', 'a', 'r', 'a', 't', 'h', 'o', 'n')) +print(fs) + +print(s1.union(fs)) +print(s1.intersection(fs)) diff --git a/students/visokoo/lesson4_assignments/mailroom-p2.py b/students/visokoo/lesson4_assignments/mailroom-p2.py new file mode 100755 index 0000000..c4bb863 --- /dev/null +++ b/students/visokoo/lesson4_assignments/mailroom-p2.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +''' +---------------------------------------------------- +Assignment: mailroom-p2.py +Author: visokoo | Created: 2/1/2019 +ChangeLog: 2/2/2019, Created file + +You work in the mail room at a local charity. Part of your job is to write incredibly boring, +repetitive emails thanking your donors for their generous gifts. You are tired of doing this +over and over again, so you’ve decided to let Python help you out of a jam and do your work for you. + +Pt.2 +- Convert to dict data type where it makes sense +- Write a full set of letters to all donors to individual files on disk. +---------------------------------------------------- +''' + +import re + +donors = [ + {"name": "William Gates, III", "total_donation": 458978.00, "total_donation_count": 3}, + {"name": "Mark Zuckerberg", "total_donation": 54687.00, "total_donation_count": 2}, + {"name": "Jeff Bezos", "total_donation": 87485.50, "total_donation_count": 2}, + {"name": "Paul Allen", "total_donation": 55000.00, "total_donation_count": 1}, + {"name": "Arya Stark", "total_donation": 60000.00, "total_donation_count": 1} +] + +def display_donors(donordb): + for row in donordb: + print(row) + +def find_donor(donor_name): + for row in donors: + if row["name"].lower() == donor_name.lower(): + return donor_name + return donor_name + +def thank_you(): + full_name = input("Name of the giftee (Enter 'list' if you want to see existing donors): ").title() + if full_name == "List": + display_donors(donors) + else: + donor_name = find_donor(full_name) + donation_amount = float(input("Donation Amount: ")) + add_donation(donor_name, donation_amount) + print(generate_email(donor_name, donation_amount)) + +def thank_all(): + for row in donors: + email = generate_email(row["name"], row["total_donation"]) + # replacing non-alphanumeric chars w/ "_" + with open(f"{re.sub('[^a-zA-Z0-9_]', '_', row['name'])}.txt", "w") as file: + file.write(email) + print("Saved email templates to local directory as .txt") + +def add_donation(donor_name, donation): + donor_count = donor_history(donor_name) + for row in donors: + if row["name"].lower() == donor_name.lower(): + new_amount = row["total_donation"] + donation + # remove old donor from list completely & insert again with new data + row.clear() + row.update({"name": donor_name, "total_donation": new_amount, "total_donation_count": donor_count}) + return new_amount + else: + donors.append({"name": donor_name, "total_donation": donation, "total_donation_count": donor_count}) + return donation + +def donor_history(donor_name): + for row in donors: + if row["name"].lower() == donor_name.lower(): + return row["total_donation_count"] + 1 + return 1 + +def generate_email(donor_name, donation): + email = "Dear {}, \n\nThank you so much for your generous donation amount of ${:.2f}.\n" \ + "Your help will directly benefit the thousands of starving families this winter.\n" \ + "Thank you again for making a difference in their lives.\n\n" \ + "Cheers,\n" \ + "A grateful volunteer".format(donor_name.split(' ')[0].capitalize(), donation) + return email + +def generate_report(): + sorted_donors = sorted(donors, key=lambda k: k["total_donation"], reverse=True) + print("{:25}| {:^16} | {:^11} | {}".format("Donor Name", + "Total Given", + "Num Gifts", + "Average Gift"), + "-" * 72, sep="\n") + for row in sorted_donors: + print("{:25}| ${:>15.2f} | {:>11} | ${:11.2f}".format(row["name"], row["total_donation"], row["total_donation_count"], row["total_donation"] / row["total_donation_count"])) + print() + +def display_menu(): + print("Select an option:" + "\n" + + "1. Send a Thank You" + "\n" + + "2. Create a Report" + "\n" + + "3. Send a Thank You to everyone" + "\n" + + "4. Quit" + "\n") + +def main(): + choice_options = { + 1: thank_you, + 2: generate_report, + 3: thank_all, + 4: quit + } + while True: + display_menu() + option = int(input("Choice: ")) + choice_options[option]() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/students/visokoo/lesson4_assignments/sherlock_small.txt b/students/visokoo/lesson4_assignments/sherlock_small.txt new file mode 100644 index 0000000..dcccaab --- /dev/null +++ b/students/visokoo/lesson4_assignments/sherlock_small.txt @@ -0,0 +1,17 @@ +One night--it was on the twentieth of March, 1888--I was +returning from a journey to a patient (for I had now returned to +civil practice), when my way led me through Baker Street. As I +passed the well-remembered door, which must always be associated +in my mind with my wooing, and with the dark incidents of the +Study in Scarlet, I was seized with a keen desire to see Holmes +again, and to know how he was employing his extraordinary powers. +His rooms were brilliantly lit, and, even as I looked up, I saw +his tall, spare figure pass twice in a dark silhouette against +the blind. He was pacing the room swiftly, eagerly, with his head +sunk upon his chest and his hands clasped behind him. To me, who +knew his every mood and habit, his attitude and manner told their +own story. He was at work again. He had risen out of his +drug-created dreams and was hot upon the scent of some new +problem. I rang the bell and was shown up to the chamber which +had formerly been in part my own. + diff --git a/students/visokoo/lesson4_assignments/trigrams.py b/students/visokoo/lesson4_assignments/trigrams.py new file mode 100755 index 0000000..955618d --- /dev/null +++ b/students/visokoo/lesson4_assignments/trigrams.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +''' +---------------------------------------------------- +Assignment: trigrams.py +Author: visokoo | Created: 2/2/2019 +ChangeLog: 2/2/2019, Created file + +"I wish" => ["I", "I"] +"wish I" => ["may", "might"] +"may I" => ["wish"] +"I may" => ["I"] +---------------------------------------------------- +''' + +import random +punc = ["'", ",", "\"", "(", ")", ".", "-"] +test_str = "I wish I may I wish I might".split() + +def build_trigrams(str): + trigrams = {} + for word in range(len(str) - 2): + pair = tuple(str[word:word + 2]) + follower = str[word + 2] + if pair in trigrams: + trigrams[pair] += [follower] + else: + trigrams.update({pair: [follower]}) + return trigrams + +def read_file(file): + list_of_words = [] + with open(file, 'r') as infile: + scrape = infile.readlines() + for line in scrape: + for punctuation in punc: + line = line.replace(punctuation, " ") + else: + l = line.split() + list_of_words += l + return list_of_words + +def build_new_text(tri): + start_pair = random.choice(list(tri.keys())) + word_list = list(start_pair) + while True: + if start_pair in tri: + third_word = random.choice(tri[start_pair]) + word_list.append(third_word) + # reset start_pair to last two values as key + start_pair = tuple(word_list[-2:]) + else: + break + # cap first word in list + word_list[0] = word_list[0].capitalize() + print(" ".join(word_list)) + +if __name__ == '__main__': + + filename = "sherlock_small.txt" + trigrams = build_trigrams(read_file(filename)) + build_new_text(trigrams) \ No newline at end of file diff --git a/students/visokoo/lesson5_assignments/except_exercise.py b/students/visokoo/lesson5_assignments/except_exercise.py new file mode 100644 index 0000000..29b7b9c --- /dev/null +++ b/students/visokoo/lesson5_assignments/except_exercise.py @@ -0,0 +1,63 @@ +#!/usr/bin/python +""" +Assignment: except_exercise.py +Author: visokoo | Created: 2/5/2019 +ChangeLog: 2/5/2019, Created file + +An exercise in playing with Exceptions. +Make lots of try/except blocks for fun and profit. + +Make sure to catch specifically the error you find, rather than all errors. + +Final output should be: +Spam, Spam, Spam, Spam, Beautiful Spam + +Customer: Not much of a cheese shop really, is it? +Shopkeeper: Finest in the district, sir. +Customer: And what leads you to that conclusion? +Shopkeeper: Well, it's so clean. +Customer: It's certainly uncontaminated by cheese. +""" + +from except_test import fun, more_fun, last_fun + +# Figure out what the exception is, catch it and while still +# in that catch block, try again with the second item in the list +first_try = ['spam', 'cheese', 'mr death'] + +try: + joke = fun(first_try[0]) +except NameError: + joke = fun(first_try[1]) +# Here is a try/except block. Add an else that prints not_joke +try: + not_joke = fun(first_try[2]) +except SyntaxError: + print('Run Away!') +else: + print(not_joke) + +# What did that do? You can think of else in this context, as well as in +# loops as meaning: "else if nothing went wrong" +# (no breaks in loops, no exceptions in try blocks) + +# Figure out what the exception is, catch it and in that same block +# +# try calling the more_fun function with the 2nd language in the list, +# again assigning it to more_joke. +# +# If there are no exceptions, call the more_fun function with the last +# language in the list + +# Finally, while still in the try/except block and regardless of whether +# there were any exceptions, call the function last_fun with no +# parameters. (pun intended) + +langs = ['java', 'c', 'python'] + +try: + more_joke = more_fun(langs[0]) +except IndexError: + more_joke = more_fun(langs[1]) +else: + more_fun(langs[2]) \ No newline at end of file diff --git a/students/visokoo/lesson5_assignments/except_test.py b/students/visokoo/lesson5_assignments/except_test.py new file mode 100644 index 0000000..905dd67 --- /dev/null +++ b/students/visokoo/lesson5_assignments/except_test.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 + +""" +silly little test module that is designed to trigger Exceptions when +run from the except_exercise.py file +""" + +import time + +conclude = "And what leads you to that conclusion?" +district = "Finest in the district, sir." +cheese = "It's certainly uncontaminated by cheese." +clean = "Well, it's so clean." +shop = "Not much of a cheese shop really, is it?" +cust = "Customer: " +clerk = "Shopkeeper: " + + +def fun(reaper): + if reaper == 'spam': + print(s) + elif reaper == 'cheese': + print() + print('Spam, Spam, Spam, Spam, Beautiful Spam') + elif reaper == 'mr death': + print() + return('{}{}\n{}{}'.format(cust, shop, clerk, district)) + + +def more_fun(language): + if language == 'java': + test = [1, 2, 3] + test[5] = language + elif language == 'c': + print('{}{}\n{}{}'.format(cust, conclude, clerk, clean)) + + +def last_fun(): + print(cust, cheese) + time.sleep(1) + import antigravity diff --git a/students/visokoo/lesson5_assignments/mailroom-p3.py b/students/visokoo/lesson5_assignments/mailroom-p3.py new file mode 100755 index 0000000..bdb15bb --- /dev/null +++ b/students/visokoo/lesson5_assignments/mailroom-p3.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +''' +---------------------------------------------------- +Assignment: mailroom-p3.py +Author: visokoo | Created: 2/1/2019 +ChangeLog: 2/10/2019, Added error handling + +You work in the mail room at a local charity. Part of your job is to write incredibly boring, +repetitive emails thanking your donors for their generous gifts. You are tired of doing this +over and over again, so you’ve decided to let Python help you out of a jam and do your work for you. + +Pt.3 +- Add exceptions and error handling to your code +---------------------------------------------------- +''' + +import re + +donors = [ + {"name": "William Gates, III", "total_donation": 458978.00, "total_donation_count": 3}, + {"name": "Mark Zuckerberg", "total_donation": 54687.00, "total_donation_count": 2}, + {"name": "Jeff Bezos", "total_donation": 87485.50, "total_donation_count": 2}, + {"name": "Paul Allen", "total_donation": 55000.00, "total_donation_count": 1}, + {"name": "Arya Stark", "total_donation": 60000.00, "total_donation_count": 1} +] + +def display_donors(donordb): + [print(row) for row in donordb] + +def find_donor(donor_name): + [donor_name if row["name"].lower() == donor_name.lower() else donor_name for row in donors] + return donor_name + +def thank_you(): + try: + full_name = input("Name of the giftee (Enter 'list' if you want to see existing donors): ").title() + if full_name == "List": + display_donors(donors) + else: + donor_name = find_donor(full_name) + donation_amount = float(input("Donation Amount: ")) + add_donation(donor_name, donation_amount) + print(generate_email(donor_name, donation_amount)) + except ValueError: raise Exception("The donation amount must be a number, try again.") + +def thank_all(): + for row in donors: + email = generate_email(row["name"], row["total_donation"]) + # replacing non-alphanumeric chars w/ "_" + with open(f"{re.sub('[^a-zA-Z0-9_]', '_', row['name'])}.txt", "w") as file: + file.write(email) + print("Saved email templates to local directory as .txt") + +def add_donation(donor_name, donation): + donor_count = donor_history(donor_name) + for row in donors: + if row["name"].lower() == donor_name.lower(): + new_amount = row["total_donation"] + donation + # remove old donor from list completely & insert again with new data + row.clear() + row.update({"name": donor_name, "total_donation": new_amount, "total_donation_count": donor_count}) + return new_amount + else: + donors.append({"name": donor_name, "total_donation": donation, "total_donation_count": donor_count}) + return donation + +def donor_history(donor_name): + for row in donors: + if row["name"].lower() == donor_name.lower(): + return row["total_donation_count"] + 1 + return 1 + +def generate_email(donor_name, donation): + email = "Dear {}, \n\nThank you so much for your generous donation amount of ${:.2f}.\n" \ + "Your help will directly benefit the thousands of starving families this winter.\n" \ + "Thank you again for making a difference in their lives.\n\n" \ + "Cheers,\n" \ + "A grateful volunteer".format(donor_name.split(' ')[0].capitalize(), donation) + return email + +def generate_report(): + sorted_donors = sorted(donors, key=lambda k: k["total_donation"], reverse=True) + print("{:25}| {:^16} | {:^11} | {}".format("Donor Name", + "Total Given", + "Num Gifts", + "Average Gift"), + "-" * 72, sep="\n") + for row in sorted_donors: + print("{:25}| ${:>15.2f} | {:>11} | ${:11.2f}".format(row["name"], row["total_donation"], row["total_donation_count"], row["total_donation"] / row["total_donation_count"])) + print() + +def display_menu(): + return ("Select an option:" + "\n" + + "1. Send a Thank You" + "\n" + + "2. Create a Report" + "\n" + + "3. Send a Thank You to everyone" + "\n" + + "4. Quit" + "\n") + +def main(): + choice_options = { + 1: thank_you, + 2: generate_report, + 3: thank_all, + 4: quit + } + while True: + print(display_menu()) + try: + option = int(input("Choice: ")) + choice_options[option]() + except KeyError: + print("Invalid option. Please choose a choice that exist in the menu.") + except Exception as e: + print("Python reported this error: ", e) + +if __name__ == '__main__': + main() +else: raise Exception("This file is not meant to be imported.") \ No newline at end of file diff --git a/students/visokoo/lesson6_assignments/mailroom.py b/students/visokoo/lesson6_assignments/mailroom.py new file mode 100644 index 0000000..8a5aa5c --- /dev/null +++ b/students/visokoo/lesson6_assignments/mailroom.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +""" +---------------------------------------------------- +Assignment: mailroom.py +Author: visokoo | Created: 2/1/2019 +ChangeLog: 2/15/2019, Added unit testing + +You work in the mail room at a local charity. Part of your job is to write incredibly boring, +repetitive emails thanking your donors for their generous gifts. You are tired of doing this +over and over again, so you’ve decided to let Python help you out of a jam and do your work for you. + +Pt.3 +- Add a full suite of unit tests. +---------------------------------------------------- +""" + +import re + +donors = [ + {"name": "William Gates, III", "total_donation": 458978.00, "total_donation_count": 3}, + {"name": "Mark Zuckerberg", "total_donation": 54687.00, "total_donation_count": 2}, + {"name": "Jeff Bezos", "total_donation": 87485.50, "total_donation_count": 2}, + {"name": "Paul Allen", "total_donation": 55000.00, "total_donation_count": 1}, + {"name": "Arya Stark", "total_donation": 60000.00, "total_donation_count": 1} +] + +def display_donors(donordb): + [print(row) for row in donordb] + +def thank_you(): + try: + full_name = input("Name of the giftee (Enter 'list' if you want to see existing donors): ").title() + if full_name == "List": + display_donors(donors) + else: + donor_name = full_name + donation_amount = input("Donation Amount: ") + add_donation(donor_name, donation_amount) + print(generate_email(donor_name, donation_amount)) + except ValueError: + raise ValueError("The donation amount must be a number, try again.") + +def thank_all(): + for row in donors: + email = generate_email(row["name"], row["total_donation"]) + # replacing non-alphanumeric chars w/ "_" + with open(f"{re.sub('[^a-zA-Z0-9_]', '_', row['name'])}.txt", "w") as file: + file.write(email) + print("Saved email templates to local directory as .txt") + +def add_donation(donor_name, donation): + donor_count = donor_history(donor_name) + donation = float(donation) + for row in donors: + if row["name"].lower() == donor_name.lower(): + new_amount = row["total_donation"] + donation + # remove old donor from list completely & insert again with new data + row.clear() + row.update({"name": donor_name, "total_donation": new_amount, "total_donation_count": donor_count}) + return new_amount + else: + donors.append({"name": donor_name, "total_donation": donation, "total_donation_count": donor_count}) + return donation + +def donor_history(donor_name): + donor_history_count = next((row["total_donation_count"] + 1 for row in donors if row["name"].lower() == donor_name.lower()), 1) + return donor_history_count + +def generate_email(donor_name, donation): + email = "Dear {}, \n\nThank you so much for your generous donation amount of ${:.2f}.\n" \ + "Your help will directly benefit the thousands of starving families this winter.\n" \ + "Thank you again for making a difference in their lives.\n\n" \ + "Cheers,\n" \ + "A grateful volunteer".format(donor_name.split(' ')[0].capitalize(), float(donation)) + return email + +def generate_report(): + sorted_donors = sorted(donors, key=lambda k: k["total_donation"], reverse=True) + print("{:25}| {:^16} | {:^11} | {}".format("Donor Name", + "Total Given", + "Num Gifts", + "Average Gift"), + "-" * 72, sep="\n") + + for row in sorted_donors: + print("{:25}| ${:>15.2f} | {:>11} | ${:11.2f}".format(row["name"], + row["total_donation"], + row["total_donation_count"], + row["total_donation"] / row["total_donation_count"])) + print() + +def display_menu(): + return ("Select an option:" + "\n" + + "1. Send a Thank You" + "\n" + + "2. Create a Report" + "\n" + + "3. Send a Thank You to everyone" + "\n" + + "4. Quit" + "\n") + +def main(): + choice_options = { + 1: thank_you, + 2: generate_report, + 3: thank_all, + 4: quit + } + + while True: + print(display_menu()) + try: + option = int(input("Choice: ")) + choice_options[option]() + except KeyError: + print("Invalid option. Please choose a choice that exist in the menu.") + except Exception as e: + print("Python reported this error: ", e) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/students/visokoo/lesson6_assignments/test_mailroom-p4.py b/students/visokoo/lesson6_assignments/test_mailroom-p4.py new file mode 100644 index 0000000..6b9bc05 --- /dev/null +++ b/students/visokoo/lesson6_assignments/test_mailroom-p4.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +""" +---------------------------------------------------- +Assignment: test_mailroom-p4.py +Author: visokoo | Created: 2/15/2019 +ChangeLog: 2/15/2019, Created pytest file + +Pytest file for testing mailroom.py +---------------------------------------------------- +""" +import pytest +import glob +from mailroom import thank_all, add_donation, donor_history + +def test_add_donation_existing_donor(): + assert add_donation("Paul Allen", 45000) == 100000 + +def test_add_donation_new_donor(): + assert add_donation("James Cameron", 30000) == 30000 + +def test_add_donation_invalid_value(): + with pytest.raises(Exception): + add_donation("Sansa Stark", "invalid value") + +def test_send_letters_correct_count(): + thank_all() + assert len(glob.glob("*.txt")) == 6 + +def test_send_letters_correct_content(): + thank_all() + line_to_check = open("Mark_Zuckerberg.txt").readline() + assert line_to_check == "Dear Mark, \n" + +def test_donor_history_count_orig(): + assert donor_history("Mark Zuckerberg") == 3 + +def test_donor_history_count_edited(): + assert donor_history("Paul Allen") == 3 \ No newline at end of file diff --git a/students/visokoo/lesson7_assignments/html_render.py b/students/visokoo/lesson7_assignments/html_render.py new file mode 100644 index 0000000..d1ce51c --- /dev/null +++ b/students/visokoo/lesson7_assignments/html_render.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 + +""" +A class-based system for rendering html. +""" + + +# This is the framework for the base class +class Element(object): + + tag = "html" + indent = " " + def __init__(self, content=None, **kwargs): + if content is None: + self.contents = [] + else: + self.contents = [content] + self.attributes = kwargs + + def append(self, new_content): + self.contents.append(new_content) + + def render(self, out_file, ind=""): + out_file.write(ind) + out_file.write(self._open_tag()) + for content in self.contents: + try: + content.render(out_file, ind + self.indent) + except AttributeError: + out_file.write(ind + self.indent) + out_file.write(f"{self.indent + content}") + out_file.write("\n") + out_file.write(f"{ind + self.indent}{self._close_tag()}\n") + + def _open_tag(self, ind=indent): + if self.attributes: + tags = "" + for attribute, value in self.attributes.items(): + tags += f"{attribute}=\"{value}\" " + return f"{ind}<{self.tag} {tags.rstrip()}>\n" + else: + return f"{ind}<{self.tag}>\n" + + def _close_tag(self): + return f"" + +class Html(Element): + tag = "html" + + def render(self, out_file, ind=""): + out_file.write("\n") + super().render(out_file=out_file, ind=ind) + +class Body(Element): + tag = "body" + +class P(Element): + tag = "p" + +class Head(Element): + tag = "head" + +class OneLineTag(Element): + def append(self, new_content): + raise NotImplementedError + + def _open_tag(self): + open_tag = f'<{self.tag}' + for attributes in self.attributes: + open_tag = open_tag + ( + f' {attributes}="{self.attributes[attributes]}"') + open_tag = open_tag + (">") + return open_tag + + def render(self, out_file, ind=""): + out_file.write(ind + self.indent) + out_file.write(self._open_tag()) + for content in self.contents: + if content: + try: + content.render(out_file, ind + self.indent) + except AttributeError: + out_file.write(content) + out_file.write(self._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__(content=content, **kwargs) + + def append(self, *args): + raise TypeError("You can not add content to a SelfClosingTag") + + def render(self, out_file, ind=""): + tag = self._open_tag()[:-2] + " />\n" + out_file.write(f"{ind}{tag}") + +class Hr(SelfClosingTag): + tag = "hr" + +class Br(SelfClosingTag): + tag = "br" + +class A(OneLineTag): + tag = "a" + + def __init__(self, link, content=None, **kwargs): + kwargs['href'] = link + super().__init__(content=content, **kwargs) + +class Ul(Element): + tag = "ul" + +class Li(Element): + tag = "li" + +class H(OneLineTag): + def __init__(self, size, content=None, **kwargs): + self.size = int(size) + if self.size: + self.tag = f"h{size}" + super().__init__(content=content, **kwargs) + +class Meta(SelfClosingTag): + tag = "meta" + indent = "" diff --git a/students/visokoo/lesson7_assignments/run_html_render.py b/students/visokoo/lesson7_assignments/run_html_render.py new file mode 100644 index 0000000..02e5d07 --- /dev/null +++ b/students/visokoo/lesson7_assignments/run_html_render.py @@ -0,0 +1,230 @@ +#!/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/visokoo/lesson7_assignments/test_html_output1.html b/students/visokoo/lesson7_assignments/test_html_output1.html new file mode 100644 index 0000000..36d2709 --- /dev/null +++ b/students/visokoo/lesson7_assignments/test_html_output1.html @@ -0,0 +1,4 @@ + + Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text + And here is another piece of text -- you should be able to add any number + diff --git a/students/visokoo/lesson7_assignments/test_html_output2.html b/students/visokoo/lesson7_assignments/test_html_output2.html new file mode 100644 index 0000000..e2e1e7d --- /dev/null +++ b/students/visokoo/lesson7_assignments/test_html_output2.html @@ -0,0 +1,14 @@ + + + +

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

+ +

+ And here is another piece of text -- you should be able to add any number +

+ + + + diff --git a/students/visokoo/lesson7_assignments/test_html_output3.html b/students/visokoo/lesson7_assignments/test_html_output3.html new file mode 100644 index 0000000..3d3f90d --- /dev/null +++ b/students/visokoo/lesson7_assignments/test_html_output3.html @@ -0,0 +1,18 @@ + + + + PythonClass = Revision 1087: + + + +

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

+ +

+ And here is another piece of text -- you should be able to add any number +

+ + + + diff --git a/students/visokoo/lesson7_assignments/test_html_output4.html b/students/visokoo/lesson7_assignments/test_html_output4.html new file mode 100644 index 0000000..bbb2f5a --- /dev/null +++ b/students/visokoo/lesson7_assignments/test_html_output4.html @@ -0,0 +1,14 @@ + + + + PythonClass = Revision 1087: + + + +

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

+ + + + diff --git a/students/visokoo/lesson7_assignments/test_html_output5.html b/students/visokoo/lesson7_assignments/test_html_output5.html new file mode 100644 index 0000000..4eafc23 --- /dev/null +++ b/students/visokoo/lesson7_assignments/test_html_output5.html @@ -0,0 +1,16 @@ + + + + PythonClass = Revision 1087: + + + +

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

+ +
+ + + + diff --git a/students/visokoo/lesson7_assignments/test_html_output6.html b/students/visokoo/lesson7_assignments/test_html_output6.html new file mode 100644 index 0000000..2578fa1 --- /dev/null +++ b/students/visokoo/lesson7_assignments/test_html_output6.html @@ -0,0 +1,19 @@ + + + + PythonClass = Revision 1087: + + + +

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

+ +
+ + And this is a + link + to google + + + diff --git a/students/visokoo/lesson7_assignments/test_html_output7.html b/students/visokoo/lesson7_assignments/test_html_output7.html new file mode 100644 index 0000000..4df39b6 --- /dev/null +++ b/students/visokoo/lesson7_assignments/test_html_output7.html @@ -0,0 +1,34 @@ + + + + PythonClass = Revision 1087: + + + +

PythonClass - Class 6 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 +

+ +
+ +
    +
  • + The first item in a list +
  • + +
  • + This is the second item +
  • + +
  • + And this is a + link + to google +
  • + +
+ + + + diff --git a/students/visokoo/lesson7_assignments/test_html_output8.html b/students/visokoo/lesson7_assignments/test_html_output8.html new file mode 100644 index 0000000..463fa1c --- /dev/null +++ b/students/visokoo/lesson7_assignments/test_html_output8.html @@ -0,0 +1,36 @@ + + + + + + PythonClass = Revision 1087: + + + +

PythonClass - 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 +

+ +
+ +
    +
  • + The first item in a list +
  • + +
  • + This is the second item +
  • + +
  • + And this is a + link + to google +
  • + +
+ + + + diff --git a/students/visokoo/lesson7_assignments/test_html_render.py b/students/visokoo/lesson7_assignments/test_html_render.py new file mode 100644 index 0000000..6df59db --- /dev/null +++ b/students/visokoo/lesson7_assignments/test_html_render.py @@ -0,0 +1,429 @@ +""" +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 is 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).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("") + assert file_contents.count("") == 1 + assert file_contents.count("") == 1 + + print(file_contents) + + +# 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(): + e = Head("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_title(): + e = Title("this is some text") + + file_contents = render_result(e).strip() + + assert ("this is some text") in file_contents + + assert file_contents.startswith("") + assert file_contents.endswith("") + assert "\n" not in file_contents + assert ("this is some text") + + +def test_title_disallow_append(): + e = Title("this is the original text") + + with pytest.raises(NotImplementedError): + e.append("trying to append....") + + +######## +# Step 4 +######## +def test_attributes(): + e = P("A paragraph of text", style="text-align: center", id="intro") + + file_contents = render_result(e).strip() + 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. + # so using only a "P" tag is fine + assert "A paragraph of text" in file_contents + # but make sure the embedded element's tags get rendered! + # first test the end tag is there -- same as always: + assert file_contents.endswith("

") + + # but now the opening tag is far more complex + # but it starts the same: + assert file_contents.startswith("")].count(" ") == 3 + assert file_contents[:-1].index(">") > file_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_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("some content") + + +def test_append_content_in_br(): + with pytest.raises(TypeError): + br = Br() + br.append("some content") + + +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_attr(): + """a line break with an attribute""" + br = Br(width=400) + file_contents = render_result(br) + print(file_contents) + assert file_contents == '
\n' + + +######## +# Step 6 +######## +def test_anchor(): + a = A("http://google.com", "link to google") + file_contents = render_result(a) + print(file_contents) + assert file_contents == ' link to google' + + +######## +# Step 7 +######## +def test_ul(): + u = Ul(id="TheList", style="line-height:200%") + u.append("This is more") + file_contents = render_result(u) + print(file_contents) + + assert file_contents.startswith(" ") + + +def test_header1(): + h = H(1, "This is a header2.") + file_contents = render_result(h) + print(file_contents) + assert file_contents.startswith("

") + + +######## +# Step 8 +######## + + +def test_meta(): + meta = Meta(charset="UTF-8") + file_contents = render_result(meta) + print(file_contents) + assert '' in file_contents + + +##################### +# 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[1].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 + print(lines[1]) + 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] == Element.indent + "" + assert file_contents.endswith("")