import json
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse, parse_qs
from views import (get_all_animals,
            get_single_animal,
            create_animal,
            delete_animal,
            update_animal,
            get_animals_by_location,
            get_animals_by_status,
            get_all_locations,
            get_single_location,
            create_location,
            delete_location,
            update_location,
            get_all_customers,
            get_single_customer,
            create_customer,
            delete_customer,
            update_customer,
            get_customers_by_email,
            get_all_employees,
            get_single_employee,
            create_employee,
            delete_employee,
            update_employee,
            get_employees_by_location)

# Here's a class. It inherits from another class.
# For now, think of a class as a container for functions that
# work together for a common purpose. In this case, that
# common purpose is to respond to HTTP requests from a client.

# It looks like you have a basic HTTP server implementation
# in your request_handler.py file,
# using the BaseHTTPRequestHandler class
# from Python's built-in http.server module.
# The HandleRequests class defined in the file
# inherits from this base class
# and overrides several of its methods,
# including do_GET(), do_POST(), do_PUT(), and do_OPTIONS(),
# to handle incoming HTTP requests of different types.

class HandleRequests(BaseHTTPRequestHandler):
    # This is a Docstring it should be at the beginning of all classes and functions
    # It gives a description of the class or function
    """Controls the functionality of any GET, PUT, POST, DELETE requests to the server
    """

    # # Here's a class function

    # # Here's a method on the class that overrides the parent's method.
    # # It handles any GET request.

    # # In do_GET(), for example, the server responds
    # # with a JSON-encoded list of animal objects
    # # if the request path is /animals, and an empty list otherwise.
    # # The response is sent back to the client with an HTTP status code of 200,
    # # along with the necessary headers to indicate that the response contains JSON data
    # # and can be accessed from any domain (i.e., via cross-origin resource sharing, or CORS).
    # # def do_GET(self):
    # #     """Handles GET requests to the server
    # #     """
    # #     # Set the response code to 'Ok'
    # #     self._set_headers(200)

    # #     # Your new console.log() that outputs to the terminal
    # #     print(self.path)

    # #     # # It's an if..else statement
    # #     # if self.path == "/animals":
    # #     #     # In Python, this is a list of dictionaries
    # #     #     # In JavaScript you would call it an array of objects
    # #     #     response = [
    # #     #         {"id": 1, "name": "Snickers", "species": "Dog"},
    # #     #         {"id": 2, "name": "Lenny", "species": "Cat"}
    # #     #     ]

    # #     # else:
    # #     #     response = []

    # #     if self.path == "/animals":
    # #         response = get_all_animals()
    # #     else:
    # #         response = []

    # #     # Send a JSON formatted string as a response
    # #     self.wfile.write(json.dumps(response).encode())

    # def do_GET(self):
    #     """GET
    #     """
    #     self._set_headers(200)
    #     response = {}  # Default response

    #     # Parse the URL and capture the tuple that is returned
    #     (resource, id) = self.parse_url(self.path)

    #     if resource == "animals":
    #         if id is not None:
    #             response = get_single_animal(id)
    #         else:
    #             response = get_all_animals()

    #     if resource == "locations":
    #         if id is not None:
    #             response = get_single_location(id)
    #         else:
    #             response = get_all_locations()

    #     if resource == "customers":
    #         if id is not None:
    #             response = get_single_customer(id)
    #         else:
    #             response = get_all_customers()

    #     if resource == "employees":
    #         if id is not None:
    #             response = get_single_employee(id)
    #         else:
    #             response = get_all_employees()

    #     self.wfile.write(json.dumps(response).encode())

    def do_GET(self):
        """GET refactored
        """
        self._set_headers(200)

        response = {}

        # Parse URL and store entire tuple in a variable
        parsed = self.parse_url(self.path)

        # If the path does not include a query parameter, continue with the original if block
        if '?' not in self.path:
            ( resource, id ) = parsed

            if resource == "animals":
                if id is not None:
                    response = get_single_animal(id)
                else:
                    response = get_all_animals()
            elif resource == "customers":
                if id is not None:
                    response = get_single_customer(id)
                else:
                    response = get_all_customers()
            elif resource == "employees":
                if id is not None:
                    response = get_single_employee(id)
                else:
                    response = get_all_employees()
            elif resource == "locations":
                if id is not None:
                    response = get_single_location(id)
                else:
                    response = get_all_locations()

        else: # There is a ? in the path, run the query param functions
            (resource, query) = parsed

            # see if the query dictionary has an email key
            if query.get('email') and resource == 'customers':
                response = get_customers_by_email(query['email'][0])
            elif query.get('location_id') and resource == 'animals':
                response = get_animals_by_location(query['location_id'][0])
            elif query.get('location_id') and resource == 'employees':
                response = get_employees_by_location(query['location_id'][0])
            elif query.get('status') and resource == 'animals':
                response = get_animals_by_status(query['status'][0])

        self.wfile.write(json.dumps(response).encode())

    # def parse_url(self, path):
    #     """
    #     parse
    #     """
    #     # Just like splitting a string in JavaScript. If the
    #     # path is "/animals/1", the resulting list will
    #     # have "" at index 0, "animals" at index 1, and "1"
    #     # at index 2.
    #     path_params = path.split("/")
    #     resource = path_params[1]
    #     id = None

    #     # Try to get the item at index 2
    #     try:
    #         # Convert the string "1" to the integer 1
    #         # This is the new parseInt()
    #         id = int(path_params[2])
    #     except IndexError:
    #         pass  # No route parameter exists: /animals
    #     except ValueError:
    #         pass  # Request had trailing slash: /animals/

    #     return (resource, id)  # This is a tuple

    # replace the parse_url function in the class
    def parse_url(self, path):
        """Parse the url into the resource and id"""
        parsed_url = urlparse(path)
        path_params = parsed_url.path.split('/')  # ['', 'animals', 1]
        resource = path_params[1]

        if parsed_url.query:
            query = parse_qs(parsed_url.query)
            return (resource, query)

        pk = None
        try:
            pk = int(path_params[2])
        except (IndexError, ValueError):
            pass
        return (resource, pk)

    # # Here's a method on the class that overrides the parent's method.
    # # It handles any POST request.

    # # The do_POST() method is similarly implemented to handle incoming HTTP POST requests,
    # # reading the request body and echoing it back to the client
    # # as a JSON payload in the response body.
    # def do_POST(self):
    #     """Handles POST requests to the server"""

    #     # Set response code to 'Created'
    #     self._set_headers(201)

    #     content_len = int(self.headers.get('content-length', 0))
    #     post_body = self.rfile.read(content_len)
    #     response = {"payload": post_body}
    #     self.wfile.write(json.dumps(response).encode())

    def do_POST(self):
        """POST creates new resource"""
        self._set_headers(201)
        content_len = int(self.headers.get('content-length', 0))
        post_body = self.rfile.read(content_len)

        # Convert JSON string to a Python dictionary
        post_body = json.loads(post_body)

        # Parse the URL
        (resource, id) = self.parse_url(self.path)

        # Initialize new animal
        new_animal = None
        new_location = None
        new_employee = None
        new_customer = None

        # Add a new animal to the list
        if resource == "animals":
            new_animal = create_animal(post_body)
        # Encode the new animal and send in response
            self.wfile.write(json.dumps(new_animal).encode())

        if resource == "locations":
            new_location = create_location(post_body)
            self.wfile.write(json.dumps(new_location).encode())

        if resource == "employees":
            new_employee = create_employee(post_body)
            self.wfile.write(json.dumps(new_employee).encode())

        if resource == "customers":
            new_customer = create_customer(post_body)
            self.wfile.write(json.dumps(new_customer).encode())

    # # A method that handles any PUT request.
    # def do_PUT(self):
    #     """PUT replaces WHOLE resource with UPDATE
    #     """
    #     self._set_headers(204)
    #     content_len = int(self.headers.get('content-length', 0))
    #     post_body = self.rfile.read(content_len)
    #     post_body = json.loads(post_body)

    #     # Parse the URL
    #     (resource, id) = self.parse_url(self.path)

    #     # Delete a single animal from the list
    #     if resource == "animals":
    #         update_animal(id, post_body)
    #     if resource == "customers":
    #         update_customer(id, post_body)
    #     if resource == "employees":
    #         update_employee(id, post_body)
    #     if resource == "locations":
    #         update_location(id, post_body)

    #     # Encode the new animal and send in response
    #     self.wfile.write("".encode())

    def do_PUT(self):
        """PUT UPDATES and REPLACES whole resource
        """
        content_len = int(self.headers.get('content-length', 0))
        post_body = self.rfile.read(content_len)
        post_body = json.loads(post_body)

        # Parse the URL
        (resource, id) = self.parse_url(self.path)

        success = False

        if resource == "animals":
            success = update_animal(id, post_body)
        # rest of the elif's
        elif resource == "locations":
            update_location(id, post_body)
        elif resource == "employees":
            update_employee(id, post_body)
        elif resource == "customers":
            update_customer(id, post_body)

        if success:
            self._set_headers(204)
        else:
            self._set_headers(404)

        self.wfile.write("".encode())

    def _set_headers(self, status):
        # Notice this Docstring also includes information about the arguments passed to the function
        """Sets the status code, Content-Type and Access-Control-Allow-Origin
        headers on the response

        Args:
            status (number): the status code to return to the front end
        """
        self.send_response(status)
        self.send_header('Content-type', 'application/json')
        self.send_header('Access-Control-Allow-Origin', '*')
        self.end_headers()

    # Another method! This supports requests with the OPTIONS verb.
    def do_OPTIONS(self):
        """Sets the options headers
        """
        self.send_response(200)
        self.send_header('Access-Control-Allow-Origin', '*')
        self.send_header('Access-Control-Allow-Methods',
                        'GET, POST, PUT, DELETE')
        self.send_header('Access-Control-Allow-Headers',
                        'X-Requested-With, Content-Type, Accept')
        self.end_headers()

    def do_DELETE(self):
        """deletes element from list. This is still in class HandleRequests
        """
        # Set a 204 response code
        self._set_headers(204)

        # Parse the URL
        (resource, id) = self.parse_url(self.path)

        # Delete a single resource from the list
        if resource == "animals":
            delete_animal(id)
        if resource == "customers":
            delete_customer(id)
        if resource == "employees":
            delete_employee(id)
        if resource == "locations":
            delete_location(id)

        # Encode the new animal and send in response
        self.wfile.write("".encode())

