toji 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/example/koji_making.ipynb +84 -0
- data/example/koji_making.rb +14 -0
- data/example/koji_making2.rb +69 -0
- data/example/moromi.ipynb +159 -0
- data/example/moromi.rb +15 -0
- data/example/shubo.ipynb +84 -0
- data/example/shubo.rb +14 -0
- data/example/three_step_mashing_recipe.rb +25 -25
- data/lib/toji/graph/ab.rb +23 -19
- data/lib/toji/graph/bmd.rb +56 -0
- data/lib/toji/graph/progress.rb +84 -0
- data/lib/toji/graph.rb +2 -0
- data/lib/toji/product/base.rb +74 -0
- data/lib/toji/{progress → product}/job.rb +32 -21
- data/lib/toji/{progress → product}/job_accessor.rb +1 -1
- data/lib/toji/{progress/make_koji.rb → product/koji_making.rb} +16 -22
- data/lib/toji/{progress → product}/moromi.rb +70 -110
- data/lib/toji/{progress → product}/shubo.rb +22 -50
- data/lib/toji/product.rb +11 -0
- data/lib/toji/recipe/rice_rate/cooked.rb +1 -25
- data/lib/toji/recipe/rice_rate/steamed.rb +0 -12
- data/lib/toji/version.rb +1 -1
- data/lib/toji.rb +1 -1
- metadata +18 -10
- data/example/make_koji.rb +0 -18
- data/lib/toji/progress/jobs.rb +0 -142
- data/lib/toji/progress.rb +0 -11
data/lib/toji/graph/ab.rb
CHANGED
@@ -9,27 +9,27 @@ module Toji
|
|
9
9
|
@expects = []
|
10
10
|
end
|
11
11
|
|
12
|
-
def actual(
|
13
|
-
|
14
|
-
xs = data.map{|a| a[1]}
|
15
|
-
ys = data.map{|a| a[2]}
|
16
|
-
texts = data.map{|a| "%s<br />alc=%s, be=%s" % a}
|
17
|
-
@actuals << {x: xs, y: ys, text: texts, name: name}
|
18
|
-
|
12
|
+
def actual(moromi, name=:actual)
|
13
|
+
@actuals << [moromi, name]
|
19
14
|
self
|
20
15
|
end
|
21
16
|
|
22
17
|
def expect(alcohol, nihonshudo)
|
23
18
|
@expects << [alcohol.to_f, nihonshudo.to_f]
|
24
|
-
|
25
19
|
self
|
26
20
|
end
|
27
21
|
|
28
22
|
def data
|
29
23
|
result = []
|
30
24
|
|
31
|
-
@actuals.each {|
|
32
|
-
|
25
|
+
@actuals.each {|moromi, name|
|
26
|
+
data = moromi.map{|j| [j.time || j.elapsed_time + moromi.day_offset, j.alcohol, j.baume]}.select{|a| a[1] && a[2]}
|
27
|
+
|
28
|
+
xs = data.map{|a| a[1]}
|
29
|
+
ys = data.map{|a| a[2]}
|
30
|
+
texts = data.map{|a| "%s<br />alc=%s, be=%s" % a}
|
31
|
+
|
32
|
+
result << {x: xs, y: ys, text: texts, name: name}
|
33
33
|
}
|
34
34
|
|
35
35
|
@expects.each {|alcohol, nihonshudo|
|
@@ -44,18 +44,22 @@ module Toji
|
|
44
44
|
result
|
45
45
|
end
|
46
46
|
|
47
|
-
def
|
48
|
-
|
47
|
+
def min_baume
|
48
|
+
data.map{|h| h[:y].min}.min || 0
|
49
|
+
end
|
50
|
+
|
51
|
+
def max_baume
|
52
|
+
data.map{|h| h[:y].max}.max || 0
|
53
|
+
end
|
49
54
|
|
50
|
-
|
51
|
-
#
|
52
|
-
|
55
|
+
def plot
|
56
|
+
#_min_baume = [min_baume-1, 0].min
|
57
|
+
_min_baume = 0
|
53
58
|
|
54
|
-
|
55
|
-
max_baume = [max_baume+1, 10].max
|
59
|
+
_max_baume = [max_baume+1, 10].max
|
56
60
|
|
57
61
|
Plotly::Plot.new(
|
58
|
-
data:
|
62
|
+
data: data,
|
59
63
|
layout: {
|
60
64
|
xaxis: {
|
61
65
|
title: "Alcohol",
|
@@ -65,7 +69,7 @@ module Toji
|
|
65
69
|
yaxis: {
|
66
70
|
title: "Baume",
|
67
71
|
dtick: 1,
|
68
|
-
range: [
|
72
|
+
range: [_min_baume, _max_baume],
|
69
73
|
}
|
70
74
|
}
|
71
75
|
)
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Toji
|
2
|
+
module Graph
|
3
|
+
class Bmd
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@actuals = []
|
7
|
+
@expects = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def actual(moromi, name=:actual)
|
11
|
+
@actuals << [moromi, name]
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def data
|
16
|
+
result = []
|
17
|
+
|
18
|
+
@actuals.each {|moromi, name|
|
19
|
+
jobs = moromi.select{|j| j.moromi_time && j.bmd}
|
20
|
+
|
21
|
+
xs = jobs.map(&:moromi_time)
|
22
|
+
ys = jobs.map(&:bmd)
|
23
|
+
texts = jobs.map{|j| "%s<br />moromi day=%d, be=%s, bmd=%s" % [j.time || j.elapsed_time + moromi.day_offset, j.moromi_day, j.baume, j.bmd]}
|
24
|
+
|
25
|
+
result << {x: xs, y: ys, text: texts, name: name}
|
26
|
+
}
|
27
|
+
|
28
|
+
result
|
29
|
+
end
|
30
|
+
|
31
|
+
def max_moromi_day
|
32
|
+
@actuals.map(&:first).map(&:moromi_days).max
|
33
|
+
end
|
34
|
+
|
35
|
+
def plot
|
36
|
+
_max_moromi_day = [max_moromi_day, 14].max
|
37
|
+
|
38
|
+
Plotly::Plot.new(
|
39
|
+
data: data,
|
40
|
+
layout: {
|
41
|
+
xaxis: {
|
42
|
+
title: "Moromi day",
|
43
|
+
dtick: Product::Job::DAY,
|
44
|
+
range: [1, _max_moromi_day].map{|d| d*Product::Job::DAY},
|
45
|
+
tickvals: _max_moromi_day.times.map{|d| d*Product::Job::DAY},
|
46
|
+
ticktext: _max_moromi_day.times.map(&:succ)
|
47
|
+
},
|
48
|
+
yaxis: {
|
49
|
+
title: "BMD",
|
50
|
+
}
|
51
|
+
}
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Toji
|
2
|
+
module Graph
|
3
|
+
class Progress
|
4
|
+
|
5
|
+
def initialize(product)
|
6
|
+
@product = product
|
7
|
+
end
|
8
|
+
|
9
|
+
def data(keys=nil)
|
10
|
+
data = @product.map(&:to_h).map(&:compact)
|
11
|
+
if !keys
|
12
|
+
keys = data.map(&:keys).flatten.uniq
|
13
|
+
end
|
14
|
+
|
15
|
+
result = []
|
16
|
+
|
17
|
+
keys &= [:temps, :preset_temp, :room_temp, :room_psychrometry, :baume, :acid, :amino_acid, :alcohol]
|
18
|
+
|
19
|
+
keys.each {|key|
|
20
|
+
xs = []
|
21
|
+
ys = []
|
22
|
+
text = []
|
23
|
+
data.each {|h|
|
24
|
+
if h[key]
|
25
|
+
[h[key]].flatten.each_with_index {|v,i|
|
26
|
+
xs << h[:elapsed_time] + i + @product.day_offset
|
27
|
+
ys << v
|
28
|
+
text << h[:display_time]
|
29
|
+
}
|
30
|
+
end
|
31
|
+
}
|
32
|
+
|
33
|
+
line_shape = :linear
|
34
|
+
if key==:preset_temp
|
35
|
+
line_shape = :hv
|
36
|
+
end
|
37
|
+
|
38
|
+
result << {x: xs, y: ys, text: text, name: key, line: {shape: line_shape}}
|
39
|
+
}
|
40
|
+
|
41
|
+
if 0<@product.day_offset
|
42
|
+
result = result.map{|h|
|
43
|
+
h[:x].unshift(0)
|
44
|
+
h[:y].unshift(nil)
|
45
|
+
h[:text].unshift(nil)
|
46
|
+
h
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
result
|
51
|
+
end
|
52
|
+
|
53
|
+
def annotations
|
54
|
+
@product.select{|j| j.id}.map {|j|
|
55
|
+
{
|
56
|
+
x: j.elapsed_time + @product.day_offset,
|
57
|
+
y: j.temps.first || 0,
|
58
|
+
xref: 'x',
|
59
|
+
yref: 'y',
|
60
|
+
text: j.id,
|
61
|
+
showarrow: true,
|
62
|
+
arrowhead: 1,
|
63
|
+
ax: 0,
|
64
|
+
ay: -40
|
65
|
+
}
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def plot
|
70
|
+
Plotly::Plot.new(
|
71
|
+
data: data,
|
72
|
+
layout: {
|
73
|
+
xaxis: {
|
74
|
+
dtick: Product::Job::DAY,
|
75
|
+
tickvals: @product.days.times.map{|d| d*Product::Job::DAY},
|
76
|
+
ticktext: @product.day_labels
|
77
|
+
},
|
78
|
+
annotations: annotations,
|
79
|
+
}
|
80
|
+
)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/toji/graph.rb
CHANGED
@@ -0,0 +1,74 @@
|
|
1
|
+
module Toji
|
2
|
+
module Product
|
3
|
+
class Base
|
4
|
+
include Enumerable
|
5
|
+
extend JobAccessor
|
6
|
+
|
7
|
+
attr_accessor :date_line
|
8
|
+
attr_reader :day_offset
|
9
|
+
|
10
|
+
def initialize(arr=[], date_line: 0)
|
11
|
+
@_arr = []
|
12
|
+
@_hash = {}
|
13
|
+
@date_line = date_line
|
14
|
+
@day_offset = 0
|
15
|
+
|
16
|
+
arr.each {|j|
|
17
|
+
self << j
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](id)
|
22
|
+
@_hash[id]
|
23
|
+
end
|
24
|
+
|
25
|
+
def <<(job)
|
26
|
+
job = Job.create(job)
|
27
|
+
if job.id
|
28
|
+
if @_hash[job.id]
|
29
|
+
@_arr.delete(@_hash[job.id])
|
30
|
+
end
|
31
|
+
@_hash[job.id] = job
|
32
|
+
end
|
33
|
+
@_arr << job
|
34
|
+
job.jobs = self
|
35
|
+
_normalization
|
36
|
+
self
|
37
|
+
end
|
38
|
+
alias_method :add, :<<
|
39
|
+
|
40
|
+
def _normalization
|
41
|
+
min_time = @_arr.select{|j| j.time}.map(&:time).sort.first
|
42
|
+
|
43
|
+
if min_time
|
44
|
+
@_arr.each {|j|
|
45
|
+
if j.time
|
46
|
+
j.elapsed_time = (j.time - min_time).to_i
|
47
|
+
else
|
48
|
+
j.time = min_time + j.elapsed_time
|
49
|
+
end
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
@_arr = @_arr.sort{|a,b| a.elapsed_time<=>b.elapsed_time}
|
54
|
+
|
55
|
+
t = @_arr.first&.time
|
56
|
+
if t
|
57
|
+
@day_offset = t - Time.mktime(t.year, t.month, t.day)
|
58
|
+
else
|
59
|
+
@day_offset = 0
|
60
|
+
end
|
61
|
+
@day_offset += ((24 - @date_line) % 24) * Job::HOUR
|
62
|
+
end
|
63
|
+
|
64
|
+
def each(&block)
|
65
|
+
_normalization
|
66
|
+
@_arr.each(&block)
|
67
|
+
end
|
68
|
+
|
69
|
+
def days
|
70
|
+
((to_a.last.elapsed_time.to_f + @date_line * Job::HOUR) / Job::DAY).ceil + 1
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Toji
|
2
|
-
module
|
2
|
+
module Product
|
3
3
|
class Job
|
4
4
|
HOUR = 60 * 60
|
5
5
|
DAY = 24 * HOUR
|
@@ -13,8 +13,7 @@ module Toji
|
|
13
13
|
attr_accessor :time
|
14
14
|
attr_accessor :elapsed_time
|
15
15
|
attr_accessor :id
|
16
|
-
attr_accessor :
|
17
|
-
attr_accessor :after_temp
|
16
|
+
attr_accessor :temps
|
18
17
|
|
19
18
|
attr_accessor :preset_temp
|
20
19
|
attr_accessor :room_temp
|
@@ -30,15 +29,14 @@ module Toji
|
|
30
29
|
attr_accessor :note
|
31
30
|
|
32
31
|
def initialize(
|
33
|
-
time: nil, elapsed_time: nil, id: nil,
|
32
|
+
time: nil, elapsed_time: nil, id: nil, temps: nil,
|
34
33
|
preset_temp: nil, room_temp: nil, room_psychrometry: nil,
|
35
34
|
baume: nil, nihonshudo: nil, acid: nil, amino_acid: nil, alcohol: nil,
|
36
35
|
warming: nil, note: nil)
|
37
36
|
@time = time
|
38
37
|
@elapsed_time = elapsed_time
|
39
38
|
@id = id
|
40
|
-
|
41
|
-
@after_temp = after_temp
|
39
|
+
self.temps = temps
|
42
40
|
|
43
41
|
@preset_temp = preset_temp
|
44
42
|
@room_temp = room_temp
|
@@ -54,18 +52,8 @@ module Toji
|
|
54
52
|
@note = note
|
55
53
|
end
|
56
54
|
|
57
|
-
def temps
|
58
|
-
|
59
|
-
|
60
|
-
if @before_temp
|
61
|
-
result << @before_temp
|
62
|
-
end
|
63
|
-
|
64
|
-
if @after_temp
|
65
|
-
result << @after_temp
|
66
|
-
end
|
67
|
-
|
68
|
-
result
|
55
|
+
def temps=(val)
|
56
|
+
@temps = [val].flatten.select{|t| t}
|
69
57
|
end
|
70
58
|
|
71
59
|
def baume
|
@@ -86,7 +74,7 @@ module Toji
|
|
86
74
|
end
|
87
75
|
end
|
88
76
|
|
89
|
-
def
|
77
|
+
def display_baume
|
90
78
|
if @baume || @nihonshudo
|
91
79
|
b = baume
|
92
80
|
if b<3.0
|
@@ -97,6 +85,14 @@ module Toji
|
|
97
85
|
end
|
98
86
|
end
|
99
87
|
|
88
|
+
def display_time(format="%m/%d %H:%M")
|
89
|
+
if @time
|
90
|
+
time.strftime(format)
|
91
|
+
else
|
92
|
+
Time.at(elapsed_time).strftime(format)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
100
96
|
def moromi_time
|
101
97
|
tome = jobs[:tome]
|
102
98
|
if tome
|
@@ -124,6 +120,14 @@ module Toji
|
|
124
120
|
end
|
125
121
|
end
|
126
122
|
|
123
|
+
def expected_alcohol(target_alc, target_nihonshudo, coef)
|
124
|
+
_baume = baume
|
125
|
+
|
126
|
+
if _baume
|
127
|
+
target_alc - (_baume - target_nihonshudo * -0.1) * coef
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
127
131
|
def warmings
|
128
132
|
result = []
|
129
133
|
if @warming & WARM_DAKI
|
@@ -142,10 +146,9 @@ module Toji
|
|
142
146
|
{
|
143
147
|
time: time,
|
144
148
|
elapsed_time: elapsed_time,
|
149
|
+
display_time: display_time,
|
145
150
|
id: id,
|
146
151
|
preset_temp: preset_temp,
|
147
|
-
before_temp: before_temp,
|
148
|
-
after_temp: after_temp,
|
149
152
|
temps: temps,
|
150
153
|
room_temp: room_temp,
|
151
154
|
room_psychrometry: room_psychrometry,
|
@@ -160,6 +163,14 @@ module Toji
|
|
160
163
|
note: note,
|
161
164
|
}
|
162
165
|
end
|
166
|
+
|
167
|
+
def self.create(j)
|
168
|
+
if Job===j
|
169
|
+
j
|
170
|
+
else
|
171
|
+
new(**j.to_h)
|
172
|
+
end
|
173
|
+
end
|
163
174
|
end
|
164
175
|
end
|
165
176
|
end
|
@@ -1,67 +1,62 @@
|
|
1
1
|
module Toji
|
2
|
-
module
|
3
|
-
class
|
4
|
-
extend JobAccessor
|
2
|
+
module Product
|
3
|
+
class KojiMaking < Base
|
5
4
|
|
6
5
|
TEMPLATES = {
|
7
6
|
default: [
|
8
7
|
Job.new(
|
9
8
|
elapsed_time: 0 * Job::HOUR,
|
10
9
|
id: :hikikomi,
|
11
|
-
|
10
|
+
temps: 35.0,
|
12
11
|
room_temp: 28.0,
|
13
12
|
room_psychrometry: nil,
|
14
13
|
),
|
15
14
|
Job.new(
|
16
15
|
elapsed_time: 1 * Job::HOUR,
|
17
16
|
id: :tokomomi,
|
18
|
-
|
17
|
+
temps: 32.0,
|
19
18
|
room_temp: 28.0,
|
20
19
|
room_psychrometry: nil,
|
21
20
|
),
|
22
21
|
Job.new(
|
23
22
|
elapsed_time: 9 * Job::HOUR,
|
24
23
|
id: :kirikaeshi,
|
25
|
-
|
26
|
-
after_temp: 31.0,
|
24
|
+
temps: [32.0, 31.0],
|
27
25
|
room_temp: 28.0,
|
28
26
|
room_psychrometry: nil,
|
29
27
|
),
|
30
28
|
Job.new(
|
31
29
|
elapsed_time: 21 * Job::HOUR,
|
32
30
|
id: :mori,
|
33
|
-
|
34
|
-
after_temp: 33.0,
|
31
|
+
temps: [35.0, 33.0],
|
35
32
|
room_temp: 28.0,
|
36
33
|
room_psychrometry: 4,
|
37
34
|
),
|
38
35
|
Job.new(
|
39
36
|
elapsed_time: 29 * Job::HOUR,
|
40
37
|
id: :naka_shigoto,
|
41
|
-
|
42
|
-
after_temp: 35.0,
|
38
|
+
temps: [37.0, 35.0],
|
43
39
|
room_temp: 28.0,
|
44
40
|
room_psychrometry: 4,
|
45
41
|
),
|
46
42
|
Job.new(
|
47
43
|
elapsed_time: 35 * Job::HOUR,
|
48
44
|
id: :shimai_shigoto,
|
49
|
-
|
50
|
-
after_temp: 37.0,
|
45
|
+
temps: [38.0, 37.0],
|
51
46
|
room_temp: 28.0,
|
52
47
|
room_psychrometry: 5,
|
53
48
|
),
|
54
49
|
Job.new(
|
55
50
|
elapsed_time: 39 * Job::HOUR,
|
56
51
|
id: :tsumikae,
|
57
|
-
|
52
|
+
temps: 40.0,
|
58
53
|
room_temp: 28.0,
|
59
54
|
room_psychrometry: 5,
|
60
55
|
),
|
61
56
|
Job.new(
|
62
57
|
elapsed_time: 44 * Job::HOUR,
|
63
58
|
id: :dekoji,
|
64
|
-
|
59
|
+
temps: 40.0,
|
65
60
|
room_temp: 28.0,
|
66
61
|
room_psychrometry: 5,
|
67
62
|
),
|
@@ -79,17 +74,16 @@ module Toji
|
|
79
74
|
job_reader :dekoji
|
80
75
|
|
81
76
|
|
82
|
-
def initialize(jobs=[])
|
83
|
-
|
77
|
+
def initialize(jobs=[], date_line: 0)
|
78
|
+
super(jobs, date_line: date_line)
|
84
79
|
end
|
85
80
|
|
86
|
-
def
|
87
|
-
|
88
|
-
self
|
81
|
+
def day_labels
|
82
|
+
days.times.map(&:succ)
|
89
83
|
end
|
90
84
|
|
91
|
-
def
|
92
|
-
|
85
|
+
def progress
|
86
|
+
Graph::Progress.new(self)
|
93
87
|
end
|
94
88
|
|
95
89
|
def self.template(key=:default)
|