<file_sep>#!/usr/bin/env bash

export FLASK_APP=server.py
flask run<file_sep>import requests
import json
from server import DATABASE_NAME

sample_post = {
    "userId": "<KEY>",
    "sessionId": "XYZ456ABC",
    "actions": [
        {
            "time": "2018-10-18T21:37:28-06:00",
            "type": "CLICK",
            "properties": {
                "locationX": 52,
                "locationY": 11
            }
        },
        {
            "time": "2018-10-18T21:37:30-06:00",
            "type": "VIEW",
            "properties": {
                "viewedId": "FDJKLHSLD"
            }
        },
        {
            "time": "2018-10-18T21:37:30-06:00",
            "type": "NAVIGATE",
            "properties": {
                "pageFrom": "communities",
                "pageTo": "inventory"
            }
        }
    ]
}

r = requests.post('http://localhost:5000/add-log', json=json.dumps(sample_post))
assert (r.json()['message'] == 'added log to database')

NUMBER_OF_USERS = 100
for i in range(0, NUMBER_OF_USERS):
    sample_post['userId'] = str(i)
    r = requests.post('http://localhost:5000/add-log', json=json.dumps(sample_post))
    assert (r.json()['message'] == 'added log to database')

<file_sep>from flask import Flask, request, jsonify, app
import json
import sqlite3

app = Flask(__name__)

DATABASE_NAME = 'openhouse-ai.db'


def setup_database():
    connection = sqlite3.connect(DATABASE_NAME)
    cursor = connection.cursor()
    cursor.execute(
        'CREATE TABLE IF NOT EXISTS logs ('
        'userId varchar(9) NOT NULL, '
        'sessionId varchar(9) NOT NULL, '
        'time datetime NOT NULL, '
        'type varchar NOT NULL, '
        'properties varchar NOT NULL'
        ')'
    )
    cursor.close()
    connection.commit()
    connection.close()


@app.route('/add-log', methods=['POST'])
def add_log():
    try:
        data = json.loads(request.json)
        connection = sqlite3.connect(DATABASE_NAME)
        cursor = connection.cursor()
        for action in data['actions']:
            cursor.execute(
                "INSERT INTO logs (userId, sessionId, time, type, properties) VALUES (?, ?, ?, ?, ?)",
                (data['userId'], data['sessionId'], action['time'], action['type'], json.dumps(action['properties']))
            )
        cursor.close()
        connection.commit()
        connection.close()

        return jsonify({'message': 'added log to database'}), 200
    except (KeyError, TypeError) as e:
        app.logger.error(e)
        return jsonify({"message": "error: please pass a json request in the format specified in the documentation."}), 400


@app.route('/get-logs', methods=['GET'])
def get_logs():
    try:
        data = request.data

        connection = sqlite3.connect(DATABASE_NAME)
        cursor = connection.cursor()
        cursor.execute(
            "SELECT * FROM logs WHERE userId=? AND time<=? AND time>=? and type=?",
            (data['userId'], data['startTime'], data['endTime'], data['type'])
        )
        rows = cursor.fetchall()
        output = jsonify(rows)
        cursor.close()
        connection.close()

        return output
    except (KeyError, TypeError) as e:
        app.logger.error(e)
        return jsonify({"message": "error: please pass a request in the format specified in the documentation."}), 400


setup_database()
<file_sep># Openhouse AI Challenge

This application takes a request made by a front end and stores it in a database. 

Calls can then be made to the application to get data from the database.

# To test
Please install [Python 3](python.org/downloads/) and add it to your terminal path.

After installing, run:
```bash
pip install -r requirements.txt
```

To run server, run:
```bash
./run-server.sh
```

After the server is running, to run tests, run:
```bash
python tests.py
```

# REST API

POST /add-log

Posting to /add-log adds the log to the database. It returns a JSON string object with a message.

The format of the request must be:
```json
{
    "userId": <string>,
    "sessionId": <string>,
    "actions": [
        ...,
        {
            "time": <iso-datetime as a string>,
            "type": <string>,
            "properties": <json object>
        },
        ...
    ]
}
```

GET /get-logs?userId=<userId>&startTime=<startTime>&endTime=<endTime>&type<type>

Making a get request to the above URL will get the logs with the desired userId and type between the startTime and the endTime.


# How to make application scalable
There are a few ways I could make this application scalable:

1. Use a different database. Sqlite is good for testing, but it only allows for one write at a time. Currently, this is a bottleneck as Sqlite will block on concurrent writes. Other SQL servers, like MSSQL, PostgreSQL or MySQL support multi-threaded writes.
2. Make Flask support asynchronous requests. Python is synchronous by default. This is can be done by using celery or some other multi-threaded queueing methods.
3. Launch multiple instances of the application. This could be done with AWS lambda or with multiple machines running multiple instances of the application. This would all be put behind a load balancer to route incoming requests to various machines.