# This function is not inside the class. It is the starting
# point of this application.

# THIS MAKE IT GO
# Finally, the main() function at the end of the file starts the server on port 8088
# and listens for incoming requests indefinitely,
# using the HTTPServer class also from the http.server module.
def main():
    """Starts the server on port 8088 using the HandleRequests class
    """
    host = ''
    port = 8088
    HTTPServer((host, port), HandleRequests).serve_forever()

if __name__ == "__main__":
    main()


import numpy as np
import pandas as pd
import requests
import io
import datetime as datetime

#SX7P data high_low_close_open_prices from https://www.wsj.com/market-data/quotes/index/XX/XSTX/FX7/historical-prices
df = pd.read_csv('HistoricalPrices.csv')
df['Date'] = pd.to_datetime(df['Date'])
#remove non-trading days historical data
#https://www.xetra.com/resource/blob/281576/967747fbc75fa120c2142e05d2e40711/data/trading-calendar
non_trading_days = ['01-01-2014','21-04-2014','01-05-2014','03-10-2014','24-12-2014','25-12-2014','26-12-2014','31-12-2014',
                    '01-01-2015','06-04-2015','25-05-2015','24-12-2015','25-12-2015','31-12-2015',
                    '01-01-2016','28-03-2016','16-05-2016','03-10-2016','26-12-2016',
                    '14-04-2017','17-04-2017','01-05-2017','05-06-2017','03-10-2017','31-10-2017','25-12-2017','26-12-2017',
                    '01-01-2018','30-03-2018','02-04-2018','01-05-2018','21-05-2018','03-10-2018','24-12-2018','25-12-2018','26-12-2018','31-12-2018',
                    '01-01-2019','19-04-2019','22-04-2019','01-05-2019','10-06-2019','03-10-2019','24-12-2019','25-12-2019','26-12-2019','31-12-2019',
                    '01-01-2020','10-03-2020','13-03-2020','01-05-2020','01-06-2020','24-12-2020','25-12-2020','31-12-2020','01-01-2020',
                ]
non_trading_days = (datetime.datetime.strptime(a,'%d-%m-%Y') for a in non_trading_days)

SX7P = df[~df['Date'].isin(non_trading_days)]

########## NEW
entrypoint = 'https://sdw-wsrest.ecb.europa.eu/service/'
resource = 'data'
parameters = {'startPeriod' : '2014-01-01',
        'endPeriod' : '2020-12-30'}

data = {'currency_pair': ['daily', 'EXR' + '/' + 'D.USD.EUR.SP00.A'],
        'debt_sec': ['monthly', 'BSI' + '/' + 'M.U2.N.R.L40.H.1.A1.0000.Z01.E'],
        'exc_reserves': ['monthly', 'BSI' + '/' + 'M.U2.N.R.LRE.X.1.A1.3000.Z01.E'],
        'bank_notes_in_circulation':['monthly', 'ILM' + '/' + 'M.U2.C.L010000.Z5.EUR'],
        'current_accounts': ['monthly', 'ILM' + '/' + 'M.U2.C.L020100.U2.EUR'],
        'interest_rate_on_loans': ['monthly', 'MIR' + '/' + 'M.U2.B.A20.A.I.A.2240.EUR.O'],
        'interest_rate_on_deposits': ['monthly', 'MIR' + '/' + 'M.U2.B.L21.A.I.A.2230.EUR.N'],
        'index_debt_securities': ['monthly', 'SEC' + '/' + 'M.A1.1000.F33000.N.I.EUR.E.Z'],
        'HICP': ['monthly', 'ICP' + '/' + 'M.U2.N.000000.4.INX'],
        'building_permits': ['monthly', 'STS' + '/' + 'M.I8.N.BPER.CC1109.4.000'],
        'industrial_new_orders': ['monthly', 'STS' + '/' + 'M.I8.Y.ORDT.NSC002.3.000' ],
        'commodities_index': ['monthly', 'STS' + '/' + 'M.I8.N.ECPE.CFOOD0.3.000'],
        'total_employment': ['daily', 'ENA' + '/' + 'Q.N.I8.W2.S1.S1._Z.EMP._Z._T._Z.PS._Z.N'],
        'labour_cost_index': ['daily', 'LCI' + '/' + 'Q.I8.W.LCI_T.BTS'],
        'import_price_index': ['daily', 'STS' + '/' + 'M.I8.N.IMPX.2C0000.4.000'],
        'total_trade_volume': ['daily', 'TRD' + '/' + 'M.I8.N.M.TTT.J8.4.VAL'],
        'interest_rate_10yrs': ['daily', 'IRS' + '/' + 'M.D0.L.L40.CI.0000.Z01.N.Z'],
        'yield_curve_spot_rate': ['daily', 'YC' + '/' + 'B.U2.EUR.4F.G_N_A.SV_C_YM.SR_10Y'],
        'revenue_EU19': ['daily', 'GFS' + '/' + 'A.N.I8.W0.SZX.S1.N.C.OTE._Z._Z._Z.XDC_R_B1GQ._Z.S.V.N._T'],
        'expenses_EU19': ['daily', 'GFS' + '/' + 'A.N.I8.W0.SZX.S1.N.D.OTR._Z._Z._Z.XDC_R_B1GQ._Z.S.V.N._T'],
        'gov_change_debt_%_GDP_EU19': ['daily', 'GFS' + '/' + 'Q.N.I8.W0.S13.S1.C.L.LX.GD.T._Z.XDC_R_B1GQ_CY._T.F.V.CY._T']
}

for attr in data.keys():

        # query
        response = requests.get(entrypoint + resource + '/'+data[attr][1], params = parameters, headers = {'Accept': 'text/csv'})
        df = pd.read_csv(io.StringIO(response.text))

        # select columns
        df = df.loc[:,['TIME_PERIOD','OBS_VALUE']]
        df.rename(columns={'OBS_VALUE': attr}, inplace=True)
        df['TIME_PERIOD'] = pd.to_datetime(df['TIME_PERIOD'])

        # merge in terms of whether data is provided on a daily or monthly basis
        if data[attr][0]=='daily':
                SX7P = pd.merge(SX7P, df, left_on=SX7P['Date'],
                        right_on=df['TIME_PERIOD'],
                        how='left')
        else:
                SX7P = pd.merge(SX7P, df, left_on=SX7P['Date'].apply(lambda x: (x.year, x.month)),
                        right_on=df['TIME_PERIOD'].apply(lambda y: (y.year, y.month)),
                        how='left')

        # drop extra attributes
        SX7P.drop(['key_0','TIME_PERIOD'], axis = 1 , inplace = True)
x_18 = SX7P.loc[(SX7P['Date']>= '01-12-2017') & (SX7P['Date']<='28-12-2018')]

#daily trading volume and %variance
#algorithm_datset_b = pd.read_csv('algorithm_dataset_a.csv')
volume = pd.read_csv('Stoxx.csv')
volume.dtypes
SX7P['Volume_traded'] = volume['Vol.']
SX7P['%_variance'] = volume['% var.']
SX7P.drop(labels=(0,1), axis = 1,)
SX7P.columns
SX7P.drop(['Unnamed: 0','bank_loan_margin_competition'], axis = 1, inplace=True)

