appydave-tools 0.74.1 → 0.75.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef8315540ea80d07de1a6eba6733c1be84e21caaa4251dbb60e9a2207abf9758
4
- data.tar.gz: 3a386ab9c8b399ceca1056f20fae6543bc534ddf37a7d104363acc09ad694e48
3
+ metadata.gz: e45ff57b507a705f826281a7d361abf0724d719eaf1c1ba4795198f3a4551a7f
4
+ data.tar.gz: edfc8fcdb775c290d9c59a588215de3e2c613075ee49332836bf19de92a5126b
5
5
  SHA512:
6
- metadata.gz: 70af1beaa0539e9a71ffb1e4435ffe1afad1da73cfbbe4f16c780110684975fdbf3de5602e7cc5b9bd226404158d0f24aee5126186b3821235b782508bc9150b
7
- data.tar.gz: 6e06cc27ccebd7d49ba9831f3e9f1345f96bc0ffce681fc56be6a08799a32d828b8c68e32633d74a99b397a1a7bde699c89a5077745439ccbc9f3c8cf3e2af2e
6
+ metadata.gz: d5d45772ed9304ac22cca51417c0d48b0d176d824933feaafbc4a34c50ff8585bbce93a4f423ecadc39ac64aeca5e0e2724556a1407c428a318fa86926a65899
7
+ data.tar.gz: ab16611805c302aeb6fe1930e3502d4330cb00833bd282e2631e9cbb25f6fc179eb66c7e6cd4535a89005cd743cc0db6c3e9eaaaeea0a22c671274e5b6f9b43d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [0.74.1](https://github.com/appydave/appydave-tools/compare/v0.74.0...v0.74.1) (2026-02-04)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * remove redundant rubocop disable comments blocking CI ([0aa8956](https://github.com/appydave/appydave-tools/commit/0aa89561f229488c01c2a9467c829ac3a8851e72))
7
+
1
8
  # [0.74.0](https://github.com/appydave/appydave-tools/compare/v0.73.0...v0.74.0) (2025-12-18)
2
9
 
3
10
 
@@ -241,16 +241,25 @@ module Appydave
241
241
 
242
242
  def run_report(args)
243
243
  format = format_option(args)
244
+ limit = extract_option(args, '--limit')&.to_i
245
+ skip_unassigned = !args.delete('--include-unassigned')
244
246
  report_type = args.shift
245
247
  filter = args.first
246
248
 
247
249
  unless report_type
248
- output.puts 'Usage: jump report <type> [filter]'
250
+ output.puts 'Usage: jump report <type> [filter] [--limit N] [--include-unassigned]'
249
251
  output.puts 'Types: categories, brands, clients, types, tags, by-brand, by-client, by-type, by-tag, summary'
250
252
  return EXIT_INVALID_INPUT
251
253
  end
252
254
 
253
- cmd = Commands::Report.new(load_config, report_type, filter: filter, path_validator: path_validator)
255
+ cmd = Commands::Report.new(
256
+ load_config,
257
+ report_type,
258
+ filter: filter,
259
+ limit: limit,
260
+ skip_unassigned: skip_unassigned,
261
+ path_validator: path_validator
262
+ )
254
263
  result = cmd.run
255
264
 
256
265
  format_output(result, format)
@@ -577,7 +586,7 @@ module Appydave
577
586
  output.puts <<~HELP
578
587
  jump report - Generate reports
579
588
 
580
- Usage: jump report <type> [filter]
589
+ Usage: jump report <type> [filter] [--limit N] [--include-unassigned]
581
590
 
582
591
  Report Types:
583
592
  categories List all category definitions
@@ -591,9 +600,15 @@ module Appydave
591
600
  by-tag Group locations by tag
592
601
  summary Overview of all data
593
602
 
603
+ Options:
604
+ --limit N Limit output to top N items/groups
605
+ --include-unassigned Show unassigned groups (hidden by default)
606
+
594
607
  Examples:
595
608
  jump report brands
596
- jump report by-brand appydave
609
+ jump report tags --limit 20
610
+ jump report by-brand --include-unassigned
611
+ jump report by-client appydave --limit 5
597
612
  jump report tags --format json
598
613
  HELP
599
614
  end
@@ -12,12 +12,14 @@ module Appydave
12
12
  summary
13
13
  ].freeze
