A Drinks Tracker: Really?
This is a CLI app which allows the daily tracking of alcoholic beverages, not for the purpose of bragging, but of moderating or quitting. It also tracks mood and weight. This is yet another work in progress, with the following ideas:
- starting with a plain text parsing CLI,
- potentially making a basic web app with Sequel and Roda,
- eventually turning this into an API, which could be used by various apps.
This post will only cover the beginning of part 1.
To make it as simple as can be, one could use this by taking a note in plain text in the following format:
DATE $NUMBER_OF_DRINKS $MOOD(0 to 10) $WEIGHT(optional).
It would look something like:
31/12 0 7
30/12 5 3 93
29/12 8 5
28/12 6 6
27/12 0 9 90
And one could update it each day on a phone’s notes app.
To begin with, we will need to parse these lines.
class DrinksTracker
def initialize(data_file)
file = File.open(data_file)
file_data = file.readlines.map(&:chomp)
# Get data into arrays of strings
days = (0...file_data.length).each_with_object([]) do |day, days|
days.push(file_data[day].split)
end
We don’t necessarily weigh ourselves every day, so if no weight is provided, we shall use the value from the previous day. Since no value could be provided for multiple days, we start back at the first day when parsing the whole file, with .downto below.
# If no weight provided, same as yesterday
(days.length - 1).downto(0) do |day|
days[day][3] = days[day + 1][3] unless days[day][3]
end
# Convert relevant numbers to float for calculation
self.days = days.each do |day|
(1..3).each do |idx|
day[idx] = day[idx].to_f
end
end
file.close
end
And we convert to float so that we can make averages without worrying about number format.
For what comes next, I expect that I will improve it dramatically later. It looks to me like there is the potential for a much better formulation, but I’m not there yet. So I will show what I have now:
- Public methods for averaging the past and previous x days
# Average drinks per day, average mood, average weight
def past_x_days(x)
descriptor,
duration,
average_drinks,
average_mood,
average_weight = calculate(1, x)
REPORT % {descriptor:, duration:, average_drinks:, average_mood:, average_weight:}
end
def previous_x_days(x)
descriptor,
duration,
average_drinks,
average_mood,
average_weight = calculate(1 + x, 2 * x)
REPORT % {descriptor:, duration:, average_drinks:, average_mood:, average_weight:}
end
- A private method to calculate the values
def calculate(first_day, last_day)
drinks = 0
mood = 0
weight = 0
((first_day - 1)..(last_day - 1)).each do |day|
drinks += days[day][1]
mood += days[day][2]
weight += days[day][3]
end
descriptor = first_day == 1 ? "past" : "previous"
number_of_days = last_day - first_day + 1
average_drinks = drinks / number_of_days
average_mood = mood / number_of_days
average_weight = weight / number_of_days
[descriptor, number_of_days, average_drinks, average_mood, average_weight]
end
The values then get formatted into a “Report” string:
REPORT = "In the %<descriptor>s %<duration>d days, your drinks per day average was %<average_drinks>d, your average mood was %<average_mood>d out of 10, and your weight averaged %<average_weight>d kilos."
And getting some data is as easy as:
if __FILE__ == $PROGRAM_NAME
FILE = './drinks.txt'
record = DrinksTracker.new(FILE)
p record.days
p record.past_x_days(7)
p record.previous_x_days(7)
p record.past_x_days(17)
p record.previous_x_days(17)
p record.past_x_days(30)
p record.previous_x_days(30)
end
Returning (skipping the example data):
"In the past 7 days, your drinks per day average was 3, your average mood was 5 out of 10, and your weight averaged 90 kilos."
"In the previous 7 days, your drinks per day average was 0, your average mood was 9 out of 10, and your weight averaged 88 kilos."
"In the past 17 days, your drinks per day average was 2, your average mood was 7 out of 10, and your weight averaged 89 kilos."
"In the previous 17 days, your drinks per day average was 2, your average mood was 7 out of 10, and your weight averaged 89 kilos."
"In the past 30 days, your drinks per day average was 1, your average mood was 7 out of 10, and your weight averaged 89 kilos."
"In the previous 30 days, your drinks per day average was 2, your average mood was 7 out of 10, and your weight averaged 89 kilos."
Next steps
The above can be considered basically interesting, or interestingly basic. What next?
- At the very least, we should be able to get direct comparisons (“In the past 7 days, you’ve had 56 more drinks than in the previous 7 days, and your mood improved by 0, while your weight increased by 2.”).
- I know a former mentor who would really like it if the user could choose to use pounds or kilos. Also defining the units of drinking somewhere could be useful. (For this example, I’ve applied the French driving test notion that 25cl beer, 10cl wine, or 4cl liquor each equals 1 drink. But we could also ask the user how many beers, how many shots of liquor, etc. Granted, a full report on a drinking night may be difficult to program in.)
- I’m actually hoping to get suggestions from current or future mentors about where to take this. So if you’ve read this far and have thoughts, please feel free to let me know at self@anantone.me