#macroeconomic indicators #https://stats.oecd.org/#
M1 = pd.read_csv('M1.csv') #M1
EUR_M1 = M1.loc[M1['LOCATION'] == 'EA19',['TIME','Value']]
EUR_M1[~EUR_M1['TIME'].str.contains('Q')]
EUR_M1.drop(EUR_M1.index[np.where(EUR_M1.index > 399)[0]], inplace = True)
EUR_M1['TIME'] = pd.to_datetime(EUR_M1['TIME'])
SX7P = pd.merge(SX7P,EUR_M1, left_on=SX7P['Date'].apply(lambda x: (x.year, x.month)),
         right_on=EUR_M1['TIME'].apply(lambda y: (y.year, y.month)),
         how='left')

financial_indicators = pd.read_csv('financial_indicators.csv') #real effective exchange rate
financial_indicators['TIME'] = pd.to_datetime(financial_indicators['TIME'])
financial = financial_indicators.loc[:,['TIME','Value']]
SX7P = pd.merge(SX7P,financial, left_on=SX7P['Date'].apply(lambda x: (x.year, x.month)),
         right_on=financial['TIME'].apply(lambda y: (y.year, y.month)),
         how='left')

prices_indexes = pd.read_csv('prices_indexes.csv') #CPI
CPI = prices_indexes.loc[:,['TIME','Value']]
CPI.drop(CPI.index[np.where(CPI.index > 399)[0]], inplace = True)
CPI['TIME'] = pd.to_datetime(CPI['TIME'])
SX7P = pd.merge(SX7P ,CPI, left_on=SX7P['Date'].apply(lambda x: (x.year, x.month)),
         right_on=CPI['TIME'].apply(lambda y: (y.year, y.month)),
         how='left')

confidence_indicators = pd.read_csv('confidence_indicators.csv') #confidence sentiment
confidence_indicators.drop(axis = 0, index = 121, inplace=True)
confidence = confidence_indicators.loc[:,['TIME','Value']]
confidence['TIME'] = pd.to_datetime(confidence['TIME'])
SX7P = pd.merge(SX7P,confidence, left_on=SX7P['Date'].apply(lambda x: (x.year, x.month)),
         right_on=confidence['TIME'].apply(lambda y: (y.year, y.month)))

SX7P.to_csv('algorithm_dataset.csv')


# -*- coding: utf-8 -*-
"""
Created on Tue Mar 31 18:19:58 2020
updated on 04132020 -- fixed homework 10 additions, remaining required courses and remaining elective courses
updated on 04142020 -- added a new student grade summary table pulled using sqlite3 from a query performed in datagrip
@author: Christopher
"""

from typing import Dict, DefaultDict,Set, List, Tuple
from collections import defaultdict
from HW08_Chris_Parmentier_Fixed import file_reader
import os
from prettytable import PrettyTable
import sqlite3
import unittest

class Major:
    """ store everythying about a single major """
    PT_MAJOR_FIELD_NAMES = ['MAJOR', 'REQUIRED COURSES', 'ELECTIVE COURSES']

    def __init__(self, major: str)-> None:
        """ major stores an instance for each major on the major.txt file and pulls major, the required/elective, and the course number """
        self.major:str = major
        self.required: Set[str] = set() # we want these to be sets because i wasn't storing differing values with a dict in the key and value. so the dict wasn't helping us.
        self.elective: Set[str] = set()

    def add_Rcourse(self, course: str) -> None:
        """add the course to the required course dictionary """
        self.required.add(course)

    def add_Ecourse(self, course: str) -> None:
        """add the course to the elective course dictionary """
        self.elective.add(course)

    def info(self):
        """ tell us what we need to return for information from this major class"""
        return[self.major, sorted(self.required), sorted(self.elective)]

class Student:
    """ store everythying about a single Student """
    PT_STUDENT_FIELD_NAMES = ['CWID', 'Name', 'Major', 'Completed Courses', 'Remaining Required', 'Remaining Elective', 'GPA']

    def __init__(self, cwid: str, name: str, major: str, required: Set[str], electives: Set[str])-> None:
        """ student stores an instance for each student on the student.txt file and pulls cwid, name, major """
        self.cwid:str = cwid
        self.name:str = name
        self.major:str = major
        self.rem_required: Set[str] = set(required)
        self.rem_electives: Set[str] = set(electives)
        self.courses: Dict[str, str] = dict() # courses[course_name] = grade
        self.grades: int= 0
        self.gpa: int = 0

    def store_course_grade(self, course: str, grade: str) -> None:
        """Note that this student took course and earned grade """
        passing = {'A', 'A-', 'B+', 'B', 'B-', 'C+', 'C'}
        self.courses[course] = grade
        grades_scale:Dict[str,float] = {'A': 4.0, 'A-': 3.75,
                        'B+': 3.25, 'B': 3.0,'B-': 2.75,
                        'C+': 2.25,'C': 2.0, 'C-': 0,
                        'D+': 0, 'D': 0,'D-': 0, 'F': 0}

        if grade in grades_scale:
            grade_points = grades_scale[grade]
        # working on calculating the GPA here, but if the amount of courses taken is zero this gpa will default to zero

        self.grades += grade_points
        if len(self.courses)!= 0:
            self.gpa = self.grades/len(self.courses)

        if grade in passing:
            if course in self.rem_required:
                self.rem_required.remove(course)
                # each student only needs one elective
                # if we see one elective then we can set the remaining elective courses to a blank container b/c there will be no electives remaining
            elif course in self.rem_electives:
                self.rem_electives = set()

    def info(self):
        """ return a list of information about me/self needed for pretty table"""
        return[self.cwid, self.name, self.major, sorted(self.courses), sorted(self.rem_required), sorted(self.rem_electives), round(self.gpa,2)]

