diff --git a/notes.7z.001 b/notes.7z.001 new file mode 100644 index 0000000..f6b0393 Binary files /dev/null and b/notes.7z.001 differ diff --git a/notes.7z.002 b/notes.7z.002 new file mode 100644 index 0000000..f87c4ce Binary files /dev/null and b/notes.7z.002 differ diff --git a/practice/prep11.py b/practice/prep11.py new file mode 100755 index 0000000..cbe0a77 --- /dev/null +++ b/practice/prep11.py @@ -0,0 +1,165 @@ +"""CSC110 Fall 2021 Prep 11: Programming Exercises + +Instructions (READ THIS FIRST!) +=============================== + +This Python module contains the data class definitions that serve as the foundation +for our computational model of a food delivery system, as we discussed in this week's +prep reading. + +Please read through this file carefully, and follow all instructions described below. +We have marked each place you need to write code with the word "TODO". +As you complete your work in this file, delete each TODO comment. + + +Copyright and Usage Information +=============================== + +This file is provided solely for the personal and private use of students +taking CSC110 at the University of Toronto St. George campus. All forms of +distribution of this code, whether as given or with any changes, are +expressly prohibited. For more information on copyright for CSC110 materials, +please consult our Course Syllabus. + +This file is Copyright (c) 2021 David Liu, Mario Badr, and Tom Fairgrieve. +""" +from __future__ import annotations + +from dataclasses import dataclass +import datetime +from typing import Optional + + +@dataclass +class Restaurant: + """A place that serves food. + + Attributes: + - name: the name of the restaurant + - address: the address of the restaurant + - menu: the menu of the restaurant with the name of the dish mapping to + the price + - location: the location of the restaurant as (latitude, longitude) + + Representation Invariants: + - self.name != '' + - self.address != '' + - all(self.menu[item] >= 0 for item in self.menu) + - -90 <= self.location[0] <= 90 + - -180 <= self.location[1] <= 180 + + Sample Usage: + >>> mcdonalds = Restaurant(name='McDonalds', address='160 Spadina Ave',\ + menu={'fries': 4.5}, location=(43.649, -79.397)) + """ + name: str + address: str + menu: dict[str, float] + location: tuple[float, float] + + +@dataclass +class Customer: + """A person who orders food. + + Instance Attributes: + - name: Customer's name + - location: Customer's position in (latitutde, longitude) format. + + Representation Invariants: + - self.name != '' + - -90 <= self.location[0] <= 90 + - -180 <= self.location[1] <= 180 + + Sample Usage: + >>> david = Customer('David', (44.649, -79.115)) + """ + name: str + location: tuple[float, float] + + +@dataclass +class Order: + """A food order from a customer. + + Attributes: + - customer: the customer who placed this order + - restaurant: the restaurant the order is placed for + - food_items: a mapping from names of food to the quantity being ordered + - start_time: the time the order was placed + - courier: the courier assigned to this order (initially None) + - end_time: the time the order was completed by the courier (initially None) + + Representation Invariants: + - self.food_items != {} + - all(self.food_items[item] > 0 for item in self.food_items) + - all(f in self.restaurant.menu for f in self.food_items) + + Sample Usage: + >>> david = Customer('David', (44.649, -79.115)) + >>> mcdonalds = Restaurant(name='McDonalds', address='160 Spadina Ave',\ + menu={'fries': 4.5}, location=(43.649, -79.397)) + >>> order = Order(customer=david, restaurant=mcdonalds,\ + food_items={'fries': 10},\ + start_time=datetime.datetime(2020, 11, 5, 11, 30)) + >>> order.courier is None # Illustrating default values + True + >>> order.end_time is None + True + """ + customer: Customer + restaurant: Restaurant + food_items: dict[str, int] + start_time: datetime.datetime + courier: Optional[Courier] = None + end_time: Optional[datetime.datetime] = None + + +@dataclass +class Courier: + """A person who delivers food orders from restaurants to customers. + + Deliberately left blank (you don't need to do anything here). + We'll discuss this data class in lecture! + """ + + +def total_cost(order: Order) -> float: + """Return the total cost of the food items in this order. + + Done: add one doctest example for this function that: + 1. Creates a valid customer and restaurant, where the restaurant + has at least two different foods on the menu. + 2. Creates an order for this customer and restaurant that orders + >= 2 different food items, and >= 3 quantity of each item. + 3. Calculates the total cost of the order. + (Use math.isclose to avoid rounding error with floats.) + + >>> import math + >>> david = Customer('David', (44.649, -79.115)) + >>> mcdonalds = Restaurant(name='McDonalds', address='160 Spadina Ave',\ + menu={'fries': 4.5, 'strawberries': 9999, 'ice cream': 1},\ + location=(43.649, -79.397)) + >>> order = Order(customer=david, restaurant=mcdonalds,\ + food_items={'fries': 10, 'strawberries': 4},\ + start_time=datetime.datetime(2020, 11, 5, 11, 30)) + >>> math.isclose(total_cost(order), 40041.0) + True + """ + return sum([order.food_items[f] * order.restaurant.menu[f] for f in order.food_items]) + + +if __name__ == '__main__': + import python_ta + python_ta.check_all(config={ + 'max-line-length': 100, + 'extra-imports': ['python_ta.contracts', 'dataclasses', 'datetime'], + 'disable': ['R1705', 'C0200'], + }) + + import python_ta.contracts + python_ta.contracts.DEBUG_CONTRACTS = False + python_ta.contracts.check_all_contracts() + + import doctest + doctest.testmod() diff --git a/scrape-addon.html b/scrape-addon.html new file mode 100644 index 0000000..d17be26 --- /dev/null +++ b/scrape-addon.html @@ -0,0 +1,155 @@ + + + + diff --git a/scrape.py b/scrape.py new file mode 100644 index 0000000..1c36bb0 --- /dev/null +++ b/scrape.py @@ -0,0 +1,129 @@ +""" +Since the final test is open-notes, I've made this script to combine all course notes into one html +file, so I can ctrl+F without needing to find which chapter some definition is from. +""" +import binascii +import os +import re +import secrets +from pathlib import Path + +import requests + + +def write(file: str, text: str) -> None: + """ + Write text to a file + + :param file: File path + :param text: Text + :return: None + """ + if '/' in file: + Path(file).parent.mkdir(parents=True, exist_ok=True) + + with open(file, 'w', encoding='utf-8') as f: + f.write(text) + + +def read(file: str) -> str: + """ + Read file content + + :param file: File path (will be converted to lowercase) + :return: None + """ + with open(file, 'r', encoding='utf-8') as f: + return f.read() + + +def get(url: str) -> str: + req = requests.get(url) + req.encoding = req.apparent_encoding + return req.text + + +if __name__ == '__main__': + host = 'https://www.teach.cs.toronto.edu' + baseurl = f'{host}/~csc110y/fall/notes/' + basedir = 'notes' + + r = get(baseurl) + + # Replace references + g: set[str] = set(re.findall('(?<=href=").*?(?=")', r)) + for href in g: + url = href + filename = href.replace('~', '-') + if not (url.startswith('//') or url.startswith('http')): + if url.startswith('/'): + url = host + url + else: + url = baseurl + url + else: + filename = filename.split("//")[1] + + if filename.endswith('/'): + filename += 'index.html' + + r = r.replace(href, filename) + path = os.path.join(basedir, filename) + if os.path.isfile(path): + continue + + content = get(url) + write(path, content) + + write(os.path.join(basedir, 'index.html'), r) + + # Create full file + r = re.sub('<[/]*(section|p).*?>', '', r) + r = r.replace('CSC110 Course Notes Home', + '{INJECT_HERE}') + + links: list[str] = re.findall('.*?', r) + for link in links: + uid = secrets.token_hex(5) + href: str = re.findall('(?<=href=").*?(?=")', link)[0] + anchor = '-'.join([i[:2] for i in href.split('/')]) + html = read(os.path.join(basedir, href)) + html = re.sub('<(!DOCTYPE|html|/html|body|/body).*?>', '', html) + html = re.sub('<(head|div style="display:none"|footer)>.*?', '', html, + flags=re.DOTALL) + # Replace IDs + ids = set(re.findall('(?<=id=").*?(?=")', html)) + for id in ids: + html = html.replace(f'"{id}"', f'"{id}-{uid}"') + html = html.replace(f'"#{id}"', f'"#{id}-{uid}"') + + if '
' in html and '
' not in html: + html += '' + + # Download images + g: set[str] = set(re.findall('(?<=src=").*?(?=")', html)) + for src in g: + if src.startswith('//') or src.startswith('http'): + continue + path = os.path.join(basedir, src) + # if os.path.isfile(path): + # continue + + if src.startswith('/'): + url = host + src + else: + url = baseurl + f'{href.split("/")[-2]}/' + src + + print(url) + img_data = requests.get(url).content + Path(path).parent.mkdir(parents=True, exist_ok=True) + with open(path, 'wb') as f: + f.write(img_data) + + r = r.replace(link, f'' + html) + # print(link.replace(href, f'#anchor-{anchor}')) + + r = r.replace('{INJECT_HERE}', read('scrape-addon.html')) + write(os.path.join(basedir, 'full.html'), r) + + +