rate-card 0.1.3 → 0.1.4
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/exe/rate-card +9 -3
- data/lib/rate_card/run_spec.rb +9 -6
- data/lib/rate_card/tui/app.rb +102 -58
- data/lib/rate_card/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ccc684b0241775fa979051ffd3b9aeed1b08e9eaf8a5bb40bc8560d8f0e2ab39
|
|
4
|
+
data.tar.gz: e3fe60187c6db2cf746a7adabe90a139da79cd655a2b0445e92060db23db2d35
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 67bb66aab90c7942ec5cf6d29ce62b23240dd347fdd27c8665d4c1d191c398f97d75ec2b2ddaad989ee21c0645ab5120c00dfa9c2a953d7f176298850aa635bc
|
|
7
|
+
data.tar.gz: 6997aabfddff427e90ecfb38153c3262513d31160af889109da5fe729cfd00b239d63c62137a9f733b47c8d5199bee4e7593b91dc29fefe36291d8b608a584ba
|
data/exe/rate-card
CHANGED
|
@@ -102,7 +102,13 @@ else
|
|
|
102
102
|
exit 1
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
# Usually one pass. UPS rural mode with more than one surcharge type checked
|
|
106
|
+
# produces several - each is its own card with its own files, run and
|
|
107
|
+
# reported independently so one failing does not stop the others from
|
|
108
|
+
# writing. The process exit code is the worst of all of them.
|
|
109
|
+
exit_codes = app.results.map do |result|
|
|
110
|
+
result[:spec].show_table = options[:show_table]
|
|
111
|
+
RateCard::Runner.new(spec: result[:spec], grid: result[:grid], ui: ui).run
|
|
112
|
+
end
|
|
107
113
|
|
|
108
|
-
exit
|
|
114
|
+
exit exit_codes.max
|
data/lib/rate_card/run_spec.rb
CHANGED
|
@@ -162,13 +162,16 @@ module RateCard
|
|
|
162
162
|
Pathname.new(output_base).join("#{slug}_#{customer_id}_#{timestamp}#{rural_suffix}")
|
|
163
163
|
end
|
|
164
164
|
|
|
165
|
+
# UPS rural mode runs one pass per selected surcharge type (see
|
|
166
|
+
# TUI::App#build_specs), so a single spec's zones is exactly one symbol -
|
|
167
|
+
# the suffix names it, so two same-second passes don't collide on
|
|
168
|
+
# run_dir. USPS rural mode sweeps real zones as usual, so it just gets
|
|
169
|
+
# the generic suffix.
|
|
165
170
|
def rural_suffix
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
else ''
|
|
171
|
-
end
|
|
171
|
+
return '' unless rural?
|
|
172
|
+
|
|
173
|
+
surcharge = zones.find { |zone| zone.is_a?(Symbol) }
|
|
174
|
+
surcharge ? "_#{surcharge}" : '_rural'
|
|
172
175
|
end
|
|
173
176
|
|
|
174
177
|
def validate!
|
data/lib/rate_card/tui/app.rb
CHANGED
|
@@ -24,11 +24,14 @@ module RateCard
|
|
|
24
24
|
|
|
25
25
|
# No :token stage — see TokenPrompt for why it cannot live in the loop.
|
|
26
26
|
STAGES = %i[
|
|
27
|
-
loading rate_mode carrier rural services zones unit weights
|
|
28
|
-
rate_keys confirm fetching
|
|
27
|
+
loading rate_mode carrier rural rural_surcharges services zones unit weights
|
|
28
|
+
package_type rate_keys confirm fetching
|
|
29
29
|
].freeze
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
# #spec/#grid hold the most recently finished pass, for every caller that
|
|
32
|
+
# only ever produced one (every carrier except UPS rural mode with more
|
|
33
|
+
# than one surcharge type checked). #results holds every pass.
|
|
34
|
+
attr_reader :spec, :grid, :error, :notifier, :results
|
|
32
35
|
|
|
33
36
|
# notifier is set by the caller to the Bubbletea::Runner, whose #send is
|
|
34
37
|
# the only way a worker thread can get a message onto the event loop.
|
|
@@ -54,6 +57,7 @@ module RateCard
|
|
|
54
57
|
@failure_sparkline = Ntcharts::Sparkline.new(32, 1)
|
|
55
58
|
@failure_sparkline.style = Lipgloss::Style.new.foreground(Theme::WARNING)
|
|
56
59
|
@log = []
|
|
60
|
+
@results = []
|
|
57
61
|
end
|
|
58
62
|
|
|
59
63
|
def cancelled? = @cancelled
|
|
@@ -69,7 +73,7 @@ module RateCard
|
|
|
69
73
|
when ServicesLoaded then services_loaded(message.services)
|
|
70
74
|
when LoadFailed then return fail_with(message.error)
|
|
71
75
|
when ProgressAdvanced then progress_advanced(message)
|
|
72
|
-
when FetchFinished then return
|
|
76
|
+
when FetchFinished then return fetch_finished(message.grid)
|
|
73
77
|
when FetchFailed then return fail_with(message.error)
|
|
74
78
|
else
|
|
75
79
|
return [self, spinner_or_field(message)]
|
|
@@ -175,10 +179,11 @@ module RateCard
|
|
|
175
179
|
|
|
176
180
|
def field_for(stage)
|
|
177
181
|
case stage
|
|
178
|
-
when :rate_mode
|
|
179
|
-
when :carrier
|
|
180
|
-
when :rural
|
|
181
|
-
when :
|
|
182
|
+
when :rate_mode then rate_mode_field
|
|
183
|
+
when :carrier then carrier_field
|
|
184
|
+
when :rural then rural_field
|
|
185
|
+
when :rural_surcharges then rural_surcharges_field
|
|
186
|
+
when :services then services_field
|
|
182
187
|
when :zones then zones_field
|
|
183
188
|
when :unit then unit_field
|
|
184
189
|
when :weights then weights_field
|
|
@@ -241,39 +246,36 @@ module RateCard
|
|
|
241
246
|
|
|
242
247
|
# Only USPS and UPS have a rural/DAS chart (Addresses::USPS_RURAL_DAS,
|
|
243
248
|
# Addresses::UPS_RURAL); every other carrier is decided :normal (nil)
|
|
244
|
-
# and skips this stage, same as any other single-answer stage.
|
|
249
|
+
# and skips this stage, same as any other single-answer stage. Same
|
|
250
|
+
# Normal/Rural toggle for both carriers - which surcharge type(s) UPS
|
|
251
|
+
# means by "rural" is a separate question, :rural_surcharges below.
|
|
245
252
|
def rural_field
|
|
246
253
|
return nil unless Constants::Carriers.rural_aware?(carrier)
|
|
247
254
|
|
|
248
|
-
choices =
|
|
249
|
-
Fields::Select.new(label: 'Rural / DAS
|
|
255
|
+
choices = [['Normal', false], ['Rural (DAS)', true]]
|
|
256
|
+
Fields::Select.new(label: 'Rural / DAS', choices: choices,
|
|
250
257
|
selected: choices.index { |_, v| v == @answers[:rural] } || 0)
|
|
251
258
|
end
|
|
252
259
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
+
# UPS has no per-zone rural chart - each surcharge type is one fixed
|
|
261
|
+
# address, not a zone sweep - so this replaces the zones question for
|
|
262
|
+
# UPS rural mode. Checking both produces two separate rate cards (see
|
|
263
|
+
# #build_specs), one per surcharge type, rather than one card that
|
|
264
|
+
# conflates two different addresses under one "zone" axis.
|
|
265
|
+
def rural_surcharges_field
|
|
266
|
+
return nil unless ups_rural?
|
|
260
267
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
when :both then %i[edas rdas]
|
|
269
|
-
end
|
|
268
|
+
choices = [['EDAS', :edas], ['RDAS', :rdas]]
|
|
269
|
+
answered = @answers[:rural_surcharges]
|
|
270
|
+
Fields::MultiSelect.new(
|
|
271
|
+
label: 'Surcharge types',
|
|
272
|
+
choices: choices,
|
|
273
|
+
checked: answered ? checked_indexes(choices.map(&:last), answered) : (0...choices.length)
|
|
274
|
+
)
|
|
270
275
|
end
|
|
271
276
|
|
|
272
277
|
def zones_field
|
|
273
|
-
|
|
274
|
-
@answers[:zones] = decided
|
|
275
|
-
return nil
|
|
276
|
-
end
|
|
278
|
+
return nil if ups_rural?
|
|
277
279
|
|
|
278
280
|
available = usps_rural? ? Constants::Addresses::USPS_RURAL_DAS.keys.sort : Constants::Addresses.available_zones(carrier)
|
|
279
281
|
full = "#{available.first}-#{available.last}"
|
|
@@ -295,6 +297,10 @@ module RateCard
|
|
|
295
297
|
carrier == 'USPS' && @answers[:rural] == true
|
|
296
298
|
end
|
|
297
299
|
|
|
300
|
+
def ups_rural?
|
|
301
|
+
carrier == 'UPS' && @answers[:rural] == true
|
|
302
|
+
end
|
|
303
|
+
|
|
298
304
|
# Weight is fixed per cubic tier, so asking for a display unit is
|
|
299
305
|
# meaningless in cubic mode — decided as :oz (unused) and skipped.
|
|
300
306
|
def unit_field
|
|
@@ -413,18 +419,39 @@ module RateCard
|
|
|
413
419
|
|
|
414
420
|
# ---------------------------------------------------------------- fetch
|
|
415
421
|
|
|
422
|
+
# UPS rural mode with more than one surcharge type checked runs as
|
|
423
|
+
# several independent passes (see #build_specs) - each one calls
|
|
424
|
+
# Grid.build, feeds the same progress bar, and lands its own {spec:,
|
|
425
|
+
# grid:} in #results before the next pass starts. Every other carrier
|
|
426
|
+
# and mode has exactly one pass, same as before this existed.
|
|
416
427
|
def start_fetch
|
|
417
|
-
@
|
|
428
|
+
@pending_specs = build_specs
|
|
418
429
|
# Before the first call, so a bad path is not discovered after 128 of them.
|
|
419
430
|
begin
|
|
420
|
-
CsvWriter.ensure_writable!(
|
|
431
|
+
@pending_specs.each { |spec| CsvWriter.ensure_writable!(spec.output_base) }
|
|
421
432
|
rescue OutputNotWritable => e
|
|
422
433
|
return fail_with(e).last
|
|
423
434
|
end
|
|
424
435
|
|
|
436
|
+
@results = []
|
|
437
|
+
@total_passes = @pending_specs.length
|
|
438
|
+
@pass_index = 0
|
|
439
|
+
next_pass
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
def next_pass
|
|
443
|
+
@spec = @pending_specs.shift
|
|
444
|
+
return Bubbletea.quit if @spec.nil?
|
|
445
|
+
|
|
446
|
+
@pass_index += 1
|
|
447
|
+
@completed = 0
|
|
448
|
+
@failed = 0
|
|
449
|
+
fetch_command(@spec)
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def fetch_command(spec)
|
|
425
453
|
client = @client_factory.call(@token)
|
|
426
454
|
notifier = @notifier
|
|
427
|
-
spec = @spec
|
|
428
455
|
provider = @provider
|
|
429
456
|
|
|
430
457
|
lambda do
|
|
@@ -448,9 +475,10 @@ module RateCard
|
|
|
448
475
|
@failure_sparkline.push(message.failed)
|
|
449
476
|
end
|
|
450
477
|
|
|
451
|
-
def
|
|
478
|
+
def fetch_finished(grid)
|
|
452
479
|
@grid = grid
|
|
453
|
-
|
|
480
|
+
@results << { spec: @spec, grid: grid }
|
|
481
|
+
[self, next_pass]
|
|
454
482
|
end
|
|
455
483
|
|
|
456
484
|
def fail_with(error)
|
|
@@ -458,7 +486,23 @@ module RateCard
|
|
|
458
486
|
[self, Bubbletea.quit]
|
|
459
487
|
end
|
|
460
488
|
|
|
489
|
+
# One RunSpec per pass. UPS rural mode runs one pass per checked
|
|
490
|
+
# surcharge type (each is a fixed single address, not a zone sweep -
|
|
491
|
+
# see Addresses::UPS_RURAL) rather than folding both into one card's
|
|
492
|
+
# zone axis. Every other carrier/mode is the single pass it always was.
|
|
493
|
+
def build_specs
|
|
494
|
+
if ups_rural?
|
|
495
|
+
(@answers[:rural_surcharges] || []).map { |surcharge| run_spec(zones: [surcharge]) }
|
|
496
|
+
else
|
|
497
|
+
[run_spec(zones: @answers[:zones])]
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
461
501
|
def build_spec
|
|
502
|
+
build_specs.first
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
def run_spec(zones:)
|
|
462
506
|
cubic = @answers[:rate_mode] == :cubic
|
|
463
507
|
RunSpec.new(
|
|
464
508
|
token: @token,
|
|
@@ -466,7 +510,7 @@ module RateCard
|
|
|
466
510
|
customer_id: identity[:customer_id],
|
|
467
511
|
carrier: carrier,
|
|
468
512
|
services: @answers[:services],
|
|
469
|
-
zones:
|
|
513
|
+
zones: zones,
|
|
470
514
|
weight_unit: @answers[:unit] || :oz,
|
|
471
515
|
weights: cubic ? [] : @answers[:weights],
|
|
472
516
|
cubic_tiers: cubic ? @answers[:weights] : [],
|
|
@@ -498,18 +542,24 @@ module RateCard
|
|
|
498
542
|
# Everything the run will do, gathered in one block. The answers are also
|
|
499
543
|
# in the transcript above, but they arrived one at a time over eight
|
|
500
544
|
# screens; this is the only place they can be read against each other.
|
|
545
|
+
#
|
|
546
|
+
# Built from real RunSpecs (build_specs) rather than raw @answers, so a
|
|
547
|
+
# UPS rural run with both surcharge types checked recaps as the two
|
|
548
|
+
# separate cards it will actually produce, one line each.
|
|
501
549
|
def recap_view
|
|
550
|
+
specs = build_specs
|
|
502
551
|
carrier_line = " #{Theme.bold(carrier)}#{rural_recap} · #{@answers[:services].map(&:name).join(', ')}"
|
|
503
552
|
[
|
|
504
553
|
carrier_line,
|
|
505
|
-
" zones #{
|
|
554
|
+
*specs.map { |spec| " zones #{spec.zone_summary} · #{rows_recap} · #{@answers[:package_type]}" },
|
|
506
555
|
" columns: #{@answers[:rate_keys].map { |k| RunSpec::RATE_KEY_LABELS.fetch(k) }.join(', ')}",
|
|
507
|
-
" #{Theme.bold(call_count.to_s)} rate calls against #{Theme.danger('production')}"
|
|
556
|
+
" #{Theme.bold(specs.sum(&:call_count).to_s)} rate calls against #{Theme.danger('production')}" \
|
|
557
|
+
"#{specs.length > 1 ? " (#{specs.length} separate cards)" : ''}"
|
|
508
558
|
].join("\n")
|
|
509
559
|
end
|
|
510
560
|
|
|
511
561
|
def rural_recap
|
|
512
|
-
return ''
|
|
562
|
+
return '' unless @answers[:rural]
|
|
513
563
|
|
|
514
564
|
" (#{rural_label(@answers[:rural])})"
|
|
515
565
|
end
|
|
@@ -522,16 +572,13 @@ module RateCard
|
|
|
522
572
|
end
|
|
523
573
|
end
|
|
524
574
|
|
|
525
|
-
def call_count
|
|
526
|
-
@answers[:weights].length * @answers[:zones].length
|
|
527
|
-
end
|
|
528
|
-
|
|
529
575
|
def fetch_view
|
|
530
576
|
total = @spec.call_count
|
|
531
577
|
percent = total.zero? ? 0.0 : @completed.to_f / total
|
|
578
|
+
title = @total_passes > 1 ? "fetching rates (pass #{@pass_index}/#{@total_passes})" : 'fetching rates'
|
|
532
579
|
line = " #{@progress.view_as(percent)} #{@completed}/#{total}"
|
|
533
580
|
line += " #{Theme.warning("#{Theme::ALERT} #{@failed} failed")}" if @failed.positive?
|
|
534
|
-
view = "#{Theme.bold(
|
|
581
|
+
view = "#{Theme.bold(" #{title}")}\n#{line}"
|
|
535
582
|
view += "\n#{failure_sparkline_view}" if @failed.positive?
|
|
536
583
|
view
|
|
537
584
|
end
|
|
@@ -548,8 +595,9 @@ module RateCard
|
|
|
548
595
|
# transcript of the run stays on screen — the recap is then a summary of
|
|
549
596
|
# what is already visible rather than the first chance to check it.
|
|
550
597
|
def answered_line(stage, value)
|
|
551
|
-
label = { rate_mode: 'rate by', carrier: 'carrier', rural: 'rural / DAS',
|
|
552
|
-
|
|
598
|
+
label = { rate_mode: 'rate by', carrier: 'carrier', rural: 'rural / DAS',
|
|
599
|
+
rural_surcharges: 'surcharge types', services: 'services', zones: 'zones',
|
|
600
|
+
unit: 'unit', weights: 'weights', package_type: 'package',
|
|
553
601
|
rate_keys: 'columns' }[stage]
|
|
554
602
|
label = 'cubic tiers' if stage == :weights && @answers[:rate_mode] == :cubic
|
|
555
603
|
return nil if label.nil?
|
|
@@ -559,22 +607,18 @@ module RateCard
|
|
|
559
607
|
|
|
560
608
|
def describe(stage, value)
|
|
561
609
|
case stage
|
|
562
|
-
when :services
|
|
563
|
-
when :zones
|
|
564
|
-
when :weights
|
|
565
|
-
when :rural
|
|
566
|
-
when :
|
|
610
|
+
when :services then value.map(&:name).join(', ')
|
|
611
|
+
when :zones then RunSpec.compact_range(value)
|
|
612
|
+
when :weights then RunSpec.compact_range(value)
|
|
613
|
+
when :rural then rural_label(value)
|
|
614
|
+
when :rural_surcharges then value.map { |v| v.to_s.upcase }.join(', ')
|
|
615
|
+
when :rate_keys then value.map { |k| RunSpec::RATE_KEY_LABELS.fetch(k) }.join(', ')
|
|
567
616
|
else value.to_s
|
|
568
617
|
end
|
|
569
618
|
end
|
|
570
619
|
|
|
571
620
|
def rural_label(value)
|
|
572
|
-
|
|
573
|
-
when false, nil then 'normal'
|
|
574
|
-
when true then 'rural (DAS)'
|
|
575
|
-
when :edas, :rdas then value.to_s.upcase
|
|
576
|
-
when :both then 'EDAS + RDAS'
|
|
577
|
-
end
|
|
621
|
+
value ? 'rural (DAS)' : 'normal'
|
|
578
622
|
end
|
|
579
623
|
end
|
|
580
624
|
end
|
data/lib/rate_card/version.rb
CHANGED