class Instructor:
    """ store everythying about a single instructor """
    PT_INSTRUCTOR_FIELD_NAMES = ['CWID', 'Name', 'Major', 'Courses', 'Students']

    def __init__(self, incwid: str, name: str, dept: str)-> None:
        """store structor instance class information """
        self.incwid:str = incwid
        self.name:str = name
        self.dept:str = dept
        self.courses: DefaultDict[str, int] = defaultdict(int)  # courses[course_name] = # of students who have taken that class

    def store_course_student(self, course: str)-> None:
        """ note that instructor taught one more student in course"""
        self.courses[course] +=1

    def info(self):
        """ return a list of information about me/self needed for pretty table"""
        for courses, numstud in self.courses.items(): # courses is a default dict so we have to define/grab the key and values
            return[self.incwid, self.name, self.dept, courses, numstud] # changed from return to yield to make it into a generator

    def one_instructor(self):
        """Inner loop withing the instructor loop to get all the courses for one instructor"""
        for courses, numstud in self.courses.items(): # courses is a default dict so we have to define/grab the key and values
            yield[self.incwid, self.name, self.dept, courses, numstud]

class Repository:
    """ store all students, instructors for a university and print pretty tables"""
    def __init__(self, path: str) -> None:
        """ store all students, instructors,
            read students.txt, grades.txt, instructors.txt
            print pretty tables
        """
        self.path: str = path
        self.majors: Dict[str, Major] = dict() # key: name of major value: instance of class Major
        self.students: Dict[str, Student] = dict() #_student[cwid]  = Student()
        self.instructors: Dict[str, Instructor] = dict() #instructor[csid] = Instructor()

        # read the students file and create instances of class student
        # read the instructors file and create instance of class instructor
        # read the grades file and process each grade
        self.read_majors(self.path)
        self.read_students(self.path)
        self.read_instructors(self.path)
        self.read_grades(self.path)
        #this cool trick I picked up from your review of HW10. i'm just printing a new line with the title of the pretty table
        # i think this makes it look a lot nicer and more readable
        print("\nMajor Summary")
        self.major_pretty_table()
        print("\nStudent Summary")
        self.student_pretty_table()
        print("\nInstructor Summary")
        self.instructor_pretty_table()
        # this is the new addition for HW11, I am using sqlite3 to grab the data that I querred on datagrip
        print("\nStudent Grade Summary")
        self.student_grades_table_db('/Users/Christopher/Desktop/HW11/startup.db')

    def read_majors(self, path: str) -> None:
        """ read each line from path/majors.txt and create an instance of class major for each line"""

        try:

            for major, status, course in file_reader(os.path.join(self.path, "majors.txt"), 3,'\t', True):
                if major not in self.majors:
                    self.majors[major] = Major(major)

                if status == "R":
                    self.majors[major].add_Rcourse(course)

                else:
                    self.majors[major].add_Ecourse(course)

        except (FileNotFoundError, ValueError) as e:
            print(e)

    def read_students(self, path: str) -> None:
        """ read each line from path/students.txt and create an instance of class student for each line"""
        try:
            #cwid,name,major = file_reader(os.path.join(self.path, 'students.txt'), 3,'\t', False)
            for cwid, name, major in file_reader(os.path.join(self.path, "students.txt"), 3,'\t', True):

                if major not in self.majors:
                    print("error")
                else:
                    self.students[cwid] = Student(cwid, name, major, self.majors[major].required, self.majors[major].elective)

        except (FileNotFoundError, ValueError) as e:
            print(e)

    def read_instructors(self, path: str) -> None:  # write me
        """ read each line from path/students.txt and create an instance of class student for each line"""
        try:
            #incwid, name, dept = file_reader(os.path.join(self.path, 'instructors.txt'), 3,'\t', False)
            for incwid, name, dept in file_reader(os.path.join(self.path,"instructors.txt"), 3,'\t', True):
                self.instructors[incwid] = Instructor(incwid, name, dept)

        except (FileNotFoundError, ValueError) as e:
            print(e)

    def read_grades(self, path: str)-> None:
        """ read each line from path/grades.txt and add to student and instructor and major classes based off results """
        #read students_cwid, course, grade, instructor_cwid
        # define the passing grades that we'll use to see if a student gets credit for the class

        try:
            #student_cwid, course, grade, instructor_incwid = file_reader(os.path.join(self.path, 'grades.txt'), 4,'\t', False)
            for student_cwid, course, grade, instructor_incwid  in file_reader(os.path.join(self.path,"grades.txt"), 4,'\t', True):
                if student_cwid in self.students:
                    s: Student = self.students[student_cwid]
                    s.store_course_grade(course, grade)
                else:
                    print("Student not found") #need to include an error message if the student or instructor is not in the students/instructors file

                # we need to take into account if a student takes a course but doesn't pass the course
                # we will only take the course out of the remaining required if they pass

                # this code is unchanged, we are adding a count to the instructors total students count
                if instructor_incwid in self.instructors:
                    inst: Instructor = self.instructors[instructor_incwid]
                    inst.store_course_student(course)
                else:
                    print("Instructor not found") #need to include an error message if the student or instructor is not in the students/instructors file

        except (FileNotFoundError, ValueError) as e:
            print(e)

    def major_pretty_table(self) -> None:
        """ gives us our for loop for rows of the major pretty table  """
        pt = PrettyTable(field_names=Major.PT_MAJOR_FIELD_NAMES)
        for m in self.majors.values():
            pt.add_row(m.info())
            #add a row to the pretty table
        print(pt)

    def student_pretty_table(self) -> None:
        """ print a pretty table with student information"""
        pt = PrettyTable(field_names=Student.PT_STUDENT_FIELD_NAMES)
        for stu in self.students.values():
            pt.add_row(stu.info())
            #add a row to the pretty table

        print(pt)

    def instructor_pretty_table(self) -> None:
        pt = PrettyTable(field_names=Instructor.PT_INSTRUCTOR_FIELD_NAMES)
        for inst in self.instructors.values():
            for line in inst.one_instructor(): # we only want to add one instructor row to the pretty table row at a time
                pt.add_row(line)

        print(pt)

    def student_grades_table_db(self, db_path):
        """ using a sqlite3 connection and a query tested first in data grip pull data for a new student grade summary
            then print the prety table in this function too  """
        try: # see if we're connected
            db: sqlite3.Connection = sqlite3.connect(db_path)
        except sqlite3.OperationalError as e:
            print(e) #tell us to try again
        else:
            pt: PrettyTable = PrettyTable(field_names=['Name', 'CWID', 'Course', 'Grade', 'Instructor'])
            # here i took the whole query i typed out in data grip, i'm only calling this function once
            for tup in db.execute("""
                    select students.name, students.CWID, grades.course, grades.grade, instructors.Name
                    from students join grades on students.cwid = grades.StudentCWID
                    join instructors on instructors.CWID = grades.InstructorCWID
                    order by students.name"""):
                pt.add_row(tup)
            print(pt)

