diff --git a/challenges/backend/backend.py b/challenges/backend/backend.py
new file mode 100644
index 000000000..c3ebb8a4b
--- /dev/null
+++ b/challenges/backend/backend.py
@@ -0,0 +1,30 @@
+from flask import Flask, jsonify, request
+import json
+app = Flask(__name__)
+
+data_list = []
+
+# TODO: better (more verbose) errors
+@app.route('/data', methods=['GET', 'POST', 'PATCH'])
+def data_api():
+ global data_list
+ if request.method == 'GET':
+ return jsonify(data_list)
+ elif request.method == 'POST':
+ in_json = request.get_json()
+ is_num = lambda x: type(x) (int, float)
+ if len(in_json) == 500 and all(map(is_num, in_json)):
+ data_list = list(sorted(in_json))
+ return jsonify(data_list)
+ else:
+ return 'Bad request', 400
+ elif request.method == 'PATCH':
+ try:
+ data_list = list(sorted([int(request.data)] + data_list))
+ return jsonify(data_list)
+ except BaseException as e:
+ print(e)
+ return 'Bad request', 400
+
+if __name__ == '__main__':
+ app.run(debug=True)
diff --git a/challenges/database/README.md b/challenges/database/README.md
new file mode 100644
index 000000000..f7ee99cbd
--- /dev/null
+++ b/challenges/database/README.md
@@ -0,0 +1,3 @@
+# Why?
+I have very little prior database knowledge, so this is my best estimate after a short time of learning.
+Thinking of it in terms of objects, the only two "objects" I could think of were `Customer` and `Order`, so I split the database into tables for each.
diff --git a/challenges/database/generate_input.py b/challenges/database/generate_input.py
new file mode 100644
index 000000000..fe6f43a01
--- /dev/null
+++ b/challenges/database/generate_input.py
@@ -0,0 +1,14 @@
+import json
+with open('in.json', 'w') as f:
+ f.write(json.dumps({
+ 1234: {
+ 'name': 'Joe Smith',
+ 'cellPhone': '405.867.5309',
+ 'workPhone': '123.123.1234',
+ 'email': 'joe_s@gmail.com',
+ 'address': '123 Vic Way, Dallas TX 75001',
+ 'basicWidgetOrder': 37,
+ 'advancedWidgetOrder': 12,
+ 'protectionPlan': True
+ }
+ }))
diff --git a/challenges/database/in.json b/challenges/database/in.json
new file mode 100644
index 000000000..5f6caf1d9
--- /dev/null
+++ b/challenges/database/in.json
@@ -0,0 +1,12 @@
+{
+ "1234": {
+ "name": "Joe Smith",
+ "cellPhone": "405.867.5309",
+ "workPhone": "123.123.1234",
+ "email": "joe_s@gmail.com",
+ "address": "123 Vic Way, Dallas TX 75001",
+ "basicWidgetOrder": 37,
+ "advancedWidgetOrder": 12,
+ "protectionPlan": true
+ }
+}
diff --git a/challenges/database/jsontosql.py b/challenges/database/jsontosql.py
new file mode 100644
index 000000000..94196bc28
--- /dev/null
+++ b/challenges/database/jsontosql.py
@@ -0,0 +1,38 @@
+import json
+#import sqlite3
+
+def main():
+ # potential improvements include using command line arguments
+ # (or another method) to pick the input file
+ input_file = 'in.json'
+ with open(input_file, 'r') as f:
+ old_database = json.loads(f.read())
+ #new_database = sqlite3.connect('output.db')
+ create='''CREATE TABLE Customer (
+ customerID int,
+ name varchar(255),
+ cellPhone varchar(255),
+ workPhone varchar(255),
+ email varchar(255),
+ address varchar(255)
+);'''
+ #new_database.execute(create)
+ print(create)
+ create2='''CREATE TABLE Order (
+ customerID int,
+ basicWidgetOrder int,
+ advancedWidgetOrder int,
+ protectionPlan int,
+);'''
+ print(create2)
+ for obj, values in old_database.items():
+ print(f"\
+INSERT INTO Customer VALUES({obj}, \'{values['name']}\',\
+\'{values['cellPhone']}\', \'{values['workPhone']}\',\
+\'{values['email']}\', \'{values['address']}\');")
+ print(f"\
+INSERT INTO Order VALUES({obj}, {values['basicWidgetOrder']}, \
+{values['advancedWidgetOrder']}, {values['protectionPlan']});")
+
+if __name__ == '__main__':
+ main()
diff --git a/challenges/database/relational_db.png b/challenges/database/relational_db.png
new file mode 100644
index 000000000..d010c2bf8
Binary files /dev/null and b/challenges/database/relational_db.png differ
diff --git a/challenges/frontend/.gitignore b/challenges/frontend/.gitignore
new file mode 100644
index 000000000..8ee01d321
--- /dev/null
+++ b/challenges/frontend/.gitignore
@@ -0,0 +1 @@
+yarn.lock
diff --git a/challenges/frontend/README.md b/challenges/frontend/README.md
new file mode 100644
index 000000000..1b73305db
--- /dev/null
+++ b/challenges/frontend/README.md
@@ -0,0 +1,5 @@
+# How to run
+```sh
+yarn run http-server
+```
+
diff --git a/challenges/frontend/favicon-32x32.ico b/challenges/frontend/favicon-32x32.ico
new file mode 100644
index 000000000..a534bcfb9
Binary files /dev/null and b/challenges/frontend/favicon-32x32.ico differ
diff --git a/challenges/frontend/index.html b/challenges/frontend/index.html
new file mode 100644
index 000000000..7a4bc7125
--- /dev/null
+++ b/challenges/frontend/index.html
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/challenges/frontend/main.css b/challenges/frontend/main.css
new file mode 100644
index 000000000..7f393742a
--- /dev/null
+++ b/challenges/frontend/main.css
@@ -0,0 +1,5 @@
+@tailwind base;
+
+@tailwind components;
+
+@tailwind utilities;
diff --git a/challenges/frontend/output.css b/challenges/frontend/output.css
new file mode 100644
index 000000000..13b5b26df
--- /dev/null
+++ b/challenges/frontend/output.css
@@ -0,0 +1,626 @@
+/*! tailwindcss v2.0.4 | MIT License | https://tailwindcss.com */
+
+/*! modern-normalize v1.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */
+
+/*
+Document
+========
+*/
+
+/**
+Use a better box model (opinionated).
+*/
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+/**
+Use a more readable tab size (opinionated).
+*/
+
+:root {
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+}
+
+/**
+1. Correct the line height in all browsers.
+2. Prevent adjustments of font size after orientation changes in iOS.
+*/
+
+html {
+ line-height: 1.15; /* 1 */
+ -webkit-text-size-adjust: 100%; /* 2 */
+}
+
+/*
+Sections
+========
+*/
+
+/**
+Remove the margin in all browsers.
+*/
+
+body {
+ margin: 0;
+}
+
+/**
+Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)
+*/
+
+body {
+ font-family:
+ system-ui,
+ -apple-system, /* Firefox supports this but not yet `system-ui` */
+ 'Segoe UI',
+ Roboto,
+ Helvetica,
+ Arial,
+ sans-serif,
+ 'Apple Color Emoji',
+ 'Segoe UI Emoji';
+}
+
+/*
+Grouping content
+================
+*/
+
+/**
+1. Add the correct height in Firefox.
+2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
+*/
+
+/*
+Text-level semantics
+====================
+*/
+
+/**
+Add the correct text decoration in Chrome, Edge, and Safari.
+*/
+
+/**
+Add the correct font weight in Edge and Safari.
+*/
+
+/**
+1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)
+2. Correct the odd 'em' font sizing in all browsers.
+*/
+
+/**
+Add the correct font size in all browsers.
+*/
+
+/**
+Prevent 'sub' and 'sup' elements from affecting the line height in all browsers.
+*/
+
+/*
+Tabular data
+============
+*/
+
+/**
+1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
+2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
+*/
+
+/*
+Forms
+=====
+*/
+
+/**
+1. Change the font styles in all browsers.
+2. Remove the margin in Firefox and Safari.
+*/
+
+button,
+input {
+ font-family: inherit; /* 1 */
+ font-size: 100%; /* 1 */
+ line-height: 1.15; /* 1 */
+ margin: 0; /* 2 */
+}
+
+/**
+Remove the inheritance of text transform in Edge and Firefox.
+1. Remove the inheritance of text transform in Firefox.
+*/
+
+button { /* 1 */
+ text-transform: none;
+}
+
+/**
+Correct the inability to style clickable types in iOS and Safari.
+*/
+
+button,
+[type='button'],
+[type='submit'] {
+ -webkit-appearance: button;
+}
+
+/**
+Remove the inner border and padding in Firefox.
+*/
+
+/**
+Restore the focus styles unset by the previous rule.
+*/
+
+/**
+Remove the additional ':invalid' styles in Firefox.
+See: https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737
+*/
+
+/**
+Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers.
+*/
+
+/**
+Add the correct vertical alignment in Chrome and Firefox.
+*/
+
+/**
+Correct the cursor style of increment and decrement buttons in Safari.
+*/
+
+/**
+1. Correct the odd appearance in Chrome and Safari.
+2. Correct the outline style in Safari.
+*/
+
+/**
+Remove the inner padding in Chrome and Safari on macOS.
+*/
+
+/**
+1. Correct the inability to style clickable types in iOS and Safari.
+2. Change font properties to 'inherit' in Safari.
+*/
+
+/*
+Interactive
+===========
+*/
+
+/*
+Add the correct display in Chrome and Safari.
+*/
+
+/**
+ * Manually forked from SUIT CSS Base: https://github.com/suitcss/base
+ * A thin layer on top of normalize.css that provides a starting point more
+ * suitable for web applications.
+ */
+
+/**
+ * Removes the default spacing and border for appropriate elements.
+ */
+
+button {
+ background-color: transparent;
+ background-image: none;
+}
+
+/**
+ * Work around a Firefox/IE bug where the transparent `button` background
+ * results in a loss of the default `button` focus styles.
+ */
+
+button:focus {
+ outline: 1px dotted;
+ outline: 5px auto -webkit-focus-ring-color;
+}
+
+/**
+ * Tailwind custom reset styles
+ */
+
+/**
+ * 1. Use the user's configured `sans` font-family (with Tailwind's default
+ * sans-serif font stack as a fallback) as a sane default.
+ * 2. Use Tailwind's default "normal" line-height so the user isn't forced
+ * to override it to ensure consistency even when using the default theme.
+ */
+
+html {
+ font-family: JetBrains Mono, Font Awesome; /* 1 */
+ line-height: 1.5; /* 2 */
+}
+
+/**
+ * Inherit font-family and line-height from `html` so users can set them as
+ * a class directly on the `html` element.
+ */
+
+body {
+ font-family: inherit;
+ line-height: inherit;
+}
+
+/**
+ * 1. Prevent padding and border from affecting element width.
+ *
+ * We used to set this in the html element and inherit from
+ * the parent element for everything else. This caused issues
+ * in shadow-dom-enhanced elements like where the content
+ * is wrapped by a div with box-sizing set to `content-box`.
+ *
+ * https://github.com/mozdevs/cssremedy/issues/4
+ *
+ *
+ * 2. Allow adding a border to an element by just adding a border-width.
+ *
+ * By default, the way the browser specifies that an element should have no
+ * border is by setting it's border-style to `none` in the user-agent
+ * stylesheet.
+ *
+ * In order to easily add borders to elements by just setting the `border-width`
+ * property, we change the default border-style for all elements to `solid`, and
+ * use border-width to hide them instead. This way our `border` utilities only
+ * need to set the `border-width` property instead of the entire `border`
+ * shorthand, making our border utilities much more straightforward to compose.
+ *
+ * https://github.com/tailwindcss/tailwindcss/pull/116
+ */
+
+*,
+::before,
+::after {
+ box-sizing: border-box; /* 1 */
+ border-width: 0; /* 2 */
+ border-style: solid; /* 2 */
+ border-color: #e5e9f0; /* 2 */
+}
+
+/*
+ * Ensure horizontal rules are visible by default
+ */
+
+/**
+ * Undo the `border-style: none` reset that Normalize applies to images so that
+ * our `border-{width}` utilities have the expected effect.
+ *
+ * The Normalize reset is unnecessary for us since we default the border-width
+ * to 0 on all elements.
+ *
+ * https://github.com/tailwindcss/tailwindcss/issues/362
+ */
+
+input::-moz-placeholder {
+ opacity: 1;
+ color: #4c566a;
+}
+
+input:-ms-input-placeholder {
+ opacity: 1;
+ color: #4c566a;
+}
+
+input::placeholder {
+ opacity: 1;
+ color: #4c566a;
+}
+
+button {
+ cursor: pointer;
+}
+
+/**
+ * Reset links to optimize for opt-in styling instead of
+ * opt-out.
+ */
+
+a {
+ color: inherit;
+ text-decoration: inherit;
+}
+
+/**
+ * Reset form element properties that are easy to forget to
+ * style explicitly so you don't inadvertently introduce
+ * styles that deviate from your design system. These styles
+ * supplement a partial reset that is already applied by
+ * normalize.css.
+ */
+
+button,
+input {
+ padding: 0;
+ line-height: inherit;
+ color: inherit;
+}
+
+/**
+ * Use the configured 'mono' font family for elements that
+ * are expected to be rendered with a monospace font, falling
+ * back to the system monospace stack if there is no configured
+ * 'mono' font family.
+ */
+
+/**
+ * Make replaced elements `display: block` by default as that's
+ * the behavior you want almost all of the time. Inspired by
+ * CSS Remedy, with `svg` added as well.
+ *
+ * https://github.com/mozdevs/cssremedy/issues/14
+ */
+
+/**
+ * Constrain images and videos to the parent width and preserve
+ * their instrinsic aspect ratio.
+ *
+ * https://github.com/mozdevs/cssremedy/issues/14
+ */
+
+.bg-gray-200 {
+ --tw-bg-opacity: 1;
+ background-color: rgba(229, 233, 240, var(--tw-bg-opacity));
+}
+
+.bg-gray-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgba(46, 52, 64, var(--tw-bg-opacity));
+}
+
+.hover\:bg-gray-400:hover {
+ --tw-bg-opacity: 1;
+ background-color: rgba(76, 86, 106, var(--tw-bg-opacity));
+}
+
+.border-gray-500 {
+ --tw-border-opacity: 1;
+ border-color: rgba(67, 76, 94, var(--tw-border-opacity));
+}
+
+.rounded-sm {
+ border-radius: 0.125rem;
+}
+
+.flex {
+ display: flex;
+}
+
+.flex-row {
+ flex-direction: row;
+}
+
+.flex-col {
+ flex-direction: column;
+}
+
+.justify-center {
+ justify-content: center;
+}
+
+.flex-grow {
+ flex-grow: 1;
+}
+
+.font-normal {
+ font-weight: 400;
+}
+
+.font-extrabold {
+ font-weight: 800;
+}
+
+.font-black {
+ font-weight: 900;
+}
+
+.text-xs {
+ font-size: 0.75rem;
+ line-height: 1rem;
+}
+
+.text-lg {
+ font-size: 1.125rem;
+ line-height: 1.75rem;
+}
+
+.text-2xl {
+ font-size: 1.5rem;
+ line-height: 2rem;
+}
+
+.text-4xl {
+ font-size: 2.25rem;
+ line-height: 2.5rem;
+}
+
+.leading-normal {
+ line-height: 1.5;
+}
+
+.m-4 {
+ margin: 1rem;
+}
+
+.mb-1 {
+ margin-bottom: 0.25rem;
+}
+
+.mt-2 {
+ margin-top: 0.5rem;
+}
+
+.mb-2 {
+ margin-bottom: 0.5rem;
+}
+
+.mt-3 {
+ margin-top: 0.75rem;
+}
+
+.mt-4 {
+ margin-top: 1rem;
+}
+
+* {
+ --tw-shadow: 0 0 #0000;
+}
+
+* {
+ --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
+ --tw-ring-offset-width: 0px;
+ --tw-ring-offset-color: #fff;
+ --tw-ring-color: rgba(59, 130, 246, 0.5);
+ --tw-ring-offset-shadow: 0 0 #0000;
+ --tw-ring-shadow: 0 0 #0000;
+}
+
+.text-left {
+ text-align: left;
+}
+
+.text-center {
+ text-align: center;
+}
+
+.text-gray-100 {
+ --tw-text-opacity: 1;
+ color: rgba(236, 239, 244, var(--tw-text-opacity));
+}
+
+.text-gray-300 {
+ --tw-text-opacity: 1;
+ color: rgba(216, 222, 233, var(--tw-text-opacity));
+}
+
+.text-gray-700 {
+ --tw-text-opacity: 1;
+ color: rgba(46, 52, 64, var(--tw-text-opacity));
+}
+
+.w-20 {
+ width: 5rem;
+}
+
+@-webkit-keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@-webkit-keyframes ping {
+ 75%, 100% {
+ transform: scale(2);
+ opacity: 0;
+ }
+}
+
+@keyframes ping {
+ 75%, 100% {
+ transform: scale(2);
+ opacity: 0;
+ }
+}
+
+@-webkit-keyframes pulse {
+ 50% {
+ opacity: .5;
+ }
+}
+
+@keyframes pulse {
+ 50% {
+ opacity: .5;
+ }
+}
+
+@-webkit-keyframes bounce {
+ 0%, 100% {
+ transform: translateY(-25%);
+ -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1);
+ animation-timing-function: cubic-bezier(0.8,0,1,1);
+ }
+
+ 50% {
+ transform: none;
+ -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1);
+ animation-timing-function: cubic-bezier(0,0,0.2,1);
+ }
+}
+
+@keyframes bounce {
+ 0%, 100% {
+ transform: translateY(-25%);
+ -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1);
+ animation-timing-function: cubic-bezier(0.8,0,1,1);
+ }
+
+ 50% {
+ transform: none;
+ -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1);
+ animation-timing-function: cubic-bezier(0,0,0.2,1);
+ }
+}
+
+@media (min-width: 640px) {
+ .sm\:container {
+ width: 100%;
+ }
+
+ @media (min-width: 640px) {
+ .sm\:container {
+ max-width: 640px;
+ }
+ }
+
+ @media (min-width: 768px) {
+ .sm\:container {
+ max-width: 768px;
+ }
+ }
+
+ @media (min-width: 1024px) {
+ .sm\:container {
+ max-width: 1024px;
+ }
+ }
+
+ @media (min-width: 1280px) {
+ .sm\:container {
+ max-width: 1280px;
+ }
+ }
+
+ @media (min-width: 1536px) {
+ .sm\:container {
+ max-width: 1536px;
+ }
+ }
+
+ .sm\:mx-auto {
+ margin-left: auto;
+ margin-right: auto;
+ }
+}
+
+@media (min-width: 768px) {
+ .md\:max-w-md {
+ max-width: 28rem;
+ }
+
+ .md\:w-80 {
+ width: 20rem;
+ }
+}
diff --git a/challenges/frontend/package.json b/challenges/frontend/package.json
new file mode 100644
index 000000000..04b739b37
--- /dev/null
+++ b/challenges/frontend/package.json
@@ -0,0 +1,10 @@
+{
+ "dependencies": {
+ "autoprefixer": "^10.2.5",
+ "http-server": "^0.12.3",
+ "tailwindcss": "^2.0.4"
+ },
+ "scripts": {
+ "run": "http-server"
+ }
+}
diff --git a/challenges/frontend/tailwind.config.js b/challenges/frontend/tailwind.config.js
new file mode 100644
index 000000000..99cbcaf3d
--- /dev/null
+++ b/challenges/frontend/tailwind.config.js
@@ -0,0 +1,49 @@
+module.exports = {
+ purge: {
+ mode: 'all',
+ preserveHtmlElements: false,
+ enabled: true,
+ content: ['./*.html'],
+ },
+ //purge: [],ZZ
+ darkMode: 'media', // or 'media' or 'class'
+ theme: {
+ colors: {
+ gray: {
+ 100: '#eceff4',
+ 200: '#e5e9f0',
+ 300: '#d8dee9',
+ 400: '#4c566a',
+ 500: '#434c5e',
+ 600: '#3b4252',
+ 700: '#2e3440',
+ },
+ blue: {
+ 100: '#8fbcbb',
+ 200: '#88c0d0',
+ 300: '#81a1c1',
+ 400: '#5e81ac',
+ },
+ red: '#bf616a',
+ orange: '#d08770',
+ yellow: '#ebcb8b',
+ green: '#a3be8c',
+ purple: '#b48ead',
+ },
+
+ fontFamily: {
+ sans: ['JetBrains Mono', 'Font Awesome'],
+ },
+ extend: {},
+ },
+ variants: {
+ extend: {},
+ },
+ plugins: [
+ //require('tailwindcss-theming')({
+ // preset: 'nord', // Change your preset here.
+ //}),
+ require('tailwindcss'),
+ require('autoprefixer'),
+ ],
+}