insulin 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|