toji 1.2.0 → 1.3.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 +4 -4
- data/example/koji_making.ipynb +42 -8
- data/example/moromi.ipynb +21 -21
- data/example/schedule.ipynb +393 -0
- data/example/shubo.ipynb +43 -7
- data/example/three_step_mashing_recipe.rb +7 -5
- data/lib/toji/brew/base.rb +41 -8
- data/lib/toji/brew/graph/progress.rb +4 -7
- data/lib/toji/brew/state.rb +0 -12
- data/lib/toji/recipe/ingredient/koji/actual.rb +9 -0
- data/lib/toji/recipe/ingredient/koji/base.rb +9 -0
- data/lib/toji/recipe/ingredient/koji/expected.rb +4 -9
- data/lib/toji/recipe/ingredient/rice/actual.rb +10 -1
- data/lib/toji/recipe/ingredient/rice/base.rb +9 -0
- data/lib/toji/recipe/ingredient/rice/expected.rb +2 -7
- data/lib/toji/recipe/ingredient.rb +0 -1
- data/lib/toji/recipe/step.rb +39 -4
- data/lib/toji/recipe/three_step_mashing.rb +201 -26
- data/lib/toji/recipe.rb +0 -1
- data/lib/toji/schedule/calendar.rb +115 -0
- data/lib/toji/schedule/date_column.rb +30 -0
- data/lib/toji/schedule/date_interval_enumerator.rb +45 -0
- data/lib/toji/schedule/date_row.rb +29 -0
- data/lib/toji/schedule/product.rb +80 -0
- data/lib/toji/schedule/product_event.rb +44 -0
- data/lib/toji/schedule.rb +11 -0
- data/lib/toji/version.rb +1 -1
- data/lib/toji.rb +1 -0
- metadata +10 -4
- data/lib/toji/recipe/ingredient/lactic_acid.rb +0 -21
- data/lib/toji/recipe/lactic_acid_rate.rb +0 -17
@@ -0,0 +1,115 @@
|
|
1
|
+
module Toji
|
2
|
+
module Schedule
|
3
|
+
class Calendar
|
4
|
+
attr_reader :products
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@products = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def <<(product)
|
11
|
+
@products << product
|
12
|
+
self
|
13
|
+
end
|
14
|
+
alias_method :add, :<<
|
15
|
+
|
16
|
+
def date_rows
|
17
|
+
events = @products.map{|product| product.events}.flatten
|
18
|
+
|
19
|
+
result = {}
|
20
|
+
events.each {|event|
|
21
|
+
result[event.date] ||= DateRow.new(event.date)
|
22
|
+
result[event.date] << event
|
23
|
+
}
|
24
|
+
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
def table_data
|
29
|
+
events = @products.map{|product| product.events}.flatten
|
30
|
+
|
31
|
+
koji_len = events.select{|e| e.type==:koji}.map(&:group_index).max + 1
|
32
|
+
rice_len = events.select{|e| e.type==:rice}.map(&:group_index).max + 1
|
33
|
+
min_date = events.map(&:date).min
|
34
|
+
max_date = events.map(&:date).max
|
35
|
+
|
36
|
+
headers = [:date]
|
37
|
+
|
38
|
+
case koji_len
|
39
|
+
when 1
|
40
|
+
headers += [:koji]
|
41
|
+
when 2
|
42
|
+
headers += [:moto_koji, :moromi_koji]
|
43
|
+
else
|
44
|
+
headers += [:moto_koji]
|
45
|
+
(2..koji_len).each {|i|
|
46
|
+
headers << "moromi_koji#{i-1}"
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
case rice_len
|
51
|
+
when 1
|
52
|
+
headers += [:moto]
|
53
|
+
when 2
|
54
|
+
headers += [:moto, :soe]
|
55
|
+
when 3
|
56
|
+
headers += [:moto, :soe, :naka]
|
57
|
+
when 4
|
58
|
+
headers += [:moto, :soe, :naka, :tome]
|
59
|
+
else
|
60
|
+
headers += [:moto, :soe, :naka, :tome]
|
61
|
+
(4...rice_len).each {|i|
|
62
|
+
headers << "#{i}dan"
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
rows = date_rows
|
67
|
+
|
68
|
+
cells = []
|
69
|
+
|
70
|
+
date = min_date
|
71
|
+
while date<=max_date
|
72
|
+
columns = [date.strftime("%m/%d")]
|
73
|
+
|
74
|
+
row = rows[date]
|
75
|
+
if row
|
76
|
+
koji_len.times {|i|
|
77
|
+
columns << (row.kojis[i]&.text || "")
|
78
|
+
}
|
79
|
+
rice_len.times {|i|
|
80
|
+
columns << (row.rices[i]&.text || "")
|
81
|
+
}
|
82
|
+
else
|
83
|
+
(koji_len + rice_len).times {
|
84
|
+
columns << ""
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
cells << columns
|
89
|
+
|
90
|
+
date = date.tomorrow
|
91
|
+
end
|
92
|
+
|
93
|
+
{header: headers, cells: cells.transpose}
|
94
|
+
end
|
95
|
+
|
96
|
+
def table
|
97
|
+
data = table_data
|
98
|
+
|
99
|
+
Plotly::Plot.new(
|
100
|
+
data: [{
|
101
|
+
type: :table,
|
102
|
+
header: {
|
103
|
+
values: data[:header]
|
104
|
+
},
|
105
|
+
cells: {
|
106
|
+
values: data[:cells]
|
107
|
+
},
|
108
|
+
}],
|
109
|
+
layout: {
|
110
|
+
}
|
111
|
+
)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Toji
|
2
|
+
module Schedule
|
3
|
+
class DateColumn
|
4
|
+
attr_reader :events
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@events = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def <<(event)
|
11
|
+
@events << event
|
12
|
+
end
|
13
|
+
alias_method :add, :<<
|
14
|
+
|
15
|
+
def event_groups
|
16
|
+
@events.group_by {|e|
|
17
|
+
e.group_key
|
18
|
+
}.values
|
19
|
+
end
|
20
|
+
|
21
|
+
def text
|
22
|
+
event_groups.map {|es|
|
23
|
+
name = es.first.product.name
|
24
|
+
weight = "%.17g" % es.map(&:weight).sum
|
25
|
+
"#{name}: #{weight}"
|
26
|
+
}.join("<br>")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Toji
|
2
|
+
module Schedule
|
3
|
+
class DateIntervalEnumerator
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def initialize(intervals, afterwards)
|
7
|
+
@intervals = intervals
|
8
|
+
@afterwards = afterwards
|
9
|
+
end
|
10
|
+
|
11
|
+
def each(&block)
|
12
|
+
Enumerator.new do |y|
|
13
|
+
y << 0
|
14
|
+
@intervals.each {|interval|
|
15
|
+
y << interval
|
16
|
+
}
|
17
|
+
loop {
|
18
|
+
y << @afterwards
|
19
|
+
}
|
20
|
+
end.each(&block)
|
21
|
+
end
|
22
|
+
|
23
|
+
def merge(dates, length)
|
24
|
+
dates = [dates].flatten
|
25
|
+
enum = each
|
26
|
+
|
27
|
+
length.times {|i|
|
28
|
+
add = enum.next
|
29
|
+
|
30
|
+
if i==0
|
31
|
+
dates[i] = dates[i].to_time
|
32
|
+
elsif Integer===dates[i] && dates[i-1]
|
33
|
+
dates[i] = dates[i-1].since(dates[i].days)
|
34
|
+
elsif !dates[i] && dates[i-1]
|
35
|
+
dates[i] = dates[i-1].since(add.days)
|
36
|
+
else
|
37
|
+
dates[i] = dates[i].to_time
|
38
|
+
end
|
39
|
+
}
|
40
|
+
|
41
|
+
dates
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Toji
|
2
|
+
module Schedule
|
3
|
+
class DateRow
|
4
|
+
attr_reader :date
|
5
|
+
attr_reader :kojis
|
6
|
+
attr_reader :rices
|
7
|
+
|
8
|
+
def initialize(date)
|
9
|
+
@date = date
|
10
|
+
@kojis = []
|
11
|
+
@rices = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def <<(event)
|
15
|
+
case event.type
|
16
|
+
when :koji
|
17
|
+
index = event.group_index
|
18
|
+
@kojis[index] ||= DateColumn.new
|
19
|
+
@kojis[index] << event
|
20
|
+
when :rice
|
21
|
+
index = event.group_index
|
22
|
+
@rices[index] ||= DateColumn.new
|
23
|
+
@rices[index] << event
|
24
|
+
end
|
25
|
+
end
|
26
|
+
alias_method :add, :<<
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Toji
|
2
|
+
module Schedule
|
3
|
+
class Product
|
4
|
+
attr_reader :name
|
5
|
+
attr_reader :description
|
6
|
+
attr_reader :recipe
|
7
|
+
|
8
|
+
attr_reader :koji_dates
|
9
|
+
attr_reader :rice_dates
|
10
|
+
|
11
|
+
attr_reader :color
|
12
|
+
|
13
|
+
def initialize(name, description, recipe, koji_dates, rice_dates, color=nil)
|
14
|
+
@name = name
|
15
|
+
@description = description
|
16
|
+
@recipe = recipe
|
17
|
+
|
18
|
+
@koji_dates = DateIntervalEnumerator.new([], 0).merge(koji_dates, recipe.steps.length)
|
19
|
+
@rice_dates = DateIntervalEnumerator.new([recipe.moto_days, recipe.odori_days+1, 1], 1).merge(rice_dates, recipe.steps.length)
|
20
|
+
|
21
|
+
@color = color
|
22
|
+
end
|
23
|
+
|
24
|
+
def events
|
25
|
+
events = []
|
26
|
+
|
27
|
+
@koji_dates.length.times {|i|
|
28
|
+
events << ProductEvent.new(self, :koji, i)
|
29
|
+
}
|
30
|
+
|
31
|
+
@rice_dates.length
|
32
|
+
.times.map {|i|
|
33
|
+
ProductEvent.new(self, :rice, i)
|
34
|
+
}
|
35
|
+
.delete_if {|e|
|
36
|
+
4<=e.index && e.weight==0
|
37
|
+
}
|
38
|
+
.each {|e|
|
39
|
+
events << e
|
40
|
+
}
|
41
|
+
|
42
|
+
events
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.create(args)
|
46
|
+
if self===args
|
47
|
+
args
|
48
|
+
elsif Hash===args
|
49
|
+
recipe = args.fetch(:recipe)
|
50
|
+
if Symbol===recipe
|
51
|
+
recipe = Recipe::ThreeStepMashing::TEMPLATES.fetch(recipe)
|
52
|
+
end
|
53
|
+
if args[:scale]
|
54
|
+
recipe = recipe.scale(args[:scale])
|
55
|
+
end
|
56
|
+
if args[:round]
|
57
|
+
recipe = recipe.round(args[:round])
|
58
|
+
end
|
59
|
+
if args[:moto_days]
|
60
|
+
recipe.moto_days = args[:moto_days]
|
61
|
+
end
|
62
|
+
if args[:odori_days]
|
63
|
+
recipe.odori_days = args[:odori_days]
|
64
|
+
end
|
65
|
+
|
66
|
+
new(
|
67
|
+
args[:name],
|
68
|
+
args[:description],
|
69
|
+
recipe,
|
70
|
+
args[:koji_dates],
|
71
|
+
args[:rice_dates],
|
72
|
+
args[:color]
|
73
|
+
)
|
74
|
+
else
|
75
|
+
raise "not supported class: #{args.class}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Toji
|
2
|
+
module Schedule
|
3
|
+
class ProductEvent
|
4
|
+
attr_reader :product
|
5
|
+
attr_reader :type
|
6
|
+
attr_reader :index
|
7
|
+
|
8
|
+
def initialize(product, type, index)
|
9
|
+
@product = product
|
10
|
+
@type = type
|
11
|
+
@index = index
|
12
|
+
end
|
13
|
+
|
14
|
+
def date
|
15
|
+
method = "#{@type}_dates".to_sym
|
16
|
+
@product.send(method)[@index]
|
17
|
+
end
|
18
|
+
|
19
|
+
def group_index
|
20
|
+
method = "#{@type}_dates".to_sym
|
21
|
+
dates = @product.send(method)
|
22
|
+
date = dates[@index]
|
23
|
+
|
24
|
+
if @type==:koji && dates.all? {|d| d==date}
|
25
|
+
1
|
26
|
+
else
|
27
|
+
dates.index(date)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def group_key
|
32
|
+
a = []
|
33
|
+
a << product.name
|
34
|
+
a << type
|
35
|
+
a << group_index
|
36
|
+
a.map(&:to_s).join(":")
|
37
|
+
end
|
38
|
+
|
39
|
+
def weight
|
40
|
+
@product.recipe.steps[@index].send(@type).raw
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'toji/schedule/calendar'
|
2
|
+
require 'toji/schedule/product'
|
3
|
+
require 'toji/schedule/product_event'
|
4
|
+
require 'toji/schedule/date_row'
|
5
|
+
require 'toji/schedule/date_column'
|
6
|
+
require 'toji/schedule/date_interval_enumerator'
|
7
|
+
|
8
|
+
module Toji
|
9
|
+
module Schedule
|
10
|
+
end
|
11
|
+
end
|
data/lib/toji/version.rb
CHANGED
data/lib/toji.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: toji
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yoshida Tetsuya
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-03-
|
11
|
+
date: 2020-03-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -115,6 +115,7 @@ files:
|
|
115
115
|
- example/moromi.ipynb
|
116
116
|
- example/moromi.rb
|
117
117
|
- example/rice_recipe.rb
|
118
|
+
- example/schedule.ipynb
|
118
119
|
- example/shubo.ipynb
|
119
120
|
- example/shubo.rb
|
120
121
|
- example/three_step_mashing_recipe.rb
|
@@ -140,7 +141,6 @@ files:
|
|
140
141
|
- lib/toji/recipe/ingredient/koji/base.rb
|
141
142
|
- lib/toji/recipe/ingredient/koji/expected.rb
|
142
143
|
- lib/toji/recipe/ingredient/koji/expected_fermentable.rb
|
143
|
-
- lib/toji/recipe/ingredient/lactic_acid.rb
|
144
144
|
- lib/toji/recipe/ingredient/rice.rb
|
145
145
|
- lib/toji/recipe/ingredient/rice/actual.rb
|
146
146
|
- lib/toji/recipe/ingredient/rice/actual_steamable.rb
|
@@ -149,7 +149,6 @@ files:
|
|
149
149
|
- lib/toji/recipe/ingredient/rice/expected_steamable.rb
|
150
150
|
- lib/toji/recipe/ingredient/yeast.rb
|
151
151
|
- lib/toji/recipe/koji_rate.rb
|
152
|
-
- lib/toji/recipe/lactic_acid_rate.rb
|
153
152
|
- lib/toji/recipe/rice_rate.rb
|
154
153
|
- lib/toji/recipe/rice_rate/base.rb
|
155
154
|
- lib/toji/recipe/rice_rate/cooked.rb
|
@@ -157,6 +156,13 @@ files:
|
|
157
156
|
- lib/toji/recipe/step.rb
|
158
157
|
- lib/toji/recipe/three_step_mashing.rb
|
159
158
|
- lib/toji/recipe/yeast_rate.rb
|
159
|
+
- lib/toji/schedule.rb
|
160
|
+
- lib/toji/schedule/calendar.rb
|
161
|
+
- lib/toji/schedule/date_column.rb
|
162
|
+
- lib/toji/schedule/date_interval_enumerator.rb
|
163
|
+
- lib/toji/schedule/date_row.rb
|
164
|
+
- lib/toji/schedule/product.rb
|
165
|
+
- lib/toji/schedule/product_event.rb
|
160
166
|
- lib/toji/version.rb
|
161
167
|
- toji.gemspec
|
162
168
|
homepage: https://github.com/yoshida-eth0/ruby-toji
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module Toji
|
2
|
-
module Recipe
|
3
|
-
module Ingredient
|
4
|
-
class LacticAcid
|
5
|
-
|
6
|
-
def initialize(total, rate: Recipe::LacticAcidRate::SIMPLE_SOKUJO)
|
7
|
-
@total = total
|
8
|
-
@rate = rate
|
9
|
-
end
|
10
|
-
|
11
|
-
def moto
|
12
|
-
@total * @rate.moto_rate
|
13
|
-
end
|
14
|
-
|
15
|
-
def soe
|
16
|
-
@total * @rate.soe_rate
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Toji
|
2
|
-
module Recipe
|
3
|
-
class LacticAcidRate
|
4
|
-
attr_reader :moto_rate
|
5
|
-
attr_reader :soe_rate
|
6
|
-
|
7
|
-
def initialize(moto_rate, soe_rate)
|
8
|
-
@moto_rate = moto_rate
|
9
|
-
@soe_rate = soe_rate
|
10
|
-
end
|
11
|
-
|
12
|
-
SOKUJO = new(0.007, 0.0)
|
13
|
-
SIMPLE_SOKUJO = new(0.006, 0.002)
|
14
|
-
KIMOTO = new(0.0, 0.0)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|