1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
# frozen_string_literal: true
module Ml
class CandidatesCsvPresenter
CANDIDATE_ASSOCIATIONS = [:latest_metrics, :params, :experiment].freeze
# This file size limit is mainly to avoid the generation to hog resources from the server. The value is arbitrary
# can be update once we have better insight into usage.
TARGET_FILESIZE = 2.megabytes
def initialize(candidates)
@candidates = candidates
end
def present
CsvBuilder.new(@candidates, headers, CANDIDATE_ASSOCIATIONS).render(TARGET_FILESIZE)
end
private
def headers
metric_names = columns_names(&:metrics)
param_names = columns_names(&:params)
candidate_to_metrics = @candidates.to_h do |candidate|
[candidate.id, candidate.latest_metrics.to_h { |m| [m.name, m.value] }]
end
candidate_to_params = @candidates.to_h do |candidate|
[candidate.id, candidate.params.to_h { |m| [m.name, m.value] }]
end
{
project_id: 'project_id',
experiment_iid: ->(c) { c.experiment.iid },
candidate_iid: 'internal_id',
name: 'name',
external_id: 'eid',
start_time: 'start_time',
end_time: 'end_time',
**param_names.index_with { |name| ->(c) { candidate_to_params.dig(c.id, name) } },
**metric_names.index_with { |name| ->(c) { candidate_to_metrics.dig(c.id, name) } }
}
end
def columns_names(&selector)
@candidates.flat_map(&selector).map(&:name).uniq
end
end
end
|