Module: Legion::Extensions::Rfp::Analytics::Runners::Metrics

Extended by:
Helpers::Client
Includes:
Helpers::Lex
Included in:
Client
Defined in:
lib/legion/extensions/rfp/analytics/runners/metrics.rb

Instance Method Summary collapse

Methods included from Helpers::Client

client

Instance Method Details

#record_outcome(proposal_id:, outcome:, revenue: nil, feedback: nil) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/legion/extensions/rfp/analytics/runners/metrics.rb', line 23

def record_outcome(proposal_id:, outcome:, revenue: nil, feedback: nil, **)
  valid_outcomes = %i[won lost no_decision pending]
  outcome_sym = outcome.to_sym
  return { result: nil, error: "Invalid outcome: #{outcome}. Valid: #{valid_outcomes.join(', ')}" } unless valid_outcomes.include?(outcome_sym)

  {
    result: {
      proposal_id: proposal_id,
      outcome:     outcome_sym,
      revenue:     revenue,
      feedback:    feedback,
      recorded_at: Time.now.iso8601
    }
  }
end

#record_proposal(proposal_id:, rfp_source:, submitted_at: nil, sections: 0, word_count: 0) ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
# File 'lib/legion/extensions/rfp/analytics/runners/metrics.rb', line 11

def record_proposal(proposal_id:, rfp_source:, submitted_at: nil, sections: 0, word_count: 0, **)
  metric = {
    proposal_id:  proposal_id,
    rfp_source:   rfp_source,
    submitted_at:  || Time.now.iso8601,
    sections:     sections,
    word_count:   word_count,
    recorded_at:  Time.now.iso8601
  }
  { result: metric }
end

#response_time_stats(proposals:) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/legion/extensions/rfp/analytics/runners/metrics.rb', line 58

def response_time_stats(proposals:, **)
  times = proposals.filter_map do |p|
    next unless p[:created_at] && p[:submitted_at]

    created = Time.parse(p[:created_at])
     = Time.parse(p[:submitted_at])
    ( - created).to_f
  end

  return { result: { count: 0 } } if times.empty?

  {
    result: {
      count:  times.length,
      avg:    (times.sum / times.length).round(2),
      min:    times.min.round(2),
      max:    times.max.round(2),
      median: times.sort[times.length / 2].round(2)
    }
  }
end

#summary(proposals:) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/legion/extensions/rfp/analytics/runners/metrics.rb', line 39

def summary(proposals:, **)
  total = proposals.length
  won = proposals.count { |p| p[:outcome] == :won }
  lost = proposals.count { |p| p[:outcome] == :lost }
  pending = proposals.count { |p| p[:outcome] == :pending || p[:outcome].nil? }
  total_revenue = proposals.select { |p| p[:outcome] == :won }.sum { |p| p[:revenue].to_f }

  {
    result: {
      total_proposals: total,
      won:             won,
      lost:            lost,
      pending:         pending,
      win_rate:        total.positive? ? (won.to_f / (won + lost)).round(4) : 0.0,
      total_revenue:   total_revenue
    }
  }
end