Skip to content

Commit

Permalink
Add data model and API for snapshots
Browse files Browse the repository at this point in the history
  • Loading branch information
onejgordon committed Apr 2, 2017
1 parent 57bf556 commit b0dd926
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 2 deletions.
31 changes: 30 additions & 1 deletion api.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

from datetime import datetime, timedelta, time
from models import Project, Habit, HabitDay, Goal, MiniJournal, User, Task, \
Readable, TrackingDay, Event, JournalTag, Report, Quote
Readable, TrackingDay, Event, JournalTag, Report, Quote, Snapshot
from constants import READABLE
from google.appengine.ext import ndb
from oauth2client import client
Expand Down Expand Up @@ -623,6 +623,35 @@ def _get_task_due_date(self):
return datetime.combine((now + timedelta(hours=24+8)).date(), time(0,0))


class SnapshotAPI(handlers.JsonRequestHandler):

@authorized.role('user')
def list(self, d):
limit = self.request.get_range('limit', default=500)
snapshots = Snapshot.Recent(self.user, limit=limit)
self.set_response({
'snapshots': [s.json() for s in snapshots if s]
}, success=True, debug=True)

@authorized.role('user')
def submit(self, d):
'''
Submit a snapshot. Assume snapshot is now
'''
params = tools.gets(self,
strings=['lat', 'lon', 'activity', 'where'],
json=['metrics'],
lists=['people']
)
snap = Snapshot.Create(self.user, **params)
snap.put()
self.success = True

self.set_response({
'snapshot': snap.json() if snap else None
})


class UserAPI(handlers.JsonRequestHandler):
@authorized.role('admin')
def list(self, d):
Expand Down
2 changes: 2 additions & 0 deletions flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
webapp2.Route('/api/journal/today', handler=api.JournalAPI, handler_method="today", methods=["GET"]),
webapp2.Route('/api/journal', handler=api.JournalAPI, handler_method="submit", methods=["POST"]),
webapp2.Route('/api/journal', handler=api.JournalAPI, handler_method="list", methods=["GET"]),
webapp2.Route('/api/snapshot', handler=api.SnapshotAPI, handler_method="submit", methods=["POST"]),
webapp2.Route('/api/snapshot', handler=api.SnapshotAPI, handler_method="list", methods=["GET"]),
webapp2.Route('/api/task', handler=api.TaskAPI, handler_method="list", methods=["GET"]),
webapp2.Route('/api/task', handler=api.TaskAPI, handler_method="update", methods=["POST"]),
webapp2.Route('/api/readable', handler=api.ReadableAPI, handler_method="list", methods=["GET"]),
Expand Down
55 changes: 55 additions & 0 deletions models.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,61 @@ def get_data_value(self, prop):
return data.get(prop)


class Snapshot(UserAccessible):
"""
Key - ID
Randomly collect data points throughout day/week (frequency customizable)
Metrics:
- Activity
- Location (generic, e.g. home/office/restaurant)
- People (who with)
Passive metrics:
- GPS location, if available
"""
date = ndb.DateProperty() # Date for entry
dt_created = ndb.DateTimeProperty(auto_now_add=True)
mins = ndb.IntegerProperty() # Minutes into day (0 - 1439)
activity = ndb.StringProperty() # JSON (keys are data names, values are responses) (UTC)
where = ndb.StringProperty()
people = ndb.StringProperty(repeated=True)
metrics = ndb.TextProperty() # JSON
location = ndb.GeoPtProperty()

def json(self):
res = {
'id': self.key.id(),
'iso_date': tools.iso_date(self.date),
'metrics': tools.getJson(self.metrics),
'people': self.people,
'where': self.where,
'activity': self.activity,
'mins': self.mins
}
if self.location:
res.update({
'lat': self.location.lat,
'lon': self.location.lon
})
return res

@staticmethod
def Create(user, activity=None, where=None, people=None, metrics=None, location=None):
date = datetime.now()
mins = tools.minutes_in(date)
return Snapshot(dt_created=date, mins=mins, where=where, people=people if people else [],
activity=activity, metrics=json.dumps(metrics) if metrics else None,
parent=user.key)

@staticmethod
def Recent(user, limit=500):
return Snapshot.query().order(-Snapshot.dt_created).fetch(limit=limit)

def get_data_value(self, prop):
metrics = tools.getJson(self.metrics, {})
return metrics.get(prop)


class Event(UserAccessible):
"""
Key - ID
Expand Down
4 changes: 4 additions & 0 deletions src/js/components/Auth.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
var React = require('react');
import {Link} from 'react-router';
var AppConstants = require('constants/AppConstants');
var api = require('utils/api');
import GoogleLogin from 'react-google-login';
var client_secrets = require('constants/client_secrets');
import {RaisedButton} from 'material-ui';
var toastr = require('toastr');

export default class Auth extends React.Component {
Expand Down Expand Up @@ -68,6 +70,8 @@ export default class Auth extends React.Component {

<h2 style={{marginTop: "140px", marginBottom: "60px"}}>To connect to {provider.name}, sign in to {SITENAME}</h2>

<Link to="/app/about"><RaisedButton label="Learn More about Flow" /></Link>

<GoogleLogin
clientId={client_secrets.G_OAUTH_CLIENT_ID}
buttonText="Login"
Expand Down
17 changes: 16 additions & 1 deletion testing/testing_apis.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from base_test_case import BaseTestCase
from models import Goal
from flow import app as tst_app
from models import Habit, Task, Project, Event, Readable, Quote
from models import Habit, Task, Project, Event, Readable, Quote, Snapshot


class APITestCase(BaseTestCase):
Expand Down Expand Up @@ -190,3 +190,18 @@ def test_quote_calls(self):
quotes = response.get('quotes')
self.assertEqual(len(quotes), 1)

def test_snapshot_calls(self):
# Create
snap = Snapshot.Create(self.u, activity="Eating", where="Restaurant", people=["Elizabeth"],
metrics={'stress': 2})
snap.put()

self.assertEqual(snap.get_data_value('stress'), 2)
self.assertEqual(snap.activity, "Eating")

# List
response = self.get_json("/api/snapshot", {}, headers=self.api_headers)
snap = response.get('snapshots')[0]
print response
self.assertEqual(snap.get('activity'), "Eating")

11 changes: 11 additions & 0 deletions testing/testing_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,14 @@ def testFromISO(self):
target = v[1]
actual = tools.fromISODate(v[0])
self.assertEqual(actual, target)

def testMinutesIn(self):
volley = [
("00:01", 1),
("8:35", 515),
]

for v in volley:
time_str, expected_mins = v
time = tools.parseTimeString(time_str)
self.assertEqual(expected_mins, tools.minutes_in(time))
13 changes: 13 additions & 0 deletions tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,3 +502,16 @@ def sign_gcs_url(gcs_filename, expires_after_seconds=6):
resource=gcs_filename,
querystring=urllib.urlencode(query_params))
return str(result)


def parseTimeString(raw):
"""
Takes time string like "14:25"
"""
try:
iso_match = re.match(r'^(\d{1,2}:\d{2})', raw)
if iso_match:
dt = datetime.strptime(iso_match.group(0), "%H:%M")
return dt.time()
except Exception, e:
return None

0 comments on commit b0dd926

Please sign in to comment.