Skip to content

Instantly share code, notes, and snippets.

/weight.rb Secret

Created July 24, 2015 20:41
Show Gist options
  • Save anonymous/57af48ac306dcdf55dba to your computer and use it in GitHub Desktop.
Save anonymous/57af48ac306dcdf55dba to your computer and use it in GitHub Desktop.
#!/usr/bin/ruby
require 'sequel'
require 'bigdecimal'
require 'chronic'
Sequel.default_timezone = :utc
def usage(extra=nil)
puts extra.to_s + "\n" if extra
puts <<-EOF
usage: #{$0} <command> [args...]
#{$0} record <weight_spec> [weigh_time]
#{$0} output [start_time] [end_time]
#{$0} output-facts [start_time] [end_time]
#{$0} minmax [start_time] [end_time]
EOF
exit 1
end
def setup_db!(connstring="")
db = Sequel.connect(:adapter => "postgres", :database => "lupine", :encoding => "utf8", :charset => "utf8")
[db, db[:weight]]
end
def record_weight(weight_spec, weighed_at=Time.now)
db, tbl = setup_db!
weight = interpret_weight_spec(weight_spec)
weighed_at = Chronic::parse(weighed_at) if weighed_at.is_a?(String)
STDOUT.print "#{format_g(weight)} at #{weighed_at.strftime("%F %T")} [N/y] ? "
STDOUT.flush
rsp = STDIN.readline
STDOUT.print "\n"
exit 2 unless %w|y Y yes|.include?(rsp.chomp)
tbl << {:weight_g => weight, :weighed_at => weighed_at }
puts "Recorded"
end
def output_weight(start_time=nil, end_time=nil, style="simple")
db, tbl = setup_db!
start_time = Chronic.parse(start_time) if start_time.is_a?(String)
end_time = Chronic.parse(end_time) if end_time.is_a?(String)
set = tbl.order(:weighed_at)
set.filter(:weighed_at > start_time) if start_time
set.filter(:weighed_at < end_time) if end_time
result = set.to_a
puts "Time,Weight(g)"
case style
when "simple"
result.each do |rec|
puts "#{rec[:weighed_at].strftime("%F %T")},#{rec[:weight_g]}"
end
when "json"
puts "["
result.each do |rec|
puts %Q| {"fact":"number", "name":"weight", "number":#{rec[:weight_g]}, "unit":"g", "recorded":"#{rec[:weighed_at].strftime("%F %T")}" },|
end
puts "]"
else
raise "Unknown style: " + style
end
end
def minmax_weight(start_time=nil, end_time=nil)
db, tbl = setup_db!
start_time = Chronic.parse(start_time) if start_time.is_a?(String)
end_time = Chronic.parse(end_time) if end_time.is_a?(String)
set = tbl.order(:weight_g)
set.filter(:weighed_at > start_time) if start_time
set.filter(:weighed_at < end_time) if end_time
min = set.first
max = set.last
puts "min: #{format_g(min[:weight_g])} at #{min[:weighed_at].strftime("%F %T")}"
puts "max: #{format_g(max[:weight_g])} at #{max[:weighed_at].strftime("%F %T")}"
days = if min[:weighed_at] >= max[:weighed_at]
( min[:weighed_at] - max[:weighed_at] ).to_i / 86400
else
( min[:weighed_at] - max[:weighed_at] ).to_i / 86400
end
diff = max[:weight_g] - min[:weight_g]
puts "diff: #{format_g( diff )} over #{days} days - or #{ format_g( diff / days )} per day"
end
# Takes a string specifying a weight and attempts to parse it into a number of grams.
# Valid examples: "10st 4lb", "185.45kg", etc.
NUM_MATCHER = "([0-9]*\.[0-9]+|[0-9]+)"
def interpret_weight_spec(spec)
total = spec.split(" ").collect {|part|
amount, multiplier = case part
when /#{NUM_MATCHER}kg\Z/mi then [ $1, "1000.0" ]
when /#{NUM_MATCHER}g\Z/mi then [ $1, "1.0" ]
when /#{NUM_MATCHER}st\Z/mi then [ $1, "6350.29318" ]
when /#{NUM_MATCHER}lb\Z/mi then [ $1, "453.59237" ]
else
raise ArgumentError.new("Couldn't work out units for #{part.inspect} in #{spec.inspect}")
end
amount, multiplier = [ BigDecimal(amount), BigDecimal(multiplier) ]
(amount * multiplier).to_i
}.inject(0) {|tot,x| tot + x }
end
def format_g(grams)
grams.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1,") + "g"
end
if __FILE__ == $0
COMMAND = ARGV.shift
case COMMAND
when "record" then record_weight(*ARGV)
when "output" then output_weight(*ARGV)
when "minmax" then minmax_weight(*ARGV)
else
usage("Unknown command: #{COMMAND}")
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment