toji 1.6.8 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/example/calendar.ipynb +383 -0
  3. data/example/{schedule.yaml → calendar.yaml} +6 -30
  4. data/example/calendar_file.ipynb +338 -0
  5. data/example/example_core.rb +354 -0
  6. data/example/kake_ingredient.rb +26 -0
  7. data/example/{koji_recipe.rb → koji_ingredient.rb} +1 -2
  8. data/example/koji_making.ipynb +16 -15
  9. data/example/koji_making.rb +2 -1
  10. data/example/koji_making.yaml +10 -9
  11. data/example/koji_making_multi.ipynb +27 -61
  12. data/example/moromi.ipynb +25 -24
  13. data/example/moromi.rb +1 -0
  14. data/example/moromi.yaml +10 -4
  15. data/example/recipe.rb +75 -0
  16. data/example/shubo.ipynb +15 -14
  17. data/example/shubo.rb +2 -1
  18. data/example/shubo.yaml +12 -11
  19. data/lib/toji.rb +3 -1
  20. data/lib/toji/brew.rb +4 -3
  21. data/lib/toji/brew/base.rb +8 -92
  22. data/lib/toji/brew/builder.rb +77 -6
  23. data/lib/toji/brew/graph/bmd.rb +1 -2
  24. data/lib/toji/brew/graph/multi_progress.rb +0 -121
  25. data/lib/toji/brew/graph/progress.rb +17 -52
  26. data/lib/toji/brew/koji.rb +2 -11
  27. data/lib/toji/brew/moromi.rb +4 -37
  28. data/lib/toji/brew/shubo.rb +2 -6
  29. data/lib/toji/brew/state.rb +90 -103
  30. data/lib/toji/brew/wrapped_state.rb +136 -0
  31. data/lib/toji/calendar.rb +123 -0
  32. data/lib/toji/{schedule → calendar}/date_column.rb +3 -6
  33. data/lib/toji/{schedule → calendar}/date_row.rb +6 -6
  34. data/lib/toji/ingredient.rb +10 -0
  35. data/lib/toji/ingredient/kake.rb +17 -0
  36. data/lib/toji/ingredient/kake/actual.rb +26 -0
  37. data/lib/toji/ingredient/kake/base.rb +18 -0
  38. data/lib/toji/ingredient/kake/expected.rb +40 -0
  39. data/lib/toji/ingredient/koji.rb +19 -0
  40. data/lib/toji/ingredient/koji/actual.rb +29 -0
  41. data/lib/toji/ingredient/koji/actual_fermentable.rb +15 -0
  42. data/lib/toji/ingredient/koji/base.rb +35 -0
  43. data/lib/toji/ingredient/koji/expected.rb +45 -0
  44. data/lib/toji/ingredient/koji/expected_fermentable.rb +15 -0
  45. data/lib/toji/{recipe → ingredient}/koji_rate.rb +1 -1
  46. data/lib/toji/ingredient/rice.rb +10 -0
  47. data/lib/toji/ingredient/rice/actual_steamable.rb +27 -0
  48. data/lib/toji/ingredient/rice/base.rb +40 -0
  49. data/lib/toji/ingredient/rice/expected_steamable.rb +27 -0
  50. data/lib/toji/ingredient/rice_rate.rb +23 -0
  51. data/lib/toji/product.rb +65 -0
  52. data/lib/toji/{schedule/product_event.rb → product/event.rb} +4 -4
  53. data/lib/toji/recipe.rb +111 -5
  54. data/lib/toji/recipe/step.rb +49 -64
  55. data/lib/toji/version.rb +1 -1
  56. metadata +33 -39
  57. data/example/rice_recipe.rb +0 -28
  58. data/example/schedule.ipynb +0 -393
  59. data/example/schedule_file.ipynb +0 -337
  60. data/example/three_step_mashing_recipe.rb +0 -67
  61. data/lib/toji/brew/state_accessor.rb +0 -13
  62. data/lib/toji/brew/state_record.rb +0 -72
  63. data/lib/toji/recipe/ingredient.rb +0 -10
  64. data/lib/toji/recipe/ingredient/koji.rb +0 -21
  65. data/lib/toji/recipe/ingredient/koji/actual.rb +0 -32
  66. data/lib/toji/recipe/ingredient/koji/actual_fermentable.rb +0 -17
  67. data/lib/toji/recipe/ingredient/koji/base.rb +0 -37
  68. data/lib/toji/recipe/ingredient/koji/expected.rb +0 -48
  69. data/lib/toji/recipe/ingredient/koji/expected_fermentable.rb +0 -17
  70. data/lib/toji/recipe/ingredient/rice.rb +0 -21
  71. data/lib/toji/recipe/ingredient/rice/actual.rb +0 -29
  72. data/lib/toji/recipe/ingredient/rice/actual_steamable.rb +0 -29
  73. data/lib/toji/recipe/ingredient/rice/base.rb +0 -51
  74. data/lib/toji/recipe/ingredient/rice/expected.rb +0 -43
  75. data/lib/toji/recipe/ingredient/rice/expected_steamable.rb +0 -31
  76. data/lib/toji/recipe/ingredient/yeast.rb +0 -21
  77. data/lib/toji/recipe/rice_rate.rb +0 -10
  78. data/lib/toji/recipe/rice_rate/base.rb +0 -21
  79. data/lib/toji/recipe/rice_rate/cooked.rb +0 -67
  80. data/lib/toji/recipe/rice_rate/steamed.rb +0 -30
  81. data/lib/toji/recipe/three_step_mashing.rb +0 -274
  82. data/lib/toji/recipe/yeast_rate.rb +0 -41
  83. data/lib/toji/schedule.rb +0 -11
  84. data/lib/toji/schedule/calendar.rb +0 -139
  85. data/lib/toji/schedule/date_interval_enumerator.rb +0 -45
  86. data/lib/toji/schedule/product.rb +0 -117
