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 @@ + + + + + + + + + + + + Company Title: A New Horizon + + + + + + + + + +
+

Contact Me

+
+ +
+
+
+
+
+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/oldData.txt b/oldData.txt new file mode 100644 index 000000000..ce32f04b7 --- /dev/null +++ b/oldData.txt @@ -0,0 +1,25 @@ +1785; Mary Simmer; 2690175222; 1304370303; SFRvYtzUDymLJsr@gmail.com; 6356 Bethany Ranch Apt. 796 Brownfurt, IA 98767; 604; 1528; True; +1045; Martha Mccoy; 8179795075; 6978684276; MqtCFhoNEYzaegy@gmail.com; 04307 Monica Mall West Breanna, ME 51243; 609; 624; False; +1967; Charlotte Hershey; 1214902815; 6735265431; CRvJRKgzLGKnaeE@gmail.com; 756 Jacqueline Crescent Apt. 072 Victoriachester, HI 55142; 1623; 1605; True; +925; Idella Brown; 8949144699; 6563726364; vXZpraQqgSnCuoB@gmail.com; 4173 Kathleen Court Apt. 962 Julieburgh, AR 67414; 1037; 1741; False; +254; Martin Thompson; 7754106150; 9673530514; UjvmpglnGlAlIRP@gmail.com; 11457 Russell Parkways Apt. 449 Port Curtischester, NJ 45460; 1613; 314; False; +1024; Bonnie Mitchell; 1846020140; 9296540382; AmYdCsmKtWgggvW@gmail.com; Unit 9708 Box 4539 DPO AE 18616; 1613; 1771; False; +1984; Marie Orona; 3539449144; 2356949131; yJcbQmrwwKnOMKl@gmail.com; 545 Rodgers Bypass Millerberg, NC 28758; 912; 1383; False; +565; Julie Pierce; 9099331270; 7645174016; PlHUeGbQgohYfSi@gmail.com; PSC 9081, Box 2446 APO AE 81270; 946; 646; False; +1123; Sonja Wagoner; 1006289674; 7608903378; iApxFmerNTkAcZn@gmail.com; 508 Leslie Summit Suite 638 East Gabriel, ND 48735; 1063; 461; True; +909; David Cross; 3142926865; 7611185394; yqYesTlWrxwwLDe@gmail.com; 76847 Wright Turnpike Apt. 457 Millerland, IL 65899; 650; 105; False; +940; Susan Newton; 2445331621; 4159013134; LNnpZTGerpFwfGP@gmail.com; 54783 Austin Shoals Suite 668 Allenberg, VT 46440; 308; 48; False; +6; Ruth Saldivar; 2288169164; 4530547090; NEYTiIMhFvKPJpI@gmail.com; 28833 Felicia Radial Lake Micheleshire, HI 87840; 195; 1404; False; +844; Cassandra Jones; 9031806207; 7663108826; HuUHeHUZgJKUyTh@gmail.com; 461 Raymond Meadows North Adamborough, OK 17005; 48; 937; False; +1986; Jayme Murphy; 3371272946; 1779622004; pLZZjcsQARptbNi@gmail.com; 993 Holden Camp Apt. 437 West Lisaborough, WV 69178; 1452; 1556; False; +1035; Mary Fyall; 8428725296; 1087071071; PuhxJfLEDLRCvYb@gmail.com; 20910 Dylan Heights Apt. 178 Rodriguezborough, DE 56334; 1967; 325; False; +1682; Clint Barnes; 1958875411; 9555349579; JryllXhBFDumJUL@gmail.com; 3602 Joe Underpass Suite 295 East Mariabury, LA 86858; 181; 722; True; +154; Yesenia Crook; 6278970113; 7991538452; XplVAMqGiIupEYh@gmail.com; 111 Stevens Pike Suite 147 West Juanside, AK 12854; 1754; 1682; True; +1244; Barbara Wallace; 1454107749; 8763131082; XdkpASkvpCxtnle@gmail.com; 92263 Kimberly Pass South Mary, TX 19471; 1532; 1740; True; +988; Ina Koelling; 6579626087; 9289509798; GSoiYlYkQUFXuDw@gmail.com; 06446 Robert Glen Apt. 833 Andrewview, WI 14681; 1837; 1400; False; +1785; David Cooper; 4670865569; 8284376404; fGOMhpzwKuAyitA@gmail.com; PSC 3558, Box 4253 APO AA 64934; 1410; 1669; True; +706; Brian Koroma; 1890871912; 4899792378; fTidYgMorRvfFuB@gmail.com; 638 Jacob Manor Apt. 189 Leeberg, MD 00888; 1869; 1852; False; +1515; Megan Miller; 7089943029; 5513552086; aiZzUFFqiqMJwjJ@gmail.com; 829 Cook Bypass Morganfurt, FL 65614; 685; 1495; True; +698; Doris Liefer; 3336404101; 5898317116; jYjKOVHSSUStIfG@gmail.com; 951 Chung Skyway East Kimberlyview, VT 12746; 1487; 592; True; +948; Gayle Vidot; 8777934481; 1175692524; kRapwdMLpgYuiHY@gmail.com; 713 Glenn Park North Williamville, MI 57301; 1271; 1659; False; +1291; Brenda Maddox; 3983690736; 6983501758; NRgwfNQQaUUryJV@gmail.com; 93209 Spears Throughway South Heather, CT 42645; 1654; 1555; False; diff --git a/testingFile.py b/testingFile.py new file mode 100644 index 000000000..f3c07e135 --- /dev/null +++ b/testingFile.py @@ -0,0 +1,34 @@ +import random, names, string +from faker import Faker +import json +from random import randint + +oldData = [] +fake = Faker() + +def random_char(y): + return ''.join(random.choice(string.ascii_letters) for x in range(y)) + +def random_with_N_digits(n): + range_start = 10**(n-1) + range_end = (10**n)-1 + return randint(range_start, range_end) + + +textfile = open("oldData.txt", "w") +for i in range(0,25): + record = random.randint(0,2000) + basicWidgetOrder = random.randint(0,2000) + advWidgetOrder = random.randint(0,2000) + protectPlan = bool(random.getrandbits(1)) + fullName = names.get_full_name() + cellPhone = str(random_with_N_digits(10)) + workPhone = str(random_with_N_digits(10)) + email = (random_char(15) + "@gmail.com") + address = fake.address().replace('\n', ' ') + tempData = [record, fullName, cellPhone, workPhone, email, address, basicWidgetOrder, advWidgetOrder, protectPlan] + for j in tempData: + textfile.write(str(j) + '; ') + textfile.write('\n') + +textfile.close() \ No newline at end of file