14
14
 
15
- attr_reader :report_type, :filter
15
+ attr_reader :report_type, :filter, :limit, :skip_unassigned
16
16
 
17
- def initialize(config, report_type, filter: nil, path_validator: PathValidator.new, **options)
18
- super(config, path_validator: path_validator, **options)
17
+ def initialize(config, report_type, **options)
18
+ super(config, path_validator: options[:path_validator] || PathValidator.new, **options)
19
19
  @report_type = report_type
20
- @filter = filter
20
+ @filter = options[:filter]
21
+ @limit = options[:limit]
22
+ @skip_unassigned = options.fetch(:skip_unassigned, true)
21
23
  end
22
24
 
23
25
  def run
@@ -96,10 +98,15 @@ module Appydave
96
98
  { type: type, location_count: count }
97
99
  end
98
100
 
101
+ sorted = types.sort_by { |t| -t[:location_count] }
102
+ limited = limit ? sorted.take(limit) : sorted
103
+
99
104
  success_result(
100
105
  report: 'types',
101
- count: types.size,
102
- results: types.sort_by { |t| -t[:location_count] }
106
+ count: limited.size,
107
+ total_count: types.size,
108
+ results: limited,
109
+ truncated: limit && types.size > limit
103
110
  )
104
111
  end
105
112
 
@@ -113,37 +120,51 @@ module Appydave
113
120
  { tag: tag, location_count: count }
114
121
  end
115
122
 
123
+ sorted = tags.sort_by { |t| -t[:location_count] }
124
+ limited = limit ? sorted.take(limit) : sorted
125
+
116
126
  success_result(
117
127
  report: 'tags',
118
- count: tags.size,
119
- results: tags.sort_by { |t| -t[:location_count] }
128
+ count: limited.size,
129
+ total_count: tags.size,
130
+ results: limited,
131
+ truncated: limit && tags.size > limit
120
132
  )
121
133
  end
122
134
 
123
135
  def report_by_brand
124
136
  grouped = group_by_field(:brand, filter)
137
+ grouped = apply_group_filters(grouped)
125
138
  success_result(
126
139
  report: 'by-brand',
127
140
  filter: filter,
128
- groups: grouped
141
+ groups: grouped,
142
+ limit: limit,
143
+ skip_unassigned: skip_unassigned
129
144
  )
130
145
  end
131
146
 
132
147
  def report_by_client
133
148
  grouped = group_by_field(:client, filter)
149
+ grouped = apply_group_filters(grouped)
134
150
  success_result(
135
151
  report: 'by-client',
136
152
  filter: filter,
137
- groups: grouped
153
+ groups: grouped,
154
+ limit: limit,
155
+ skip_unassigned: skip_unassigned
138
156
  )
139
157
  end
140
158
 
141
159
  def report_by_type
142
160
  grouped = group_by_field(:type, filter)
161
+ grouped = apply_group_filters(grouped)
143
162
  success_result(
144
163
  report: 'by-type',
145
164
  filter: filter,
146
- groups: grouped
165
+ groups: grouped,
166
+ limit: limit,
167
+ skip_unassigned: skip_unassigned
147
168
  )
148
169
  end
149
170
 
@@ -158,10 +179,15 @@ module Appydave
158
179
  end
159
180
  end
160
181
 
182
+ grouped = grouped.sort_by { |_, locs| -locs.size }.to_h
183
+ grouped = apply_group_filters(grouped)
184
+
161
185
  success_result(
162
186
  report: 'by-tag',
163
187
  filter: filter,
164
- groups: grouped.sort_by { |_, locs| -locs.size }.to_h
188
+ groups: grouped,
189
+ limit: limit,
190
+ skip_unassigned: skip_unassigned
165
191
  )
166
192
  end
167
193
 
@@ -207,6 +233,24 @@ module Appydave
207
233
  description: location.description
208
234
  }.compact
209
235
  end
236
+
237
+ def apply_group_filters(grouped)
238
+ # Remove unassigned group if skip_unassigned is true
239
+ grouped = grouped.reject { |key, _| key == 'unassigned' } if skip_unassigned
240
+
241
+ # Apply limit to each group (limit items per group)
242
+ if limit
243
+ grouped.transform_values do |locations|
244
+ {
245
+ items: locations.take(limit),
246
+ total: locations.size,
247
+ truncated: locations.size > limit
248
+ }
249
+ end
250
+ else
251
+ grouped
252
+ end
253
+ end
210
254
  end
