diff --git a/LICENSE b/LICENSE index 7666b16bc..267a776e3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Headstorm +Copyright (c) 2022 Francisco Munoz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index f2d817b00..c7d31893d 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ -## Headstorm Interview +# HeadStorm_Challenge +Repository that holds work for the HeadStorm Challenge -Welcome to the Headstorm interview challenge! This repository is designed for candidates to [fork and create Pull Requests](https://help.github.com/en/articles/creating-a-pull-request-from-a-fork) with their solutions. There are two types of 'take-home' problems here: +## backEnd_challenge.py +This is the file that holds the code for the back end challenge. It is a standalone file with on dependencies on any other file. -### Challenges -These are domain specific problems that can be submitted individually. You can choose from backend, frontend, databases, or data-science. You can submit a PR for one, many, or all the challenges. +## database_challenge.py +This is the file that holds the code for the database challenge. Using postgreSQL, python commands were used to create the database, the tables, and the INSERT commands to insert the data into the tables. Some dependencies include the testingFile.py, which randomly generated 25 entries that would be used to insert into the tables in the database and the oldData.txt, which held this information. The database_relationalModel.jpg shows the ER model of how this database is constructed. -### Interviews -These are language specific interview questions and you can choose the language in which you implement your solution. +## frontEnd_Challenge.html +This is the file that holds the code for the front end challenge. One of the dependencies is the browserIcon.png file that is used for the icon on the browser, but aside from that this html file is a standalone file. diff --git a/backEnd_challenge.py b/backEnd_challenge.py new file mode 100644 index 000000000..33f972c42 --- /dev/null +++ b/backEnd_challenge.py @@ -0,0 +1,79 @@ +""" +Assumptions made: +1. For the GET method, assuming that whenever that GET method is ran, list will always be size 500, +so no need to check and just return the sorted list. + +2. For the overall API, assuming this would be ran locally so enabled it to be ran locally. Otherwise, +possible to corrected and be ran on a server and use a curl command to access it. +""" + +""" +Used Flask to be able to create the REST API and used some in built functions to be able to execute +certain calculations more efficiently. +""" + +from flask import Flask, request +from flask_restful import Resource, Api, reqparse, abort +import pandas as pd +import ast +import random, json, requests, re, bisect + +app = Flask(__name__) +api = Api(app) +NUMBERLIST = {"list":[]} + +# Class Declaration that contains all 3 different methods +class ListData(Resource): + # Retrieve Data, sort before returning + def get(self): + NUMBERLIST['list'].sort() + return NUMBERLIST + + # Create Data, make sure all entries are numbers and size is of 500 + def post(self): + numlist = [] + templist = str(request.data).split(',') + for i in templist: + temp = re.sub("[^0-9]", "", i) + numlist.append(int(temp)) + + if(len(numlist) == 500): + result = all(isinstance(x, int) for x in numlist) + if (result == True): + NUMBERLIST['list'] = numlist + return NUMBERLIST + else: + abort(400,"Invalid Input. All entries have to be numbers.") + else: + abort(400, "Invalid input. Length is not 500.") + + # Insert one element in corresponding order + def patch(self): + entry = str(request.data).strip() + temp = re.sub("[^0-9]", "", entry) + + # Check if the single entry is a number or not + if(temp.isnumeric()): + # Use built in function to insert entry in a sorted list + bisect.insort(NUMBERLIST['list'], int(temp)) + return NUMBERLIST + else: + abort(400,"Invalid Input. All entries have to be numbers.") + +api.add_resource(ListData, '/data/') # '/data' is our entry point +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000, debug=True) + + +# Testing Commands +payload = [] +for i in range(0, 500): + payload.append(random.randint(0,1000)) + +newPayload = json.dumps(payload) +newData = str(random.randint(0,1000)) + +r=requests.post("http://localhost:5000/data/",data=newPayload) +r=requests.get("http://localhost:5000/data/") +r=requests.patch("http://localhost:5000/data/",data=newData) + diff --git a/browserIcon.png b/browserIcon.png new file mode 100644 index 000000000..6bb707b5f Binary files /dev/null and b/browserIcon.png differ diff --git a/database_challenge.py b/database_challenge.py new file mode 100644 index 000000000..a2d1da722 --- /dev/null +++ b/database_challenge.py @@ -0,0 +1,56 @@ +import psycopg2 + +# Variables that will be used for this program +customerTable = '''CREATE TABLE IF NOT EXISTS Customer + (customerID INT PRIMARY KEY NOT NULL, + Name VARCHAR(50) NOT NULL, + Address VARCHAR(256) NOT NULL, + CellPhone VARCHAR(30) NOT NULL, + Email VARCHAR(30) NOT NULL, + WorkPhone VARCHAR(50) NOT NULL) + ;''' + +recordsTable = '''CREATE TABLE IF NOT EXISTS Records + (recordID INT PRIMARY KEY NOT NULL, + AdvWidgetOrder INT NOT NULL, + BasicWidgetOrder INT NOT NULL, + ProtectPlan BOOLEAN NOT NULL, + customerID INT NOT NULL, + FOREIGN KEY(customerID) REFERENCES Customer(customerID)) + ;''' + +#establishing the connection +conn = psycopg2.connect( + database="postgres", user='postgres', password='password', host='127.0.0.1', port= '5432' +) +#Creating a cursor object using the cursor() method +cursor = conn.cursor() + +# Create Tables if the tables don't exist +cursor.execute(customerTable) +cursor.execute(recordsTable) + +#Closing the connection +conn.close() + +# Since Database is created and the tables have been created successfully, we need to migrate the old data +# into this new database model. Here we will read in the JSON file (assuming it is structured in a normal text +# file). We will also assume that the data will come in as defined in the table on Github under the Database Challenge +# Description. +# Assumption here is that the data will be separated by semicolons to make the splitting easier but depending how they are split, +# we can make changes to the splitting logic and it would still work. + +dataFile = open('oldData.txt', 'r') +i = 1 +j = 2 +for line in dataFile: + tempLine = line.split(';') + customerString = '''INSERT INTO Customer(customerID, Name, Address, CellPhone, Email, WorkPhone) + VALUES ({}, {}, {}, {}, {}, {})'''.format(i, tempLine[1], tempLine[5], tempLine[2], tempLine[4], tempLine[3]) + + recordString = '''INSERT INTO Records(recordID, AdvWidgetOrder, BasicWidgetOrder, ProtectPlan, + customerID) VALUES ({}, {}, {}, {}, {})'''.format(j, int(tempLine[7]), int(tempLine[6]), bool(tempLine[8]), i) + j = j + 1 + i = i + 1 + +dataFile.close() diff --git a/database_relationalModel.jpg b/database_relationalModel.jpg new file mode 100644 index 000000000..6bcf0059d Binary files /dev/null and b/database_relationalModel.jpg differ diff --git a/frontEnd_Challenge.html b/frontEnd_Challenge.html new file mode 100644 index 000000000..e12bd5462 --- /dev/null +++ b/frontEnd_Challenge.html @@ -0,0 +1,60 @@ + + + + + + + +
+ + + +