Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e8ee1af
Merge pull request #19 from tmthyjames/ACHOO-16
tmthyjames Jan 8, 2018
f3a23dd
duhhhh
tmthyjames Jan 8, 2018
36e3ee2
set debug to false
tmthyjames Jan 8, 2018
5880197
update .gitignore
tmthyjames Jan 8, 2018
38962a3
modify file structure
tmthyjames Jan 9, 2018
e634303
change app.py to application.py for elasticbeanstalk compatibility
tmthyjames Jan 9, 2018
86c041b
add buttons for github and blog
tmthyjames Jan 9, 2018
770b9c8
fix button
tmthyjames Jan 9, 2018
576959d
change wording
tmthyjames Jan 10, 2018
44331ee
Add files via upload
tmthyjames Jan 10, 2018
62ce70c
add link to achoo blog post
tmthyjames Jan 10, 2018
8706007
make UI a little more user friendly
tmthyjames Jan 11, 2018
c1fd23b
add error msg if error when signing up
tmthyjames Jan 11, 2018
7103bbe
add http -> https redirect
tmthyjames Jan 11, 2018
7fb4f47
Update README.md
tmthyjames Jan 11, 2018
2a669b4
update blog post link
tmthyjames Jan 11, 2018
74363d9
add zipcode to treatment data
tmthyjames Mar 4, 2018
d2bb3b8
change UI colors
tmthyjames Mar 27, 2018
8a4f572
fix typo
tmthyjames Mar 29, 2018
e24f65a
Update README.md
tmthyjames Mar 29, 2018
a42d9e3
Documentation Update
sparklespdx Apr 14, 2018
c3cec49
Treatment.timestamp needs BigInt
sparklespdx Apr 14, 2018
cd2b2be
Safe engine.execute for zipcode lookup
sparklespdx Apr 14, 2018
3f907ca
Slight shuffling
sparklespdx Apr 14, 2018
66f7521
Fix 405 issue
sparklespdx Apr 14, 2018
d16cf18
Forgot to commit this file
sparklespdx Apr 14, 2018
a2877a9
Slight refactor
sparklespdx Apr 14, 2018
c40f208
Merge pull request #28 from sparklespdx/bugfix
tmthyjames Apr 14, 2018
149cc30
Merge pull request #29 from sparklespdx/docs
tmthyjames Apr 14, 2018
ded82de
Merge pull request #30 from sparklespdx/login-405
tmthyjames Apr 14, 2018
cdfd77a
Bump werkzeug from 0.12.2 to 0.15.3
dependabot[bot] Nov 2, 2019
cf7168d
Merge pull request #33 from tmthyjames/dependabot/pip/werkzeug-0.15.3
tmthyjames Dec 4, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
__pycache__
*.py[cod]
.DS_Store