211
255
  end
212
256
  end
@@ -26,6 +26,10 @@ module Appydave
26
26
  data[:results] || []
27
27
  end
28
28
 
29
+ def groups
30
+ data[:groups] || {}
31
+ end
32
+
29
33
  def success?
30
34
  data[:success]
31
35
  end
@@ -26,7 +26,12 @@ module Appydave
26
26
  def format
27
27
  return format_error unless success?
28
28
  return format_info if info_result?
29
+ return format_summary if summary_result?
30
+ return format_groups unless groups.empty?
29
31
  return format_empty if results.empty?
32
+ return format_definition_report if definition_report?
33
+ return format_count_report if count_report?
34
+ return format_category_report if category_report?
30
35
 
31
36
  format_results
32
37
  end
@@ -41,13 +46,47 @@ module Appydave
41
46
  end
42
47
 
43
48
  def format_empty
44
- colorize('No locations found.', :yellow)
49
+ message = case data[:report]
50
+ when 'brands'
51
+ 'No brands defined in config.'
52
+ when 'clients'
53
+ 'No clients defined in config.'
54
+ else
55
+ 'No locations found.'
56
+ end
57
+
58
+ colorize(message, :yellow)
45
59
  end
46
60
 
47
61
  def info_result?
48
62
  data.key?(:config_path)
49
63
  end
50
64
 
65
+ def summary_result?
66
+ data.key?(:total_locations) && data.key?(:by_type)
67
+ end
68
+
69
+ def count_report?
70
+ return false if results.empty?
71
+
72
+ first = results.first
73
+ first.key?(:location_count) && (first.key?(:tag) || first.key?(:type))
74
+ end
75
+
76
+ def definition_report?
77
+ return false if results.empty?
78
+
79
+ first = results.first
80
+ first.key?(:key) && first.key?(:description) && first.key?(:aliases) && first.key?(:location_count)
81
+ end
82
+
83
+ def category_report?
84
+ return false if results.empty?
85
+
86
+ first = results.first
87
+ first.key?(:name) && first.key?(:description) && first.key?(:values)
88
+ end
89
+
51
90
  def format_info
52
91
  [
53
92
  format_info_header,
@@ -84,6 +123,103 @@ module Appydave
84
123
  ]
85
124
  end
86
125
 