@@ -1,67 +0,0 @@
1
- module Toji
2
- module Recipe
3
- module RiceRate
4
- class Cooked
5
- include Base
6
-
7
- # 炊飯器を使う場合
8
- #
9
- # 浸漬。
10
- # 25〜30分くらい浸漬させないと中心部まで水を吸わない。
11
- # => 吸水歩合 約1.30。
12
- #
13
- # 炊飯。
14
- # 吸水と炊く用の水を合わせて歩合1.50くらいにしないと中心部まで火が通らない、且つ側面が焦げる。
15
- # 圧力がかかる熟成炊きみたいな長めの炊飯が良い。
16
- # => 蒸米歩合 約1.40。
17
- #
18
- # 白米の60%の水で炊くと蒸米吸水率はだいたい38%くらいになる
19
- # 炊飯、放冷の過程で水分のうち36%程度は蒸発する
20
- #
21
- # 放冷。
22
- # 歩合1.36〜1.37くらいになる。
23
- # 更に歩合を下げたければ炊飯までは同じで、放冷に注力するのが良い。
24
-
25
- # 重量推移の例
26
- #
27
- # 浸漬
28
- # before
29
- # 白米(無洗米コシヒカリ) 100g
30
- # after
31
- # 吸水後白米 130g
32
- # (吸水歩合 30%)
33
- #
34
- # 炊飯
35
- # before
36
- # 吸水後白米 130g
37
- # 炊飯用水 20g
38
- # (吸水歩合+炊飯水率 50%)
39
- # after
40
- # 炊飯米 140g
41
- # (炊飯米吸水率 40%)
42
- #
43
- # 放冷
44
- # before
45
- # 炊飯米 140g
46
- # after
47
- # 放冷後炊飯米 136g
48
- # (放冷後炊飯米吸水率 36%)
49
-
50
- def initialize(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
51
- @soaked_rate = soaked_rate
52
- @before_steaming_rate = before_steaming_rate
53
- @steamed_rate = steamed_rate
54
- @cooled_rate = cooled_rate
55
- end
56
-
57
-
58
- DEFAULT = new(0.30, 0.5, 0.40, 0.36)
59
- end
60
-
61
-
62
- def self.cooked(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
63
- Cooked.new(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
64
- end
65
- end
66
- end
67
- end
@@ -1,30 +0,0 @@
1
- module Toji
2
- module Recipe
3
- module RiceRate
4
- class Steamed
5
- include Base
6
-
7
- def initialize(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
8
- @soaked_rate = soaked_rate
9
- @before_steaming_rate = before_steaming_rate
10
- @steamed_rate = steamed_rate
11
- @cooled_rate = cooled_rate
12
- end
13
-
14
- def before_steaming_rate
15
- @before_steaming_rate || begin
16
- @soaked_rate - 0.04
17
- end
18
- end
19
-
20
-
21
- DEFAULT = new(0.33, nil, 0.41, 0.33)
22
- end
23
-
24
-
25
- def self.steamed(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
26
- Steamed.new(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
27
- end
28
- end
29
- end
30
- end
@@ -1,274 +0,0 @@
1
- module Toji
2
- module Recipe
3
- class ThreeStepMashing
4
-
5
- attr_reader :steps
6
- attr_reader :yeast
7
-
8
- attr_accessor :moto_days
9
- attr_accessor :odori_days
10
-
11
- def initialize(steps, yeast_rate, moto_days: 1, odori_days: 1)
12
- @steps = steps
13
-
14
- @yeast_rate = yeast_rate
15
- weight_total = @steps.map(&:weight_total).sum
16
- @yeast = Ingredient::Yeast.new(weight_total, rate: yeast_rate)
17
-
18
- @moto_days = moto_days
19
- @odori_days = odori_days
20
- end
21
-
22
- def scale(rice_total, yeast_rate=@yeast_rate)
23
- rate = rice_total / @steps.map(&:rice_total).sum
24
- new_steps = @steps.map {|step|
25
- step * rate
26
- }
27
-
28
- self.class.new(new_steps, yeast_rate, moto_days: @moto_days, odori_days: @odori_days)
29
- end
30
-
31
- def round(ndigit=0, half: :up)
32
- new_steps = @steps.map {|step|
33
- step.round(ndigit, half: half)
34
- }
35
- self.class.new(new_steps, @yeast_rate, moto_days: @moto_days, odori_days: @odori_days)
36
- end
37
-
38
- # 内容量の累計
39
- def cumulative_weight_totals
40
- weight_total = @steps.map(&:weight_total)
41
-
42
- weight_total.map.with_index {|x,i|
43
- weight_total[0..i].inject(:+)
44
- }
45
- end
46
-
47
- # 総米の累計
48
- def cumulative_rice_totals
49
- rice_total = @steps.map(&:rice_total)
50
-
51
- rice_total.map.with_index {|x,i|
52
- rice_total[0..i].inject(&:+)
53
- }
54
- end
55
-
56
- # 酒母歩合の累計
57
- def cumulative_shubo_rates
58
- rice_total = @steps.map(&:rice_total)
59
- shubo = rice_total[0]
60
-
61
- rice_total.map.with_index {|x,i|
62
- shubo / rice_total[0..i].inject(&:+)
63
- }
64
- end
65
-
66
- # 酒母歩合
67
- #
68
- # 7%が標準である
69
- # 汲水歩合が大きい高温糖化酒母では6%程度である
70
- #
71
- # 出典: 酒造教本 P96
72
- def shubo_rate
73
- cumulative_shubo_rates.last || 0.0
74
- end
75
-
76
- # 白米比率
77
- #
78
- # 発酵型と白米比率
79
- # 発酵の型 酒母 添 仲 留 特徴
80
- # 湧き進め型 1 2 4 6 短期醪、辛口、軽快な酒質
81
- # 湧き抑え型 1 2 4 7 長期醪、甘口、おだやかな酒質
82
- #
83
- # 出典: 酒造教本 P95
84
- def rice_rates
85
- @steps.map {|step|
86
- step.rice_total / @steps[0].rice_total
87
- }
88
- end
89
-
90
- def to_h
91
- {
92
- steps: steps.map(&:to_h),
93
- cumulative_rice_totals: cumulative_rice_totals,
94
- cumulative_shubo_rates: cumulative_shubo_rates,
95
- shubo_rate: shubo_rate,
96
- rice_rates: rice_rates,
97
- }
98
- end
99
-
100
- def table_data
101
- headers = [""] + @steps.map(&:name) + [:total]
102
- keys = [:rice_total, :rice, :koji, :alcohol, :water, :lactic_acid]
103
-
104
- cells = [keys]
105
- cells += @steps.map {|step|
106
- [step.rice_total, step.rice, step.koji, step.alcohol, step.water, step.lactic_acid]
107
- }
108
- cells << keys.map {|key|
109
- @steps.map(&key).compact.sum
110
- }
111
-
112
- cells = cells.map {|cell|
113
- cell.map {|c|
114
- if Ingredient::Rice::Base===c
115
- c = c.raw
116
- end
117
-
118
- case c
119
- when NilClass
120
- ""
121
- when 0
122
- ""
123
- else
124
- c
125
- end
126
- }
127
- }
128
-
129
- {header: headers, rows: cells.transpose}
130
- end
131
-
132
- def table
133
- data = table_data
134
-
135
- Plotly::Plot.new(
136
- data: [{
137
- type: :table,
138
- header: {
139
- values: data[:header]
140
- },
141
- cells: {
142
- values: data[:rows].transpose
143
- },
144
- }],
145
- layout: {
146
- }
147
- )
148
- end
149
-
150
-
151
- # 乳酸は汲水100L当たり比重1.21の乳酸(90%乳酸と称される)を650〜720ml添加する。
152
- # ここでは間をとって685mlとする
153
- #
154
- # 出典: 酒造教本 P38
155
- TEMPLATES = {
156
- # 酒造教本による標準型仕込配合
157
- # 出典: 酒造教本 P97
158
- sokujo_textbook: new(
159
- [
160
- Step.new(
161
- name: :moto,
162
- rice: 45,
163
- koji: 20,
164
- water: 70,
165
- lactic_acid: 70*6.85/1000
166
- ),
167
- Step.new(
168
- name: :soe,
169
- rice: 100,
170
- koji: 40,
171
- water: 130
172
- ),
173
- Step.new(
174
- name: :naka,
175
- rice: 215,
176
- koji: 60,
177
- water: 330
178
- ),
179
- Step.new(
180
- name: :tome,
181
- rice: 360,
182
- koji: 80,
183
- water: 630
184
- ),
185
- Step.new(
186
- name: :yodan,
187
- rice: 80,
188
- water: 120
189
- ),
190
- ],
191
- YeastRate::RED_STAR,
192
- moto_days: 14,
193
- odori_days: 1,
194
- ),
195
- # 灘における仕込配合の平均値
196
- # 出典: http://www.nada-ken.com/main/jp/index_shi/234.html
197
- sokujo_nada: new(
198
- [
199
- Step.new(
200
- name: :moto,
201
- rice: 93,
202
- koji: 47,
203
- water: 170,
204
- lactic_acid: 170*6.85/1000
205
- ),
206
- Step.new(
207
- name: :soe,
208
- rice: 217,
209
- koji: 99,
210
- water: 270
211
- ),
212
- Step.new(
213
- name: :naka,
214
- rice: 423,
215
- koji: 143,
216
- water: 670
217
- ),
218
- Step.new(
219
- name: :tome,
220
- rice: 813,
221
- koji: 165,
222
- water: 1330
223
- ),
224
- Step.new(
225
- name: :alcohol,
226
- alcohol: 900
227
- ),
228
- ],
229
- YeastRate::RED_STAR,
230
- moto_days: 14,
231
- odori_days: 1,
232
- ),
233
- # 簡易酒母省略仕込
234
- # 出典: https://www.jstage.jst.go.jp/article/jbrewsocjapan1915/60/11/60_11_999/_article/-char/ja/
235
- simple_sokujo_himeno: new(
236
- [
237
- Step.new(
238
- name: :moto,
239
- rice: 0,
240
- koji: 70,
241
- water: 245,
242
- lactic_acid: 1.6
243
- ),
244
- Step.new(
245
- name: :soe,
246
- rice: 130,
247
- koji: 0,
248
- water: 0
249
- ),
250
- Step.new(
251
- name: :naka,
252
- rice: 300,
253
- koji: 100,
254
- water: 400
255
- ),
256
- Step.new(
257
- name: :tome,
258
- rice: 490,
259
- koji: 110,
260
- water: 800
261
- ),
262
- Step.new(
263
- name: :yodan,
264
- water: 255
265
- ),
266
- ],
267
- YeastRate::RED_STAR,
268
- moto_days: 1,
269
- odori_days: 2,
270
- ),
271
- }.freeze
272
- end
273
- end
274
- end
@@ -1,41 +0,0 @@
1
- module Toji
2
- module Recipe
3
- class YeastRate
4
-
5
- # 1リットル醸造のために必要な酵母の量
6
- attr_reader :yeast_rate
7
-
8
- # 乾燥酵母を戻すのに必要な水の量
9
- attr_reader :water_rate
10
-
11
- def initialize(yeast_rate, water_rate)
12
- @yeast_rate = yeast_rate
13
- @water_rate = water_rate
14
- end
15
-
16
- # RedStar酵母
17
- #
18
- # 容量 5g
19
- # 醸造可能量 20〜23L
20
- #
21
- # ドライイーストは、生イーストの保存性を高めるために、その水分を大部分除いたものです。
22
- # 使用時にはイーストの10倍以上の30-35℃の無菌のお湯(ミネラルウオーターや湯冷まし)で20-25分程度なじませてください。
23
- # これにより水分を再吸収させると同 時に発酵力を回復させ、生イーストの状態にもどします。
24
- # このときの温湯の温度が、イーストの発酵力に影響します
25
- RED_STAR = new(5.0 / 20.0, 10.0)
26
-
27
- # 酒粕
28
- #
29
- # 以下の計算式を用いて酒粕にどの程度水を足せば醪時点での緩さになるかを算出する。
30
- # 計算が面倒なので麹歩合は無視して蒸米時点での重量とする。
31
- # (白米重量 * 蒸米歩合 + 白米重量 * 汲水歩合) / (粕歩合 * 100) - 1
32
- #
33
- # 蒸米歩合135%、汲水歩合130%、粕歩合30%の場合、酒粕に対して7.833倍の水を足せば理論上醪と同じ緩さになる。
34
- # (100 * 1.35 + 100 * 1.3) / (0.3 * 100) - 1
35
- # => 7.833333333333334
36
- #
37
- # しかし櫂入れ出来る程度の緩さになれば良いのではないだろうか。
38
- SAKE_LEES = new(5.0, 3.0)
39
- end
40
- end
41
- end
@@ -1,11 +0,0 @@
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
@@ -1,139 +0,0 @@
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
- headers += [:moto, :soe, :naka, :tome, :yodan][0...rice_len]
51
- (5...rice_len).each {|i|
52
- headers << "#{i}dan"
53
- }
54
-
55
- _date_rows = date_rows
56
-
57
- rows = []
58
-
59
- date = min_date
60
- while date<=max_date
61
- columns = [date.strftime("%m/%d")]
62
-
63
- date_row = _date_rows[date]
64
- if date_row
65
- koji_len.times {|i|
66
- columns << (date_row.kojis[i]&.to_h_a || [])
67
- }
68
- rice_len.times {|i|
69
- columns << (date_row.rices[i]&.to_h_a || [])
70
- }
71
- else
72
- (koji_len + rice_len).times {
73
- columns << []
74
- }
75
- end
76
-
77
- rows << columns
78
-
79
- date = date.tomorrow
80
- end
81
-
82
- {header: headers, rows: rows}
83
- end
84
-
85
- def table_text_data
86
- data = table_data
87
- data[:rows] = data[:rows].map {|row|
88
- row.map {|column|
89
- if Array===column
90
- column.map{|c|
91
- name = c[:product_name]
92
- weight = "%.17g" % c[:weight]
93
- "#{name}: #{weight}"
94
- }.join("<br>")
95
- else
96
- column
97
- end
98
- }
99
- }
100
- data
101
- end
102
-
103
- def table
104
- data = table_text_data
105
-
106
- Plotly::Plot.new(
107
- data: [{
108
- type: :table,
109
- header: {
110
- values: data[:header]
111
- },
112
- cells: {
113
- values: data[:rows].transpose
114
- },
115
- }],
116
- layout: {
117
- }
118
- )
119
- end
120
-
121
- def self.load_hash(hash)
122
- hash = hash.deep_symbolize_keys
123
- products = hash[:products] || []
124
-
125
- cal = new
126
- products.each {|product|
127
- cal.add(Product.create(product))
128
- }
129
-
130
- cal
131
- end
132
-
133
- def self.load_yaml_file(fname)
134
- hash = YAML.load_file(fname)
135
- load_hash(hash)
136
- end
137
- end
138
- end
139
- end