Открыть боковую панель
GitLab.org
Gitlab
Коммиты
5688a917
Коммит
5688a917
создал
Мар 27, 2023
по автору
Eduardo Bonet
Просмотр файлов
wip
владелец
dfe07be4
Изменения
11
Скрыть пробелы
Построчно
Рядом
app/assets/javascripts/ml/experiment_tracking/routes/experiments/show/ml_experiments_show.vue
Просмотр файла @
5688a917
...
...
@@ -180,7 +180,7 @@ export default {
</
template
>
<
template
#cell(nameColumn)=
"data"
>
<gl-link
:href=
"data.value.details
_p
ath"
>
<gl-link
:href=
"data.value.details
P
ath"
>
<span
v-if=
"data.value.name"
>
{{
data
.
value
.
name
}}
</span>
<span
v-else
class=
"gl-font-style-italic"
>
{{
$options
.
i18n
.
NO_CANDIDATE_NAME
}}
</span>
</gl-link>
...
...
@@ -195,7 +195,7 @@ export default {
</div>
</
template
>
<
template
#cell(created
_a
t)=
"data"
>
<
template
#cell(created
A
t)=
"data"
>
<time-ago
:time=
"data.value"
/>
</
template
>
...
...
app/assets/javascripts/pages/projects/ml/experiments/show/index.js
Просмотр файла @
5688a917
import
Vue
from
'
vue
'
;
import
{
initSimpleApp
}
from
'
~/helpers/init_simple_app_helper
'
;
import
MlExperimentsShow
from
'
~/ml/experiment_tracking/routes/experiments/show/ml_experiments_show.vue
'
;
import
{
convertObjectPropsToCamelCase
}
from
'
~/lib/utils/common_utils
'
;
const
initShowExperiment
=
()
=>
{
const
element
=
document
.
querySelector
(
'
#js-show-ml-experiment
'
);
if
(
!
element
)
{
return
undefined
;
}
const
props
=
{
candidates
:
JSON
.
parse
(
element
.
dataset
.
candidates
),
metricNames
:
JSON
.
parse
(
element
.
dataset
.
metrics
),
paramNames
:
JSON
.
parse
(
element
.
dataset
.
params
),
pageInfo
:
convertObjectPropsToCamelCase
(
JSON
.
parse
(
element
.
dataset
.
pageInfo
)),
};
return
new
Vue
({
el
:
element
,
render
(
h
)
{
return
h
(
MlExperimentsShow
,
{
props
});
},
});
};
initShowExperiment
();
initSimpleApp
(
'
#js-show-ml-experiment
'
,
MlExperimentsShow
);
app/controllers/projects/ml/experiments_controller.rb
Просмотр файла @
5688a917
...
...
@@ -29,13 +29,12 @@ def show
.
transform_keys
(
&
:underscore
)
.
permit
(
:name
,
:order_by
,
:sort
,
:order_by_type
)
paginator
=
CandidateFinder
@
paginator
=
CandidateFinder
.
new
(
@experiment
,
find_params
)
.
execute
.
keyset_paginate
(
cursor:
params
[
:cursor
],
per_page:
MAX_CANDIDATES_PER_PAGE
)
@candidates
=
paginator
.
records
.
each
(
&
:artifact_lazy
)
@page_info
=
page_info
(
paginator
)
@candidates
=
@paginator
.
records
.
each
(
&
:artifact_lazy
)
end
def
destroy
...
...
app/helpers/projects/ml/experiments_helper.rb
Просмотр файла @
5688a917
...
...
@@ -24,26 +24,6 @@ def show_candidate_view_model(candidate)
Gitlab
::
Json
.
generate
(
data
)
end
def
candidates_table_items
(
candidates
)
items
=
candidates
.
map
do
|
candidate
|
{
**
candidate
.
params
.
to_h
{
|
p
|
[
p
.
name
,
p
.
value
]
},
**
candidate
.
latest_metrics
.
to_h
{
|
m
|
[
m
.
name
,
number_with_precision
(
m
.
value
,
precision:
4
)]
},
artifact:
link_to_artifact
(
candidate
),
details:
link_to_details
(
candidate
),
name:
candidate
.
name
,
created_at:
candidate
.
created_at
,
user:
user_info
(
candidate
)
}
end
Gitlab
::
Json
.
generate
(
items
)
end
def
unique_logged_names
(
candidates
,
&
selector
)
Gitlab
::
Json
.
generate
(
candidates
.
flat_map
(
&
selector
).
map
(
&
:name
).
uniq
)
end
def
experiments_as_data
(
project
,
experiments
)
data
=
experiments
.
map
do
|
exp
|
{
...
...
app/presenters/projects/ml/base_presenter.rb
0 → 100644
Просмотр файла @
5688a917
# frozen_string_literal: true
module
Projects
module
Ml
class
BasePresenter
include
Rails
.
application
.
routes
.
url_helpers
def
view_model
raise
NotImplementedError
,
"
#{
self
.
class
}
does not implement
#{
__method__
}
"
end
def
view_model_as_json
Gitlab
::
Json
.
generate
(
view_model
.
deep_transform_keys!
{
|
key
|
key
.
to_s
.
camelize
(
:lower
)
})
end
def
link_to_artifact
(
candidate
)
artifact
=
candidate
.
artifact
return
unless
artifact
.
present?
project_package_path
(
candidate
.
project
,
artifact
)
end
def
link_to_details
(
candidate
)
project_ml_candidate_path
(
candidate
.
project
,
candidate
.
iid
)
end
def
link_to_experiment
(
project
,
experiment
)
project_ml_experiment_path
(
project
,
experiment
.
iid
)
end
def
page_info
(
paginator
)
{
has_next_page:
paginator
.
has_next_page?
,
has_previous_page:
paginator
.
has_previous_page?
,
start_cursor:
paginator
.
cursor_for_previous_page
,
end_cursor:
paginator
.
cursor_for_next_page
}
end
end
end
end
app/presenters/projects/ml/experiment_presenter.rb
0 → 100644
Просмотр файла @
5688a917
# frozen_string_literal: true
module
Projects
module
Ml
class
ExperimentPresenter
<
BasePresenter
def
initialize
(
experiment
,
candidates
,
paginator
)
@experiment
=
experiment
@candidates
=
candidates
@paginator
=
paginator
end
def
view_model
{
experiment:
experiment_view_model
,
candidates:
candidates_view_model
,
metric_names:
unique_logged_names
(
&
:latest_metrics
),
param_names:
unique_logged_names
(
&
:params
),
page_info:
page_info
(
@paginator
)
}
end
private
def
experiment_view_model
{
name:
@experiment
.
name
,
path:
link_to_experiment
(
@experiment
.
project
,
@experiment
)
}
end
def
candidates_view_model
@candidates
.
map
do
|
candidate
|
{
**
candidate
.
params
.
to_h
{
|
p
|
[
p
.
name
,
p
.
value
]
},
**
candidate
.
latest_metrics
.
to_h
{
|
m
|
[
m
.
name
,
number_with_precision
(
m
.
value
,
precision:
4
)]
},
artifact:
link_to_artifact
(
candidate
),
details:
link_to_details
(
candidate
),
name:
candidate
.
name
,
created_at:
candidate
.
created_at
,
user:
user_info
(
candidate
)
}
end
end
def
unique_logged_names
(
&
selector
)
@candidates
.
flat_map
(
&
selector
).
map
(
&
:name
).
uniq
end
end
end
end
app/views/projects/ml/experiments/show.html.haml
Просмотр файла @
5688a917
-
add_to_breadcrumbs
_
(
"
E
xperiments"
),
project_ml_experiments_path
(
@project
)
-
add_to_breadcrumbs
s
_
(
"
MlExperimentTracking|Model e
xperiments"
),
project_ml_experiments_path
(
@project
)
-
breadcrumb_title
@experiment
.
name
-
page_title
@experiment
.
name
-
add_page_specific_style
'page_bundles/ml_experiment_tracking'
-
items
=
candidates_table_items
(
@candidates
)
-
metrics
=
unique_logged_names
(
@candidates
,
&
:latest_metrics
)
-
params
=
unique_logged_names
(
@candidates
,
&
:params
)
-
page_info
=
formatted_page_info
(
@page_info
)
.page-title-holder.d-flex.align-items-center
%h1
.page-title.gl-font-size-h-display
=
@experiment
.
name
#js-show-ml-experiment
{
data:
{
candidates:
items
,
metrics:
metrics
,
params:
params
,
page_info:
page_info
}
}
#js-show-ml-experiment
{
data:
{
view_model:
show_experiment_view_model
(
@experiment
,
@candidates
,
@paginator
)}
}
spec/factories/ml/experiments.rb
Просмотр файла @
5688a917
...
...
@@ -11,5 +11,15 @@
e
.
metadata
=
FactoryBot
.
create_list
(
:ml_experiment_metadata
,
2
,
experiment:
e
)
# rubocop:disable StrategyInCallback
end
end
factory
:experiment_with_candidates
do
transient
do
candidates_count
{
2
}
end
candidates
do
Array
.
new
(
candidates_count
)
{
association
(
:ml_candidates
)
}
end
end
end
end
spec/helpers/projects/ml/experiments_helper_spec.rb
Просмотр файла @
5688a917
# frozen_string_literal: true
require
'rspec'
require
'spec_helper'
require
'mime/types'
...
...
@@ -26,57 +24,6 @@
let_it_be
(
:candidates
)
{
[
candidate0
,
candidate1
]
}
describe
'#candidates_table_items'
do
subject
{
Gitlab
::
Json
.
parse
(
helper
.
candidates_table_items
(
candidates
))
}
it
'creates the correct model for the table'
,
:aggregate_failures
do
expected_values
=
[
{
'param1'
=>
'p1'
,
'param2'
=>
'p2'
,
'metric1'
=>
'0.1000'
,
'metric2'
=>
'0.2000'
,
'metric3'
=>
'0.3000'
,
'artifact'
=>
"/
#{
project
.
full_path
}
/-/packages/
#{
candidate0
.
artifact
.
id
}
"
,
'details'
=>
"/
#{
project
.
full_path
}
/-/ml/candidates/
#{
candidate0
.
iid
}
"
,
'name'
=>
candidate0
.
name
,
'created_at'
=>
candidate0
.
created_at
.
strftime
(
'%Y-%m-%dT%H:%M:%S.%LZ'
),
'user'
=>
{
'username'
=>
candidate0
.
user
.
username
,
'path'
=>
"/
#{
candidate0
.
user
.
username
}
"
}
},
{
'param2'
=>
'p3'
,
'param3'
=>
'p4'
,
'metric3'
=>
'0.4000'
,
'artifact'
=>
nil
,
'details'
=>
"/
#{
project
.
full_path
}
/-/ml/candidates/
#{
candidate1
.
iid
}
"
,
'name'
=>
candidate1
.
name
,
'created_at'
=>
candidate1
.
created_at
.
strftime
(
'%Y-%m-%dT%H:%M:%S.%LZ'
),
'user'
=>
{
'username'
=>
candidate1
.
user
.
username
,
'path'
=>
"/
#{
candidate1
.
user
.
username
}
"
}
}
]
subject
.
sort_by!
{
|
s
|
s
[
:name
]
}
expect
(
subject
[
0
]).
to
eq
(
expected_values
[
0
])
expect
(
subject
[
1
]).
to
eq
(
expected_values
[
1
])
end
context
'when candidate does not have user'
do
let
(
:candidates
)
{
[
candidate0
]
}
before
do
allow
(
candidate0
).
to
receive
(
:user
).
and_return
(
nil
)
end
it
'has the user property, but is nil'
do
expect
(
subject
[
0
][
'user'
]).
to
be_nil
end
end
end
describe
'#unique_logged_names'
do
context
'when for params'
do
subject
{
Gitlab
::
Json
.
parse
(
helper
.
unique_logged_names
(
candidates
,
&
:params
))
}
it
{
is_expected
.
to
match_array
(
%w[param1 param2 param3]
)
}
end
context
'when latest_metrics is passed'
do
subject
{
Gitlab
::
Json
.
parse
(
helper
.
unique_logged_names
(
candidates
,
&
:latest_metrics
))
}
it
{
is_expected
.
to
match_array
(
%w[metric1 metric2 metric3]
)
}
end
end
describe
'#show_candidate_view_model'
do
let
(
:candidate
)
{
candidate0
}
...
...
spec/presenters/projects/ml/base_presenter_spec.rb
0 → 100644
Просмотр файла @
5688a917
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Projects
::
Ml
::
BasePresenter
,
feature_category: :mlops
do
let_it_be
(
:project
)
{
build_stubbed
(
:project
,
:private
)
}
let_it_be
(
:experiment
)
do
build_stubbed
(
:experiment_with_candidates
,
user_id:
project
.
creator
,
project:
project
,
iid:
1
)
end
let_it_be
(
:candidate
)
{
experiment
.
candidates
[
0
]
}
let_it_be
(
:artifact
)
do
FactoryBot
.
build_stubbed
(
:generic_package
,
name:
candidate
.
package_name
,
version:
candidate
.
package_version
,
project:
candidate
.
project
)
end
let
(
:described_object
)
{
described_class
.
new
}
describe
'.view_model'
do
it
'raises error'
do
expect
{
described_object
.
view_model
}.
to
raise_error
(
NotImplementedError
)
end
end
describe
'.view_model_as_json'
do
before
do
allow
(
described_object
).
to
receive
(
:view_model
).
and_return
({
some_object:
[{
another_object:
1
}]
})
end
subject
{
described_object
.
view_model_as_json
}
it
'transforms to json'
do
expect
(
Gitlab
::
Json
.
parse
(
subject
)).
to
eq
({
'someObject'
=>
[{
"anotherObject"
=>
1
}]
})
end
end
describe
'.link_to_artifact'
do
subject
{
described_object
.
link_to_artifact
(
candidate
)
}
context
'when candidate has artifact'
do
before
do
allow
(
candidate
).
to
receive
(
:artifact
).
and_return
(
artifact
)
end
it
'returns link to artifact'
do
expect
(
subject
).
to
eq
(
"/
#{
project
.
full_path
}
/-/packages/
#{
artifact
.
id
}
"
)
end
end
context
'when there is not artifact'
do
before
do
allow
(
candidate
).
to
receive
(
:artifact
).
and_return
(
nil
)
end
it
'returns nil'
do
expect
(
subject
).
to
be_nil
end
end
end
describe
'.link_to_details'
do
subject
{
described_object
.
link_to_details
(
candidate
)
}
it
'returns path to the candidate'
do
expect
(
subject
).
to
eq
(
"/
#{
project
.
full_path
}
/-/ml/candidates/
#{
candidate
.
iid
}
"
)
end
end
describe
'.link_to_experiment'
do
subject
{
described_object
.
link_to_experiment
(
project
,
experiment
)
}
it
'returns path to the experiment'
do
expect
(
subject
).
to
eq
(
"/
#{
project
.
full_path
}
/-/ml/experiments/
#{
experiment
.
iid
}
"
)
end
end
describe
'#page_info'
do
let
(
:paginator
)
do
# Ideally we would test using experiment.candidates.keyset_paginate, but it doesn't work
# with build_stubbed
Class
.
new
do
def
has_previous_page?
true
end
def
has_next_page?
true
end
def
cursor_for_previous_page
"abc"
end
def
cursor_for_next_page
"def"
end
end
.
new
end
subject
{
described_object
.
page_info
(
paginator
)
}
it
'generates the correct page_info'
do
is_expected
.
to
include
({
has_next_page:
true
,
has_previous_page:
true
,
start_cursor:
"abc"
,
end_cursor:
"def"
})
end
end
end
spec/presenters/projects/ml/experiment_presenter_spec.rb
0 → 100644
Просмотр файла @
5688a917
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Projects
::
Ml
::
ExperimentPresenter
,
feature_category: :mlops
do
let_it_be
(
:project
)
{
build_stubbed
(
:project
,
:private
)
}
let_it_be
(
:experiment
)
do
build_stubbed
(
:experiment_with_candidates
,
user_id:
project
.
creator
,
project:
project
,
iid:
1
)
end
let_it_be
(
:candidate
)
{
experiment
.
candidates
[
0
]
}
let_it_be
(
:artifact
)
do
FactoryBot
.
build_stubbed
(
:generic_package
,
name:
candidate
.
package_name
,
version:
candidate
.
package_version
,
project:
candidate
.
project
)
end
let
(
:described_object
)
{
described_class
.
new
(
experiment
,
experiment
.
candidates
,
nil
)
}
describe
'.view_model'
do
subject
{
described_object
.
view_model
}
it
'creates the correct model for the table'
,
:aggregate_failures
do
expected_values
=
[
{
'param1'
=>
'p1'
,
'param2'
=>
'p2'
,
'metric1'
=>
'0.1000'
,
'metric2'
=>
'0.2000'
,
'metric3'
=>
'0.3000'
,
'artifact'
=>
"/
#{
project
.
full_path
}
/-/packages/
#{
candidate0
.
artifact
.
id
}
"
,
'details'
=>
"/
#{
project
.
full_path
}
/-/ml/candidates/
#{
candidate0
.
iid
}
"
,
'name'
=>
candidate0
.
name
,
'created_at'
=>
candidate0
.
created_at
.
strftime
(
'%Y-%m-%dT%H:%M:%S.%LZ'
),
'user'
=>
{
'username'
=>
candidate0
.
user
.
username
,
'path'
=>
"/
#{
candidate0
.
user
.
username
}
"
}
},
{
'param2'
=>
'p3'
,
'param3'
=>
'p4'
,
'metric3'
=>
'0.4000'
,
'artifact'
=>
nil
,
'details'
=>
"/
#{
project
.
full_path
}
/-/ml/candidates/
#{
candidate1
.
iid
}
"
,
'name'
=>
candidate1
.
name
,
'created_at'
=>
candidate1
.
created_at
.
strftime
(
'%Y-%m-%dT%H:%M:%S.%LZ'
),
'user'
=>
{
'username'
=>
candidate1
.
user
.
username
,
'path'
=>
"/
#{
candidate1
.
user
.
username
}
"
}
}
]
subject
.
sort_by!
{
|
s
|
s
[
:name
]
}
expect
(
subject
[
0
]).
to
eq
(
expected_values
[
0
])
expect
(
subject
[
1
]).
to
eq
(
expected_values
[
1
])
end
context
'when candidate does not have user'
do
let
(
:candidates
)
{
[
candidate0
]
}
before
do
allow
(
candidate0
).
to
receive
(
:user
).
and_return
(
nil
)
end
it
'has the user property, but is nil'
do
expect
(
subject
[
0
][
'user'
]).
to
be_nil
end
end
end
describe
'#unique_logged_names'
do
context
'when for params'
do
subject
{
Gitlab
::
Json
.
parse
(
helper
.
unique_logged_names
(
candidates
,
&
:params
))
}
it
{
is_expected
.
to
match_array
(
%w[param1 param2 param3]
)
}
end
context
'when latest_metrics is passed'
do
subject
{
Gitlab
::
Json
.
parse
(
helper
.
unique_logged_names
(
candidates
,
&
:latest_metrics
))
}
it
{
is_expected
.
to
match_array
(
%w[metric1 metric2 metric3]
)
}
end
end
end
Редактирование
Предварительный просмотр
Поддерживает Markdown
0%
Попробовать снова
или
прикрепить новый файл
.
Отмена
You are about to add
0
people
to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Отмена
Пожалуйста,
зарегистрируйтесь
или
войдите
чтобы прокомментировать