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