-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcount_csv_from_export.rb
More file actions
331 lines (298 loc) · 9.79 KB
/
Copy pathcount_csv_from_export.rb
File metadata and controls
331 lines (298 loc) · 9.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# coding: utf-8
require 'csv'
require 'time'
require 'erb'
def erb_result(str, b)
#ERB.new(str, nil, '-').result b
ERB.new(str, trim_mode: '-').result b
# str, safe_level=nil, trim_mode='-'
end # def erb_result(str, b)
require 'i18n'
I18n.load_path += Dir[File.join(File.dirname(__FILE__),'locale','*.{rb,yml}')]
module TandL
@@effective = false
def self.effective; @@effective; end
def self.effective=(v)
@@effective = v
end # def self.effective=(v)
private
def l(object, **options)
@@effective ? I18n.l(object, **options) : object
end # def l(object, *options)
def t(object, **options)
#@@effective ? I18n.t(object, *options) : object
#@@effective ? I18n.t(object, raise: true) : object
result = @@effective ? I18n.t(object, **options) : object
unless /translation missing: / =~ result then
result
else# unless /translation missing: / =~ result
result.+("\n").display $stderr
object
end # unless /translation missing: / =~ result
end # def t(object, *options)
def u(object, type=nil)
t object, scope: [:unit, type]
end # def u(object, type=nil)
# private
end # module TandL
module TypeDates
Headers = %w[type startDate endDate creationDate sourceName sourceVersion]
# Headers + %w[value unit]
Rel = %w[type value unit] # Correlation
Headers.each{ |key| define_method(key){ field key }}
def value
field Headers.size+0 unless /Correlation/ =~ type
end # def value
def unit
field Headers.size+1 unless /Correlation/ =~ type
end # def unit
def values
values_at (Headers.size+0)..-1
end # def values
def behinds
values_at (Headers.size+2)..-1
end # def behinds
def rels
if /Correlation/ =~ type then
values.each_slice(3) \
.map do |rel|
rel.extend \
Module.new{ Rel.each_with_index{ |k, i| define_method(k){ slice i }}}
end \
.sort.reverse
# type の文字列順、reverse で血圧が収縮期(Systolic)拡張期(Diastolic)の順に
end # if /Correlation/ =~ type
end # def rels
end # module TypeDates
module Integrate
def integrate
each_with_object([0.0, 0.0, 0.0]) do |row, s_w_i|
delta = (row.endDate - row.startDate)
s_w_i[0] += row.value.to_f # s_um
s_w_i[1] += row.value.to_f * delta # w_eighted_sum
s_w_i[2] += delta # i_nterval
end # each_with_object([0.0, 0.0, 0.0]) do |row, s_w_i|
end # def integrate
private
def minute(interval)
'(%4.1f %s)' % [interval/60, u('min')]
end # def minute(interval)
def hour_min(interval)
case interval
when 0...60*100 # 概ね1時間半以下
minute interval
when 60*91...3600*24 # 1日未満は、時間 分、表示/
time = Time.at(interval).utc
'(%2d%s%2d%s)' % [time.hour, u('hour'), time.min, u('min')]
else
days interval
end # if interval < 60*91
end # def hour_min(interval)
def days(interval)
'(%4.1f %s)' % [interval/(60*60 * 24), u('days')]
end # def days(interval)
# private
end # module Integrate
class Counts < Hash
include TandL
def initialize
super() do |hash, key|
arr = []
arr.extend TandL, Integrate
case key
when /Correlation/ then
def arr.report
map do |row|
[ row.startDate.strftime('%H:%M'),
row.rels.map{ |rel|
[t(rel.type), rel.value, u(rel.unit)].join(' ')
}.join(' / ')
].join(' ')
end # map do |row|
end # def arr.report
when /StepCount/,/FlightsClimbed/,/Burned/ then
def arr.report
#sum = inject(0){|s, row| s + row.value.to_i }
#[" %d %s" % [sum, u(first.unit, first.type)]]
sum, w, interval = self.integrate
[[' %5d' % sum.to_i,
'%s ' % u(first.unit, first.type),
#minute(interval),
hour_min(interval),
].join(' ')]
end # def arr.report
when /DistanceWalkingRunning/ then
def arr.report
#sum = inject(0.0){|s, row| s + row.value.to_f }
#[" %.3f %s %s" % [sum, u(first.unit), interval/60]]
sum, w, interval = self.integrate
[[' %.3f' % sum,
'%s' % u(first.unit),
minute(interval),
].join(' ')]
end # def arr.report
when /erWalking/ then
# HKQuantityTypeIdentifierWalking..
def arr.report
s, weighted, interval = self.integrate
w_sum = if first.unit=='%' then
'%6.1f' % (weighted/interval * 100)
else# if first.unit=='%'
'%6.3f' % (weighted/interval)
end # if first.unit=='%'
[[' ',
w_sum,
'%-4s'.%('%2s'.%(u first.unit)),
minute(interval),
].join(' ')]
end # def arr.report
when /AppleWalkingSteadiness/ then
def arr.report
map do |row|
[' ',
'%3.1f %s' % [row.value.to_f*100, u(row.unit)],
days(row.endDate - row.startDate),
].join(' ')
end # map do |row|
end # def arr.report
when /BodyMassIndex/ then # /BodyMass/ より前にマッチしましょう
def arr.report
map do |row|
[ row.startDate.strftime('%H:%M'),
'%4.1f %s' % [row.value, u(row.unit, row.type)],
].join(' ')
end # map do |row|
end # def arr.report
when /BodyMass/,/BodyTemperature/ then
def arr.report
map do |row|
[ row.startDate.strftime('%H:%M'),
'%4.1f %s' % [row.value, u(row.unit)],
].join(' ')
end # map do |row|
end # def arr.report
when /HeadphoneAudioExposure/ then
def arr.report
map do |row|
[ row.startDate.strftime('%H:%M'),
'%4.1f %s' % [row.value, u(row.unit)],
minute(row.endDate-row.startDate),
].join(' ')
end # map do |row|
end # def arr.report
else
def arr.report
map do |row|
[ row.startDate.strftime('%H:%M'),
#[row.value, u(row.unit)],
row.unit == '%' ? [row.value.to_f*100, u(row.unit)] : [row.value, u(row.unit)],
row.behinds,
].flatten.join(' ')
end # map do |row|
end # def arr.report
end # case key
hash[key] = arr
end # super() do |hash, key|
end # def initialize
def report
map do |key, arr|
next if /BloodPressure(Systolic|Diastolic)/ =~ key
keyword = key.sub /HK.*Identifier/, ''
erb_result(<<-EOReport, binding).force_encoding 'UTF-8'
<%# coding: utf-8 -%>
<%=t keyword %>:
<%- arr.report.each do |line| -%>
<%= line.gsub keyword, '' %>
<%- end -%>
EOReport
# gsub keyword で BloodPressure の表示をちょっと抑制
# それで translation missing エラーメッセージから識別が失われるの注意
end \
.join
end # def report
end # class Counts < Hash
class DailyCounts < Hash
include TandL
def initialize
super() do |hash, key|
hash[key] = Counts.new
end # super() do |hash, key|
end # def initialize
def add(row)
row.extend TypeDates
self[Date.parse row.startDate.to_s][row.type] << row
self
end # def add(row)
def report(startDate, endDate, options)
reverse_or = options[:reverse] ? :reverse_each : :each
startDate ||= keys.min
endDate ||= keys.max
(startDate..endDate).send(reverse_or).map do |day|
erb_result(<<-EOReport, binding).force_encoding 'UTF-8'
<%# coding: utf-8 -%>
<%=l day %>:
<%= self[day].report %>
EOReport
end.join
end # def report(startDate, endDate, options)
Entry = <<-EntryEnd # author title date category body img
AUTHOR: %<author>s
TITLE: %<title>s
DATE: %<date>s
PRIMARY CATEGORY: %<category>s
-----
BODY:
%<body>s%<img>s
-----
--------
EntryEnd
def to_mt(startDate, endDate, options)
reverse_or = options[:reverse] ? :reverse_each : :each
startDate ||= keys.min
endDate ||= keys.max
(startDate..endDate).send(reverse_or).map do |day|
Entry % {
author: 'ヘルスケア',
title: l(day),
date: day.strftime('%m/%d/%Y 00:00:00'),
category: 'ヘルスケア',
body: self[day].report.gsub(/^ /,'') \
.gsub(/ /,' ').chomp,
img: nil,
}
#body: self[day].report.gsub(/^ /,'').gsub(/ +/,' ').chomp,
end.join
end # def to_mt(startDate, endDate, options)
end # class DailyCounts < Hash
CSV::Converters[:time13] = ->(cell, info) \
{ ((1..3).include? info.index) ? Time.parse(cell) : cell }
if $PROGRAM_NAME == __FILE__ then
require 'optparse'
OptionParser.accept(Date) do |s,|
begin
Date.parse(s) if s
rescue
raise OptionParser::InvalidArgument, s
end
end
TandL.effective = false
report = :report
startDate, endDate = nil, nil
reverse = false
ARGV.options do |opts|
opts.banner += ' <path to (oga_)export.csv> or stdin'
opts.on('-l L', '--locale=L', 'activate translation to [ja|en]') \
{ |l| TandL.effective = true; I18n.locale = l.to_sym }
opts.on( '--to_mt', 'report to MT') \
{ report = :to_mt }
opts.on('--startDate=DATE', Date, 'format 2020/08/01') \
{ |date| startDate = date }
opts.on('--endDate=DATE', Date, 'format 2020/08/31') \
{ |date| endDate = date }
opts.on('--reverse', 'reverse order in dates') { |r| reverse = true }
opts.parse!
end # ARGV.options do |opts|
CSV.parse(ARGF.read, converters: :time13, headers: TypeDates::Headers) \
.inject(DailyCounts.new){ |dcs, row| dcs.add row } \
.send(report, startDate, endDate, reverse: reverse).display
end # if $PROGRAM_NAME == __FILE__