aidp 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +210 -0
- data/bin/aidp +5 -0
- data/lib/aidp/analyze/agent_personas.rb +71 -0
- data/lib/aidp/analyze/agent_tool_executor.rb +445 -0
- data/lib/aidp/analyze/data_retention_manager.rb +426 -0
- data/lib/aidp/analyze/database.rb +243 -0
- data/lib/aidp/analyze/dependencies.rb +335 -0
- data/lib/aidp/analyze/error_handler.rb +486 -0
- data/lib/aidp/analyze/export_manager.rb +425 -0
- data/lib/aidp/analyze/feature_analyzer.rb +397 -0
- data/lib/aidp/analyze/focus_guidance.rb +517 -0
- data/lib/aidp/analyze/incremental_analyzer.rb +543 -0
- data/lib/aidp/analyze/language_analysis_strategies.rb +897 -0
- data/lib/aidp/analyze/large_analysis_progress.rb +504 -0
- data/lib/aidp/analyze/memory_manager.rb +365 -0
- data/lib/aidp/analyze/parallel_processor.rb +460 -0
- data/lib/aidp/analyze/performance_optimizer.rb +694 -0
- data/lib/aidp/analyze/prioritizer.rb +402 -0
- data/lib/aidp/analyze/progress.rb +75 -0
- data/lib/aidp/analyze/progress_visualizer.rb +320 -0
- data/lib/aidp/analyze/report_generator.rb +582 -0
- data/lib/aidp/analyze/repository_chunker.rb +702 -0
- data/lib/aidp/analyze/ruby_maat_integration.rb +572 -0
- data/lib/aidp/analyze/runner.rb +245 -0
- data/lib/aidp/analyze/static_analysis_detector.rb +577 -0
- data/lib/aidp/analyze/steps.rb +53 -0
- data/lib/aidp/analyze/storage.rb +600 -0
- data/lib/aidp/analyze/tool_configuration.rb +456 -0
- data/lib/aidp/analyze/tool_modernization.rb +750 -0
- data/lib/aidp/execute/progress.rb +76 -0
- data/lib/aidp/execute/runner.rb +135 -0
- data/lib/aidp/execute/steps.rb +113 -0
- data/lib/aidp/shared/cli.rb +117 -0
- data/lib/aidp/shared/config.rb +35 -0
- data/lib/aidp/shared/project_detector.rb +119 -0
- data/lib/aidp/shared/providers/anthropic.rb +26 -0
- data/lib/aidp/shared/providers/base.rb +17 -0
- data/lib/aidp/shared/providers/cursor.rb +102 -0
- data/lib/aidp/shared/providers/gemini.rb +26 -0
- data/lib/aidp/shared/providers/macos_ui.rb +26 -0
- data/lib/aidp/shared/sync.rb +15 -0
- data/lib/aidp/shared/util.rb +41 -0
- data/lib/aidp/shared/version.rb +7 -0
- data/lib/aidp/shared/workspace.rb +21 -0
- data/lib/aidp.rb +53 -0
- data/templates/ANALYZE/01_REPOSITORY_ANALYSIS.md +100 -0
- data/templates/ANALYZE/02_ARCHITECTURE_ANALYSIS.md +151 -0
- data/templates/ANALYZE/03_TEST_ANALYSIS.md +182 -0
- data/templates/ANALYZE/04_FUNCTIONALITY_ANALYSIS.md +200 -0
- data/templates/ANALYZE/05_DOCUMENTATION_ANALYSIS.md +202 -0
- data/templates/ANALYZE/06_STATIC_ANALYSIS.md +233 -0
- data/templates/ANALYZE/07_REFACTORING_RECOMMENDATIONS.md +316 -0
- data/templates/COMMON/AGENT_BASE.md +129 -0
- data/templates/COMMON/CONVENTIONS.md +19 -0
- data/templates/COMMON/TEMPLATES/ADR_TEMPLATE.md +21 -0
- data/templates/COMMON/TEMPLATES/DOMAIN_CHARTER.md +27 -0
- data/templates/COMMON/TEMPLATES/EVENT_EXAMPLE.yaml +16 -0
- data/templates/COMMON/TEMPLATES/MERMAID_C4.md +46 -0
- data/templates/COMMON/TEMPLATES/OPENAPI_STUB.yaml +11 -0
- data/templates/EXECUTE/00_PRD.md +36 -0
- data/templates/EXECUTE/01_NFRS.md +27 -0
- data/templates/EXECUTE/02A_ARCH_GATE_QUESTIONS.md +13 -0
- data/templates/EXECUTE/02_ARCHITECTURE.md +42 -0
- data/templates/EXECUTE/03_ADR_FACTORY.md +22 -0
- data/templates/EXECUTE/04_DOMAIN_DECOMPOSITION.md +24 -0
- data/templates/EXECUTE/05_CONTRACTS.md +27 -0
- data/templates/EXECUTE/06_THREAT_MODEL.md +23 -0
- data/templates/EXECUTE/07_TEST_PLAN.md +24 -0
- data/templates/EXECUTE/08_TASKS.md +29 -0
- data/templates/EXECUTE/09_SCAFFOLDING_DEVEX.md +25 -0
- data/templates/EXECUTE/10_IMPLEMENTATION_AGENT.md +30 -0
- data/templates/EXECUTE/11_STATIC_ANALYSIS.md +22 -0
- data/templates/EXECUTE/12_OBSERVABILITY_SLOS.md +21 -0
- data/templates/EXECUTE/13_DELIVERY_ROLLOUT.md +21 -0
- data/templates/EXECUTE/14_DOCS_PORTAL.md +23 -0
- data/templates/EXECUTE/15_POST_RELEASE.md +25 -0
- metadata +301 -0
@@ -0,0 +1,504 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "yaml"
|
5
|
+
require "time"
|
6
|
+
require "securerandom"
|
7
|
+
|
8
|
+
module Aidp
|
9
|
+
class LargeAnalysisProgress
|
10
|
+
# Progress tracking states
|
11
|
+
PROGRESS_STATES = %w[pending running paused completed failed cancelled].freeze
|
12
|
+
|
13
|
+
# Default configuration
|
14
|
+
DEFAULT_CONFIG = {
|
15
|
+
checkpoint_interval: 100, # Save progress every 100 items
|
16
|
+
max_checkpoints: 50, # Keep last 50 checkpoints
|
17
|
+
progress_file: ".aidp-large-analysis-progress.yml",
|
18
|
+
auto_save: true,
|
19
|
+
detailed_logging: false
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
def initialize(project_dir = Dir.pwd, config = {})
|
23
|
+
@project_dir = project_dir
|
24
|
+
@config = DEFAULT_CONFIG.merge(config)
|
25
|
+
@progress_file = File.join(@project_dir, @config[:progress_file])
|
26
|
+
@current_progress = load_progress || create_initial_progress
|
27
|
+
@checkpoints = []
|
28
|
+
@start_time = nil
|
29
|
+
@last_save_time = Time.now
|
30
|
+
end
|
31
|
+
|
32
|
+
# Start a large analysis job
|
33
|
+
def start_analysis(analysis_config)
|
34
|
+
@current_progress = {
|
35
|
+
id: generate_analysis_id,
|
36
|
+
state: "running",
|
37
|
+
config: analysis_config,
|
38
|
+
start_time: Time.now,
|
39
|
+
last_update: Time.now,
|
40
|
+
total_items: analysis_config[:total_items] || 0,
|
41
|
+
processed_items: 0,
|
42
|
+
failed_items: 0,
|
43
|
+
current_phase: "initialization",
|
44
|
+
phases: analysis_config[:phases] || [],
|
45
|
+
phase_progress: {},
|
46
|
+
checkpoints: [],
|
47
|
+
errors: [],
|
48
|
+
warnings: [],
|
49
|
+
statistics: {
|
50
|
+
items_per_second: 0,
|
51
|
+
estimated_completion: nil,
|
52
|
+
memory_usage: 0,
|
53
|
+
cpu_usage: 0
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
@start_time = @current_progress[:start_time]
|
58
|
+
save_progress
|
59
|
+
|
60
|
+
@current_progress
|
61
|
+
end
|
62
|
+
|
63
|
+
# Update progress for current item
|
64
|
+
def update_item_progress(item_index, item_data = {})
|
65
|
+
return unless @current_progress
|
66
|
+
|
67
|
+
@current_progress[:processed_items] += 1
|
68
|
+
@current_progress[:last_update] = Time.now
|
69
|
+
|
70
|
+
# Update statistics
|
71
|
+
update_statistics
|
72
|
+
|
73
|
+
# Check if checkpoint should be saved
|
74
|
+
save_checkpoint(item_index, item_data) if should_save_checkpoint?
|
75
|
+
|
76
|
+
# Auto-save if enabled
|
77
|
+
save_progress if @config[:auto_save] && should_auto_save?
|
78
|
+
|
79
|
+
@current_progress
|
80
|
+
end
|
81
|
+
|
82
|
+
# Update phase progress
|
83
|
+
def update_phase_progress(phase_name, phase_data = {})
|
84
|
+
return unless @current_progress
|
85
|
+
|
86
|
+
@current_progress[:current_phase] = phase_name
|
87
|
+
@current_progress[:phase_progress][phase_name] = {
|
88
|
+
start_time: Time.now,
|
89
|
+
items_processed: 0,
|
90
|
+
items_failed: 0,
|
91
|
+
data: phase_data
|
92
|
+
}
|
93
|
+
|
94
|
+
save_progress
|
95
|
+
@current_progress
|
96
|
+
end
|
97
|
+
|
98
|
+
# Mark item as failed
|
99
|
+
def mark_item_failed(item_index, error_data = {})
|
100
|
+
return unless @current_progress
|
101
|
+
|
102
|
+
@current_progress[:failed_items] += 1
|
103
|
+
@current_progress[:errors] << {
|
104
|
+
item_index: item_index,
|
105
|
+
timestamp: Time.now,
|
106
|
+
error: error_data[:error],
|
107
|
+
details: error_data[:details]
|
108
|
+
}
|
109
|
+
|
110
|
+
# Update current phase if applicable
|
111
|
+
if @current_progress[:current_phase] && @current_progress[:phase_progress][@current_progress[:current_phase]]
|
112
|
+
@current_progress[:phase_progress][@current_progress[:current_phase]][:items_failed] += 1
|
113
|
+
end
|
114
|
+
|
115
|
+
save_progress
|
116
|
+
@current_progress
|
117
|
+
end
|
118
|
+
|
119
|
+
# Pause analysis
|
120
|
+
def pause_analysis(reason = nil)
|
121
|
+
return unless @current_progress
|
122
|
+
|
123
|
+
@current_progress[:state] = "paused"
|
124
|
+
@current_progress[:pause_reason] = reason
|
125
|
+
@current_progress[:pause_time] = Time.now
|
126
|
+
@current_progress[:last_update] = Time.now
|
127
|
+
|
128
|
+
save_progress
|
129
|
+
@current_progress
|
130
|
+
end
|
131
|
+
|
132
|
+
# Resume analysis
|
133
|
+
def resume_analysis
|
134
|
+
return unless @current_progress
|
135
|
+
|
136
|
+
@current_progress[:state] = "running"
|
137
|
+
@current_progress[:resume_time] = Time.now
|
138
|
+
@current_progress[:last_update] = Time.now
|
139
|
+
|
140
|
+
# Calculate pause duration
|
141
|
+
if @current_progress[:pause_time]
|
142
|
+
pause_duration = @current_progress[:resume_time] - @current_progress[:pause_time]
|
143
|
+
@current_progress[:total_pause_time] ||= 0
|
144
|
+
@current_progress[:total_pause_time] += pause_duration
|
145
|
+
end
|
146
|
+
|
147
|
+
save_progress
|
148
|
+
@current_progress
|
149
|
+
end
|
150
|
+
|
151
|
+
# Complete analysis
|
152
|
+
def complete_analysis(completion_data = {})
|
153
|
+
return unless @current_progress
|
154
|
+
|
155
|
+
@current_progress[:state] = "completed"
|
156
|
+
@current_progress[:completion_time] = Time.now
|
157
|
+
@current_progress[:last_update] = Time.now
|
158
|
+
@current_progress[:completion_data] = completion_data
|
159
|
+
|
160
|
+
# Calculate final statistics
|
161
|
+
calculate_final_statistics
|
162
|
+
|
163
|
+
save_progress
|
164
|
+
@current_progress
|
165
|
+
end
|
166
|
+
|
167
|
+
# Fail analysis
|
168
|
+
def fail_analysis(error_data = {})
|
169
|
+
return unless @current_progress
|
170
|
+
|
171
|
+
@current_progress[:state] = "failed"
|
172
|
+
@current_progress[:failure_time] = Time.now
|
173
|
+
@current_progress[:last_update] = Time.now
|
174
|
+
@current_progress[:failure_data] = error_data
|
175
|
+
|
176
|
+
save_progress
|
177
|
+
@current_progress
|
178
|
+
end
|
179
|
+
|
180
|
+
# Cancel analysis
|
181
|
+
def cancel_analysis(reason = nil)
|
182
|
+
return unless @current_progress
|
183
|
+
|
184
|
+
@current_progress[:state] = "cancelled"
|
185
|
+
@current_progress[:cancellation_time] = Time.now
|
186
|
+
@current_progress[:last_update] = Time.now
|
187
|
+
@current_progress[:cancellation_reason] = reason
|
188
|
+
|
189
|
+
save_progress
|
190
|
+
@current_progress
|
191
|
+
end
|
192
|
+
|
193
|
+
# Get current progress
|
194
|
+
def get_progress
|
195
|
+
return nil unless @current_progress
|
196
|
+
|
197
|
+
# Update statistics before returning
|
198
|
+
update_statistics
|
199
|
+
|
200
|
+
{
|
201
|
+
id: @current_progress[:id],
|
202
|
+
state: @current_progress[:state],
|
203
|
+
progress_percentage: calculate_progress_percentage,
|
204
|
+
processed_items: @current_progress[:processed_items],
|
205
|
+
total_items: @current_progress[:total_items],
|
206
|
+
failed_items: @current_progress[:failed_items],
|
207
|
+
current_phase: @current_progress[:current_phase],
|
208
|
+
elapsed_time: calculate_elapsed_time,
|
209
|
+
estimated_remaining: calculate_estimated_remaining,
|
210
|
+
statistics: @current_progress[:statistics],
|
211
|
+
errors: @current_progress[:errors].last(10), # Last 10 errors
|
212
|
+
warnings: @current_progress[:warnings].last(10) # Last 10 warnings
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
# Get detailed progress report
|
217
|
+
def get_detailed_progress
|
218
|
+
return nil unless @current_progress
|
219
|
+
|
220
|
+
{
|
221
|
+
basic_progress: get_progress,
|
222
|
+
phase_progress: @current_progress[:phase_progress],
|
223
|
+
all_errors: @current_progress[:errors],
|
224
|
+
all_warnings: @current_progress[:warnings],
|
225
|
+
checkpoints: @current_progress[:checkpoints],
|
226
|
+
configuration: @current_progress[:config],
|
227
|
+
timing: {
|
228
|
+
start_time: @current_progress[:start_time],
|
229
|
+
last_update: @current_progress[:last_update],
|
230
|
+
elapsed_time: calculate_elapsed_time,
|
231
|
+
pause_time: @current_progress[:total_pause_time] || 0,
|
232
|
+
effective_time: calculate_effective_time
|
233
|
+
}
|
234
|
+
}
|
235
|
+
end
|
236
|
+
|
237
|
+
# Get progress history
|
238
|
+
def get_progress_history(limit = 10)
|
239
|
+
return [] unless File.exist?(@progress_file)
|
240
|
+
|
241
|
+
begin
|
242
|
+
progress_data = YAML.load_file(@progress_file)
|
243
|
+
history = progress_data[:history] || []
|
244
|
+
history.last(limit)
|
245
|
+
rescue
|
246
|
+
[]
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# Reset progress
|
251
|
+
def reset_progress
|
252
|
+
@current_progress = create_initial_progress
|
253
|
+
@checkpoints = []
|
254
|
+
@start_time = nil
|
255
|
+
@last_save_time = Time.now
|
256
|
+
|
257
|
+
# Clear progress file
|
258
|
+
File.delete(@progress_file) if File.exist?(@progress_file)
|
259
|
+
|
260
|
+
@current_progress
|
261
|
+
end
|
262
|
+
|
263
|
+
# Export progress data
|
264
|
+
def export_progress(format = "json")
|
265
|
+
return nil unless @current_progress
|
266
|
+
|
267
|
+
case format.downcase
|
268
|
+
when "json"
|
269
|
+
JSON.pretty_generate(@current_progress)
|
270
|
+
when "yaml"
|
271
|
+
YAML.dump(@current_progress)
|
272
|
+
else
|
273
|
+
raise "Unsupported export format: #{format}"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# Import progress data
|
278
|
+
def import_progress(data, format = "json")
|
279
|
+
parsed_data = case format.downcase
|
280
|
+
when "json"
|
281
|
+
JSON.parse(data)
|
282
|
+
when "yaml"
|
283
|
+
YAML.load(data)
|
284
|
+
else
|
285
|
+
raise "Unsupported import format: #{format}"
|
286
|
+
end
|
287
|
+
|
288
|
+
@current_progress = parsed_data
|
289
|
+
save_progress
|
290
|
+
|
291
|
+
{
|
292
|
+
success: true,
|
293
|
+
imported_progress: @current_progress
|
294
|
+
}
|
295
|
+
rescue => e
|
296
|
+
{
|
297
|
+
success: false,
|
298
|
+
error: e.message
|
299
|
+
}
|
300
|
+
end
|
301
|
+
|
302
|
+
private
|
303
|
+
|
304
|
+
def load_progress
|
305
|
+
return nil unless File.exist?(@progress_file)
|
306
|
+
|
307
|
+
begin
|
308
|
+
YAML.load_file(@progress_file)
|
309
|
+
rescue
|
310
|
+
nil
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def save_progress
|
315
|
+
return unless @current_progress
|
316
|
+
|
317
|
+
# Add to history if this is a significant update
|
318
|
+
add_to_history if should_add_to_history?
|
319
|
+
|
320
|
+
# Save current progress
|
321
|
+
File.write(@progress_file, YAML.dump(@current_progress))
|
322
|
+
@last_save_time = Time.now
|
323
|
+
end
|
324
|
+
|
325
|
+
def create_initial_progress
|
326
|
+
{
|
327
|
+
id: generate_analysis_id,
|
328
|
+
state: "pending",
|
329
|
+
start_time: nil,
|
330
|
+
last_update: Time.now,
|
331
|
+
total_items: 0,
|
332
|
+
processed_items: 0,
|
333
|
+
failed_items: 0,
|
334
|
+
current_phase: "initialization",
|
335
|
+
phases: [],
|
336
|
+
phase_progress: {},
|
337
|
+
checkpoints: [],
|
338
|
+
errors: [],
|
339
|
+
warnings: [],
|
340
|
+
statistics: {
|
341
|
+
items_per_second: 0,
|
342
|
+
estimated_completion: nil,
|
343
|
+
memory_usage: 0,
|
344
|
+
cpu_usage: 0
|
345
|
+
}
|
346
|
+
}
|
347
|
+
end
|
348
|
+
|
349
|
+
def generate_analysis_id
|
350
|
+
timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
|
351
|
+
"analysis_#{timestamp}_#{SecureRandom.hex(4)}"
|
352
|
+
end
|
353
|
+
|
354
|
+
def should_save_checkpoint?
|
355
|
+
return false unless @current_progress
|
356
|
+
|
357
|
+
@current_progress[:processed_items] % @config[:checkpoint_interval] == 0
|
358
|
+
end
|
359
|
+
|
360
|
+
def save_checkpoint(item_index, item_data)
|
361
|
+
return unless @current_progress
|
362
|
+
|
363
|
+
checkpoint = {
|
364
|
+
timestamp: Time.now,
|
365
|
+
item_index: item_index,
|
366
|
+
processed_items: @current_progress[:processed_items],
|
367
|
+
failed_items: @current_progress[:failed_items],
|
368
|
+
current_phase: @current_progress[:current_phase],
|
369
|
+
data: item_data
|
370
|
+
}
|
371
|
+
|
372
|
+
@current_progress[:checkpoints] << checkpoint
|
373
|
+
|
374
|
+
# Keep only the last N checkpoints
|
375
|
+
return unless @current_progress[:checkpoints].length > @config[:max_checkpoints]
|
376
|
+
|
377
|
+
@current_progress[:checkpoints] = @current_progress[:checkpoints].last(@config[:max_checkpoints])
|
378
|
+
end
|
379
|
+
|
380
|
+
def should_auto_save?
|
381
|
+
return false unless @current_progress
|
382
|
+
|
383
|
+
(Time.now - @last_save_time) > 60 # Save every minute
|
384
|
+
end
|
385
|
+
|
386
|
+
def should_add_to_history?
|
387
|
+
return false unless @current_progress
|
388
|
+
|
389
|
+
# Add to history on significant events
|
390
|
+
%w[completed failed cancelled].include?(@current_progress[:state])
|
391
|
+
end
|
392
|
+
|
393
|
+
def add_to_history
|
394
|
+
return unless @current_progress
|
395
|
+
|
396
|
+
history_file = @progress_file.sub(".yml", "_history.yml")
|
397
|
+
history = []
|
398
|
+
|
399
|
+
if File.exist?(history_file)
|
400
|
+
begin
|
401
|
+
history_data = YAML.load_file(history_file)
|
402
|
+
history = history_data[:history] || []
|
403
|
+
rescue
|
404
|
+
history = []
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
# Add current progress to history
|
409
|
+
history << {
|
410
|
+
timestamp: Time.now,
|
411
|
+
progress: @current_progress.dup
|
412
|
+
}
|
413
|
+
|
414
|
+
# Keep only last 100 entries
|
415
|
+
history = history.last(100)
|
416
|
+
|
417
|
+
# Save history
|
418
|
+
File.write(history_file, YAML.dump({history: history}))
|
419
|
+
end
|
420
|
+
|
421
|
+
def update_statistics
|
422
|
+
return unless @current_progress && @start_time
|
423
|
+
|
424
|
+
elapsed_time = calculate_elapsed_time
|
425
|
+
return if elapsed_time <= 0
|
426
|
+
|
427
|
+
# Calculate items per second
|
428
|
+
@current_progress[:statistics][:items_per_second] =
|
429
|
+
@current_progress[:processed_items].to_f / elapsed_time
|
430
|
+
|
431
|
+
# Calculate estimated completion
|
432
|
+
if @current_progress[:statistics][:items_per_second] > 0
|
433
|
+
remaining_items = @current_progress[:total_items] - @current_progress[:processed_items]
|
434
|
+
estimated_seconds = remaining_items / @current_progress[:statistics][:items_per_second]
|
435
|
+
@current_progress[:statistics][:estimated_completion] = Time.now + estimated_seconds
|
436
|
+
end
|
437
|
+
|
438
|
+
# Update resource usage (simplified)
|
439
|
+
@current_progress[:statistics][:memory_usage] = get_memory_usage
|
440
|
+
@current_progress[:statistics][:cpu_usage] = get_cpu_usage
|
441
|
+
end
|
442
|
+
|
443
|
+
def calculate_final_statistics
|
444
|
+
return unless @current_progress
|
445
|
+
|
446
|
+
total_time = calculate_elapsed_time
|
447
|
+
effective_time = calculate_effective_time
|
448
|
+
|
449
|
+
@current_progress[:final_statistics] = {
|
450
|
+
total_time: total_time,
|
451
|
+
effective_time: effective_time,
|
452
|
+
pause_time: @current_progress[:total_pause_time] || 0,
|
453
|
+
average_items_per_second: @current_progress[:processed_items].to_f / effective_time,
|
454
|
+
success_rate: calculate_success_rate,
|
455
|
+
total_errors: @current_progress[:errors].length,
|
456
|
+
total_warnings: @current_progress[:warnings].length
|
457
|
+
}
|
458
|
+
end
|
459
|
+
|
460
|
+
def calculate_progress_percentage
|
461
|
+
return 0 unless @current_progress && @current_progress[:total_items] > 0
|
462
|
+
|
463
|
+
(@current_progress[:processed_items].to_f / @current_progress[:total_items] * 100).round(2)
|
464
|
+
end
|
465
|
+
|
466
|
+
def calculate_elapsed_time
|
467
|
+
return 0 unless @current_progress && @current_progress[:start_time]
|
468
|
+
|
469
|
+
end_time = @current_progress[:last_update] || Time.now
|
470
|
+
end_time - @current_progress[:start_time]
|
471
|
+
end
|
472
|
+
|
473
|
+
def calculate_effective_time
|
474
|
+
elapsed_time = calculate_elapsed_time
|
475
|
+
pause_time = @current_progress[:total_pause_time] || 0
|
476
|
+
elapsed_time - pause_time
|
477
|
+
end
|
478
|
+
|
479
|
+
def calculate_estimated_remaining
|
480
|
+
return nil unless @current_progress && @current_progress[:statistics][:items_per_second] > 0
|
481
|
+
|
482
|
+
remaining_items = @current_progress[:total_items] - @current_progress[:processed_items]
|
483
|
+
remaining_items / @current_progress[:statistics][:items_per_second]
|
484
|
+
end
|
485
|
+
|
486
|
+
def calculate_success_rate
|
487
|
+
return 0 unless @current_progress && @current_progress[:processed_items] > 0
|
488
|
+
|
489
|
+
successful_items = @current_progress[:processed_items] - @current_progress[:failed_items]
|
490
|
+
(successful_items.to_f / @current_progress[:processed_items] * 100).round(2)
|
491
|
+
end
|
492
|
+
|
493
|
+
def get_memory_usage
|
494
|
+
# Get current memory usage in MB
|
495
|
+
Process.getrusage(:SELF).maxrss / 1024.0
|
496
|
+
end
|
497
|
+
|
498
|
+
def get_cpu_usage
|
499
|
+
# Simplified CPU usage calculation
|
500
|
+
# In a real implementation, this would track CPU time
|
501
|
+
0.5 # Return 50% as default
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|