Class: Omnizip::ETA::TimeEstimator

Inherits:
Object
  • Object
show all
Defined in:
lib/omnizip/eta/time_estimator.rb

Overview

Abstract base class for time estimation strategies.

This class defines the interface for ETA estimators and provides common functionality. Subclasses implement specific estimation algorithms (exponential smoothing, moving average, etc.).

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(sample_history: nil, rate_calculator: nil) ⇒ TimeEstimator

Initialize a new time estimator

Parameters:

  • sample_history (SampleHistory) (defaults to: nil)

    History of samples

  • rate_calculator (RateCalculator) (defaults to: nil)

    Rate calculator



21
22
23
24
25
# File 'lib/omnizip/eta/time_estimator.rb', line 21

def initialize(sample_history: nil, rate_calculator: nil)
  @sample_history = sample_history || SampleHistory.new
  @rate_calculator = rate_calculator ||
    RateCalculator.new(sample_history: @sample_history)
end

Instance Attribute Details

#rate_calculatorObject (readonly)

Returns the value of attribute rate_calculator.



15
16
17
# File 'lib/omnizip/eta/time_estimator.rb', line 15

def rate_calculator
  @rate_calculator
end

#sample_historyObject (readonly)

Returns the value of attribute sample_history.



15
16
17
# File 'lib/omnizip/eta/time_estimator.rb', line 15

def sample_history
  @sample_history
end

Instance Method Details

#add_sample(bytes_processed:, files_processed:, timestamp: Time.now) ⇒ Object

Add a sample to the history

Parameters:

  • bytes_processed (Integer)

    Total bytes processed

  • files_processed (Integer)

    Total files processed

  • timestamp (Time) (defaults to: Time.now)

    Sample timestamp



32
33
34
35
36
37
38
# File 'lib/omnizip/eta/time_estimator.rb', line 32

def add_sample(bytes_processed:, files_processed:, timestamp: Time.now)
  sample_history.add_sample(
    bytes_processed: bytes_processed,
    files_processed: files_processed,
    timestamp: timestamp,
  )
end

#confidence_interval(estimated_seconds, confidence_level: 0.95) ⇒ Array<Float>

Calculate confidence interval based on rate variance

Parameters:

  • estimated_seconds (Float)

    Estimated time in seconds

  • confidence_level (Float) (defaults to: 0.95)

    Confidence level (0.95 = 95%)

Returns:

  • (Array<Float>)

    [lower_bound, upper_bound] in seconds



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/omnizip/eta/time_estimator.rb', line 74

def confidence_interval(estimated_seconds, confidence_level: 0.95)
  return [0.0, 0.0] if sample_history.size < 3

  # Use standard deviation of rates to calculate confidence interval
  std_dev = sample_history.rate_std_dev
  current_rate = rate_calculator.bytes_per_second

  return [estimated_seconds, estimated_seconds] if current_rate.zero?

  # Calculate coefficient of variation
  cv = std_dev / current_rate

  # Z-score for confidence level (approximation)
  z_score = confidence_level >= 0.99 ? 2.576 : 1.96

  # Calculate interval as percentage of estimate
  margin = estimated_seconds * cv * z_score

  lower = [estimated_seconds - margin, 0.0].max
  upper = estimated_seconds + margin

  [lower, upper]
end

#estimate(remaining_bytes) ⇒ Models::ETAResult

Estimate time remaining (to be implemented by subclasses)

Parameters:

  • remaining_bytes (Integer)

    Bytes remaining to process

Returns:

Raises:

  • (NotImplementedError)

    if not implemented by subclass



45
46
47
# File 'lib/omnizip/eta/time_estimator.rb', line 45

def estimate(remaining_bytes)
  raise NotImplementedError, "#{self.class} must implement #estimate"
end

#format_time(seconds) ⇒ String

Format seconds as human-readable string

Parameters:

  • seconds (Float)

    Seconds to format

Returns:

  • (String)

    Formatted time (e.g., "2m 30s", "1h 15m")



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/omnizip/eta/time_estimator.rb', line 53

def format_time(seconds)
  return "0s" if seconds <= 0
  return "" if seconds.infinite?

  hours = (seconds / 3600).floor
  minutes = ((seconds % 3600) / 60).floor
  secs = (seconds % 60).round

  parts = []
  parts << "#{hours}h" if hours.positive?
  parts << "#{minutes}m" if minutes.positive? || hours.positive?
  parts << "#{secs}s"

  parts.join(" ")
end

#sufficient_samples?Boolean

Check if we have enough samples for reliable estimation

Returns:

  • (Boolean)

    true if enough samples



101
102
103
# File 'lib/omnizip/eta/time_estimator.rb', line 101

def sufficient_samples?
  sample_history.size >= 3
end