shunkuntype 1.1.0 → 1.1.2

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.
@@ -0,0 +1,791 @@
1
+ daddygongon:  shunkuntype -c
2
+ Shunkuntype says 'Hello world'.
3
+ /Users/bob/.shunkuntype/speed_data.txt does not exist in this directory. --init or try in another dir.
4
+ とのエラー
5
+
6
+ GitHub Copilot: このエラーは、`/Users/bob/.shunkuntype/speed_data.txt` というファイルが存在しないために発生しています。
7
+ `--init` オプションを使って初期化することで解決できます。
8
+
9
+ **コマンド例:**
10
+ ```sh
11
+ shunkuntype --init
12
+ ```
13
+
14
+ これで必要なファイルやディレクトリが作成され、エラーが解消されるはずです。
15
+
16
+ daddygongon: --initがありません.
17
+ #file:speed.rb:31-31 からDataFilesに投げて,そのdirectoryを作るmethodを呼び出すように改良
18
+
19
+ GitHub Copilot: 以下のように修正できます。
20
+
21
+ 1. speed.rb の `check_data_files` で `--init` が無い場合、`DataFiles.prepare` を呼び出して必要なディレクトリ・ファイルを作成するようにします。
22
+ 2. `require_relative` で db.rb を読み込むようにします。
23
+
24
+ ```ruby
25
+ require "shunkuntype"
26
+ require_relative "db" # 追加
27
+
28
+ class SpeedCheck
29
+ attr_reader :number, :period
30
+
31
+ def initialize
32
+ @number = 20 #default 20
33
+ @period = 60
34
+ check_data_files
35
+ data = mk_random_words
36
+ t0, t1, count = exec_speed_check(data)
37
+ # ここでnilチェック
38
+ if t0.nil? || t1.nil?
39
+ puts "Speed check was interrupted or no input was given."
40
+ return
41
+ end
42
+ keep_record(t0, t1, count)
43
+ end
44
+
45
+ def print_keyboard
46
+ Shunkuntype.print_keyboard
47
+ end
48
+
49
+ def check_data_files
50
+ begin
51
+ file = open(Shunkuntype::SPEED_FILE, "r")
52
+ if file
53
+ puts "#{Shunkuntype::SPEED_FILE} opened succcessfully"
54
+ end
55
+ rescue
56
+ puts "#{Shunkuntype::SPEED_FILE} does not exist in this directory. Creating required files..."
57
+ DataFiles.prepare # ここでDataFilesに投げる
58
+ retry # 再度ファイルオープンを試みる
59
+ end
60
+ end
61
+
62
+ # ...existing code...
63
+ ```
64
+
65
+ この修正で、`SPEED_FILE` が無い場合に自動で初期化処理が走るようになります。
66
+
67
+ daddygongon:  shunkuntype -c
68
+ Shunkuntype says 'Hello world'.
69
+ /Users/bob/.shunkuntype/speed_data.txt does not exist in this directory. Creating required files...
70
+ で無限loopに入ってます.
71
+
72
+ GitHub Copilot: `check_data_files` の `retry` で、`DataFiles.prepare` がファイルを作成できていない場合、無限ループになります。
73
+ `DataFiles.prepare` が `Shunkuntype::SPEED_FILE` を必ず作成するように修正するか、`check_data_files` で `retry` の回数を制限しましょう。
74
+
75
+ **簡単な修正例(retryを1回だけにする):**
76
+
77
+ ```ruby
78
+ def check_data_files
79
+ retried = false
80
+ begin
81
+ file = open(Shunkuntype::SPEED_FILE, "r")
82
+ if file
83
+ puts "#{Shunkuntype::SPEED_FILE} opened succcessfully"
84
+ end
85
+ rescue
86
+ if retried
87
+ puts "Failed to create #{Shunkuntype::SPEED_FILE}. Please check permissions."
88
+ exit 1
89
+ end
90
+ puts "#{Shunkuntype::SPEED_FILE} does not exist in this directory. Creating required files..."
91
+ DataFiles.prepare
92
+ retried = true
93
+ retry
94
+ end
95
+ end
96
+ ```
97
+
98
+ これで無限ループを防げます。
99
+ `DataFiles.prepare` 側も、`Shunkuntype::SPEED_FILE` を必ず作成するように見直すとより安全です。
100
+
101
+ daddygongon:  shunkuntype -c
102
+ Shunkuntype says 'Hello world'.
103
+ /Users/bob/.shunkuntype/speed_data.txt does not exist in this directory. Creating required files...
104
+ Failed to create /Users/bob/.shunkuntype/speed_data.txt. Please check permissions.
105
+
106
+ GitHub Copilot: `DataFiles.prepare` で `speed_data.txt` を作成していないため、`check_data_files` の再試行でもファイルが存在せず、失敗しています。
107
+
108
+ **修正案:`speed_data.txt` も必ず作成するようにしましょう。**
109
+
110
+ ```ruby
111
+ module DataFiles
112
+
113
+ def self.prepare
114
+ speed_path = Shunkuntype::SPEED_FILE
115
+ training_path = Shunkuntype::TRAINING_FILE
116
+
117
+ create_file_if_not_exists(speed_path)
118
+ create_file_if_not_exists(training_path)
119
+ end
120
+
121
+ def self.create_file_if_not_exists(path)
122
+ create_file_path(path)
123
+ unless File.exist?(path)
124
+ File.open(path, 'w') {} # 空ファイルを作成
125
+ puts "Created #{path}"
126
+ end
127
+ end
128
+
129
+ def self.create_file_path(path)
130
+ FileUtils.mkdir_p File.dirname(path)
131
+ end
132
+
133
+ # create_data_filesは不要なので削除
134
+ private_class_method :create_file_if_not_exists, :create_file_path
135
+ end
136
+ ```
137
+
138
+ この修正で、`speed_data.txt` と `training_data.txt` が必ず作成され、無限ループやエラーが解消されます。
139
+
140
+ daddygongon: -pでtraining, speedデータを見やすく出力する機能を追加.
141
+
142
+ GitHub Copilot: `-p` オプションで training, speed データを見やすく出力するには、
143
+ コマンドライン引数を判定し、各ファイルを整形して表示するメソッドを追加します。
144
+
145
+ 例: `bin/shunkuntype` に以下を追加してください。
146
+
147
+ ````ruby
148
+ # ...existing code...
149
+
150
+ if ARGV.include?('-p')
151
+ require_relative '../lib/shunkuntype'
152
+ require_relative '../lib/shunkuntype/db'
153
+
154
+ def print_csv_pretty(path, headers)
155
+ if File.exist?(path)
156
+ puts "== #{File.basename(path)} =="
157
+ puts headers.join("\t")
158
+ File.foreach(path) do |line|
159
+ puts line.chomp.split(',').join("\t")
160
+ end
161
+ puts
162
+ else
163
+ puts "#{path} not found."
164
+ end
165
+ end
166
+
167
+ print_csv_pretty(Shunkuntype::SPEED_FILE, %w[Time Words TimeSec Score])
168
+ print_csv_pretty(Shunkuntype::TRAINING_FILE, %w[Time Words TimeSec Score])
169
+ exit
170
+ end
171
+
172
+ # ...existing code...
173
+ ````
174
+
175
+ **ポイント**
176
+ - `print_csv_pretty` でカンマ区切りをタブ区切りで見やすく出力
177
+ - ファイルが無ければその旨を表示
178
+ - `-p` オプションでのみ実行
179
+
180
+ 必要に応じてヘッダーや整形内容は調整してください。
181
+
182
+ daddygongon: Commandの中のmethodにその機能を追加.
183
+
184
+ GitHub Copilot: ````ruby
185
+ class Command
186
+
187
+ def self.run(argv=[])
188
+ print "Shunkuntype says 'Hello world'.\n"
189
+ new(argv).execute
190
+ end
191
+
192
+ def initialize(argv=[])
193
+ @argv = argv
194
+ end
195
+
196
+ def execute
197
+ DataFiles.prepare
198
+
199
+ # -p オプションでデータ表示
200
+ if @argv.include?('-p')
201
+ print_pretty_data
202
+ exit
203
+ end
204
+
205
+ @argv << '--help' if @argv.size==0
206
+ command_parser = OptionParser.new do |opt|
207
+ opt.on('-v', '--version','show program Version.') { |v|
208
+ opt.version = Shunkuntype::VERSION
209
+ puts opt.ver
210
+ }
211
+ opt.on('-c', '--check','Check speed') {|v| SpeedCheck.new }
212
+ opt.on('-d', '--drill [VAL]','one minute Drill [VAL]', Integer) {|v| Training.new(v) }
213
+ opt.on('-h', '--help','show help message') { puts opt; exit }
214
+ opt.on('-l','--log','view training log') {|v| FinishCheck.new }
215
+ opt.on('-p', '--pretty','Show training and speed data in readable format') {}
216
+ end
217
+ command_parser.parse!(@argv)
218
+ exit
219
+ end
220
+
221
+ private
222
+
223
+ def print_pretty_data
224
+ print_csv_pretty(Shunkuntype::SPEED_FILE, %w[Time Words TimeSec Score])
225
+ print_csv_pretty(Shunkuntype::TRAINING_FILE, %w[Time Words TimeSec Score])
226
+ end
227
+
228
+ def print_csv_pretty(path, headers)
229
+ if File.exist?(path)
230
+ puts "== #{File.basename(path)} =="
231
+ puts headers.join("\t")
232
+ File.foreach(path) do |line|
233
+ puts line.chomp.split(',').join("\t")
234
+ end
235
+ puts
236
+ else
237
+ puts "#{path} not found."
238
+ end
239
+ end
240
+ end
241
+ ````
242
+
243
+ Similar code found with 1 license type
244
+
245
+ daddygongon: -p=pythonとするとpythonのmatplotlibで描画するcodeを出力する機能を追加
246
+
247
+ GitHub Copilot: ````ruby
248
+ class Command
249
+
250
+ def execute
251
+ DataFiles.prepare
252
+
253
+ # -p オプションでデータ表示
254
+ if @argv.any? { |arg| arg.start_with?('-p') }
255
+ arg = @argv.find { |a| a.start_with?('-p') }
256
+ if arg.include?('=')
257
+ lang = arg.split('=', 2)[1]
258
+ if lang == 'python'
259
+ print_python_plot_code
260
+ else
261
+ puts "Unknown language for plotting: #{lang}"
262
+ end
263
+ else
264
+ print_pretty_data
265
+ end
266
+ exit
267
+ end
268
+
269
+ @argv << '--help' if @argv.size==0
270
+ command_parser = OptionParser.new do |opt|
271
+ opt.on('-v', '--version','show program Version.') { |v|
272
+ opt.version = Shunkuntype::VERSION
273
+ puts opt.ver
274
+ }
275
+ opt.on('-c', '--check','Check speed') {|v| SpeedCheck.new }
276
+ opt.on('-d', '--drill [VAL]','one minute Drill [VAL]', Integer) {|v| Training.new(v) }
277
+ opt.on('-h', '--help','show help message') { puts opt; exit }
278
+ opt.on('-l','--log','view training log') {|v| FinishCheck.new }
279
+ opt.on('-p', '--pretty','Show training and speed data in readable format or output matplotlib code with -p=python') {}
280
+ end
281
+ command_parser.parse!(@argv)
282
+ exit
283
+ end
284
+
285
+ private
286
+
287
+ def print_pretty_data
288
+ print_csv_pretty(Shunkuntype::SPEED_FILE, %w[Time Words TimeSec Score])
289
+ print_csv_pretty(Shunkuntype::TRAINING_FILE, %w[Time Words TimeSec Score])
290
+ end
291
+
292
+ def print_csv_pretty(path, headers)
293
+ if File.exist?(path)
294
+ puts "== #{File.basename(path)} =="
295
+ puts headers.join("\t")
296
+ File.foreach(path) do |line|
297
+ puts line.chomp.split(',').join("\t")
298
+ end
299
+ puts
300
+ else
301
+ puts "#{path} not found."
302
+ end
303
+ end
304
+
305
+ def print_python_plot_code
306
+ speed_path = Shunkuntype::SPEED_FILE
307
+ training_path = Shunkuntype::TRAINING_FILE
308
+ puts "# Python (matplotlib) code for plotting your typing data"
309
+ puts "# Save this as plot_typing.py and run: python plot_typing.py"
310
+ puts <<~PYTHON
311
+ import matplotlib.pyplot as plt
312
+ import csv
313
+ from datetime import datetime
314
+
315
+ def load_data(path):
316
+ times, scores = [], []
317
+ try:
318
+ with open(path) as f:
319
+ reader = csv.reader(f)
320
+ for row in reader:
321
+ if len(row) < 4:
322
+ continue
323
+ # row[0]: time, row[3]: score
324
+ try:
325
+ t = datetime.strptime(row[0].split('.')[0], '%Y-%m-%d %H:%M:%S')
326
+ s = float(row[3])
327
+ times.append(t)
328
+ scores.append(s)
329
+ except Exception:
330
+ continue
331
+ except FileNotFoundError:
332
+ pass
333
+ return times, scores
334
+
335
+ speed_times, speed_scores = load_data("#{speed_path}")
336
+ train_times, train_scores = load_data("#{training_path}")
337
+
338
+ plt.figure(figsize=(10,5))
339
+ if speed_times:
340
+ plt.plot(speed_times, speed_scores, 'o-', label='Speed')
341
+ if train_times:
342
+ plt.plot(train_times, train_scores, 's-', label='Training')
343
+ plt.xlabel('Date')
344
+ plt.ylabel('Score')
345
+ plt.title('Typing Progress')
346
+ plt.legend()
347
+ plt.grid(True)
348
+ plt.tight_layout()
349
+ plt.show()
350
+ PYTHON
351
+ end
352
+ end
353
+ ````
354
+
355
+ Similar code found with 1 license type
356
+
357
+ daddygongon: t = datetime.strptime(row[0].split('.')[0], '%Y-%m-%d %H:%M:%S')
358
+
359
+ 2025-11-18 16:38:19 +0900
360
+ wo
361
+
362
+ GitHub Copilot: `2025-11-18 16:38:19 +0900` のような日時文字列をPythonでパースするには、
363
+ `%Y-%m-%d %H:%M:%S %z` という書式を使います。
364
+
365
+ 修正例:
366
+
367
+ ````python
368
+ t = datetime.strptime(row[0].split('.')[0], '%Y-%m-%d %H:%M:%S %z')
369
+ ````
370
+
371
+ このように `%z` を追加してください。
372
+
373
+ daddygongon: -p plot, speed_data, training_dataでそれぞれの出力を調整するように修正
374
+
375
+ GitHub Copilot: ````ruby
376
+ class Command
377
+
378
+ def execute
379
+ DataFiles.prepare
380
+
381
+ # -p オプションでデータ表示
382
+ if @argv.any? { |arg| arg.start_with?('-p') }
383
+ arg = @argv.find { |a| a.start_with?('-p') }
384
+ if arg.include?('=')
385
+ mode = arg.split('=', 2)[1]
386
+ case mode
387
+ when 'python', 'plot'
388
+ print_python_plot_code
389
+ when 'speed_data'
390
+ print_csv_pretty(Shunkuntype::SPEED_FILE, %w[Time Words TimeSec Score])
391
+ when 'training_data'
392
+ print_csv_pretty(Shunkuntype::TRAINING_FILE, %w[Time Words TimeSec Score])
393
+ else
394
+ puts "Unknown -p option: #{mode}"
395
+ end
396
+ else
397
+ print_pretty_data
398
+ end
399
+ exit
400
+ end
401
+
402
+ @argv << '--help' if @argv.size==0
403
+ command_parser = OptionParser.new do |opt|
404
+ opt.on('-v', '--version','show program Version.') { |v|
405
+ opt.version = Shunkuntype::VERSION
406
+ puts opt.ver
407
+ }
408
+ opt.on('-c', '--check','Check speed') {|v| SpeedCheck.new }
409
+ opt.on('-d', '--drill [VAL]','one minute Drill [VAL]', Integer) {|v| Training.new(v) }
410
+ opt.on('-h', '--help','show help message') { puts opt; exit }
411
+ opt.on('-l','--log','view training log') {|v| FinishCheck.new }
412
+ opt.on('-p', '--pretty','Show training and speed data in readable format or output matplotlib code with -p=python/-p=plot/-p=speed_data/-p=training_data') {}
413
+ end
414
+ command_parser.parse!(@argv)
415
+ exit
416
+ end
417
+
418
+ private
419
+
420
+ def print_pretty_data
421
+ print_csv_pretty(Shunkuntype::SPEED_FILE, %w[Time Words TimeSec Score])
422
+ print_csv_pretty(Shunkuntype::TRAINING_FILE, %w[Time Words TimeSec Score])
423
+ end
424
+
425
+ def print_csv_pretty(path, headers)
426
+ if File.exist?(path)
427
+ puts "== #{File.basename(path)} =="
428
+ puts headers.join("\t")
429
+ File.foreach(path) do |line|
430
+ puts line.chomp.split(',').join("\t")
431
+ end
432
+ puts
433
+ else
434
+ puts "#{path} not found."
435
+ end
436
+ end
437
+
438
+ def print_python_plot_code
439
+ speed_path = Shunkuntype::SPEED_FILE
440
+ training_path = Shunkuntype::TRAINING_FILE
441
+ puts "# Python (matplotlib) code for plotting your typing data"
442
+ puts "# Save this as plot_typing.py and run: python plot_typing.py"
443
+ puts <<~PYTHON
444
+ import matplotlib.pyplot as plt
445
+ import csv
446
+ from datetime import datetime
447
+
448
+ def load_data(path):
449
+ times, scores = [], []
450
+ try:
451
+ with open(path) as f:
452
+ reader = csv.reader(f)
453
+ for row in reader:
454
+ if len(row) < 4:
455
+ continue
456
+ # row[0]: time, row[3]: score
457
+ try:
458
+ t = datetime.strptime(row[0].split('.')[0], '%Y-%m-%d %H:%M:%S %z')
459
+ s = float(row[2])
460
+ times.append(t)
461
+ scores.append(s)
462
+ except Exception:
463
+ continue
464
+ except FileNotFoundError:
465
+ pass
466
+ return times, scores
467
+
468
+ speed_times, speed_scores = load_data("#{speed_path}")
469
+ train_times, train_scores = load_data("#{training_path}")
470
+
471
+ plt.figure(figsize=(10,5))
472
+ if speed_times:
473
+ plt.plot(speed_times, speed_scores, 'o-', label='Speed')
474
+ if train_times:
475
+ plt.plot(train_times, train_scores, 's-', label='Training')
476
+ plt.xlabel('Date')
477
+ plt.ylabel('Score')
478
+ plt.title('Typing Progress')
479
+ plt.legend()
480
+ plt.grid(True)
481
+ plt.tight_layout()
482
+ plt.show()
483
+ PYTHON
484
+ end
485
+ end
486
+ ````
487
+
488
+ Similar code found with 1 license type
489
+
490
+ daddygongon: =は含まず,OptionParserの通常の処理に変更
491
+
492
+ GitHub Copilot: ````ruby
493
+ class Command
494
+
495
+ def self.run(argv=[])
496
+ new(argv).execute
497
+ end
498
+
499
+ def initialize(argv=[])
500
+ @argv = argv
501
+ end
502
+
503
+ def execute
504
+ DataFiles.prepare
505
+
506
+ @mode = nil
507
+
508
+ command_parser = OptionParser.new do |opt|
509
+ opt.on('-v', '--version','show program Version.') { |v|
510
+ opt.version = Shunkuntype::VERSION
511
+ puts opt.ver
512
+ }
513
+ opt.on('-c', '--check','Check speed') {|v| SpeedCheck.new }
514
+ opt.on('-d', '--drill [VAL]','one minute Drill [VAL]', Integer) {|v| Training.new(v) }
515
+ opt.on('-h', '--help','show help message') { puts opt; exit }
516
+ opt.on('-l','--log','view training log') {|v| FinishCheck.new }
517
+ opt.on('-p MODE', '--pretty MODE', 'Show data: plot, speed_data, training_data, or all') {|mode| @mode = mode }
518
+ opt.on('-p', '--pretty', 'Show training and speed data in readable format') { @mode = 'all' }
519
+ end
520
+ command_parser.parse!(@argv)
521
+ # データ表示
522
+ case @mode
523
+ when 'plot', 'python'
524
+ print_python_plot_code
525
+ exit
526
+ when 'speed_data'
527
+ print_csv_pretty(Shunkuntype::SPEED_FILE, %w[Time Words TimeSec Score])
528
+ exit
529
+ when 'training_data'
530
+ print_csv_pretty(Shunkuntype::TRAINING_FILE, %w[Time Words TimeSec Score])
531
+ exit
532
+ when 'all'
533
+ print_pretty_data
534
+ exit
535
+ end
536
+
537
+ @argv << '--help' if @argv.size==0
538
+ command_parser.parse!(@argv)
539
+ exit
540
+ end
541
+
542
+ private
543
+
544
+ def print_pretty_data
545
+ print_csv_pretty(Shunkuntype::SPEED_FILE, %w[Time Words TimeSec Score])
546
+ print_csv_pretty(Shunkuntype::TRAINING_FILE, %w[Time Words TimeSec Score])
547
+ end
548
+
549
+ def print_csv_pretty(path, headers)
550
+ if File.exist?(path)
551
+ puts "== #{File.basename(path)} =="
552
+ puts headers.join("\t")
553
+ File.foreach(path) do |line|
554
+ puts line.chomp.split(',').join("\t")
555
+ end
556
+ puts
557
+ else
558
+ puts "#{path} not found."
559
+ end
560
+ end
561
+
562
+ def print_python_plot_code
563
+ speed_path = Shunkuntype::SPEED_FILE
564
+ training_path = Shunkuntype::TRAINING_FILE
565
+ puts "# Python (matplotlib) code for plotting your typing data"
566
+ puts "# Save this as plot_typing.py and run: python plot_typing.py"
567
+ puts <<~PYTHON
568
+ import matplotlib.pyplot as plt
569
+ import csv
570
+ from datetime import datetime
571
+
572
+ def load_data(path):
573
+ times, scores = [], []
574
+ try:
575
+ with open(path) as f:
576
+ reader = csv.reader(f)
577
+ for row in reader:
578
+ if len(row) < 4:
579
+ continue
580
+ # row[0]: time, row[2]: score
581
+ try:
582
+ t = datetime.strptime(row[0].split('.')[0], '%Y-%m-%d %H:%M:%S %z')
583
+ s = float(row[2])
584
+ times.append(t)
585
+ scores.append(s)
586
+ except Exception:
587
+ continue
588
+ except FileNotFoundError:
589
+ pass
590
+ return times, scores
591
+
592
+ speed_times, speed_scores = load_data("#{speed_path}")
593
+ train_times, train_scores = load_data("#{training_path}")
594
+
595
+ plt.figure(figsize=(10,5))
596
+ if speed_times:
597
+ plt.plot(speed_times, speed_scores, 'o-', label='Speed')
598
+ if train_times:
599
+ plt.plot(train_times, train_scores, 's-', label='Training')
600
+ plt.xlabel('Date')
601
+ plt.ylabel('Score')
602
+ plt.title('Typing Progress')
603
+ plt.legend()
604
+ plt.grid(True)
605
+ plt.tight_layout()
606
+ plt.show()
607
+ PYTHON
608
+ end
609
+ end
610
+ ````
611
+
612
+ Similar code found with 1 license type
613
+
614
+ daddygongon: -pのとりうるoptionをhelpに明示.
615
+
616
+ GitHub Copilot: ````ruby
617
+ command_parser = OptionParser.new do |opt|
618
+ opt.on('-v', '--version','show program Version.') { |v|
619
+ opt.version = Shunkuntype::VERSION
620
+ puts opt.ver
621
+ }
622
+ opt.on('-c', '--check','Check speed') {|v| SpeedCheck.new }
623
+ opt.on('-d', '--drill [VAL]','one minute Drill [VAL]', Integer) {|v| Training.new(v) }
624
+ opt.on('-h', '--help','show help message') { puts opt; exit }
625
+ opt.on('-l','--log','view training log') {|v| FinishCheck.new }
626
+ opt.on('-p MODE', '--pretty MODE',
627
+ "Show data in readable format. MODE can be:\n" \
628
+ " all Show both speed and training data (default)\n" \
629
+ " plot Output matplotlib code for plotting\n" \
630
+ " speed_data Show only speed data\n" \
631
+ " training_data Show only training data"
632
+ ) {|mode| @mode = mode }
633
+ opt.on('-p', '--pretty', 'Show both speed and training data (same as -p all)') { @mode = 'all' }
634
+ end
635
+ ````
636
+
637
+ Similar code found with 1 license type
638
+
639
+ daddygongon: -pをやめて, -r, --recordに変更
640
+
641
+ GitHub Copilot: ````ruby
642
+ class Command
643
+
644
+ def self.run(argv=[])
645
+ new(argv).execute
646
+ end
647
+
648
+ def initialize(argv=[])
649
+ @argv = argv
650
+ end
651
+
652
+ def execute
653
+ DataFiles.prepare
654
+
655
+ @mode = nil
656
+
657
+ command_parser = OptionParser.new do |opt|
658
+ opt.on('-v', '--version','show program Version.') { |v|
659
+ opt.version = Shunkuntype::VERSION
660
+ puts opt.ver
661
+ }
662
+ opt.on('-c', '--check','Check speed') {|v| SpeedCheck.new }
663
+ opt.on('-d', '--drill [VAL]','one minute Drill [VAL]', Integer) {|v| Training.new(v) }
664
+ opt.on('-h', '--help','show help message') { puts opt; exit }
665
+ opt.on('-l','--log','view training log') {|v| FinishCheck.new }
666
+ opt.on('-r MODE', '--record MODE',
667
+ "Show record data in readable format. MODE can be:\n" \
668
+ " all Show both speed and training data (default)\n" \
669
+ " plot Output matplotlib code for plotting\n" \
670
+ " speed_data Show only speed data\n" \
671
+ " training_data Show only training data"
672
+ ) {|mode| @mode = mode }
673
+ opt.on('-r', '--record', 'Show both speed and training data (same as -r all)') { @mode = 'all' }
674
+ end
675
+ command_parser.parse!(@argv)
676
+ # データ表示
677
+ case @mode
678
+ when 'plot', 'python'
679
+ print_python_plot_code
680
+ exit
681
+ when 'speed_data'
682
+ print_csv_pretty(Shunkuntype::SPEED_FILE, %w[Time Words TimeSec Score])
683
+ exit
684
+ when 'training_data'
685
+ print_csv_pretty(Shunkuntype::TRAINING_FILE, %w[Time Words TimeSec Score])
686
+ exit
687
+ when 'all'
688
+ print_pretty_data
689
+ exit
690
+ end
691
+
692
+ @argv << '--help' if @argv.size==0
693
+ command_parser.parse!(@argv)
694
+ exit
695
+ end
696
+
697
+ private
698
+
699
+ def print_pretty_data
700
+ print_csv_pretty(Shunkuntype::SPEED_FILE, %w[Time Words TimeSec Score])
701
+ print_csv_pretty(Shunkuntype::TRAINING_FILE, %w[Time Words TimeSec Score])
702
+ end
703
+
704
+ def print_csv_pretty(path, headers)
705
+ if File.exist?(path)
706
+ puts "== #{File.basename(path)} =="
707
+ puts headers.join("\t")
708
+ File.foreach(path) do |line|
709
+ puts line.chomp.split(',').join("\t")
710
+ end
711
+ puts
712
+ else
713
+ puts "#{path} not found."
714
+ end
715
+ end
716
+
717
+ def print_python_plot_code
718
+ speed_path = Shunkuntype::SPEED_FILE
719
+ training_path = Shunkuntype::TRAINING_FILE
720
+ puts "# Python (matplotlib) code for plotting your typing data"
721
+ puts "# Save this as plot_typing.py and run: python plot_typing.py"
722
+ puts <<~PYTHON
723
+ import matplotlib.pyplot as plt
724
+ import csv
725
+ from datetime import datetime
726
+
727
+ def load_data(path):
728
+ times, scores = [], []
729
+ try:
730
+ with open(path) as f:
731
+ reader = csv.reader(f)
732
+ for row in reader:
733
+ if len(row) < 4:
734
+ continue
735
+ # row[0]: time, row[2]: score
736
+ try:
737
+ t = datetime.strptime(row[0].split('.')[0], '%Y-%m-%d %H:%M:%S %z')
738
+ s = float(row[2])
739
+ times.append(t)
740
+ scores.append(s)
741
+ except Exception:
742
+ continue
743
+ except FileNotFoundError:
744
+ pass
745
+ return times, scores
746
+
747
+ speed_times, speed_scores = load_data("#{speed_path}")
748
+ train_times, train_scores = load_data("#{training_path}")
749
+
750
+ plt.figure(figsize=(10,5))
751
+ if speed_times:
752
+ plt.plot(speed_times, speed_scores, 'o-', label='Speed')
753
+ if train_times:
754
+ plt.plot(train_times, train_scores, 's-', label='Training')
755
+ plt.xlabel('Date')
756
+ plt.ylabel('Score')
757
+ plt.title('Typing Progress')
758
+ plt.legend()
759
+ plt.grid(True)
760
+ plt.tight_layout()
761
+ plt.show()
762
+ PYTHON
763
+ end
764
+ end
765
+ ````
766
+
767
+ Similar code found with 1 license type
768
+
769
+ daddygongon: #terminalSelection なんで,opt.onのargumentが間違ってない?
770
+
771
+ GitHub Copilot: Read terminal selection
772
+
773
+ `opt.on('-r MODE', '--record MODE', ...)` の書き方は正しいです。
774
+ `OptionParser` では、`-r MODE` や `--record MODE` のように、引数を取るオプションはこのように記述します。
775
+
776
+ **例:**
777
+ ```ruby
778
+ opt.on('-r MODE', '--record MODE', '説明') {|mode| ... }
779
+ ```
780
+
781
+ この場合、`shunkuntype -r plot` や `shunkuntype --record speed_data` のように使えます。
782
+
783
+ `-r` だけで引数なしの場合は、
784
+ ```ruby
785
+ opt.on('-r', '--record', '説明') { ... }
786
+ ```
787
+ と書きます。
788
+
789
+ **まとめ:**
790
+ `opt.on('-r MODE', '--record MODE', ...)` は正しい記法です。
791
+ `-r` だけで使う場合は、引数なしの `opt.on('-r', '--record', ...)` も併用しているので問題ありません。