def main ():
    stevens: Repository = Repository("/Users/Christopher/Desktop/HW11")
    #columbia: Repository = Repository(add directory)
    #NYU: Repository = Repository(add directory)

if __name__ == '__main__':
    main()


import pygame, sys
import random

pygame.init()

# Defining some colors
BLACK = (0, 0, 0)

# Setting the width and height of the screen [width, height]
width = 900
height = 600
size = (width, height)
screen = pygame.display.set_mode(size)
window = screen.get_rect()

# Defining some variables for game functionality
score_add = True # variable to indicate when to add a point
score = 0
highscore = 0
wallx = 840 # starting width of the walls (pipes)
birdy = 288 # starting height of the bird
gap = 60 # variable to manipulate the gap between walls(pipes). The actual gap is 2 times gap
offset_var = 50 # variable determining the range for the random generation of the offset
offset = random.randint(-offset_var, offset_var) # randomly generating the offset variable (the higher it is the bigger distance between consecutive gaps)
wallspeed = 0.5 # the speed at which the walls are moving to the left of the screen

# Defining the walls that the bird has to move through and the bird itself
wallup = pygame.image.load("top.png").convert_alpha() #Source: http://www.palmentieri.it/flappyjam/pipe.png
walldown = pygame.image.load("bottom.png").convert_alpha() #Source: http://www.palmentieri.it/flappyjam/pipe.png
bird = pygame.image.load("bird.png").convert_alpha() #Source: http://www.ellison.rocks/clumsy-bird/data/img/bg.png
birdRect = pygame.Rect(435, birdy, bird.get_width(), bird.get_height()) # getting rectangles of all images to make mathematics and collision correct
wallupRect = pygame.Rect(wallx, (-200 - gap + offset), wallup.get_width(), wallup.get_height())
walldownRect = pygame.Rect(wallx, (300 + gap + offset), walldown.get_width(), walldown.get_height())

# Setting the caption for the game
pygame.display.set_caption("FlappyBird the Game by Milosz Kadziela")

# Loading the image that will be used as the game background
background_image = pygame.image.load("bg.png").convert_alpha()

# Defining some fonts that will be used in the messages below
font = pygame.font.Font('freesansbold.ttf', 24) #d efining smallest font that will be used for controls description
font2 = pygame.font.Font('freesansbold.ttf', 32) # defining a bigger font that will be used for main directional messages
font3 = pygame.font.Font('freesansbold.ttf', 40) # defining even bigger font that will be used for the main_menu title of the game
font4 = pygame.font.Font('freesansbold.ttf', 50) # defining the biggest for that will be used for "top of the screen" messages

# Defining some messages that will be displayed on the screen throughout the gameplay (directional messages - key instuctions)
controls_msg = font2.render("'C' - Controls", True, BLACK)
quit_msg = font2.render("'Q' - Quit", True, BLACK)
menu_msg = font2.render("'M' - Menu", True, BLACK)
restart_msg = font2.render(" 'R' - Restart", True, BLACK)
game_msg = font2.render("'P' - Play", True, BLACK)
back_msg = font2.render("'B' - Back", True, BLACK)

# Defining some other messages that will be visible in the game
name_msg = font3.render("FlappyBird the Game by Milosz Kadziela", True, BLACK)
gameover_msg = font4.render("Game Over!", True, BLACK)
controls_heading_msg = font2.render("Controls:", True, BLACK)
controls_description_msg = font.render("To fly the bird use 'SPACEBAR'", True, BLACK)
score_msg = font.render("Score: ", True, BLACK)
highscore_msg = font.render("Highscore: ", True, BLACK)
diff_heading_msg = font4.render("Choose the diffuculty:", True, BLACK)
diff1_msg = font.render("'1' - Easy ", True, BLACK)
diff2_msg = font.render("'2' - Medium ", True, BLACK)
diff3_msg = font.render("'3' - Hard ", True, BLACK)

def update():
	'''Function responsible for conroling the movement of the walls, generating the gap, controlling the movement of the bird
	adding score and restating the position of walls when they reacg the left side of the screen'''
	global wallx, wallspeed, wallup, walldown, score, score_add, offset, x, wallupRect, walldownRect, birdy, birdRect, score_msg
	wallx -= wallspeed
	birdy += 0.2
	wallupRect = pygame.Rect(wallx, (-200 - gap + offset), wallup.get_width(), wallup.get_height())
	walldownRect = pygame.Rect(wallx, (300 + gap + offset), walldown.get_width(), walldown.get_height())
	birdRect = pygame.Rect(435, birdy, bird.get_width(), bird.get_height())
	if score_add == True:
		if wallx < 450:
			score += 1
			score_add = False
	if wallx < 0:
		score_add = True
		offset = random.randint(-offset_var, offset_var)
		wallx = 900

# Variable used to manage how fast the screen updates
clock = pygame.time.Clock()

# Setting the starting value for the variable which is used to navigate through modules in MainLoop
gamestate = "Menu"

