Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
cbad9d5
Added reusable components, for ease of use and if further development…
davidazael May 14, 2021
34f949f
Added ContactUs Form Component, it is responsible for inserting Capt…
davidazael May 14, 2021
27f7993
Modified App.js to display ContactUs form and adding the files create…
davidazael May 14, 2021
e67330b
Added Headstorm Bolt png to /public that is used as browser icon. Mod…
davidazael May 14, 2021
0491031
Commiting files that came with create-react-app
davidazael May 14, 2021
0e03e3c
Added Dockerfile and docker-compose for this app to be in ran locally…
davidazael May 14, 2021
c674675
Created Flask REST API Captcha. Created Config file that is initilize…
davidazael May 14, 2021
8c09a87
Added .gitignore to ignore appropriate files
davidazael May 14, 2021
34c4dfb
Added nginx.conf file that is used to function as a reverse proxy for…
davidazael May 14, 2021
3d2cac4
Added docker-compose that deploys Flask REST API: /backend-captcha, R…
davidazael May 14, 2021
28122f2
Modified /backend-captcha/README to be more accurate. Added README to…
davidazael May 15, 2021
8437ae5
Added README to top directory
davidazael May 15, 2021
b42f155
Merge pull request #1 from davidazael/frontend_challenge-davidazael
davidazael May 15, 2021
b9cbebc
Complete implementation of Flask REST /data endpoint within file app.…
davidazael May 17, 2021
07de810
Merge pull request #2 from davidazael/backend_challenge-davidazael
davidazael May 17, 2021
0278954
Added script to solve Database Challenge. Need to add the 'draw.io' d…
davidazael May 17, 2021
deb03e2
Modified Models to have address model, and added a View Creation that…
davidazael May 17, 2021
6504b33
Merge pull request #3 from davidazael/database-challenge
davidazael May 17, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions challenges/backend-challenge/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# General
.vim
.vscode
.git

# Byte-compiled / optimized / DLL files
__pycache__
__pycache__/
*.py[cod]
*$py.class
*.pyc
*.pyo

__pypackages__/

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
23 changes: 23 additions & 0 deletions challenges/backend-challenge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Back End Challenge

I really enjoyed this challenge, the PATCH method was new to me. I have doubts that I implemented it incorrectly, but I hope that my approach was in line with proper implementation. In order to complete the bonus challenge, I took two things into account that lead me to the implementation of a Binary Insert: first, we have a set a data that is sorted; second, we need to insert an integer in the proper order.

# Setup

Create a virtual environment:
`python3 -m venv .venv`

Activate the virtual environment:
`source .venv/bin/activate`

Install Libaries:
`pip install -r ./requirements.txt`

Make `.env` file and insert the following:
`FLASK_APP='run.py'`
OR execute this in bash/zshshell:
```sh
echo FLASK_APP='run.py' >> .env
```

The `.env` file is for the dotenv library to automatically export the variables when the application runs. Therefore you don't have to re initilize the variables each time.
13 changes: 13 additions & 0 deletions challenges/backend-challenge/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from os import environ, path
from dotenv import load_dotenv
"""Flask configuration."""


# This file should be in a Directory called Instance or Config.
# Not for this example REST API.
basedir = path.abspath(path.dirname(__file__))
load_dotenv(path.join(basedir, '.env'))

TESTING = True
DEBUG = True
FLASK_ENV = 'development'
12 changes: 12 additions & 0 deletions challenges/backend-challenge/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
certifi==2020.12.5
chardet==4.0.0
click==7.1.2
Flask==1.1.2
idna==2.10
itsdangerous==1.1.0
Jinja2==2.11.3
MarkupSafe==1.1.1
python-dotenv==0.17.1
requests==2.25.1
urllib3==1.26.4
Werkzeug==1.0.1
5 changes: 5 additions & 0 deletions challenges/backend-challenge/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#! usr/bin/python3.7
from src.app import app

if __name__ == '__main__':
app.run()
Empty file.
165 changes: 165 additions & 0 deletions challenges/backend-challenge/src/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
from flask import Flask, request, redirect, url_for
import ast
import os
import json

app = Flask(__name__)
app.config.from_pyfile('../config.py')

path = os.getcwd() + '/src/data'
new_json_data = 'data.json'


