diff --git a/Student/osiddiquee/lesson01/generator_solution.py b/Student/osiddiquee/lesson01/generator_solution.py new file mode 100644 index 0000000..1b8078a --- /dev/null +++ b/Student/osiddiquee/lesson01/generator_solution.py @@ -0,0 +1,59 @@ +def intsum(): + previous, last = 0, 0 + while last >= 0: + if last == 0: + yield previous + last += 1 + else: + sum = previous + last + previous = sum + last += 1 + yield sum + + +def doubler(): + doubled = 1 + while doubled > 0: + if doubled == 1: + yield 1 + doubled *= 2 + elif doubled == 2: + yield doubled + doubled *= 2 + else: + yield doubled + doubled *= 2 + + +def fib(): + first, second = 0, 1 + while first >= 0: + if first == 0: + yield second + first = second + second *= 2 + elif first == 1: + yield first + fib2 = second + second = first + fib2 + first = fib2 + else: + yield first + fib2 = second + second = first + fib2 + first = fib2 + + +def prime(): + integer = 2 + while integer > 1: + if integer == 2: + yield integer + else: + isprime = True + for i in range(2, integer): + if integer % i == 0: + isprime = False + if isprime: + yield integer + integer += 1 diff --git a/Student/osiddiquee/lesson01/iterator_2.py b/Student/osiddiquee/lesson01/iterator_2.py new file mode 100644 index 0000000..5d845cf --- /dev/null +++ b/Student/osiddiquee/lesson01/iterator_2.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +""" +Simple iterator examples +""" + + +class IterateMe_2: + """ + About as simple an iterator as you can get: + + returns the sequence of numbers from zero to 4 + ( like range(4) ) + """ + + def __init__(self, start = 0, stop = 5, step = 1): + self.current = start + self.stop = stop + self.step = step + + def __iter__(self): + return self + + def __next__(self): + self.current += self.step + if self.current < self.stop: + return self.current + else: + raise StopIteration + + +if __name__ == "__main__": + + print("Testing the iterator") + for i in IterateMe_2(): + print(i) diff --git a/Student/osiddiquee/lesson01/music.py b/Student/osiddiquee/lesson01/music.py new file mode 100644 index 0000000..b04f530 --- /dev/null +++ b/Student/osiddiquee/lesson01/music.py @@ -0,0 +1,35 @@ +import pandas as pd + +music = pd.read_csv('featuresdf.csv') + +artists = [artists for artists in + music[(music['danceability'] > 0.8) & + (music['loudness'] < -5.0)].artists] + +songs = [name for name in + music[(music['danceability'] > 0.8) & + (music['loudness'] < -5.0)].name] + +danceability = [danceability for danceability in + music[(music['danceability'] > 0.8) & + (music['loudness'] < -5.0)].danceability] + +spotify_data = list(zip(artists, songs, danceability)) + +sorted_spotify = sorted(spotify_data, key = lambda x: x[2], reverse = True) + +print('Simple solution output: ') +for artist, song, dance in sorted_spotify[:5]: + print(song) + +print('\nA much more elegant solution output: ') +''' A much more elegant solution is below ''' +spotify_query = [(artist, song, dance, loud) + for (artist, song, dance, loud) in + zip(music.artists, music.name, music.danceability, music.loudness) + if (dance > 0.8) & (loud < -5.0)] + +for artist, song, dance, loud in sorted(spotify_query, + key = lambda x: x[2], + reverse = True)[:5]: + print(song) diff --git a/Student/osiddiquee/lesson02/Untitled.ipynb b/Student/osiddiquee/lesson02/Untitled.ipynb new file mode 100644 index 0000000..82af52a --- /dev/null +++ b/Student/osiddiquee/lesson02/Untitled.ipynb @@ -0,0 +1,309 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def check_prime(number): \n", + " for divisor in range(2, int(number ** 0.5) + 1): \n", + " if number % divisor == 0: \n", + " return False \n", + " return True" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class Primes:\n", + " def __init__(self, max):\n", + " self.max = max\n", + "\n", + " self.number = 1\n", + "\n", + " def __iter__(self):\n", + "\n", + " return self\n", + "\n", + " def __next__(self):\n", + "\n", + " self.number += 1\n", + "\n", + " if self.number >= self.max:\n", + "\n", + " raise StopIteration\n", + "\n", + " elif check_prime(self.number):\n", + "\n", + " return self.number\n", + "\n", + " else:\n", + "\n", + " return self.__next__()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "<__main__.Primes object at 0x000001D133542B70>\n", + "5\n", + "7\n", + "9\n", + "11\n", + "13\n", + "15\n", + "17\n", + "19\n", + "21\n", + "23\n", + "25\n", + "27\n", + "29\n", + "31\n", + "33\n", + "35\n", + "37\n", + "39\n", + "41\n", + "43\n", + "45\n", + "47\n", + "49\n", + "51\n", + "53\n", + "55\n", + "57\n", + "59\n", + "61\n", + "63\n", + "65\n", + "67\n", + "69\n", + "71\n", + "73\n", + "75\n", + "77\n", + "79\n", + "81\n", + "83\n", + "85\n", + "87\n", + "89\n", + "91\n", + "93\n", + "95\n", + "97\n", + "99\n" + ] + } + ], + "source": [ + "primes = Primes(100)\n", + "print(primes)\n", + "for x in primes: \n", + " print(x) " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hey!\n" + ] + } + ], + "source": [ + " def outerFunction(text):\n", + " text = text\n", + " \n", + " def innerFunction():\n", + " print(text)\n", + " \n", + " return innerFunction # Note we are returning function WITHOUT parenthesis\n", + " \n", + "if __name__ == '__main__':\n", + " myFunction = outerFunction('Hey!')\n", + " myFunction() " + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'outside': 1, 'inside': 2}\n", + "{'outside': 1, 'inside': 2}\n" + ] + } + ], + "source": [ + " def outside():\n", + " d = {\"outside\": 1}\n", + " def inside():\n", + " d[\"inside\"] = 2\n", + " print(d)\n", + " inside()\n", + " print(d)\n", + " \n", + "outside()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 4, 6]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import heapq\n", + "\n", + "heap =[]\n", + "heapq.heappush(heap,1)\n", + "heapq.heappush(heap,2)\n", + "heapq.heappush(heap,4)\n", + "heapq.heappush(heap,6)\n", + "heapq.heappush(heap,7)\n", + "heapq.heappush(heap,9)\n", + "heapq.heappush(heap,10)\n", + "heapq.heappush(heap,12) \n", + "heapq.nsmallest(4, heap)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### With partial code\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 3, 4, 5, 6, 8]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import functools\n", + "import heapq\n", + "heap =[]\n", + "push = functools.partial(heapq.heappush, heap)\n", + "smallest = functools.partial(heapq.nsmallest, iterable=heap)\n", + "push(1)\n", + "push(3)\n", + "push(5)\n", + "push(6)\n", + "push(8)\n", + "push(11)\n", + "push(4)\n", + "push(16)\n", + "push(17)\n", + "smallest(6)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from itertools import *\n", + "\n", + "string = islice('ABCDEF', 2, None)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Student/osiddiquee/lesson02/spotify_generator.py b/Student/osiddiquee/lesson02/spotify_generator.py new file mode 100644 index 0000000..9d03647 --- /dev/null +++ b/Student/osiddiquee/lesson02/spotify_generator.py @@ -0,0 +1,27 @@ +import pandas + +spotify = pandas.read_csv('featuresdf.csv') + +#Generators +data = ((artist, song) for (artist, song) in zip(spotify.artists, spotify.name) + if artist == 'Kendrick Lamar') +print('Generator ouput - Kendrick Lamar tracks: ') +for artist, song in data: + print(song) + +#Closures +def ingest_data(filename): + spotify = pandas.read_csv(filename) + data = ((artist, song, energy) for (artist, song, energy) + in zip(spotify.artists, spotify.name, spotify.energy) + if energy >= 0.8) + def print_high_energy(): + print('Artist' + ' ' * 14, 'Song' + ' ' * 46, 'Energy') + for artist, song, energy in data: + print(artist + ' ' * (20 - len(artist)), + song + ' ' * (50 - len(song)), + energy) + return print_high_energy() + +print('Generator ouput - Kendrick Lamar tracks: ') +ingest_data('featuresdf.csv') diff --git a/Student/osiddiquee/lesson03/factorial.py b/Student/osiddiquee/lesson03/factorial.py new file mode 100644 index 0000000..366c7d6 --- /dev/null +++ b/Student/osiddiquee/lesson03/factorial.py @@ -0,0 +1,5 @@ +def factorial(number): + if number == 1: + return 1 + else: + return number * factorial(number - 1) diff --git a/Student/osiddiquee/lesson03/locke.py b/Student/osiddiquee/lesson03/locke.py new file mode 100644 index 0000000..9c9e255 --- /dev/null +++ b/Student/osiddiquee/lesson03/locke.py @@ -0,0 +1,27 @@ + +class Locke: + + def __init__(self, locke_capacity = 10, handle_error = None): + self.locke_capacity = locke_capacity + self.handle_error = handle_error + + def __enter__(self): + if not self.handle_error: + print('Stopping the pumps.\n', + 'Opening the doors.\n') + + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if not self.handle_error: + print('Closing the doors.\n', + 'Restarting the pumps.\n') + + return self.handle_error + + def move_boats_through(self, number_boats): + if number_boats <= self.locke_capacity: + print('Boats have moved through') + else: + raise RuntimeError('The number of boats is too damn high') + return self diff --git a/Student/osiddiquee/lesson04/mailroom_json.py b/Student/osiddiquee/lesson04/mailroom_json.py new file mode 100644 index 0000000..8f4bfc7 --- /dev/null +++ b/Student/osiddiquee/lesson04/mailroom_json.py @@ -0,0 +1,292 @@ +#!/usr/bin/env python +""" +mailroom assignment + +This version uses a dict for the main db, and exception handling to +check input, and has been factored to be amenable to testing. +""" + +import sys +import math + +from functools import reduce + +# handy utility to make pretty printing easier +from textwrap import dedent + + +# In memory representation of the donor database +# using a tuple for each donor +# -- kind of like a record in a database table +# using a dict with a lower case version of the donor's name as the key +# This makes it easier to have a 'normalized' key. +# you could get a bit fancier by having each "record" be a dict, with +# "name" and "donations" as keys. +def get_donor_db(): + return {'william gates iii': ("William Gates III", [653772.32, 12.17]), + 'jeff bezos': ("Jeff Bezos", [877.33]), + 'paul allen': ("Paul Allen", [663.23, 43.87, 1.32]), + 'mark zuckerberg': ("Mark Zuckerberg", [1663.23, 4300.87, 10432.0]), + } + +def get_donor_db_oo(): + db = DonorDB() + raw_data = get_donor_db() + + for k, v in raw_data.items(): + donor = Donor(k) + for donation in v[1]: + donor.add_donation(donation) + db.add_donor(donor) + + return db + + +class Donor: + def __init__(self, name, donations=None): + self._name = name + self._donations = [] if donations is None else donations + + @property + def name(self): + return self._name + + @property + def donations(self): + return self._donations + + def add_donation(self, donation): + self._donations.append(donation) + + @property + def total_donations(self): + return sum(self._donations) + + #return reduce(lambda a,x: a+x, self._donations, 0) + + # s = 0 + # for d in self._donations: + # s += d + # return s + + def __repr__(self): + return "{}: {}".format(self._name, self._donations) + + +class DonorDB: + def __init__(self): + self._donors = {} + + def add_donor(self, donor): + self._donors[donor.name.lower()] = donor + + def get_total_from_donor(self, donor_name): + return self._donors[donor_name.lower()].total_donations + + def get_donor(self, donor_name): + return self._donors[donor_name.lower()] + + @property + def num_donors(self): + return len(self._donors) + + def __repr__(self): + return str(self._donors) + + +def list_donors(): + """ + creates a list of the donors as a string, so they can be printed + + Not calling print from here makes it more flexible and easier to + test + """ + listing = ["Donor list:"] + for donor in donor_db.values(): + listing.append(donor[0]) + return "\n".join(listing) + + +def find_donor(name): + """ + find a donor in the donor db + + :param: the name of the donor + + :returns: The donor data structure -- None if not in the donor_db + """ + key = name.strip().lower() + return donor_db.get(key) + + +def add_donor(name): + """ + Add a new donor to the donor db + + :param: the name of the donor + + :returns: the new Donor data structure + """ + name = name.strip() + donor = (name, []) + donor_db[name.lower()] = donor + return donor + + +def main_menu_selection(): + """ + Print out the main application menu and then read the user input. + """ + action = input(dedent(''' + Choose an action: + + 1 - Send a Thank You + 2 - Create a Report + 3 - Send letters to everyone + 4 - Quit + + > ''')) + return action.strip() + + +def gen_letter(donor): + """ + Generate a thank you letter for the donor + + :param: donor tuple + + :returns: string with letter + + note: This doesn't actually write to a file -- that's a separate + function. This makes it more flexible and easier to test. + """ + return dedent('''Dear {0:s}, + + Thank you for your very kind donation of ${1:.2f}. + It will be put to very good use. + + Sincerely, + -The Team + '''.format(donor[0], donor[1][-1])) + + +def send_thank_you(): + """ + Execute the logic to record a donation and generate a thank you message. + """ + # Read a valid donor to send a thank you from, handling special commands to + # let the user navigate as defined. + while True: + name = input("Enter a donor's name (or list to see all donors or 'menu' to exit)> ").strip() + if name == "list": + print(list_donors()) + elif name == "menu": + return + else: + break + + # Now prompt the user for a donation amount to apply. Since this is + # also an exit point to the main menu, we want to make sure this is + # done before mutating the db. + while True: + amount_str = input("Enter a donation amount (or 'menu' to exit)> ").strip() + if amount_str == "menu": + return + # Make sure amount is a valid amount before leaving the input loop + try: + amount = float(amount_str) + # extra check here -- unlikely that someone will type "NaN", but + # it IS possible, and it is a valid floating point number: + # http://en.wikipedia.org/wiki/NaN + if math.isnan(amount) or math.isinf(amount) or round(amount, 2) == 0.00: + raise ValueError + # in this case, the ValueError could be raised by the float() call, or by the NaN-check + except ValueError: + print("error: donation amount is invalid\n") + else: + break + + # If this is a new user, ensure that the database has the necessary + # data structure. + donor = find_donor(name) + if donor is None: + donor = add_donor(name) + + # Record the donation + donor[1].append(amount) + print(gen_letter(donor)) + + +def sort_key(item): + # used to sort on name in donor_db + return item[1] + + +def generate_donor_report(): + """ + Generate the report of the donors and amounts donated. + + :returns: the donor report as a string. + """ + # First, reduce the raw data into a summary list view + report_rows = [] + for (name, gifts) in donor_db.values(): + total_gifts = sum(gifts) + num_gifts = len(gifts) + avg_gift = total_gifts / num_gifts + report_rows.append((name, total_gifts, num_gifts, avg_gift)) + + # sort the report data + report_rows.sort(key=sort_key) + report = [] + report.append("{:25s} | {:11s} | {:9s} | {:12s}".format("Donor Name", + "Total Given", + "Num Gifts", + "Average Gift")) + report.append("-" * 66) + for row in report_rows: + report.append("{:25s} ${:10.2f} {:9d} ${:11.2f}".format(*row)) + return "\n".join(report) + + +def save_letters_to_disk(): + """ + make a letter for each donor, and save it to disk. + """ + for donor in donor_db.values(): + letter = gen_letter(donor) + # I don't like spaces in filenames... + filename = donor[0].replace(" ", "_") + ".txt" + open(filename, 'w').write(letter) + + +def print_donor_report(): + print(generate_donor_report()) + + +def quit(): + sys.exit(0) + +def main(): + donor_db = get_donor_db_oo() + donor_name = input('Whose donation record would you like to see?') + donor = donor_db.get_donor(donor_name) + print("{}, you donated total of {}! Thank you!".format(donor.name, donor.total_donations)) + +if __name__ == "__main__": + main() + # donor_db = get_donor_db() + + # running = True + + # selection_dict = {"1": send_thank_you, + # "2": print_donor_report, + # "3": save_letters_to_disk, + # "4": quit} + + # while True: + # selection = main_menu_selection() + # try: + # selection_dict[selection]() + # except KeyError: + # print("error: menu selection is invalid!") diff --git a/Student/osiddiquee/lesson05/SiddiqueeDebugging.txt b/Student/osiddiquee/lesson05/SiddiqueeDebugging.txt new file mode 100644 index 0000000..0278e65 --- /dev/null +++ b/Student/osiddiquee/lesson05/SiddiqueeDebugging.txt @@ -0,0 +1,9 @@ +If you put an odd number for n, when you half n it will never become 2. +It will keep halving forever. As the n function, gets deeper into recursion. +The call stack gets much deeper and runs slower. + + +I also could not find the file for the exercise. + +But i would theoretically, utilize n and s to dig into the function. I would +use b because I wouldn't select a large n. diff --git a/Student/osiddiquee/lesson05/logging.py b/Student/osiddiquee/lesson05/logging.py new file mode 100644 index 0000000..ac20271 --- /dev/null +++ b/Student/osiddiquee/lesson05/logging.py @@ -0,0 +1,40 @@ +import logging, logging.handlers, datetime + +date = datetime.datetime.now() + +format_console = "%(asctime)s %(filename)s:%(lineno)-3d %(levelname)s %(message)s" +format_syslog = '%(filename)s:%(lineno)-3d %(levelname)s %(message)s"' + +formatter_console = logging.Formatter(format_console) +formatter_syslog = logging.Formatter(format_syslog) + +file_handler = logging.FileHandler(f'{date.day}-{date.month}-{date.year}.log') +file_handler.setLevel(logging.WARNING) +file_handler.setFormatter(formatter_console) + +console_handler = logging.StreamHandler() +console_handler.setLevel(logging.DEBUG) +console_handler.setFormatter(formatter_console) + +syslog_handler = logging.handlers.DatagramHandler('127.0.0.1', 514) +syslog_handler.setLevel(logging.ERROR) +syslog_handler.setFormatter(formatter_syslog) + +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) +logger.addHandler(file_handler) +logger.addHandler(console_handler) +logger.addHandler(syslog_handler) + +def my_fun(n): + for i in range(0, n): + logging.debug(i) + if i == 50: + logging.warning("The value of i is 50.") + try: + i / (50 - i) + except ZeroDivisionError: + logging.error("Tried to divide by zero. Var i was {}. Recovered gracefully.".format(i)) + +if __name__ == "__main__": + my_fun(100) diff --git a/Student/osiddiquee/lesson06/test.py b/Student/osiddiquee/lesson06/test.py new file mode 100644 index 0000000..9c207f2 --- /dev/null +++ b/Student/osiddiquee/lesson06/test.py @@ -0,0 +1,27 @@ +import unittest + +from roman_to_int import RomanToInt + + +class TestRomanToInteger(unittest.TestCase): + + def test_single_x(self): + self.assertEqual(RomanToInt.convert('X'), 10) + + def test_single_i(self): + self.assertEqual(RomanToInt.convert('I'), 1) + + def test_single_v(self): + self.assertEqual(RomanToInt.convert('V'), 5) + + def test_single_l(self): + self.assertEqual(RomanToInt.convert('L'), 50) + + def test_single_c(self): + self.assertEqual(RomanToInt.convert('C'), 100) + + def test_single_d(self): + self.assertEqual(RomanToInt.convert('D'), 500) + + def test_single_m(self): + self.assertEqual(RomanToInt.convert('M'), 1000) diff --git a/Student/osiddiquee/lesson07/DB.png b/Student/osiddiquee/lesson07/DB.png new file mode 100644 index 0000000..4ebb2a5 Binary files /dev/null and b/Student/osiddiquee/lesson07/DB.png differ diff --git a/Student/osiddiquee/lesson07/activity.py b/Student/osiddiquee/lesson07/activity.py new file mode 100644 index 0000000..cc4025e --- /dev/null +++ b/Student/osiddiquee/lesson07/activity.py @@ -0,0 +1,59 @@ +""" + Simple database examle with Peewee ORM, sqlite and Python + Here we define the schema + +""" + +from peewee import * + +database = SqliteDatabase('personjob.db') +database.connect() +database.execute_sql('PRAGMA foreign_keys = ON;') + +class BaseModel(Model): + class Meta: + database = database + + +class Person(BaseModel): + """ + This class defines Person, which maintains details of someone + for whom we want to research career to date. + """ + person_name = CharField(primary_key = True, max_length = 30) + lives_in_town = CharField(max_length = 40) + nickname = CharField(max_length = 20, null = True) + +class Job(BaseModel): + """ + This class defines Job, which maintains details of past Jobs + held by a Person. + """ + job_name = CharField(primary_key = True, max_length = 30) + start_date = DateField(formats = 'YYYY-MM-DD') + end_date = DateField(formats = 'YYYY-MM-DD') + duration = IntegerField() + salary = DecimalField(max_digits = 7, decimal_places = 2) + person_employed = ForeignKeyField(Person, related_name ='was_filled_by', null = False) + department_number = ForeignKeyField(Department, related_name = 'department_id', null = False) + + +class Department(BaseModel): + ''' + This class defines Department. Multiple jobs in dept + ''' + department_number = CharField(primary_key = True, max_length = 4) + department_name = CharField(max_lenth = 30) + department_manager = CharField(max_lenth = 30) + + +class PersonNumKey(BaseModel): + """ + This class defines Person, which maintains details of someone + for whom we want to research career to date. + + *** I am implemented with a numeric PK that is generated by the system *** + """ + person_name = CharField(max_length = 30) + lives_in_town = CharField(max_length = 40) + nickname = CharField(max_length = 20, null = True) diff --git a/Student/osiddiquee/lesson07/activity2.py b/Student/osiddiquee/lesson07/activity2.py new file mode 100644 index 0000000..02542ca --- /dev/null +++ b/Student/osiddiquee/lesson07/activity2.py @@ -0,0 +1,70 @@ +""" + Learning persistence with Peewee and sqlite + delete the database to start over + (but running this program does not require it) +""" + +from personjob_model import * + +import logging + +def populate_db(): + """ + this is a proof of concept because i cant download peewee with only add department data to database + """ + + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + + database = SqliteDatabase('personjob.db') + + logger.info('Working with Job class') + logger.info('Creating Job records: just like Person. We use the foreign key') + + DEPARTMENT_NUMBER = 0 + DEPARTMENT_NAME = 1 + DEPARTMENT_MANAGER = 2 + + departments = [ + ('PM01', 'Product Management', 'Mario Mario'), + ('MK01', 'Marketing', 'Luigi Mario'), + ('FN01', 'Finance', 'King Koopa') + ] + + try: + database.connect() + database.execute_sql('PRAGMA foreign_keys = ON;') + for department in departments: + with database.transaction(): + new_department = Department.create( + department_number = departments[DEPARTMENT_NUMBER], + department_name = departments[DEPARTMENT_NAME], + department_manager = departments[DEPARTMENT_MANAGER] + new_department.save() + + logger.info('Reading and print all Department rows (note the value of person)...') + for department in departments: + logger.info(f'{department.department_number} : {department.department_name}') + + except Exception as e: + logger.info(f'Error creating = {department[department_number]}') + logger.info(e) + + finally: + logger.info('database closes') + database.close() + +print_db(): +''' +in theory this function should work, but it would be 50 times easier in sql +''' +departments = (Person + .select(Person, Job) + .join(Job)) +for tweet in tweets: + print(department.person_name, department.job.job_name, department.job.department_id) + + +if __name__ == '__main__': + populate_db() + print_db() diff --git a/Student/osiddiquee/lesson07/mailroom_db.py b/Student/osiddiquee/lesson07/mailroom_db.py new file mode 100644 index 0000000..7d20096 --- /dev/null +++ b/Student/osiddiquee/lesson07/mailroom_db.py @@ -0,0 +1,82 @@ +from personjob_model import * + +import logging + +def populate_db(): + + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + + database = SqliteDatabase('mailroom.db') + + ''' + creating donor stuff + ''' + + logger.info('Creating donor records') + + DONOR_ID = 0 + DONOR_NAME = 1 + + donors = [ + (1, 'Walter White'), + (2, 'Jesse Pinkman'), + (3, 'Gustavo Fring') + ] + + try: + database.connect() + database.execute_sql('PRAGMA foreign_keys = ON;') + for donor in donors: + with database.transaction(): + new_donor = Donor.create( + donor_id = donors[DONOR_ID], + donor_name = donors[DONOR_NAME]) + new_donor.save() + + except Exception as e: + logger.info(f'Error creating = {donors[DONOR_NAME]}') + logger.info(e) + + finally: + logger.info('database closes') + database.close() + + ''' + creating donation stuff + ''' + + logger.info('Creating donation records') + + DONATION_ID = 0 + DONATION_AMOUNT = 1 + DONOR_ID = 2 + + donations = [ + (1, 5000.00, 1), + (2, 6000.00, 1), + (3, 4000.00, 2), + (4, 4500.00, 3) + ] + + try: + database.connect() + database.execute_sql('PRAGMA foreign_keys = ON;') + for donation in donations: + with database.transaction(): + new_donation = Donation.create( + donation_id = donations[DONATION_ID], + donation_amount = donations[DONATION_AMOUNT], + donor_id = donations[DONOR_ID]) + new_donation.save() + + except Exception as e: + logger.info(f'Error creating = {donations[DONATION_ID]}') + logger.info(e) + + finally: + logger.info('database closes') + database.close() + +if __name__ == '__main__': + populate_db() diff --git a/Student/osiddiquee/lesson07/mailroom_model.py b/Student/osiddiquee/lesson07/mailroom_model.py new file mode 100644 index 0000000..79fd6db --- /dev/null +++ b/Student/osiddiquee/lesson07/mailroom_model.py @@ -0,0 +1,37 @@ +""" + Donor database + Here we define the schema + +""" + +from peewee import * + +database = SqliteDatabase('mailroom.db') +database.connect() +database.execute_sql('PRAGMA foreign_keys = ON;') + + + +class BaseModel(Model): + class Meta: + database = database + + +class Donor(BaseModel): + """ + This class defines Person, which maintains details of someone + for whom we want to research career to date. + """ + donor_id = IntegerField(primary_key = True) + donor_name = CharField(max_length = 30) + +class Donation(BaseModel): + """ + This class defines Job, which maintains details of past Jobs + held by a Person. + """ + donation_id = IntegerField(primary_key = True) + donation_amount = DecimalField(max_digits = 9, decimal_places = 2) + donor_id = ForeignKeyField(Donor, related_name = 'donor_id', null = False) + +database.create_tables([Donor, Donation], safe = True) diff --git a/Student/osiddiquee/lesson07/mailroom_relational.py b/Student/osiddiquee/lesson07/mailroom_relational.py new file mode 100644 index 0000000..5631d91 --- /dev/null +++ b/Student/osiddiquee/lesson07/mailroom_relational.py @@ -0,0 +1,461 @@ +#!/usr/bin/env python +""" +This is an object oriented version +""" + +import sys +import math +from textwrap import dedent +from mailroom_model import * + + +# Utility so we have data to test with, etc. +# def get_sample_data(): +# """ +# returns a list of donor objects to use as sample data +# +# +# """ +# +# +# return [Donor("William Gates III", [653772.32, 12.17]), +# Donor("Jeff Bezos", [877.33]), +# Donor("Paul Allen", [663.23, 43.87, 1.32]), +# Donor("Mark Zuckerberg", [1663.23, 4300.87, 10432.0]), +# ] + + + + + +class Donor(): + """ + class to hold the information about a single donor + """ + + def __init__(self, name, donations=None): + """ + create a new Donor object + + :param name: the full name of the donor + + :param donations=None: iterable of past donations + """ + + self.norm_name = self.normalize_name(name) + self.name = name.strip() + if donations is None: + self.donations = [] + else: + self.donations = list(donations) + + @staticmethod + def normalize_name(name): + """ + return a normalized version of a name to use as a comparison key + + simple enough to not be in a method now, but maybe you'd want to make it fancier later. + """ + return name.lower().strip().replace(" ", "") + + @property + def last_donation(self): + """ + The most recent donation made + """ + try: + return self.donations[-1] + except IndexError: + return None + + @property + def total_donations(self): + return sum(self.donations) + + @property + def average_donation(self): + return self.total_donations / len(self.donations) + + def add_donation(self, amount): + """ + add a new donation + """ + amount = float(amount) + if amount <= 0.0: + raise ValueError("Donation must be greater than zero") + self.donations.append(amount) + + +class DonorDB(): + """ + encapsulation of the entire database of donors and data associated with them. + """ + + def __init__(self, donors=None): + """ + Initialize a new donor database + + :param donors=None: iterable of Donor objects + """ + if donors is None: + self.donor_data = {} + else: + self.donor_data = {d.norm_name: d for d in donors} + + # def save_to_file(self, filename): + # with open(filename, 'w') as outfile: + # self.to_json(outfile) + + # @classmethod + # def load_from_file(cls, filename): + # with open(filename, 'r') as infile: + # obj = js.from_json(infile) + # return obj + + @property + def donors(self): + """ + an iterable of all the donors + """ + return self.donor_data.values() + + def list_donors(self): + """ + creates a list of the donors as a string, so they can be printed + + Not calling print from here makes it more flexible and easier to + test + """ + listing = ["Donor list:"] + for donor in self.donors: + listing.append(donor.name) + return "\n".join(listing) + + def find_donor(self, name): + """ + find a donor in the donor db + + :param: the name of the donor + + :returns: The donor data structure -- None if not in the self.donor_data + """ + return self.donor_data.get(Donor.normalize_name(name)) + + def add_donor(self, name): + """ + Add a new donor to the donor db + + :param: the name of the donor + + :returns: the new Donor data structure + """ + donor = Donor(name) + self.donor_data[donor.norm_name] = donor + return donor + + def gen_letter(self, donor): + """ + Generate a thank you letter for the donor + + :param: donor tuple + + :returns: string with letter + + note: This doesn't actually write to a file -- that's a separate + function. This makes it more flexible and easier to test. + """ + return dedent('''Dear {0:s}, + + Thank you for your very kind donation of ${1:.2f}. + It will be put to very good use. + + Sincerely, + -The Team + '''.format(donor.name, donor.last_donation) + ) + + @staticmethod + def sort_key(item): + # used to sort on name in self.donor_data + return item[1] + + def generate_donor_report(self): + """ + Generate the report of the donors and amounts donated. + + :returns: the donor report as a string. + """ + # First, reduce the raw data into a summary list view + report_rows = [] + for donor in self.donor_data.values(): + name = donor.name + gifts = donor.donations + total_gifts = donor.total_donations + num_gifts = len(gifts) + avg_gift = donor.average_donation + report_rows.append((name, total_gifts, num_gifts, avg_gift)) + + # sort the report data + report_rows.sort(key=self.sort_key) + report = [] + report.append("{:25s} | {:11s} | {:9s} | {:12s}".format("Donor Name", + "Total Given", + "Num Gifts", + "Average Gift")) + report.append("-" * 66) + for row in report_rows: + report.append("{:25s} ${:10.2f} {:9d} ${:11.2f}".format(*row)) + return "\n".join(report) + + def save_letters_to_disk(self): + """ + make a letter for each donor, and save it to disk. + """ + for donor in self.donor_data.values(): + print("Writing a letter to:", donor.name) + letter = self.gen_letter(donor) + # I don't like spaces in filenames... + filename = donor.name.replace(" ", "_") + ".txt" + open(filename, 'w').write(letter) + + +# User-interaction code +# Above this is all the logic code +# The stuff you'd need if you had a totally different UI.different +# below is code only for the command line interface. + + +# import sys +# import math + +# # handy utility to make pretty printing easier +# from textwrap import dedent + +# from mailroom import model + +# create a DB with the sample data + +def main_menu_selection(): + """ + Print out the main application menu and then read the user input. + """ + action = input(dedent(''' + Choose an action: + + 1 - Send a Thank You + 2 - Create a Report + 3 - Send letters to everyone + 4 - Quit + + > ''')) + return action.strip() + + +def send_thank_you(): + """ + Record a donation and generate a thank you message. + """ + # Read a valid donor to send a thank you from, handling special commands to + # let the user navigate as defined. + while True: + name = input("Enter a donor's name" + "(or 'list' to see all donors or 'menu' to exit)> ").strip() + if name == "list": + print(db.list_donors()) + elif name == "menu": + return + else: + break + + # Now prompt the user for a donation amount to apply. Since this is + # also an exit point to the main menu, we want to make sure this is + # done before mutating the db. + while True: + amount_str = input("Enter a donation amount (or 'menu' to exit)> ").strip() + if amount_str == "menu": + return + # Make sure amount is a valid amount before leaving the input loop + try: + amount = float(amount_str) + # extra check here -- unlikely that someone will type "NaN", but + # it IS possible, and it is a valid floating point number: + # http://en.wikipedia.org/wiki/NaN + if math.isnan(amount) or math.isinf(amount) or round(amount, 2) == 0.00: + raise ValueError + # in this case, the ValueError could be raised by the float() call, or by the NaN-check + except ValueError: + print("error: donation amount is invalid\n") + else: + break + + # If this is a new user, ensure that the database has the necessary + # data structure. + donor = db.find_donor(name) + if donor is None: + donor = db.add_donor(name) + + # Record the donation + donor.add_donation(amount) + print(db.gen_letter(donor)) + + +def print_donor_report(): + print(db.generate_donor_report()) + +def quit(): + sys.exit(0) + + +''' +I realize that this next piece of code is impractical. +This is because im basically storing the db in memory which is inefficent +However, it's not like peewee doesn't have it's own reasons for being impractical +Finally, given that this code will be refactored 4 times and given that +I 100% know this database won't ever be large, I have taken an approach to +simplify it's refactoring hurdles +''' + +#transforms the db to a db_to_dict +def db_to_dict(): + database = SqliteDatabase('mailroom.db') + + database.connect() + database.execute_sql('PRAGMA foreign_keys = ON;') + + query = (Donor + .select(Donor.donor_name, Donation.donation_amount) + .join(Donation)) + + donor_dict = {} + for donor, donation in query: + if donor in donor_dict: + donor_dict[donor].append(donation) + else: + donor_dict[donor] = [donation] + + data = [] + for donor, donations in donor_dict: + if not data: + data = [Donor(donor, donations)] + else: + data.append(Donor(donor, donations)) + + return data + + +def dict_to_db(donors_database): + database = SqliteDatabase('mailroom.db') + + database.connect() + database.execute_sql('PRAGMA foreign_keys = ON;') + + ''' + creating donor stuff + ''' + + logger.info('Creating donor records') + + DONOR_ID = 0 + DONOR_NAME = 1 + + donors = [] + donor_id_lookup = {} + for id, donor in enumerate(donors_database): + if not donors: + [(id, donor.name)] + else: + donor.append((id, donor.name)) + donor_id_lookup[donor.name] = id + + + # donors = [ + # (1, 'Walter White'), + # (2, 'Jesse Pinkman'), + # (3, 'Gustavo Fring') + # ] + + try: + database.connect() + database.execute_sql('PRAGMA foreign_keys = ON;') + for donor in donors: + with database.transaction(): + new_donor = Donor.create( + donor_id = donors[DONOR_ID], + donor_name = donors[DONOR_NAME]) + new_donor.save() + + except Exception as e: + logger.info(f'Error creating = {donors[DONOR_NAME]}') + logger.info(e) + + finally: + logger.info('database closes') + database.close() + + ''' + creating donation stuff + ''' + + logger.info('Creating donation records') + + DONATION_ID = 0 + DONATION_AMOUNT = 1 + DONOR_ID = 2 + + donations = [] + for id, donor in enumerate(donors_database) + for name, donation in donor.name, donor.donations + if not donations: + donations = [(id, donation, donor_id_lookup[name])] + else: + donations.append((id, donation, donor_id_lookup[name])) + + # donations = [ + # (1, 5000.00, 1), + # (2, 6000.00, 1), + # (3, 4000.00, 2), + # (4, 4500.00, 3) + # ] + + try: + database.connect() + database.execute_sql('PRAGMA foreign_keys = ON;') + for donation in donations: + with database.transaction(): + new_donation = Donation.create( + donation_id = donations[DONATION_ID], + donation_amount = donations[DONATION_AMOUNT], + donor_id = donations[DONOR_ID]) + new_donation.save() + + except Exception as e: + logger.info(f'Error creating = {donations[DONATION_ID]}') + logger.info(e) + + finally: + logger.info('database closes') + database.close() + + + +def main(): + #this is the same as the db but provides a starting point + db = db_to_dict() + + selection_dict = {"1": send_thank_you, + "2": print_donor_report, + "3": db.save_letters_to_disk, + "4": quit} + + while True: + selection = main_menu_selection() + try: + selection_dict[selection]() + except KeyError: + print("error: menu selection is invalid!") + + dict_to_db(db) + +if __name__ == "__main__": + + main() diff --git a/Student/osiddiquee/lesson09/Acitivity09.py b/Student/osiddiquee/lesson09/Acitivity09.py new file mode 100644 index 0000000..2a105c2 --- /dev/null +++ b/Student/osiddiquee/lesson09/Acitivity09.py @@ -0,0 +1,103 @@ +''' + Notes for concurrency and async +''' + + +import sys +import threading +import time +from Queue import Queue + +''' + This is an intengration function +def f(x): +''' + return x**2 + +def integrate(f, a, b, N): + s = 0 + dx = (b-a)/N + for i in xrange(N): + s += f(a+i*dx) + return s * dx + +''' + This starts threading +''' +def func(): + for i in xrange(5): + print("hello from thread %s" % threading.current_thread().name) + time.sleep(1) + +threads = [] +for i in xrange(3): + thread = threading.Thread(target=func, args=()) + thread.start() + threads.append(thread) + +''' + This is an example of subclassing +''' +class MyThread(threading.Thread): + + def run(self): + print("hello from %s" % threading.current_thread().name) + +thread = MyThread() +thread.start() + +''' + Mutex locks (threading.Lock) + + Probably most common + Only one thread can modify shared data at any given time + Thread determines when unlocked + Must put lock/unlock around critical code in ALL threads + Difficult to manage + + Easiest with context manager: +''' +x = 0 +x_lock = threading.Lock() + +# Example critical section +with x_lock: + # statements using x + + +''' + Locking threads +''' +lock = threading.Lock() + +def f(): + lock.acquire() + print("%s got lock" % threading.current_thread().name) + time.sleep(1) + lock.release() + +threading.Thread(target=f).start() +threading.Thread(target=f).start() +threading.Thread(target=f).start() + +''' + This is a nonblocking locks +''' +lock = threading.Lock() +lock.acquire() +if not lock.acquire(False): + print("couldn't get lock") +lock.release() +if lock.acquire(False): + print("got lock") + +''' + Queueing +''' + +#from Queue import Queue +q = Queue(maxsize=10) +q.put(37337) +block = True +timeout = 2 +print(q.get(block, timeout)) diff --git a/Student/osiddiquee/lesson09/newapi.py b/Student/osiddiquee/lesson09/newapi.py new file mode 100644 index 0000000..f20df65 --- /dev/null +++ b/Student/osiddiquee/lesson09/newapi.py @@ -0,0 +1,126 @@ +import requests +import threading +from math import ceil +import time + +############################# +## Assignment ## +############################# +# If you are struggling with the assignment from +# lesson 9, then modify this script to run. +# I have purposefully disabled the threading code +# to not work (silently... no error will occur). + + +################################### +## Other Suggested modifications ## +################################### +# - Use recursive function for API pagination +# - Persist / store data collected from API +# - Use generator for API pagination +# - Trying multiprocessing and queue packages +# - Rewrite the whole script + + +########################### +## Persist or store data ## +########################### +# import sqlite3 +# import json +# import pymongo + + +################################# +## Get initial result metadata ## +################################# +api_key = "d841cf2b36404cc98115f9328bfdfaec" +keywords= ",".join(["Nintendo","Sony", "Microsoft"]) +page = 1 +url = (f'https://newsapi.org/v2/everything?' + 'q={keywords}&' + 'from=2018-06-21&' + 'sortBy=popularity&' + 'apiKey={api_key}&' + 'page={page}') + +url = url.format(keywords=keywords, page=page, api_key=api_key) +print("API URL:", url) + +response = requests.get(url) +totalResults = response.json()["totalResults"] +resultsPerPage = len(response.json()["articles"]) # defaults to 20 +numPages = ceil(totalResults/resultsPerPage) + + +################################ +## Define `get_page` function ## +## w/ `page` parameter ## +################################ +def get_page(page): + url = (f'https://newsapi.org/v2/everything?' + 'q={keywords}&' + 'from=2018-06-21&' + 'sortBy=popularity&' + 'apiKey={api_key}&' + 'page={page}') + + url = url.format(keywords=keywords, page=page, api_key=api_key) + + r = requests.get(url) + if r.status_code == 200: + raw = r.json() + articles = raw["articles"] + ############################################## + ## `articles` is essentially your data.... ## + ## do something with it here ## + ############################################## + with open('search_data.csv', 'a') as search_csv: + for article in articles: + #article_row = {} + # article_row['title'] = article['title'] + # article_row['author'] = article['author'] + # article_row['url'] = article['url'] + + title = article['title'] + author = article['author'] + link = article['url'] + + # i want a perfectly clean dataset + # i doubt i would lose much data because of multithreading... + # allowing me to get a lot efficiently + if not (title or author or link): + search_csv.write(f'''{article['title'].encode('utf8')}, + {article['author'].encode('utf8')}, + {article['url'].encode('utf8')}''') + else: + pass + + else: + print("API Error - Status Code", r.status_code) + + +############################################## +## This here is the single threaded version ## +############################################## +start = time.time() +for i in range(numPages): + get_page(i+1) +end = time.time() +print("single threaded time: %.3f seconds", end - start) + + + +############################################# +## This here is the multi-threaded version ## +############################################# +start = time.time() +threads = [] +for i in range(numPages): + thread = threading.Thread(target = get_page, args = ((i+1,))) + thread.start() + threads.append(thread) + +for thread in threads: + thread.join() +end = time.time() +print("multi-threaded time: %.3f seconds", end - start) diff --git a/Student/osiddiquee/lesson09/profiler.py b/Student/osiddiquee/lesson09/profiler.py new file mode 100644 index 0000000..8273522 --- /dev/null +++ b/Student/osiddiquee/lesson09/profiler.py @@ -0,0 +1,100 @@ + +import time + +################################################### +# # +# Decorators for profiling are located below # +# # +################################################### + +# timer decorator to test sequency functions +def timer(timed_sequence): + def timed(index): + start = time.time() + result = timed_sequence(index) + end = time.time() + print(end - start) + return result + return timed + +class Cache_sequence(): + def __init__(self, sequence_function): + self.sequence_function = sequence_function + self.cache = {} + + def __call__(self, index): + if not index in self.cache: + self.cache[index] = self.sequence_function(index) + return self.cache[index] + +############################################################### +# # +# This is the definition of an arbitrary number function # +# In this case, it is fibonacii # +# # +############################################################### + + +#uncached version for the closure +#@timer +def F(n): + if n == 0: return 0 + elif n == 1: return 1 + else: return F(n-1)+F(n-2) + + +#@timer +@Cache_sequence +def Fib(n): + if n == 0: return 0 + elif n == 1: return 1 + else: return F(n-1)+F(n-2) + +############################## +# # +# Executing functions # +# # +############################## + +@timer +def run_functions(function): + function(25) + + +print('Cached sequence function...') +run_functions(Fib) + +print('Uncached sequence function...') +run_functions(F) + +s = time.time() +Fib(50) +e = time.time() +print(e - s) + +s = time.time() +F(50) +e = time.time() +print(e - s) + + + + + + + + +''' + Failed attempt to make a closure to cached +''' +# making a cache with a closure +# @timer +# def number_sequence_cache(sequence_function): +# cached = {} +# def cache(index): +# if index not in cache: +# cached[index] = sequence_function(index) +# return cache[index] +# else: +# return cache[index] +# return cache