# HERE STARTS THE PROGRAM MAIN LOOP CONTAINING 5 SUB-LOOPS FOR MENU PURPOSES AND THE LIVE GAME
while True:
	# --- MainLoop ---
	while gamestate == "Menu":
		# Event loop displaying Menu window
		for event in pygame.event.get():
			if event.type == pygame.KEYDOWN:
				if event.key == pygame.K_q:
					sys.exit()
				if event.key == pygame.K_c:
					gamestate = "Controls"
				if event.key == pygame.K_p:
					gamestate = "Settings"
			if event.type == pygame.QUIT:
				sys.exit()

		screen.blit(background_image, [0,0]) #sets the image as the background
		screen.blit(name_msg, [60, 100]) #blicts the message in the middle of the screen
		screen.blit(game_msg, [380, 200]) #blits the message in the middle of the screen
		screen.blit(controls_msg, [50,530]) #blits the message to bottom left corner
		screen.blit(quit_msg, [705, 530]) #blits the message to bottom right corner

		# Updating the screen
		pygame.display.flip()

		# Limiting to 60 frames per second
		clock.tick(60)

	while gamestate == "Settings":
		# Event loop displaying Difficulty_settings window
		for event in pygame.event.get():
			if event.type == pygame.KEYDOWN:
				if event.key == pygame.K_q:
					sys.exit()
				if event.key == pygame.K_1:
					offset_var = 50
					wallspeed = 0.5
					gamestate = "Game"
				if event.key == pygame.K_2:
					offset_var = 100
					wallspeed = 0.7
					gamestate = "Game"
				if event.key == pygame.K_3:
					offset_var = 200
					wallspeed = 0.9
					gamestate = "Game"
				if event.key == pygame.K_b:
					gamestate = "Menu"
			if event.type == pygame.QUIT:
				sys.exit()

		screen.blit(background_image, [0, 0])
		screen.blit(diff_heading_msg, [175, 50])
		screen.blit(diff1_msg, [380, 160])
		screen.blit(diff2_msg, [365, 200])
		screen.blit(diff3_msg, [380, 240])
		screen.blit(back_msg, [705, 530])

		pygame.display.flip()
		clock.tick(60)

	while gamestate == "Controls":
		# Event loop displaying Controls window
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				sys.exit()
			if event.type == pygame.KEYDOWN:
				if event.key == pygame.K_m:
					gamestate = "Menu"

		screen.blit(background_image, [0, 0])
		screen.blit(menu_msg, [50, 530])
		screen.blit(controls_heading_msg, [400,50])
		screen.blit(controls_description_msg, [270,300])

		pygame.display.flip()

		clock.tick(60)

	while gamestate == "Game":
		# --- Live game loop ---
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				sys.exit()
			if event.type == pygame.KEYDOWN:
				if event.key == pygame.K_q:
					sys.exit()
				if event.key == pygame.K_SPACE:
					birdy -= 60

		# Declaring the collision statement check. To see if the bird has collided with any of the walls
		if birdRect.colliderect(wallupRect) or birdRect.colliderect(walldownRect):
			gamestate = "Gameover"
		# Declaring the collision statement check. To see if the bird has collided with the top or bottom of the screen
		# +20 and -20 to make it eaier to go through gaps that generate just at the top or bottom of the screen on the highest difficulty
		if birdRect.top < window.top - 20 or birdRect.bottom > window.bottom + 20:
			gamestate = "Gameover"
		update()

		score_msg = font2.render("Score: " + str(score), True, BLACK)

		screen.blit(background_image, [0, 0])
		screen.blit(wallup, (wallx, (-200 - gap + offset))) # blitting the screen with the upper wall
		screen.blit(walldown, (wallx, (300 + gap + offset))) # blitting the screen with the bottom wall
		screen.blit(bird, (435, birdy)) # blitting the screen with the bird
		screen.blit(score_msg, [400, 20]) # blitting the screen with the "live-updated" score msg at the top of the screen

		pygame.display.flip()

	while gamestate == "Gameover":
		# Event loop displaying Gameover window
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				sys.exit()
			if event.type == pygame.KEYDOWN:
				if event.key == pygame.K_m:
					wallx = 900
					birdy = 288
					score = 0
					update()
					gamestate = "Menu"
				if event.key == pygame.K_r:
					wallx = 900 # setting the position of bird and walls to the original, so that user gets a fresh start when they hit restart
					birdy = 288
					score = 0
					update()
					gamestate = "Game"

		# condition for potential change of the highscore if the player surpasses the previous best
		if score > highscore:
			highscore = score
		score_msg = font.render("Score: " + str(score), True, BLACK)
		highscore_msg = font.render("Highscore: " + str(highscore), True, BLACK)

		screen.blit(background_image, [0, 0])
		screen.blit(menu_msg, [685, 530])
		screen.blit(restart_msg, [50,530])
		screen.blit(gameover_msg, [300, 120])
		screen.blit(score_msg, [400, 200])
		screen.blit(highscore_msg, [375, 240])

		pygame.display.flip()

		clock.tick(60)
# Close the window and quit
pygame.quit()


from collections import OrderedDict
from django.conf import settings
from django.http import JsonResponse
from django.shortcuts import redirect, render
import razorpay
from commen.models import Customer
from customer.models import Cart, CartItem, Order, OrderItem
from seller.models import Category, Product
from django.db.models import Q

# Create your views here.
def index(request):
    category = Category.objects.all()
    product=Product.objects.all()

    # search bar code
    if request.method == 'POST':
        search_query = request.POST['txt_search']
        product = Product.objects.filter(Q(name__icontains = search_query)|Q(category__discription__icontains = search_query)|Q(category__catg_name__icontains = search_query))
    print(product)
    context={
        'products':product,
        'category':category,

    }

    return render(request,'customer/home.html',context)

def details(request, id):
    single_product=Product.objects.get(id=id)
    context={
        'single_product':single_product,

    }
    return render(request,'customer/details.html',context)

def product_by_category(request,id ):
    category=Category.objects.all()
    product=Product.objects.filter(category_id=id)
    context={
        'products':product,
        'category':category,
    }
    return render(request,'customer/home.html',context)

def filter(request,id):
    produ=Product.objects.filter(category_id=id)

    return render(request,'customer/filter.html',{'product':produ})

