init
commit
fcefc75cc0
@ -0,0 +1,109 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
config.py
|
||||
.terraform
|
||||
*.tfstate
|
||||
*.tfstate.backup
|
@ -0,0 +1,69 @@
|
||||
# flask-mdi
|
||||
Flask based mobile device ingestion application
|
||||
|
||||
# Setup
|
||||
|
||||
All of the below assumes you have an active/working default AWSCLI profile configured on your machine. Running `aws sts get-caller-identity` in a fresh terminal will give you an indication.
|
||||
|
||||
First deploy a Kinesis Stream:
|
||||
|
||||
```bash
|
||||
aws kinesis create-stream \
|
||||
--stream-name mdi-test \
|
||||
--shard-count 1 \
|
||||
--region us-east-1
|
||||
```
|
||||
|
||||
Then configure the app:
|
||||
|
||||
```bash
|
||||
cp mdi/config.{example.py,py}
|
||||
vim mdi/config.py
|
||||
# Modify the config file as needed
|
||||
```
|
||||
|
||||
Install dependencies and run it:
|
||||
|
||||
```bash
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install .
|
||||
export FLASK_APP=mdi/app.py
|
||||
export FLASK_SECRETS=config.py
|
||||
export FLASK_ENV=development
|
||||
export FLASK_DEBUG=1
|
||||
flask run
|
||||
```
|
||||
|
||||
# Testing
|
||||
|
||||
Open another tab/window in your terminal and `curl` the Flask app - there's only a single endpoint, `/`
|
||||
|
||||
```bash
|
||||
# GET returns simple JSON body and 200, no data is stored
|
||||
curl localhost:5000/ -X GET
|
||||
{
|
||||
"app": "mdi",
|
||||
"message": "Welcome",
|
||||
"status": "success",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
|
||||
# Empty payload on POST returns error, 400
|
||||
curl localhost:5000/ -X POST
|
||||
{
|
||||
"app": "mdi",
|
||||
"message": "No JSON data provided",
|
||||
"status": "error",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
|
||||
# Any payload on POST returns 202
|
||||
curl localhost:5000/ -X POST -d '{"hello": "world"}' -H "Content-Type: application/json"
|
||||
{
|
||||
"app": "mdi",
|
||||
"message": "Data stored for processing",
|
||||
"status": "success",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
```
|
@ -0,0 +1 @@
|
||||
from mdi._version import __version__
|
@ -0,0 +1 @@
|
||||
__version__ = "0.0.1"
|
@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from json import dumps as json_dumps
|
||||
from socket import gethostname
|
||||
from boto3 import client as boto3_client
|
||||
from flask import Flask, jsonify, request, make_response
|
||||
from datetime import datetime
|
||||
from mdi import __version__
|
||||
from mdi import config
|
||||
|
||||
|
||||
# Setup Flask application
|
||||
app = Flask(__name__)
|
||||
app.config.from_envvar('FLASK_SECRETS')
|
||||
|
||||
@app.route('/', methods=["GET", "POST"])
|
||||
def index():
|
||||
now = datetime.now()
|
||||
|
||||
if request.method == "POST":
|
||||
|
||||
if not request.data:
|
||||
return render_result(400, "error", "No JSON data provided")
|
||||
|
||||
json_data = request.get_json()
|
||||
json_data["timestamp"] = int(now.timestamp())
|
||||
json_data["datestamp"] = now.strftime("%Y-%m-%d %H:%M:%S")
|
||||
if not json_data.get("device_name"):
|
||||
json_data["device_name"] = gethostname()
|
||||
|
||||
print(f"Using payload: {json_data}")
|
||||
|
||||
publish_kinesis(config.STREAM_NAME, config.REGION, json_data, json_data["device_name"])
|
||||
write_fs(config.DATA_PATH, json_data["timestamp"], json_data)
|
||||
|
||||
return render_result(202, "success", "Data stored for processing")
|
||||
else:
|
||||
return render_result(200, "success", "Welcome")
|
||||
|
||||
|
||||
def render_result(code, status, message):
|
||||
response = make_response(jsonify({
|
||||
"status": status,
|
||||
"message": message,
|
||||
"app": config.APP_NAME,
|
||||
"version": __version__
|
||||
}), code)
|
||||
return response
|
||||
|
||||
def write_fs(path, name, data):
|
||||
fs_path = f"{path}/{name}.json"
|
||||
print(f"Writing payload to {fs_path}")
|
||||
with open(fs_path, "w") as f:
|
||||
f.write(json_dumps(data))
|
||||
print("Payload written to filesystem")
|
||||
|
||||
def publish_kinesis(stream, region, data, pk):
|
||||
print("Publishing payload to Kinesis")
|
||||
kinesis = boto3_client("kinesis", region_name=region)
|
||||
res = kinesis.put_record(
|
||||
StreamName=stream,
|
||||
Data=json_dumps(data),
|
||||
PartitionKey=pk
|
||||
)
|
||||
print(f"Kinesis response: {res}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
@ -0,0 +1,4 @@
|
||||
APP_NAME = "mdi"
|
||||
STREAM_NAME = "mdi-test"
|
||||
DATA_PATH = "/tmp"
|
||||
REGION = "us-east-1"
|
@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
from mdi import __version__
|
||||
|
||||
NAME = "mdi"
|
||||
DESCRIPTION = "Flask application for ingesting data from mobile devices"
|
||||
URL = "https://github.com/lalanza808/flask-mdi.git"
|
||||
EMAIL = "lance@lzahq.tech"
|
||||
AUTHOR = "Lance Allen"
|
||||
REQUIRES_PYTHON = ">=3.6.0"
|
||||
VERSION = __version__
|
||||
REQUIRED = [
|
||||
"boto3==1.9.74",
|
||||
"Flask==1.0.2"
|
||||
]
|
||||
EXTRAS = {}
|
||||
TESTS = []
|
||||
SETUP = []
|
||||
|
||||
setup(
|
||||
name=NAME,
|
||||
version=VERSION,
|
||||
description=DESCRIPTION,
|
||||
author=AUTHOR,
|
||||
author_email=EMAIL,
|
||||
include_package_data=True,
|
||||
extras_require=EXTRAS,
|
||||
install_requires=REQUIRED,
|
||||
setup_requires=SETUP,
|
||||
tests_require=TESTS,
|
||||
packages=find_packages(exclude=['ez_setup']),
|
||||
zip_safe=False
|
||||
)
|
Loading…
Reference in New Issue