def format_incoming_data(incoming_data: str):
'''
Uses `ast.literal_eval` to convert incoming data to a data type that it fits best.

Because we are expecting a string that is of json list format we can assume that it will be converted to a list.
'''
# Convert the incoming data.
formatted_inc = ast.literal_eval(incoming_data)
# Is it converted into a list?
if isinstance(formatted_inc, list):
# Is the list of size 500?
if len(formatted_inc) == 500:
# utilized any for quicker execution.
# are any values in data list of type other than int?
if not any(not isinstance(e, int) for e in formatted_inc):
return formatted_inc
else:
# List was of the right size
# But the contents within were not of integer type.
raise TypeError
else:
# List size is not 500
raise IndexError
else:
# If it isn't converted into a list.
# Meaning the incoming values are not of correct type.
raise ValueError


def get_json_file_data(filename=new_json_data) -> dict:
'''
Returns Dictionary from JSON file.

Uses JSON to load data. If the file is empty, it returns a dictionary that displays null.
'''
# By default it has read capabilities.
with open('{0}/{1}'.format(path, filename)) as f:
try:
data = json.load(f)
return data
except Exception as e:
data = {'data': None}
return data


def write_json_data_to_file(inc_data: list, filename=new_json_data) -> None:
'''
Opens File 'data.json' JSON dump's incoming data.

Returns None.
'''
with open('{0}/{1}'.format(path, filename), 'w') as f:
data = {'data': sorted(inc_data)}
res = json.dump(data, f)
return res


def binary_insert(lst: list, patch_val: int) -> list:
''' Binary Insert given a Sorted list. '''
# size halfpoint is 250 because we know that list can only be size of 500
# but for reusability we will use len()
size = len(lst)

# Indexes: low -> midpoint -> high
# smallest value in asc sorted list should be first in list
low = 0
# largest value in asc sorted list should be last in list
high = size-1
mp = (high + low) // 2
mp_val = lst[mp]

# Check if new # is even in between lowest and highest
if patch_val not in range(lst[low], lst[high]):
if patch_val <= lst[low]:
# If new value is less than lowest, insert before.
lst.insert(0, patch_val)
return lst
elif patch_val >= lst[high]:
# If new value is greater than highest, append.
lst.append(patch_val)
return lst

# While low index is below high index search to see if the value belongs
# within
while low < high:
# midpoint index value. Floor operator rounds down -> int
mp = (high + low) // 2 # Calculate midpoint of any two values
# mp_val: midpoint value || value that will be used for comparison
mp_val = lst[mp]

# is midpoint exaclty inbetween the low and high bound?
if low + 1 == mp or high-1 == mp:
# is lst[low] <= patch_val < lst[high] ?
if patch_val in range(lst[low], lst[high]):
lst.insert(mp, patch_val)
return lst
elif patch_val < lst[low]:
lst.insert(low-1, patch_val)
return lst
elif patch_val >= lst[high]:
lst.insert(high+1, patch_val)
return lst

# If the incoming patch value is a duplicate, just place it in at that
# location and quit.
if patch_val == mp_val:
lst.insert(mp, patch_val)
return lst
elif patch_val > mp_val:
low = mp+1
continue
elif patch_val < mp_val:
high = mp-1
continue


# read/retrive
@app.route('/data/', defaults={'inc_data': ''}, methods=['GET'])
@app.route('/data/<inc_data>', methods=['POST']) # create
@app.route('/data/<inc_data>', methods=['PATCH']) # update existing data
def process_incoming(inc_data=''):
if request.method == 'GET':
# Get data from JSON data file.
return get_json_file_data(new_json_data)
elif request.method == 'POST':
# Insert new data, re-writing existing file data.
try:
formated_data = format_incoming_data(inc_data)
write_json_data_to_file(formated_data)
return get_json_file_data(new_json_data)
except Exception as e:
message = f'{e.__class__.__name__}: {e.__class__.__doc__}'
return redirect(url_for('dataIndexError', exception=message))
elif request.method == 'PATCH':
# Appending data to json file.
# Get existing_data from existing JSON data file.
existing_data = get_json_file_data(new_json_data)
# Insert in order. Update JSON data file.
write_json_data_to_file(binary_insert(
existing_data['data'], int(inc_data)))
return get_json_file_data(new_json_data)
else:
return redirect(url_for('root'))


@app.route('/<exception>')
def dataIndexError(exception):
return f'{exception} \n Please provide a JSON formatted list of integers that is of size exactly 500.'


@app.route('/')
def root():
return "Hello! Please provide a JSON formatted list of integers that is of size 500!"
Loading