# Elastic Beanstalk Files
.elasticbeanstalk/*
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml
75 changes: 7 additions & 68 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,80 +1,19 @@
# Achoo
Achoo uses a Raspberry Pi to predict if my son will need his inhaler on any given day using weather, pollen, and air quality data. If the prediction for a given day is above a specified threshold, the Pi will email his school nurse, and myself, notifying her that he may need preemptive treatment. **TUTORIAL COMING SOON**

Prompted by [this](https://www.reddit.com/r/Python/comments/70udwq/what_routine_tasks_do_you_automate_with_programs/) reddit post. Lots of great ideas in there.

**UPDATE: 09/27/2017**

My goal is to have this repo completely decoupled from any UI and instead act as a restful-like API that could be consumed by any UI. Achoo's default UI is housed [here](https://github.com/tmthyjames/AchooUI).

**UPDATE**

I will be updating this readme fairly often for the next week or two to make sure the code and instructions are as readable as possible.

## Environment setup

First, you'll want to declare a few environment variables:

`ACHOO_FROM_EMAIL` - the sender's email address.<br/>
`ACHOO_TO_EMAIL` - the email addresses to send the alerts to.<br/>
`ACHOO_GMAIL_PASSWORD` - to send emails (if you don't already have one) you'll need to register an app with gmail.<br/>
`ACHOO_DARK_SKY_API_KEY` - for the weather data, I'm using [Dark Sky](https://darksky.net/dev). You get 1,000 free hits a day. Their pricing is really affordable.<br/>
`ACHOO_DARK_SKY_LOCATION` - The latitude, longitude for the location you'll be tracking. <br/>
`ACHOO_POLLEN_ZIPCODE` - The zipcode you'll be tracking.<br/>
`ACHOO_DB_USERNAME` - the PostgreSQL username (or other database).<br/>
`ACHOO_DB_PASSWORD` - PostgreSQL password.<br/>
`ACHOO_DB_DATABASE` - Name of the database (default: `achoo`).<br/>
`ACHOO_DB_PORT` - Port number (default: `5432`).<br/>

## Achoo workflow
![logo](app/static/img/ACHOO-FINAL.png)

For now, the work flow is reflected in the bash script that runs daily:

```bash
#!/bin/bash
**UPDATE: 01/10/2018**

Rscript app/models/exe/r/train_model.r &&
I have released Achoo beta version 0.1! Check it out at [achoo.us](https://achoo.us). Also, check out the [blog post about it](https://tmthyjames.github.io/tools/prediction/Achoo-beta-0.1/). Most of the info below is now obsolete and will be replaced shortly as Achoo is now a web app for users instead of just a repo for devs :)

python app/munging/get_allergy_data.py &&
python app/munging/get_weather_data.py &&
python app/munging/get_air_data.py &&

Rscript app/models/exe/r/run_model.r &&

python app/munging/get_results.py
```

1) Train the model (this assumes we have data, so I'll upload some test data that I've been using)
2) Get all weather, allergy, and air quality data
3) Run the model (this will write the results to the database after every run)
4) Retrieve the results (this is the step that sends the email if the prediction is greater than X).

This bash script currently runs as a cron job.

**THIS IS LIKELY TO CHANGE SOON.**

To record the inhaler/breathing treatment data, you'll run `treatment_tracker.py` from your Raspberry Pi like this:

```bash
$ python treatment_tracker.py &
```

I have two buttons on my Pi: one for his breathing treatment, the other for his inhaler (pics and a blog post coming soon).

### Outstanding questions
Achoo uses a Raspberry Pi to predict if my son will need his inhaler on any given day using weather, pollen, and air quality data. If the prediction for a given day is above a specified threshold, the Pi will email his school nurse, and myself, notifying her that he may need preemptive treatment. **TUTORIAL COMING SOON**

1) Is there a better way than using a cron job?<br/>
2) The Raspberry Pi isn't ideal. Maybe we should be using [Nomie 2](https://itunes.apple.com/us/app/nomie-2/id1190618299?mt=8) to record events? (HT to [sujins](https://www.reddit.com/user/sujins) for this suggestion). I'd like to make it easy for the user to add their own method and their own host for storing data.<br/>
3) I have a feeling many users will want to easily add their own models (Nueral Networks, GBMs, GLMs, etc.). What is the best way for users to add their own models?<br/>
4) Should we support two languages? Currently I'm using Python and R.<br/>
5) I still have a lot of architectural concerns about this project so far, but I'm hoping things will be more smooth as a couple weeks' worth of development pass.<br/>
6) **LONG TERM**: How to make this non-developer friendly? How can the typical person with allergies/asthma make use of this day-to-day? Can we make this a mobile app? I think this is my end goal.<br/>
Prompted by [this](https://www.reddit.com/r/Python/comments/70udwq/what_routine_tasks_do_you_automate_with_programs/) reddit post. Lots of great ideas in there.

### Here's my Pi setup
### Here's my Pi setup (obsolete as this is all done through the app now)

Blue button is for inhaler (first line of defense)

red button is for breathing treatment (second line of defense; use this one if his symptoms are really bad)

![From the top](img/IMG_5919.JPG)
![From the top](app/static/img/IMG_5919.JPG)

72 changes: 72 additions & 0 deletions SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Achoo Environment Setup

### Dependencies:

You will need the following installed on your OS of choice to run Achoo:
* Python 3
* R
* PostgreSQL
* PostGIS with command line tools

We recommend using a [virtualenv](https://docs.python.org/3/library/venv.html) to manage Python dependencies.

To install the Python dependencies, run the following from the project root:

`pip install -r requirements.txt`


# Flask
### Environment Variables:

Variable Name | Value
--- | ---
`ACHOO_SECRET_KEY` | [Key for signing things](http://flask.pocoo.org/docs/0.12/api/#flask.Flask.secret_key), set to strong random value.
`ACHOO_PG_CONN_STR` | [SQLAlchemy database URI (PostgreSQL)](http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls). Something like `postgresql://user@localhost:5432/achoo`.
`ACHOO_GOOGLE_MAPS_API_KEY` | [Google Maps API Key](https://developers.google.com/maps/documentation/javascript/get-api-key).

### Bootstrap the Database for the first time

From the application root directory, run the following in a python interpreter to create the database and tables:

```python
from app.models.models import db
db.create_all()
```

Initialize PostGIS:

```bash
psql achoo
CREATE EXTENSION postgis;
\q
```

Download and import GIS shapefiles and clean up:

```bash
mkdir gisdata
cd gisdata
wget http://www2.census.gov/geo/tiger/GENZ2016/shp/cb_2016_us_zcta510_500k.zip
unzip cb_2016_us_zcta510_500k.zip
shp2pgsql cb_2016_us_zcta510_500k.shp > gisdata.sql
psql achoo < gisdata.sql
cd ..
rm -r gisdata
```

### Run the Web Application

```bash
python3 application.py
```

Or:

```bash
export FLASK_PATH=/path/to/Achoo/application.py
flask run --reload
```


# Analytics
TODO
7 changes: 7 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from flask import Flask
import os

application = Flask(__name__)
application.config['SECRET_KEY'] = os.environ['ACHOO_SECRET_KEY']
application.config['SQLALCHEMY_DATABASE_URI'] = os.environ['ACHOO_PG_CONN_STR']
application.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
File renamed without changes.
30 changes: 29 additions & 1 deletion ui/app/api/api.py → app/api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,38 @@
from flask import request, url_for
from flask_login import current_user, login_user

from sqlalchemy import bindparam
from sqlalchemy.sql import text
from sqlalchemy.exc import DataError

import requests
import json

# models
from app.models.models import User, db, bcrypt, Treatment


def get_zipcode_from_coords(lat=-86.725573627, lng=36.227447713):

# bindparams casts the input to SQLAlchemy types,
# throws DataError if types are wrong.
statement = text("""
select geom.geoid10
from cb_2016_us_zcta510_500k geom
where ST_Contains(geom.geom, ST_MakePoint(:lng, :lat))
""").bindparams(
bindparam('lng', value=lng, type_=db.Float),
bindparam('lat', value=lat, type_=db.Float)
)

try:
result = db.engine.execute(statement).first()
except DataError:
return None

if result:
return result[0]

class Prediction(Resource):

def get(self):
Expand All @@ -18,6 +44,7 @@ def post(self):
capture_data = request.get_json()
latitude = capture_data.get('coords').get('latitude')
longitude = capture_data.get('coords').get('longitude')
zipcode = get_zipcode_from_coords(latitude, longitude)
timestamp = capture_data.get('timestamp')
treatment = capture_data.get('treatment')
accuracy = capture_data.get('accuracy')
Expand All @@ -28,7 +55,8 @@ def post(self):
lat=latitude,
lng=longitude,
treatment=treatment,
accuracy=accuracy
accuracy=accuracy,
zipcode=zipcode
)
db.session.add(treatment)
db.session.commit()
Expand Down
8 changes: 8 additions & 0 deletions app/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import os

ENV = os.environ.get('ACHOO_DEBUG')

class Config(object):
MSG = '(beta)'
VERSION = '0.1'
DEBUG = True if ENV == 'local' else False
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
9 changes: 5 additions & 4 deletions ui/app/models/models.py → app/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
from flask_bcrypt import Bcrypt
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
from app import app
from app import application

db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
db = SQLAlchemy(application)
bcrypt = Bcrypt(application)


class User(UserMixin, db.Model):
Expand Down Expand Up @@ -34,9 +34,10 @@ class Treatment(UserMixin, db.Model):
__tablename__ = 'treatment'
id = db.Column(db.Integer, primary_key=True)
userid = db.Column(db.Integer, nullable=False)
timestamp = db.Column(db.Integer, nullable=False)
timestamp = db.Column(db.BigInteger, nullable=False)
lng = db.Column(db.Float)
lat = db.Column(db.Float)
treatment = db.Column(db.String(100), nullable=False)
accuracy = db.Column(db.Integer)
zipcode = db.Column(db.String(10))

Loading