insulin 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +52 -0
- data/LICENSE +22 -0
- data/README.md +37 -0
- data/Rakefile +19 -0
- data/TODO.md +2 -0
- data/bin/insulin +19 -0
- data/conf/insulin.yaml +8 -0
- data/conf/insulin_dev.yaml +8 -0
- data/files/on_track.csv +18 -0
- data/insulin.gemspec +27 -0
- data/lib/insulin/config.rb +21 -0
- data/lib/insulin/event.rb +40 -0
- data/lib/insulin/mongo_handle.rb +23 -0
- data/lib/insulin/on_track_csv_file.rb +61 -0
- data/lib/insulin/on_track_csv_line.rb +39 -0
- data/lib/insulin/on_track_date.rb +25 -0
- data/lib/insulin/on_track_note.rb +46 -0
- data/lib/insulin/on_track_note_set.rb +45 -0
- data/lib/insulin/version.rb +5 -0
- data/lib/insulin.rb +17 -0
- data/spec/insulin_config_spec.rb +12 -0
- data/spec/insulin_event_spec.rb +48 -0
- data/spec/insulin_mongo_handle_spec.rb +33 -0
- data/spec/insulin_on_track_csv_file_spec.rb +60 -0
- data/spec/insulin_on_track_csv_line_spec.rb +67 -0
- data/spec/insulin_on_track_date_spec.rb +31 -0
- data/spec/insulin_on_track_note_set_spec.rb +47 -0
- data/spec/insulin_on_track_note_spec.rb +24 -0
- metadata +181 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
insulin (0.0.7)
|
5
|
+
bson_ext
|
6
|
+
mongo
|
7
|
+
thor
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
aruba (0.4.11)
|
13
|
+
childprocess (>= 0.2.3)
|
14
|
+
cucumber (>= 1.1.1)
|
15
|
+
ffi (>= 1.0.11)
|
16
|
+
rspec (>= 2.7.0)
|
17
|
+
bson (1.6.4)
|
18
|
+
bson_ext (1.6.4)
|
19
|
+
bson (~> 1.6.4)
|
20
|
+
builder (3.0.0)
|
21
|
+
childprocess (0.3.3)
|
22
|
+
ffi (~> 1.0.6)
|
23
|
+
cucumber (1.2.1)
|
24
|
+
builder (>= 2.1.2)
|
25
|
+
diff-lcs (>= 1.1.3)
|
26
|
+
gherkin (~> 2.11.0)
|
27
|
+
json (>= 1.4.6)
|
28
|
+
diff-lcs (1.1.3)
|
29
|
+
ffi (1.0.11)
|
30
|
+
gherkin (2.11.1)
|
31
|
+
json (>= 1.4.6)
|
32
|
+
json (1.7.3)
|
33
|
+
mongo (1.6.4)
|
34
|
+
bson (~> 1.6.4)
|
35
|
+
rspec (2.10.0)
|
36
|
+
rspec-core (~> 2.10.0)
|
37
|
+
rspec-expectations (~> 2.10.0)
|
38
|
+
rspec-mocks (~> 2.10.0)
|
39
|
+
rspec-core (2.10.1)
|
40
|
+
rspec-expectations (2.10.0)
|
41
|
+
diff-lcs (~> 1.1.3)
|
42
|
+
rspec-mocks (2.10.1)
|
43
|
+
thor (0.15.3)
|
44
|
+
|
45
|
+
PLATFORMS
|
46
|
+
ruby
|
47
|
+
|
48
|
+
DEPENDENCIES
|
49
|
+
aruba
|
50
|
+
cucumber
|
51
|
+
insulin!
|
52
|
+
rspec (~> 2.6)
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Sam
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
What's this?
|
2
|
+
============
|
3
|
+
|
4
|
+
Well I was recently diagnosed with Type-1 Diabetes. I've been collecting lots of data related to my condiiton using [OnTrack](https://play.google.com/store/apps/details?id=com.gexperts.ontrack), and wanted to do more interesting things with it. Hence this.
|
5
|
+
|
6
|
+
For now all it does is ingest a CSV file, parse it into some nice JSON, and stuff it into MongoDB.
|
7
|
+
|
8
|
+
Install
|
9
|
+
=======
|
10
|
+
|
11
|
+
gem install insulin
|
12
|
+
|
13
|
+
Also requires MongoDB. Instructions for installing Mongo on Ubuntu are [here](http://docs.mongodb.org/manual/tutorial/install-mongodb-on-debian-or-ubuntu-linux/).
|
14
|
+
|
15
|
+
Usage
|
16
|
+
=====
|
17
|
+
|
18
|
+
insulin ingest </path/to/on_track_export_file.csv>
|
19
|
+
|
20
|
+
This will take that file, turn it onto some nice JSON, and push into a number of collections in a MongoDB database called 'insulin'. You can see them with
|
21
|
+
|
22
|
+
$ mongo insulin
|
23
|
+
MongoDB shell version: 2.0.6
|
24
|
+
connecting to: insulin
|
25
|
+
> db.events.find({serial : 266})
|
26
|
+
{ "_id" : ObjectId("4ff07b371508cc259c8a8f0c"), "serial" : 266, "timestamp" : ISODate("2012-06-28T09:21:05Z"), "tzoffset" : "+0100", "timezone" : "BST", "unixtime" : 1340875265, "day" : "thursday", "date" : "2012-06-28", "time" : "10:21:05 BST", "type" : "medication", "subtype" : "humalog", "tag" : "after breakfast", "value" : 4, "notes" : { "food" : [ "2 bacon", "2 toast" ], "note" : [ "test note" ] } }
|
27
|
+
>
|
28
|
+
|
29
|
+
Next steps
|
30
|
+
==========
|
31
|
+
|
32
|
+
* Get it generating custom CSVs for Spreadsheeting
|
33
|
+
* Get it doing some analysis
|
34
|
+
* Give it a [meteor](http://meteor.com/) front-end (might require some help from [Chris](https://github.com/mrchrisadams)). Graphs, yo
|
35
|
+
* Possibly connect to [this API](http://platform.fatsecret.com/api/) to extract carb values from plain-text food descriptions (this may be a little ambitious, we'll see)
|
36
|
+
|
37
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
4
|
+
|
5
|
+
require "bundler/gem_tasks"
|
6
|
+
require "insulin/version"
|
7
|
+
|
8
|
+
task :build do
|
9
|
+
system "gem build insulin.gemspec"
|
10
|
+
# system "mv insulin*gem pkg/"
|
11
|
+
end
|
12
|
+
|
13
|
+
task :release => :build do
|
14
|
+
system "gem push pkg/insulin-#{Insulin::VERSION}.gem"
|
15
|
+
end
|
16
|
+
|
17
|
+
task :rspec do
|
18
|
+
system "bundle exec rspec"
|
19
|
+
end
|
data/TODO.md
ADDED
data/bin/insulin
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# needs to handle passing config. ~/.insulin or somrthing
|
3
|
+
require 'insulin'
|
4
|
+
require 'thor'
|
5
|
+
|
6
|
+
module Insulin
|
7
|
+
class I < Thor
|
8
|
+
map "-i" => :ingest
|
9
|
+
|
10
|
+
desc "ingest", "ingest OnTrack CSV export FILE"
|
11
|
+
def ingest file
|
12
|
+
csv = Insulin::OnTrackCsvFile.new file
|
13
|
+
mongo = Insulin::MongoHandle.new ({"database" => "insulin"})
|
14
|
+
csv.save_events mongo
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Insulin::I.start
|
data/conf/insulin.yaml
ADDED
data/files/on_track.csv
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
287,"Jun 30, 2012 1:55:02 PM",Glucose,,After Lunch,3.6,"N: new pot of strips"
|
2
|
+
286,"Jun 30, 2012 12:07:56 PM",Medication,Humalog,Lunch,6.0,"F: fry-up"
|
3
|
+
281,"Jun 29, 2012 8:46:58 PM",Medication,Humalog,Dinner,6.0,"F: Japanese takeaway"
|
4
|
+
278,"Jun 29, 2012 1:02:13 PM",Medication,Humalog,Lunch,6.0,"Fishfinger sandwich, salad"
|
5
|
+
277,"Jun 29, 2012 1:02:04 PM",Glucose,,Lunch,5.6,""
|
6
|
+
276,"Jun 29, 2012 10:00:54 AM",Weight,,Breakfast,58.0,""
|
7
|
+
275,"Jun 29, 2012 8:45:13 AM",Medication,Humalog,Breakfast,4.0,"F:2 eggs, 2 toast"
|
8
|
+
274,"Jun 29, 2012 8:37:47 AM",Glucose,,Breakfast,3.9,""
|
9
|
+
273,"Jun 28, 2012 10:52:14 PM",Medication,Lantus,Bedtime,14.0,""
|
10
|
+
272,"Jun 28, 2012 10:46:13 PM",Glucose,,Bedtime,5.7,""
|
11
|
+
271,"Jun 28, 2012 7:30:50 PM",Medication,Humalog,Dinner,8.0,"F: turkey stir-fry"
|
12
|
+
270,"Jun 28, 2012 7:06:35 PM",Glucose,,Dinner,5.7,""
|
13
|
+
268,"Jun 28, 2012 1:52:47 PM",Medication,Humalog,Lunch,4.0,"F: haloumi salad"
|
14
|
+
267,"Jun 28, 2012 1:35:50 PM",Glucose,,Lunch,4.3,""
|
15
|
+
266,"Jun 28, 2012 10:21:05 AM",Medication,Humalog,Breakfast,4.0,"F:2 bacon, 2 toast
|
16
|
+
N:test note"
|
17
|
+
265,"Jun 28, 2012 9:42:17 AM",Glucose,,Breakfast,6.1,""
|
18
|
+
262,"Jun 27, 2012 11:58:07 PM",Exercise,Drumming,,120.0,""
|
data/insulin.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/insulin/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Sam"]
|
6
|
+
gem.email = ["sam@cruft.co"]
|
7
|
+
gem.description = %q{Doing stuff with my diabetes data}
|
8
|
+
gem.summary = %q{Doing stuff with my diabetes data}
|
9
|
+
gem.homepage = "http://pikesley.github.com/insulin/"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "insulin"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Insulin::VERSION
|
17
|
+
|
18
|
+
gem.executables << 'insulin'
|
19
|
+
|
20
|
+
gem.add_dependency "thor"
|
21
|
+
gem.add_dependency "mongo"
|
22
|
+
gem.add_dependency "bson_ext"
|
23
|
+
|
24
|
+
gem.add_development_dependency "rspec", "~> 2.6"
|
25
|
+
gem.add_development_dependency "cucumber"
|
26
|
+
gem.add_development_dependency "aruba"
|
27
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Insulin
|
2
|
+
# Author:: Sam (mailto:sam@cruft.co)
|
3
|
+
# License:: MIT
|
4
|
+
|
5
|
+
# This class is a simple wrapper around a YAML config file
|
6
|
+
class Config < Hash
|
7
|
+
|
8
|
+
# Open the file at 'path'. If no path is provided, go to default
|
9
|
+
def initialize path = nil
|
10
|
+
if not path
|
11
|
+
path = "conf/insulin.yaml"
|
12
|
+
end
|
13
|
+
self.update YAML.load File.open path
|
14
|
+
end
|
15
|
+
|
16
|
+
# Return a particular section (as a hash)
|
17
|
+
def get_section section
|
18
|
+
return self[section]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Insulin
|
2
|
+
# Author:: Sam (mailto:sam@cruft.co)
|
3
|
+
# License:: MIT
|
4
|
+
|
5
|
+
# This class represents a single OnTrack event (BG, meds, etc)
|
6
|
+
class Event < Hash
|
7
|
+
|
8
|
+
# We expect to be passed a hash
|
9
|
+
def initialize h
|
10
|
+
self.update h
|
11
|
+
end
|
12
|
+
|
13
|
+
# Save the event to Mongo via mongo_handle
|
14
|
+
def save mongo_handle
|
15
|
+
|
16
|
+
# Save to each of these collections
|
17
|
+
clxns = [
|
18
|
+
"events",
|
19
|
+
self["type"],
|
20
|
+
self["subtype"],
|
21
|
+
self["date"]
|
22
|
+
]
|
23
|
+
|
24
|
+
clxns.each do |c|
|
25
|
+
if c
|
26
|
+
mongo_handle.db.collection(c).update(
|
27
|
+
{
|
28
|
+
"serial" => self["serial"]
|
29
|
+
},
|
30
|
+
self,
|
31
|
+
{
|
32
|
+
# Upsert: update if exists, otherwise insert
|
33
|
+
:upsert => true
|
34
|
+
}
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'mongo'
|
2
|
+
|
3
|
+
module Insulin
|
4
|
+
# Author:: Sam (mailto:sam@cruft.co)
|
5
|
+
# License:: MIT
|
6
|
+
|
7
|
+
# This class is a simple wrapper around a MongoDB connection
|
8
|
+
class MongoHandle
|
9
|
+
attr_reader :db, :connection
|
10
|
+
|
11
|
+
# Set up the connection as described by 'conf'
|
12
|
+
def initialize conf
|
13
|
+
@conf = conf
|
14
|
+
@connection = Mongo::Connection.new
|
15
|
+
@db = @connection.db @conf["database"]
|
16
|
+
end
|
17
|
+
|
18
|
+
# Drop this database
|
19
|
+
def drop_db
|
20
|
+
@connection.drop_database @conf["database"]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Insulin
|
2
|
+
# Author:: Sam (mailto:sam@cruft.co)
|
3
|
+
# License:: MIT
|
4
|
+
|
5
|
+
# This class represents a CSV file as exported by OnTrack
|
6
|
+
class OnTrackCsvFile
|
7
|
+
attr_reader :file, :lines, :events
|
8
|
+
|
9
|
+
# Take the path to the CSV, open it, process it
|
10
|
+
def initialize csv_path
|
11
|
+
@csv_path = csv_path
|
12
|
+
@file = File.new @csv_path
|
13
|
+
self.read_lines
|
14
|
+
self.create_events
|
15
|
+
end
|
16
|
+
|
17
|
+
# Read the lines
|
18
|
+
def read_lines
|
19
|
+
@lines = []
|
20
|
+
|
21
|
+
# Where an event has more than one note, a 'line' will contain '\n's
|
22
|
+
l = ""
|
23
|
+
while line = @file.gets
|
24
|
+
# Stuff the line in to 'l'
|
25
|
+
l << line
|
26
|
+
|
27
|
+
# A '"' at the end of the line closes the event
|
28
|
+
if line[-2] == '"'
|
29
|
+
|
30
|
+
# Create a CsvLine, stripping the final '\n'
|
31
|
+
o = Insulin::OnTrackCsvLine.new l[0..-2]
|
32
|
+
@lines << o
|
33
|
+
|
34
|
+
# Reset the placeholder
|
35
|
+
l = ""
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Sort the list by the serial numbers (required for a future feature)
|
40
|
+
@lines.sort_by! {|o| o["serial"]}
|
41
|
+
end
|
42
|
+
|
43
|
+
# Turn the lines into Events
|
44
|
+
def create_events
|
45
|
+
@events = []
|
46
|
+
@lines.each do |l|
|
47
|
+
e = Insulin::Event.new l
|
48
|
+
@events << e
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Save the events to Mongo
|
53
|
+
def save_events mongo
|
54
|
+
print "saving %d events to mongo... " % @events.count
|
55
|
+
@events.each do |e|
|
56
|
+
e.save mongo
|
57
|
+
end
|
58
|
+
puts "done"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Insulin
|
2
|
+
# Author:: Sam (mailto:sam@cruft.co)
|
3
|
+
# License:: MIT
|
4
|
+
|
5
|
+
# This class represents a single OnTrack CSV line
|
6
|
+
class OnTrackCsvLine < Hash
|
7
|
+
|
8
|
+
# Parse the passed-in CSV line
|
9
|
+
def initialize line
|
10
|
+
|
11
|
+
# Split on commas
|
12
|
+
bits = line.split ","
|
13
|
+
self["serial"] = bits[0].to_i
|
14
|
+
|
15
|
+
# OnTrack embeds commas in the dates of its CSVs. Duh
|
16
|
+
self.update OnTrackDate.new "%s%s" % [
|
17
|
+
bits[1],
|
18
|
+
bits[2]
|
19
|
+
]
|
20
|
+
self["type"] = bits[3].downcase
|
21
|
+
self["subtype"] = bits[4].downcase if not bits[4] == ""
|
22
|
+
self["tag"] = bits[5].downcase
|
23
|
+
self["value"] = bits[6].to_f
|
24
|
+
|
25
|
+
# Notes get complicated. Everything from field 7 to the end will be part
|
26
|
+
# of the notes
|
27
|
+
notes = bits[7..-1]
|
28
|
+
|
29
|
+
# We may have embedded commas
|
30
|
+
notes = notes.join ","
|
31
|
+
|
32
|
+
# Strip the trailing '\n'
|
33
|
+
notes = notes[1..-2]
|
34
|
+
|
35
|
+
# See Insulin::OnTrackNoteSet
|
36
|
+
self["notes"] = OnTrackNoteSet.new notes if not notes == ""
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "time"
|
2
|
+
|
3
|
+
module Insulin
|
4
|
+
# Author:: Sam (mailto:sam@cruft.co)
|
5
|
+
# License:: MIT
|
6
|
+
|
7
|
+
# OnTrack uses really shitty date formats, including embedding a comma in the
|
8
|
+
# CSV export file. Christ on a crutch
|
9
|
+
class OnTrackDate < Hash
|
10
|
+
|
11
|
+
# Parse the strind 'd', looking for datetime information
|
12
|
+
def initialize d
|
13
|
+
t = Time.parse d
|
14
|
+
|
15
|
+
# We extract loads of stuff. Might be useful one day
|
16
|
+
self["timestamp"] = t
|
17
|
+
self["tzoffset"] = t.strftime "%z"
|
18
|
+
self["timezone"] = t.zone
|
19
|
+
self["unixtime"] = t.to_i
|
20
|
+
self["day"] = t.strftime("%A").downcase
|
21
|
+
self["date"] = t.strftime "%F"
|
22
|
+
self["time"] = t.strftime "%T #{self['timezone']}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Insulin
|
2
|
+
# Author:: Sam (mailto:sam@cruft.co)
|
3
|
+
# License:: MIT
|
4
|
+
|
5
|
+
# Class representing a single OnTrack note
|
6
|
+
class OnTrackNote
|
7
|
+
|
8
|
+
# Lookups. We will only deal with notes of these types
|
9
|
+
@@keys = {
|
10
|
+
"F" => 'food',
|
11
|
+
"B" => 'booze',
|
12
|
+
"N" => 'note'
|
13
|
+
}
|
14
|
+
|
15
|
+
attr_reader :type, :content
|
16
|
+
|
17
|
+
# Parse the raw note 'n'
|
18
|
+
def initialize n
|
19
|
+
|
20
|
+
# Key/value splits on ':'
|
21
|
+
bits = n.split ":"
|
22
|
+
|
23
|
+
# Remove leading/trailing cruft from key part
|
24
|
+
t = bits[0].strip
|
25
|
+
|
26
|
+
# We only deal with the keys we know about
|
27
|
+
if @@keys.keys.include? t
|
28
|
+
|
29
|
+
# Remove cruft from content, downcase
|
30
|
+
@content = bits[1].strip.downcase
|
31
|
+
|
32
|
+
# These keys mean we turn the content into a list
|
33
|
+
if ["F", "B"].include? t
|
34
|
+
a = []
|
35
|
+
@content.split(",").each do |v|
|
36
|
+
a << v.strip
|
37
|
+
end
|
38
|
+
@content = a
|
39
|
+
end
|
40
|
+
|
41
|
+
# Set key from lookup list
|
42
|
+
@type = @@keys[t]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Insulin
|
2
|
+
# Author:: Sam (mailto:sam@cruft.co)
|
3
|
+
# License:: MIT
|
4
|
+
|
5
|
+
# This class represents a set of notes
|
6
|
+
class OnTrackNoteSet < Hash
|
7
|
+
|
8
|
+
# Parse the string 's'
|
9
|
+
def initialize s
|
10
|
+
|
11
|
+
# Notes separated by newlines
|
12
|
+
l = s.split"\n"
|
13
|
+
|
14
|
+
# For each line
|
15
|
+
l.each do |n|
|
16
|
+
|
17
|
+
# Make a note
|
18
|
+
x = OnTrackNote.new n
|
19
|
+
|
20
|
+
# This field will only exists for notes of a valid type
|
21
|
+
if x.type
|
22
|
+
|
23
|
+
# If we don't yet have this key
|
24
|
+
if not self[x.type]
|
25
|
+
|
26
|
+
# If the content is a list
|
27
|
+
if x.content.class.name == "Array"
|
28
|
+
|
29
|
+
# This becomes our value
|
30
|
+
self[x.type] = x.content
|
31
|
+
else
|
32
|
+
|
33
|
+
# Otherwise make it onto a list
|
34
|
+
self[x.type] = [x.content]
|
35
|
+
end
|
36
|
+
else
|
37
|
+
|
38
|
+
# This key exists, so we append this value
|
39
|
+
self[x.type] << x.content
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/insulin.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "insulin/version"
|
2
|
+
require "insulin/config"
|
3
|
+
require "insulin/mongo_handle"
|
4
|
+
require "insulin/on_track_date"
|
5
|
+
require "insulin/on_track_note"
|
6
|
+
require "insulin/on_track_note_set"
|
7
|
+
require "insulin/on_track_csv_line"
|
8
|
+
require "insulin/on_track_csv_file"
|
9
|
+
require "insulin/event"
|
10
|
+
# Author:: Sam (mailto:sam@cruft.co)
|
11
|
+
# License:: MIT
|
12
|
+
|
13
|
+
# OnTrack[https://play.google.com/store/apps/details?id=com.gexperts.ontrack&hl=en]
|
14
|
+
# can export its data as a CSV. Insulin takes this data, parses it into
|
15
|
+
# (possibly) useful formats, and stuffs it into MongoDB
|
16
|
+
module Insulin
|
17
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'insulin'
|
2
|
+
|
3
|
+
describe Insulin::Event do
|
4
|
+
event = Insulin::Event.new Insulin::OnTrackCsvLine.new %q{266,"Jun 28, 2012 10:21:05 AM",Medication,Humalog,After Breakfast,4.0,"F:2 bacon, 2 toast
|
5
|
+
N:test note
|
6
|
+
X:fail note
|
7
|
+
N:other note"}
|
8
|
+
|
9
|
+
it "should have the correct type" do
|
10
|
+
event["type"].should == "medication"
|
11
|
+
end
|
12
|
+
|
13
|
+
conf = Insulin::Config.new 'conf/insulin_dev.yaml'
|
14
|
+
mconf = conf.get_section "mongo"
|
15
|
+
mongo = Insulin::MongoHandle.new mconf
|
16
|
+
|
17
|
+
event.save mongo
|
18
|
+
clxns = [
|
19
|
+
"events",
|
20
|
+
event["type"],
|
21
|
+
event["subtype"],
|
22
|
+
event["date"]
|
23
|
+
]
|
24
|
+
|
25
|
+
clxns.each do |c|
|
26
|
+
item = mongo.db.collection(c).find_one(
|
27
|
+
{"serial" => 266}
|
28
|
+
)
|
29
|
+
|
30
|
+
it "should save to the '%s' collection" % c do
|
31
|
+
item.should_not == nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "saved event should have correct type" do
|
35
|
+
item["type"].should == "medication"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "saved event should have correct tag" do
|
39
|
+
item["tag"].should == "after breakfast"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "saved event should have correct value" do
|
43
|
+
item["value"].should == 4.0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
mongo.drop_db
|
48
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'insulin'
|
2
|
+
|
3
|
+
describe Insulin::MongoHandle do
|
4
|
+
conf = Insulin::Config.new 'conf/insulin_dev.yaml'
|
5
|
+
mconf = conf.get_section "mongo"
|
6
|
+
|
7
|
+
handle = Insulin::MongoHandle.new mconf
|
8
|
+
|
9
|
+
it "should make a connection to mongo" do
|
10
|
+
handle.connection.database_names.size.should >= 1
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should have a database" do
|
14
|
+
handle.db.name.should == mconf["database"]
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should drop the database" do
|
18
|
+
handle.drop_db
|
19
|
+
handle.connection.database_names.include?(mconf["database"]).should_not ==
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
it "should respond correctly to being passed a hash" do
|
25
|
+
prod_handle = Insulin::MongoHandle.new(
|
26
|
+
{
|
27
|
+
"database" => "prawn"
|
28
|
+
}
|
29
|
+
)
|
30
|
+
prod_handle.db.name.should == "prawn"
|
31
|
+
prod_handle.drop_db
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'insulin'
|
2
|
+
|
3
|
+
describe Insulin::OnTrackCsvFile do
|
4
|
+
csv_file = Insulin::OnTrackCsvFile.new 'files/on_track.csv'
|
5
|
+
|
6
|
+
it "should open the file" do
|
7
|
+
csv_file.file.path.should == 'files/on_track.csv'
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should create csv lines" do
|
11
|
+
csv_file.lines[0].class.name.should == "Insulin::OnTrackCsvLine"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "line with serial 266 should have proper noteset" do
|
15
|
+
csv_file.lines.each do |c|
|
16
|
+
if c["serial"] == 266
|
17
|
+
c["notes"].should == {
|
18
|
+
"food"=>[
|
19
|
+
"2 bacon",
|
20
|
+
"2 toast"
|
21
|
+
],
|
22
|
+
"note"=>[
|
23
|
+
"test note"
|
24
|
+
]
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it "lines should be sorted by serial" do
|
31
|
+
error = false
|
32
|
+
last_serial = 0
|
33
|
+
csv_file.lines.each do |l|
|
34
|
+
if not l["serial"] > last_serial
|
35
|
+
error = true
|
36
|
+
end
|
37
|
+
last_serial = l["serial"]
|
38
|
+
end
|
39
|
+
error.should_not == true
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should create events" do
|
43
|
+
csv_file.events[0].class.name.should == "Insulin::Event"
|
44
|
+
end
|
45
|
+
|
46
|
+
before :all do
|
47
|
+
config = Insulin::Config.new 'conf/insulin_dev.yaml'
|
48
|
+
mconf = config.get_section "mongo"
|
49
|
+
@mongo = Insulin::MongoHandle.new mconf
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should save events" do
|
53
|
+
csv_file.save_events @mongo
|
54
|
+
@mongo.db.collection("events").count.should >= 17
|
55
|
+
end
|
56
|
+
|
57
|
+
after :all do
|
58
|
+
@mongo.drop_db
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'insulin'
|
2
|
+
|
3
|
+
describe Insulin::OnTrackCsvLine do
|
4
|
+
csv = Insulin::OnTrackCsvLine.new %q{199,"Jun 22, 2012 8:01:39 AM",Glucose,,Breakfast,5.7,""}
|
5
|
+
|
6
|
+
it "should have the correct serial" do
|
7
|
+
csv["serial"].should == 199
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have the correct time" do
|
11
|
+
csv["time"].should == "08:01:39 BST"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should have the correct date" do
|
15
|
+
csv["date"].should == "2012-06-22"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should have the correct type" do
|
19
|
+
csv["type"].should == "glucose"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should have a null subtype" do
|
23
|
+
csv["subtype"].should == nil
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should have the correct tag" do
|
27
|
+
csv["tag"].should == "breakfast"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should have the correct value" do
|
31
|
+
csv["value"].should == 5.7
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should have the correct notes" do
|
35
|
+
csv["notes"].should == nil
|
36
|
+
end
|
37
|
+
|
38
|
+
csv_with_note = Insulin::OnTrackCsvLine.new %q{266,"Jun 28, 2012 10:21:05 AM",Medication,Humalog,After Breakfast,4.0,"F:2 bacon, 2 toast
|
39
|
+
N:test note
|
40
|
+
X:fail note
|
41
|
+
N:other note"}
|
42
|
+
|
43
|
+
it "should have the correct subtype" do
|
44
|
+
csv_with_note["subtype"].should == "humalog"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should have the correct day" do
|
48
|
+
csv_with_note["day"].should == "thursday"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should have the correct unixtime" do
|
52
|
+
csv_with_note["unixtime"].should == 1340875265
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should have the correct notes" do
|
56
|
+
csv_with_note["notes"].should == {
|
57
|
+
"food" => [
|
58
|
+
"2 bacon",
|
59
|
+
"2 toast"
|
60
|
+
],
|
61
|
+
"note" => [
|
62
|
+
"test note",
|
63
|
+
"other note"
|
64
|
+
]
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'insulin'
|
2
|
+
|
3
|
+
describe Insulin::OnTrackDate do
|
4
|
+
otd = Insulin::OnTrackDate.new "Jun 22 2012 9:00:12 AM"
|
5
|
+
|
6
|
+
it "should have the correct timestamp" do
|
7
|
+
otd["timestamp"].to_s.should == "2012-06-22 09:00:12 +0100"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have the correct time" do
|
11
|
+
otd["time"].should == "09:00:12 BST"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should have the correct date" do
|
15
|
+
otd["date"].should == "2012-06-22"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should have the correct day" do
|
19
|
+
otd["day"].should == "friday"
|
20
|
+
end
|
21
|
+
|
22
|
+
otd_gmt = Insulin::OnTrackDate.new "Jan 22 2012 9:00:12 PM"
|
23
|
+
|
24
|
+
it "should have the correct timestamp" do
|
25
|
+
otd_gmt["timestamp"].to_s.should == "2012-01-22 21:00:12 +0000"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should have the correct time and TZ" do
|
29
|
+
otd_gmt["time"].should == "21:00:12 GMT"
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'insulin'
|
2
|
+
|
3
|
+
describe Insulin::OnTrackNoteSet do
|
4
|
+
simple_note_set = Insulin::OnTrackNoteSet.new "F: turkey breast, salad"
|
5
|
+
|
6
|
+
it "should have the correct notes" do
|
7
|
+
simple_note_set.should == {
|
8
|
+
"food" => [
|
9
|
+
"turkey breast",
|
10
|
+
"salad"
|
11
|
+
]
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
multiple_note_set = Insulin::OnTrackNoteSet.new "F: turkey breast, salad
|
16
|
+
N:After wine. No overnight hypo.
|
17
|
+
N:some other note
|
18
|
+
B: glass of wine"
|
19
|
+
|
20
|
+
it "should have the correct notes" do
|
21
|
+
multiple_note_set.should == {
|
22
|
+
"food" => [
|
23
|
+
"turkey breast",
|
24
|
+
"salad"
|
25
|
+
],
|
26
|
+
"note" => [
|
27
|
+
"after wine. no overnight hypo.",
|
28
|
+
"some other note"
|
29
|
+
],
|
30
|
+
"booze" => [
|
31
|
+
"glass of wine"
|
32
|
+
]
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
failing_note_set = Insulin::OnTrackNoteSet.new "F: turkey breast, salad
|
37
|
+
X:This should fail"
|
38
|
+
|
39
|
+
it "should have the correct notes" do
|
40
|
+
failing_note_set.should == {
|
41
|
+
"food" => [
|
42
|
+
"turkey breast",
|
43
|
+
"salad"
|
44
|
+
]
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'insulin'
|
2
|
+
|
3
|
+
describe Insulin::OnTrackNote do
|
4
|
+
note = Insulin::OnTrackNote.new "N:After wine. No overnight hypo."
|
5
|
+
|
6
|
+
it "should have the correct note" do
|
7
|
+
note.type.should == "note"
|
8
|
+
note.content.should == "after wine. no overnight hypo."
|
9
|
+
end
|
10
|
+
|
11
|
+
list_note = Insulin::OnTrackNote.new "F: turkey breast, salad"
|
12
|
+
|
13
|
+
it "should have the correct list-type note" do
|
14
|
+
list_note.type.should == "food"
|
15
|
+
list_note.content.should == ["turkey breast", "salad"]
|
16
|
+
end
|
17
|
+
|
18
|
+
fail_note = Insulin::OnTrackNote.new "X: this one should fail"
|
19
|
+
|
20
|
+
it "should handle the fail note correctly" do
|
21
|
+
fail_note.type.should == nil
|
22
|
+
fail_note.content.should == nil
|
23
|
+
end
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: insulin
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.9
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sam
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: thor
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: mongo
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bson_ext
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '2.6'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '2.6'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: cucumber
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: aruba
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
description: Doing stuff with my diabetes data
|
111
|
+
email:
|
112
|
+
- sam@cruft.co
|
113
|
+
executables:
|
114
|
+
- insulin
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- .gitignore
|
119
|
+
- .rspec
|
120
|
+
- Gemfile
|
121
|
+
- Gemfile.lock
|
122
|
+
- LICENSE
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- TODO.md
|
126
|
+
- bin/insulin
|
127
|
+
- conf/insulin.yaml
|
128
|
+
- conf/insulin_dev.yaml
|
129
|
+
- files/on_track.csv
|
130
|
+
- insulin.gemspec
|
131
|
+
- lib/insulin.rb
|
132
|
+
- lib/insulin/config.rb
|
133
|
+
- lib/insulin/event.rb
|
134
|
+
- lib/insulin/mongo_handle.rb
|
135
|
+
- lib/insulin/on_track_csv_file.rb
|
136
|
+
- lib/insulin/on_track_csv_line.rb
|
137
|
+
- lib/insulin/on_track_date.rb
|
138
|
+
- lib/insulin/on_track_note.rb
|
139
|
+
- lib/insulin/on_track_note_set.rb
|
140
|
+
- lib/insulin/version.rb
|
141
|
+
- spec/insulin_config_spec.rb
|
142
|
+
- spec/insulin_event_spec.rb
|
143
|
+
- spec/insulin_mongo_handle_spec.rb
|
144
|
+
- spec/insulin_on_track_csv_file_spec.rb
|
145
|
+
- spec/insulin_on_track_csv_line_spec.rb
|
146
|
+
- spec/insulin_on_track_date_spec.rb
|
147
|
+
- spec/insulin_on_track_note_set_spec.rb
|
148
|
+
- spec/insulin_on_track_note_spec.rb
|
149
|
+
homepage: http://pikesley.github.com/insulin/
|
150
|
+
licenses: []
|
151
|
+
post_install_message:
|
152
|
+
rdoc_options: []
|
153
|
+
require_paths:
|
154
|
+
- lib
|
155
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
156
|
+
none: false
|
157
|
+
requirements:
|
158
|
+
- - ! '>='
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
|
+
none: false
|
163
|
+
requirements:
|
164
|
+
- - ! '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
requirements: []
|
168
|
+
rubyforge_project:
|
169
|
+
rubygems_version: 1.8.24
|
170
|
+
signing_key:
|
171
|
+
specification_version: 3
|
172
|
+
summary: Doing stuff with my diabetes data
|
173
|
+
test_files:
|
174
|
+
- spec/insulin_config_spec.rb
|
175
|
+
- spec/insulin_event_spec.rb
|
176
|
+
- spec/insulin_mongo_handle_spec.rb
|
177
|
+
- spec/insulin_on_track_csv_file_spec.rb
|
178
|
+
- spec/insulin_on_track_csv_line_spec.rb
|
179
|
+
- spec/insulin_on_track_date_spec.rb
|
180
|
+
- spec/insulin_on_track_note_set_spec.rb
|
181
|
+
- spec/insulin_on_track_note_spec.rb
|