126
+ def format_summary
127
+ lines = []
128
+ lines << colorize('Jump Locations Summary', :bold)
129
+ lines << ''
130
+ lines << "#{colorize('Total Locations:', :cyan)} #{data[:total_locations]}"
131
+ lines << ''
132
+
133
+ # By Type
134
+ lines << colorize('By Type:', :bold)
135
+ data[:by_type].sort_by { |_, count| -count }.each do |type, count|
136
+ lines << " #{pad(type, 15)} #{colorize(count.to_s.rjust(3), :green)}"
137
+ end
138
+ lines << ''
139
+
140
+ # By Brand
141
+ lines << colorize('By Brand:', :bold)
142
+ data[:by_brand].sort_by { |_, count| -count }.each do |brand, count|
143
+ lines << " #{pad(brand, 15)} #{colorize(count.to_s.rjust(3), :green)}"
144
+ end
145
+ lines << ''
146
+
147
+ # By Client
148
+ lines << colorize('By Client:', :bold)
149
+ data[:by_client].sort_by { |_, count| -count }.each do |client, count|
150
+ lines << " #{pad(client, 15)} #{colorize(count.to_s.rjust(3), :green)}"
151
+ end
152
+
153
+ lines.join("\n")
154
+ end
155
+
156
+ def format_definition_report
157
+ lines = []
158
+ report_type = data[:report] || 'items'
159
+
160
+ lines << colorize("#{report_type.capitalize} Definitions", :bold)
161
+ lines << header_separator
162
+ lines << ''
163
+
164
+ results.each do |item|
165
+ lines << colorize(item[:key], :cyan)
166
+ lines << " #{item[:description]}" if item[:description]
167
+ lines << " Aliases: #{item[:aliases].join(', ')}" if item[:aliases]&.any?
168
+ lines << " Locations: #{colorize(item[:location_count].to_s, :green)}"
169
+ lines << ''
170
+ end
171
+
172
+ lines << colorize("Total: #{count} #{report_type}", :dim)
173
+
174
+ lines.join("\n")
175
+ end
176
+
177
+ def format_count_report
178
+ lines = []
179
+ report_type = data[:report] || 'items'
180
+
181
+ lines << colorize("#{report_type.capitalize} Report", :bold)
182
+ lines << header_separator
183
+ lines << ''
184
+
185
+ results.each do |item|
186
+ name = item[:tag] || item[:type] || 'unknown'
187
+ count = item[:location_count] || 0
188
+ lines << "#{pad(name, 20)} #{colorize(count.to_s.rjust(4), :green)}"
189
+ end
190
+
191
+ lines << ''
192
+
193
+ # Show truncation message if data was limited
194
+ if data[:truncated]
195
+ total = data[:total_count] || data[:count]
196
+ shown = results.size
197
+ remaining = total - shown
198
+ lines << colorize("Showing top #{shown} of #{total} (#{remaining} more)", :dim)
199
+ else
200
+ lines << colorize("Total: #{data[:total_count] || count} #{report_type}", :dim)
201
+ end
202
+
203
+ lines.join("\n")
204
+ end
205
+
206
+ def format_category_report
207
+ lines = []
208
+
209
+ lines << colorize('Location Categories', :bold)
210
+ lines << header_separator
211
+ lines << ''
212
+
213
+ results.each do |category|
214
+ lines << colorize(category[:name], :cyan)
215
+ lines << " #{category[:description]}" if category[:description]
216
+ lines << " Values: #{category[:values].join(', ')}" if category[:values]
217
+ lines << ''
218
+ end
219
+
220
+ lines.join("\n")
221
+ end
222
+
87
223
  def format_results
88
224
  lines = []
89
225
 
@@ -103,6 +239,51 @@ module Appydave
103
239
  lines.join("\n")
104
240
  end
105
241
 
242
+ def format_groups
243
+ lines = []
244
+ total_locations = 0
245
+ total_groups = 0
246
+
247
+ groups.each do |group_name, group_data|
248
+ # Handle both old format (array) and new format (hash with items/total/truncated)
249
+ locations, total_in_group, truncated = if group_data.is_a?(Hash) && group_data.key?(:items)
250
+ [group_data[:items], group_data[:total], group_data[:truncated]]
251
+ else
252
+ [group_data, group_data.length, false]
253
+ end
254
+
255
+ next if locations.empty?
256
+
257
+ # Group header
258
+ lines << ''
259
+ lines << colorize("#{group_name.upcase} (#{total_in_group} location#{'s' unless total_in_group == 1})", :bold)
260
+ lines << header_separator
261
+
262
+ # Add index to each location for display
263
+ locations.each_with_index do |location, idx|
264
+ location[:index] = idx + 1
265
+ lines << format_row(location)
266
+ end
267
+
268
+ # Show truncation message if group was limited
269
+ if truncated
270
+ remaining = total_in_group - locations.length
271
+ lines << colorize(" ... and #{remaining} more", :dim)
272
+ end
273
+
274
+ total_locations += total_in_group
275
+ total_groups += 1
276
+ end
277
+
278
+ # Footer
279
+ lines << ''
280
+ footer = "Total: #{total_locations} location(s) in #{total_groups} group(s)"
281
+ footer += ' (unassigned hidden)' if data[:skip_unassigned]
282
+ lines << colorize(footer, :dim)
283
+
284
+ lines.join("\n")
285
+ end
286
+
106
287
  def format_header
107
288
  cols = [
108
289
  pad('#', 3),
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Appydave
4
4
  module Tools
5
- VERSION = '0.74.1'
5
+ VERSION = '0.75.0'
6
6
  end
7
7
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appydave-tools",
3
- "version": "0.74.1",
3
+ "version": "0.75.0",
4
4
  "description": "AppyDave YouTube Automation Tools",
5
5
  "scripts": {
6
6
  "release": "semantic-release"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appydave-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.74.1
4
+ version: 0.75.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-02-04 00:00:00.000000000 Z
11
+ date: 2026-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel