gopromotional-mugday 1.0.0
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +76 -0
- data/exe/gopromotional-mugday +5 -0
- data/lib/gopromotional/mugday/cli.rb +104 -0
- data/lib/gopromotional/mugday/version.rb +7 -0
- data/lib/gopromotional/mugday.rb +140 -0
- data/lib/gopromotional-mugday.rb +1 -0
- metadata +55 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 69228c57c9dde5eba70528abf2843f0ce85c359cea5ecc5a0206bb25bd74dcc4
|
|
4
|
+
data.tar.gz: 3a7e4e8ced9a1c97727d1f74cd745c74ef5ccca1ad500899c23cb58606b3b88f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 5a3a844ac2e27fceda0e1eacba245668f2e2358e79d1609dbe9a0e6db226116b62a9633172529e76cf9cd7ea22a075eece2d32aa70ddc29577a25a0fdf142b18
|
|
7
|
+
data.tar.gz: 2daf36484f85c470f95ccf8848da637ec95cfd5347110fcd16966eccde6c18d6a4960d330911171de2f9708c7755f37f9ef751dd15fd8c4a371257dd0338255f
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GoPromotional
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# gopromotional-mugday
|
|
2
|
+
|
|
3
|
+
Log coffee, tea and other drinks with typical caffeine values, compare the day's total with a limit (400 mg adult, 200 mg in pregnancy), estimate how much caffeine is still in your system, find out when it should drop below a sleep-friendly level, and track how often you used a reusable cup. Typical values only; not medical advice.
|
|
4
|
+
|
|
5
|
+
- **16 drinks** with typical caffeine (espresso 63 mg, filter coffee 140 mg, tea 75 mg, cola 40 mg...), or pass your own mg.
|
|
6
|
+
- **Daily total** against a limit: 400 mg (adult) or 200 mg (pregnancy), or your own.
|
|
7
|
+
- **Caffeine in your system** using a 5-hour half-life (configurable).
|
|
8
|
+
- **Sleep-ready time**: when the level should fall below a threshold (default 50 mg), rounded up to 15 minutes.
|
|
9
|
+
- **Reusable-cup stats** for the last 7 days.
|
|
10
|
+
- Plain **JSON log** shared by the npm, PyPI, crates.io and RubyGems versions.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
gem install gopromotional-mugday
|
|
16
|
+
# or in a Gemfile:
|
|
17
|
+
gem "gopromotional-mugday"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Command line
|
|
21
|
+
|
|
22
|
+
```console
|
|
23
|
+
$ gopromotional-mugday drinks
|
|
24
|
+
espresso 63 mg (30 ml)
|
|
25
|
+
double-espresso 126 mg (60 ml)
|
|
26
|
+
americano 126 mg (350 ml)
|
|
27
|
+
latte 126 mg (350 ml)
|
|
28
|
+
cappuccino 126 mg (350 ml)
|
|
29
|
+
...
|
|
30
|
+
|
|
31
|
+
$ gopromotional-mugday add flat-white --time 2026-10-19T08:00 --reusable
|
|
32
|
+
Logged flat-white (126 mg, reusable cup). Today: 126/400 mg.
|
|
33
|
+
|
|
34
|
+
$ gopromotional-mugday add tea --time 2026-10-19T11:00 --reusable
|
|
35
|
+
Logged tea (75 mg, reusable cup). Today: 201/400 mg.
|
|
36
|
+
|
|
37
|
+
$ gopromotional-mugday add filter-coffee --time 2026-10-19T14:00
|
|
38
|
+
Logged filter-coffee (140 mg). Today: 341/400 mg.
|
|
39
|
+
|
|
40
|
+
$ gopromotional-mugday today --date 2026-10-19
|
|
41
|
+
2026-10-19: 341 mg from 3 drink(s); 59 mg left of 400 mg.
|
|
42
|
+
|
|
43
|
+
$ gopromotional-mugday in-system --at 2026-10-19T22:00
|
|
44
|
+
About 81 mg of caffeine still in your system at 2026-10-19T22:00 (half-life 5 h).
|
|
45
|
+
|
|
46
|
+
$ gopromotional-mugday sleep-ready
|
|
47
|
+
Below 50 mg from about 2026-10-20 01:30.
|
|
48
|
+
|
|
49
|
+
$ gopromotional-mugday cups --date 2026-10-19
|
|
50
|
+
Last 7 days: 2 reusable, 1 disposable (67% reusable).
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Every command accepts `--json` for machine-readable output. Run `gopromotional-mugday --help` for all options.
|
|
54
|
+
|
|
55
|
+
Data is kept in `~/.gopromotional-mugday.json` (set `GOPROMOTIONAL_MUGDAY_FILE` or pass `--file` to use another file). The JSON format is the same across the npm, PyPI, crates.io and RubyGems versions, so you can switch tools without losing your log.
|
|
56
|
+
|
|
57
|
+
## Library
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
require "gopromotional/mugday"
|
|
61
|
+
M = GoPromotional::Mugday
|
|
62
|
+
|
|
63
|
+
log = M::CaffeineLog.new # limit 400 mg, half-life 5 h
|
|
64
|
+
log.add("flat-white", time: "2026-10-19T08:00", reusable: true)
|
|
65
|
+
log.add("filter-coffee", time: "2026-10-19T14:00")
|
|
66
|
+
log.day("2026-10-19")[:total] # => 266
|
|
67
|
+
log.sleep_ready(50) # => "2026-10-20T00:00"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Pure Ruby, no runtime dependencies, Ruby 3.0+. `require "gopromotional-mugday"` also works.
|
|
71
|
+
|
|
72
|
+
## Links
|
|
73
|
+
|
|
74
|
+
- Website: [branded travel mugs for event marketing](https://www.gopromotional.co.uk/branded-products/travel-mugs)
|
|
75
|
+
- Source code: [https://github.com/nbkdevs/GoPromotional](https://github.com/nbkdevs/GoPromotional) (`packages/travel-mugs/rubygems`)
|
|
76
|
+
- Licence: MIT
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
require_relative "../mugday"
|
|
5
|
+
|
|
6
|
+
module GoPromotional
|
|
7
|
+
module Mugday
|
|
8
|
+
# Command line interface: drinks | add | today | in-system | sleep-ready | cups | undo
|
|
9
|
+
module CLI
|
|
10
|
+
USAGE = <<~TXT
|
|
11
|
+
Usage: gopromotional-mugday <command> [options]
|
|
12
|
+
|
|
13
|
+
Commands:
|
|
14
|
+
drinks typical caffeine per drink
|
|
15
|
+
add <drink> [--time T] [--mg N] [--reusable]
|
|
16
|
+
today [--date D] [--limit N] total for a day
|
|
17
|
+
in-system [--at T] caffeine still in your system
|
|
18
|
+
sleep-ready [--threshold 50] when caffeine drops below a threshold
|
|
19
|
+
cups [--date D] reusable vs disposable cups, last 7 days
|
|
20
|
+
undo remove the last drink
|
|
21
|
+
|
|
22
|
+
Options: --file PATH (default $GOPROMOTIONAL_MUGDAY_FILE or ~/.gopromotional-mugday.json), --json
|
|
23
|
+
Times are YYYY-MM-DDTHH:MM. Caffeine values are typical figures; not medical advice.
|
|
24
|
+
TXT
|
|
25
|
+
|
|
26
|
+
module_function
|
|
27
|
+
|
|
28
|
+
def default_file
|
|
29
|
+
env = ENV.fetch("GOPROMOTIONAL_MUGDAY_FILE", "")
|
|
30
|
+
env.empty? ? File.join(Dir.home, ".gopromotional-mugday.json") : env
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def run(argv, out: $stdout, err: $stderr)
|
|
34
|
+
o = { json: false, reusable: false, threshold: 50.0 }
|
|
35
|
+
parser = OptionParser.new do |p|
|
|
36
|
+
p.banner = USAGE
|
|
37
|
+
p.on("--json") { o[:json] = true }
|
|
38
|
+
p.on("--file PATH") { |v| o[:file] = v }
|
|
39
|
+
p.on("--time T") { |v| o[:time] = v }
|
|
40
|
+
p.on("--mg N", Float) { |v| o[:mg] = v }
|
|
41
|
+
p.on("--reusable") { o[:reusable] = true }
|
|
42
|
+
p.on("--date D") { |v| o[:date] = v }
|
|
43
|
+
p.on("--limit N", Float) { |v| o[:limit] = v }
|
|
44
|
+
p.on("--at T") { |v| o[:at] = v }
|
|
45
|
+
p.on("--threshold N", Float) { |v| o[:threshold] = v }
|
|
46
|
+
p.on("-v", "--version") { out.puts VERSION; return 0 }
|
|
47
|
+
p.on("-h", "--help") { out.puts USAGE; return 0 }
|
|
48
|
+
end
|
|
49
|
+
args = parser.parse(argv)
|
|
50
|
+
cmd = args.shift
|
|
51
|
+
n = ->(x) { Mugday.num(x) }
|
|
52
|
+
case cmd
|
|
53
|
+
when nil
|
|
54
|
+
out.puts USAGE
|
|
55
|
+
return 0
|
|
56
|
+
when "drinks"
|
|
57
|
+
out.puts(o[:json] ? JSON.pretty_generate(DRINKS) :
|
|
58
|
+
DRINKS.map { |k, v| "#{k.ljust(20)} #{v[:mg].to_s.rjust(4)} mg#{v[:ml].positive? ? " (#{v[:ml]} ml)" : ''}" })
|
|
59
|
+
return 0
|
|
60
|
+
end
|
|
61
|
+
path = o[:file] || default_file
|
|
62
|
+
log = File.exist?(path) ? CaffeineLog.from_json(File.read(path, encoding: "utf-8")) : CaffeineLog.new
|
|
63
|
+
save = -> { File.write(path, "#{log.to_json}\n") }
|
|
64
|
+
case cmd
|
|
65
|
+
when "add"
|
|
66
|
+
raise ArgumentError, "add needs a drink name (see `drinks`)" if args.empty?
|
|
67
|
+
e = log.add(args.join(" "), time: o[:time] || Mugday.now_local, mg: o[:mg] && n.(o[:mg]), reusable: o[:reusable])
|
|
68
|
+
save.call
|
|
69
|
+
d = log.day(e.time[0, 10])
|
|
70
|
+
r = e.to_h
|
|
71
|
+
text = "Logged #{e.drink} (#{n.(e.mg)} mg#{e.reusable ? ', reusable cup' : ''}). Today: #{n.(d[:total])}/#{n.(d[:limit])} mg."
|
|
72
|
+
when "today"
|
|
73
|
+
r = log.day(o[:date], limit: o[:limit] && n.(o[:limit]))
|
|
74
|
+
text = "#{r[:date]}: #{n.(r[:total])} mg from #{r[:drinks]} drink(s); " +
|
|
75
|
+
(r[:over].positive? ? "#{n.(r[:over])} mg over the #{n.(r[:limit])} mg limit." : "#{n.(r[:remaining])} mg left of #{n.(r[:limit])} mg.")
|
|
76
|
+
when "in-system"
|
|
77
|
+
at = o[:at] || Mugday.now_local
|
|
78
|
+
r = { at: at, mg: log.in_system(at) }
|
|
79
|
+
text = "About #{r[:mg]} mg of caffeine still in your system at #{at} (half-life #{n.(log.half_life_hours)} h)."
|
|
80
|
+
when "sleep-ready"
|
|
81
|
+
raise ArgumentError, "--threshold must be above 0" unless o[:threshold].positive?
|
|
82
|
+
t = log.sleep_ready(o[:threshold])
|
|
83
|
+
r = { threshold: n.(o[:threshold]), time: t }
|
|
84
|
+
text = t ? "Below #{n.(o[:threshold])} mg from about #{t.tr('T', ' ')}." : "No drinks logged."
|
|
85
|
+
when "cups"
|
|
86
|
+
r = log.cups(o[:date])
|
|
87
|
+
text = "Last 7 days: #{r[:reusable]} reusable, #{r[:disposable]} disposable (#{r[:rate]}% reusable)."
|
|
88
|
+
when "undo"
|
|
89
|
+
e = log.undo
|
|
90
|
+
save.call
|
|
91
|
+
r = e&.to_h
|
|
92
|
+
text = e ? "Removed #{e.drink} at #{e.time}." : "Nothing to undo."
|
|
93
|
+
else
|
|
94
|
+
raise ArgumentError, %(Unknown command "#{cmd}". Run with --help.)
|
|
95
|
+
end
|
|
96
|
+
out.puts(o[:json] ? JSON.pretty_generate(r) : text)
|
|
97
|
+
0
|
|
98
|
+
rescue ArgumentError, OptionParser::ParseError, SystemCallError, JSON::ParserError => e
|
|
99
|
+
err.puts "Error: #{e.message}"
|
|
100
|
+
2
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "date"
|
|
4
|
+
require "json"
|
|
5
|
+
require_relative "mugday/version"
|
|
6
|
+
|
|
7
|
+
module GoPromotional
|
|
8
|
+
# Caffeine and coffee intake tracker: typical caffeine per drink, daily totals against a limit, an
|
|
9
|
+
# estimate of caffeine still in your system (half-life model), a "sleep-ready" time and reusable-cup
|
|
10
|
+
# stats. Values are typical figures; real drinks vary. Not medical advice.
|
|
11
|
+
module Mugday
|
|
12
|
+
DRINKS = {
|
|
13
|
+
"espresso" => { mg: 63, ml: 30 }, "double-espresso" => { mg: 126, ml: 60 },
|
|
14
|
+
"americano" => { mg: 126, ml: 350 }, "latte" => { mg: 126, ml: 350 },
|
|
15
|
+
"cappuccino" => { mg: 126, ml: 350 }, "flat-white" => { mg: 126, ml: 220 },
|
|
16
|
+
"filter-coffee" => { mg: 140, ml: 300 }, "instant-coffee" => { mg: 100, ml: 300 },
|
|
17
|
+
"cold-brew" => { mg: 155, ml: 350 }, "decaf-coffee" => { mg: 3, ml: 300 },
|
|
18
|
+
"tea" => { mg: 75, ml: 300 }, "green-tea" => { mg: 30, ml: 300 },
|
|
19
|
+
"cola" => { mg: 40, ml: 330 }, "energy-drink" => { mg: 80, ml: 250 },
|
|
20
|
+
"hot-chocolate" => { mg: 5, ml: 300 }, "dark-chocolate-50g" => { mg: 25, ml: 0 }
|
|
21
|
+
}.freeze
|
|
22
|
+
LIMITS = { adult: 400, pregnant: 200, single_dose: 200 }.freeze
|
|
23
|
+
DEFAULT_HALF_LIFE_HOURS = 5
|
|
24
|
+
TIME = /\A(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})\z/
|
|
25
|
+
EPOCH = Date.new(1970, 1, 1)
|
|
26
|
+
|
|
27
|
+
module_function
|
|
28
|
+
|
|
29
|
+
def round_half_up(x) = (x + 0.5).floor
|
|
30
|
+
def norm(name) = name.to_s.strip.downcase.gsub(/[\s_]+/, "-")
|
|
31
|
+
def num(x) = x.is_a?(Float) && x == x.floor ? x.to_i : x
|
|
32
|
+
|
|
33
|
+
# "YYYY-MM-DDTHH:MM" (naive local time) to minutes since 1970-01-01.
|
|
34
|
+
def to_minutes(value)
|
|
35
|
+
m = TIME.match(value.to_s.strip) or raise ArgumentError, %(Invalid time "#{value}" (expected YYYY-MM-DDTHH:MM))
|
|
36
|
+
y, mo, d, h, mi = m.captures.map(&:to_i)
|
|
37
|
+
raise ArgumentError, %(Invalid time "#{value}") unless Date.valid_date?(y, mo, d) && h < 24 && mi < 60
|
|
38
|
+
(Date.new(y, mo, d) - EPOCH).to_i * 1440 + h * 60 + mi
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def from_minutes(minutes)
|
|
42
|
+
day, rem = minutes.divmod(1440)
|
|
43
|
+
format("%sT%02d:%02d", (EPOCH + day).iso8601, rem / 60, rem % 60)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def now_local = Time.now.strftime("%Y-%m-%dT%H:%M")
|
|
47
|
+
|
|
48
|
+
def lookup_drink(name)
|
|
49
|
+
k = norm(name)
|
|
50
|
+
info = DRINKS[k] or raise ArgumentError, %(Unknown drink "#{name}". Known: #{DRINKS.keys.join(', ')} (or pass mg yourself))
|
|
51
|
+
{ drink: k, **info }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
Drink = Struct.new(:drink, :mg, :time, :reusable) do
|
|
55
|
+
def to_h = { "drink" => drink, "mg" => Mugday.num(mg), "time" => time, "reusable" => reusable }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Caffeine log. The JSON file format ({limit, halfLifeHours, entries}) is shared with the npm,
|
|
59
|
+
# PyPI and crates.io versions.
|
|
60
|
+
class CaffeineLog
|
|
61
|
+
attr_accessor :limit, :half_life_hours
|
|
62
|
+
attr_reader :entries
|
|
63
|
+
|
|
64
|
+
def initialize(limit: LIMITS[:adult], half_life_hours: DEFAULT_HALF_LIFE_HOURS, entries: [])
|
|
65
|
+
@limit = limit
|
|
66
|
+
@half_life_hours = half_life_hours
|
|
67
|
+
@entries = []
|
|
68
|
+
entries.each do |e|
|
|
69
|
+
e = e.to_h.transform_keys(&:to_s)
|
|
70
|
+
add(e["drink"], time: e["time"], mg: e["mg"], reusable: e["reusable"] || false)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.from_json(data)
|
|
75
|
+
d = data.is_a?(String) ? JSON.parse(data) : (data || {})
|
|
76
|
+
new(limit: d.fetch("limit", LIMITS[:adult]),
|
|
77
|
+
half_life_hours: d["halfLifeHours"] || d["half_life_hours"] || DEFAULT_HALF_LIFE_HOURS,
|
|
78
|
+
entries: d.fetch("entries", []))
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def to_h = { "limit" => Mugday.num(limit), "halfLifeHours" => Mugday.num(half_life_hours), "entries" => entries.map(&:to_h) }
|
|
82
|
+
def to_json(*_args) = JSON.pretty_generate(to_h)
|
|
83
|
+
|
|
84
|
+
# Log a drink. mg overrides the typical value (handy for unknown drinks).
|
|
85
|
+
def add(drink, time: nil, mg: nil, reusable: false)
|
|
86
|
+
name = Mugday.norm(drink)
|
|
87
|
+
if mg.nil?
|
|
88
|
+
info = Mugday.lookup_drink(drink)
|
|
89
|
+
name = info[:drink]
|
|
90
|
+
mg = info[:mg]
|
|
91
|
+
end
|
|
92
|
+
raise ArgumentError, "mg must be between 0 and 1000" unless mg.is_a?(Numeric) && mg >= 0 && mg <= 1000
|
|
93
|
+
e = Drink.new(name, mg, Mugday.from_minutes(Mugday.to_minutes(time || Mugday.now_local)), reusable ? true : false)
|
|
94
|
+
@entries << e
|
|
95
|
+
@entries.sort_by!.with_index { |x, i| [Mugday.to_minutes(x.time), i] }
|
|
96
|
+
e
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def undo = @entries.pop
|
|
100
|
+
def for_day(day) = @entries.select { |e| e.time[0, 10] == day }
|
|
101
|
+
|
|
102
|
+
def day(day = nil, limit: nil)
|
|
103
|
+
d = day || Mugday.now_local[0, 10]
|
|
104
|
+
lim = limit || @limit
|
|
105
|
+
items = for_day(d)
|
|
106
|
+
total = items.sum(&:mg)
|
|
107
|
+
{ date: d, drinks: items.size, total: total, limit: lim, remaining: [0, lim - total].max, over: [0, total - lim].max }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def level(t)
|
|
111
|
+
@entries.sum do |e|
|
|
112
|
+
dt = t - Mugday.to_minutes(e.time)
|
|
113
|
+
dt >= 0 ? e.mg * (0.5**(dt / 60.0 / @half_life_hours)) : 0
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Estimated caffeine (mg) still in the body at `at`.
|
|
118
|
+
def in_system(at = nil) = Mugday.round_half_up(level(Mugday.to_minutes(at || Mugday.now_local)))
|
|
119
|
+
|
|
120
|
+
# Earliest time (rounded up to 15 min) caffeine falls below threshold, if nothing more is drunk.
|
|
121
|
+
def sleep_ready(threshold = 50)
|
|
122
|
+
return nil if @entries.empty?
|
|
123
|
+
last = Mugday.to_minutes(@entries.last.time)
|
|
124
|
+
lv = level(last)
|
|
125
|
+
return Mugday.from_minutes(last) if lv <= threshold
|
|
126
|
+
minutes = @half_life_hours * 60 * Math.log2(lv / threshold.to_f)
|
|
127
|
+
Mugday.from_minutes(((last + minutes) / 15.0).ceil * 15)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Reusable vs disposable cups over the 7 days ending on end_day.
|
|
131
|
+
def cups(end_day = nil)
|
|
132
|
+
e = Mugday.to_minutes("#{end_day || Mugday.now_local[0, 10]}T00:00") / 1440
|
|
133
|
+
week = @entries.select { |x| d = Mugday.to_minutes("#{x.time[0, 10]}T00:00") / 1440; d > e - 7 && d <= e }
|
|
134
|
+
reusable = week.count(&:reusable)
|
|
135
|
+
{ reusable: reusable, disposable: week.size - reusable,
|
|
136
|
+
rate: week.empty? ? 0 : Mugday.round_half_up(reusable * 100.0 / week.size) }
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require_relative "gopromotional/mugday"
|
metadata
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: gopromotional-mugday
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- GoPromotional
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: Log coffee, tea and other drinks with typical caffeine values, compare
|
|
13
|
+
the day's total with a limit (400 mg adult, 200 mg in pregnancy), estimate how much
|
|
14
|
+
caffeine is still in your system, find out when it should drop below a sleep-friendly
|
|
15
|
+
level, and track how often you used a reusable cup. Typical values only; not medical
|
|
16
|
+
advice.
|
|
17
|
+
executables:
|
|
18
|
+
- gopromotional-mugday
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- LICENSE
|
|
23
|
+
- README.md
|
|
24
|
+
- exe/gopromotional-mugday
|
|
25
|
+
- lib/gopromotional-mugday.rb
|
|
26
|
+
- lib/gopromotional/mugday.rb
|
|
27
|
+
- lib/gopromotional/mugday/cli.rb
|
|
28
|
+
- lib/gopromotional/mugday/version.rb
|
|
29
|
+
homepage: https://www.gopromotional.co.uk/branded-products/travel-mugs
|
|
30
|
+
licenses:
|
|
31
|
+
- MIT
|
|
32
|
+
metadata:
|
|
33
|
+
homepage_uri: https://www.gopromotional.co.uk/branded-products/travel-mugs
|
|
34
|
+
source_code_uri: https://github.com/nbkdevs/GoPromotional/tree/main/packages/travel-mugs/rubygems
|
|
35
|
+
bug_tracker_uri: https://github.com/nbkdevs/GoPromotional/issues
|
|
36
|
+
rubygems_mfa_required: 'true'
|
|
37
|
+
rdoc_options: []
|
|
38
|
+
require_paths:
|
|
39
|
+
- lib
|
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
41
|
+
requirements:
|
|
42
|
+
- - ">="
|
|
43
|
+
- !ruby/object:Gem::Version
|
|
44
|
+
version: '3.0'
|
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '0'
|
|
50
|
+
requirements: []
|
|
51
|
+
rubygems_version: 3.6.7
|
|
52
|
+
specification_version: 4
|
|
53
|
+
summary: Caffeine tracker with half-life estimate, sleep-ready time and reusable-cup
|
|
54
|
+
stats.
|
|
55
|
+
test_files: []
|