def cart_view(request):
    user=Customer.objects.get(id=request.session['customer_id'])

    cart=Cart.objects.get(user_id=user.id)
    print(cart.id)
    items=CartItem.objects.filter(cart_id=cart.id)
    total=0
    for item in items:
         total+=item.product.price*item.quantity

    context={
        'cart':cart,
        'items':items,
        'total':total,
    }
    return render(request,'customer/cart.html',context)

def add_cart(request,product_id):
    product=Product.objects.get(id=product_id)
    cart, _=Cart.objects.get_or_create(user_id=request.session['customer_id'])

    product_exists = CartItem.objects.filter(cart_id=cart.id,product_id=product_id).exists()
    if product_exists:
        item=CartItem.objects.get(cart_id=cart.id,product_id=product_id)
        item.quantity+=1
        item.save()
    else:

        CartItem.objects.create(cart=cart,product=product,quantity=1)
    return redirect('customer:cart')

def remove_cart(request):
    item_id=request.POST['item_id']
    cart=Cart.objects.get(user_id=request.session['customer_id'])
    item=CartItem.objects.get(id=item_id)
    item.delete()
    return JsonResponse({'status':'success'})

def update_quantity(request):
    quantity=request.POST['quantity']
    item_id=request.POST['item_id']
    cart=Cart.objects.get(user_id=request.session['customer_id'])
    items=CartItem.objects.filter(cart_id=cart.id)
    item=CartItem.objects.get(id=item_id)
    item.quantity=quantity
    item.save()
    total_item_price=item.calculate_total_price()
    total=0
    for item in items:
         total+=item.calculate_total_price()
    data={
        'total_item_price':total_item_price ,
        'totl_price':total

    }

    return JsonResponse({'status':"success",'data':data})

def order_product(request):
    userid=request.session['customer_id']
    cart=Cart.objects.get(user_id=userid)
    items=CartItem.objects.filter(cart_id=cart.id)
    # products_orderdata=Orders.objects.filter(user_id=userid)
    total=0
    for item in items:
         total+=item.calculate_total_price()

    order_amount= total
    order_currency='INR'
    order_receipt='order_rcptid_11'
    notes= {'shipping address':'bommanahalli,bangalore'}
    type(order_amount)
    client=razorpay.Client(auth=(settings.RAZOR_KEY_ID ,settings.RAZOR_KEY_SECRET))
    payment=client.order.create({"amount":order_amount*100,"currency":order_currency,"receipt":order_receipt,'notes':notes})
    print(payment)
    order=Order(user_id=userid,order_no=payment['id'],total_amt=total)
    order.save()
    return JsonResponse(payment)

def updatepayment(request):
    if request.method=='POST':
        print("test test")
        orderid=request.POST['order_id']
        paymentid=request.POST['pay_id']
        signature=request.POST['signatur']
        print(orderid,paymentid,signature)

        #verify the  payment signature
        client=razorpay.Client(auth=(settings.RAZOR_KEY_ID ,settings.RAZOR_KEY_SECRET))
        params_dict={
            "razorpay_order_id":orderid,
            "razorpay_payment_id":paymentid,
            "razorpay_signature":signature
        }
        is_signature_valid=client.utility.verify_payment_signature(params_dict)

        if is_signature_valid:
            print("test**************")
            try:
                order=Order.objects.get(order_no=orderid)
                order.payment_status=True
                order.payment_id=paymentid
                order.signature=signature

                cart=Cart.objects.get(user_id=request.session['customer_id'])
                cart_items=CartItem.objects.filter(cart_id=cart.id)

                for item in cart_items:
                    order_item=OrderItem(order_id=order.id,
                                         product_id=item.product.id,
                                         quantity=item.quantity,
                                         price=item.product.price  )
                    order_item.save()
                    order.save()
                    cart_items.delete()
                return JsonResponse({'resp':'success'})
            except Order.DoesNotExist:
                return JsonResponse({'resp':'fail','error':'order not found'})
        else:
            return JsonResponse({'resp':'fail','error':'invalid signature'})

    return JsonResponse({'resp':'fail'})

def myorder(request):
    orders=Order.objects.filter(user_id=request.session['customer_id'])

    context={
        "orders":orders
    }
    return render(request,'customer/myorder.html',context)


import re
import json
import csv

class TrainingDataParser:

    def __init__(self, src_path):
        self.src_path = src_path

    def to_dict(self):
        user_posts = {}
        current_id = None
        for line in open(self.src_path):
            if '<user id' in line:
                user_id = re.findall('<user id="(.*?)"', line)[0]
                current_id = user_id
                topic = re.findall('topic="(.*?)"', line)[0]
                gender = re.findall('gender="(.*?)"', line)[0]
                age = re.findall('age="(.*?)"', line)[0]
                if user_id not in user_posts:
                    user_posts[user_id] = {
                        'topic': topic,
                        'gender': gender,
                        'age': age,
                    }
                else:
                    print(f"Duplicate id {user_id}")
            elif '</user>' in line:
                current_id = None
            elif line != '\n' and 'post>' not in line:
                if 'posts' in user_posts[current_id]:
                    user_posts[current_id]['posts'].append(line)
                else:
                    user_posts[current_id]['posts'] = [line]
        return user_posts

    def to_csv(self, file_output=False, output_path='src/data/post_processed', output_name='training.csv'):
        data = self.to_dict()

        rowlist = []
        for user in data:
            for sentence in data[user]['posts']:
                row = [user, data[user]['gender'], data[user]['age'], data[user]['topic'], sentence.strip()]
                rowlist.append(row)

        if file_output:
            file = open(f"{output_path}/{output_name}", "w", encoding="utf-8", newline='')
            writer = csv.writer(file, delimiter=',',
                                lineterminator='\r\n',
                                quoting=csv.QUOTE_NONNUMERIC
                                )
            writer.writerow(['Id', 'Gender', 'Age', 'Topic', 'Sentence'])
            writer.writerows(rowlist)
            file.close()
        return rowlist

    def to_json(self):
        return json.dumps(self.to_dict())