Add experiments overview service [SCI-5430] (#3116)

This commit is contained in:
Alex Kriuchykhin 2021-01-28 12:57:04 +01:00 committed by GitHub
parent 8396c086f5
commit f1124f9ea0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 266 additions and 2 deletions

View file

@ -2,6 +2,7 @@ class Project < ApplicationRecord
include ArchivableModel
include SearchableModel
include SearchableByNameModel
include ViewableModel
enum visibility: { hidden: 0, visible: 1 }
@ -150,6 +151,22 @@ class Project < ApplicationRecord
.distinct
end
def default_view_state
{
experiments: {
active: { sort: 'new' },
archived: { sort: 'new' }
}
}
end
def validate_view_state(view_state)
if %w(new old atoz ztoa).exclude?(view_state.state.dig('experiments', 'active', 'sort')) ||
%w(new old atoz ztoa archived_new archived_old).exclude?(view_state.state.dig('experiments', 'archived', 'sort'))
view_state.errors.add(:state, :wrong_state)
end
end
def last_activities(count = Constants::ACTIVITY_AND_NOTIF_SEARCH_LIMIT)
activities.order(created_at: :desc).first(count)
end

View file

@ -0,0 +1,76 @@
# frozen_string_literal: true
class ExperimentsOverviewService
def initialize(project, user, params)
@project = project
@user = user
@params = params
@view_state = @project.current_view_state(@user)
@view_mode = if @project.archived?
'archived'
else
params[:view_mode] || 'active'
end
# Update sort if changed
@sort = @view_state.state.dig('experiments', @view_mode, 'sort') || 'atoz'
if @params[:sort] && @sort != @params[:sort] &&
%w(new old atoz ztoa archived_old archived_new).include?(@params[:sort])
@view_state.state['experiments'].merge!(Hash[@view_mode, { 'sort': @params[:sort] }.stringify_keys])
@view_state.save!
@sort = @view_state.state.dig('experiments', @view_mode, 'sort')
end
end
def experiments
sort_records(
filter_records(
fetch_records
)
)
end
private
def fetch_records
@project.experiments.joins(:project)
end
def filter_records(records)
records = records.archived if @view_mode == 'archived' && @project.active?
records = records.active if @view_mode == 'active'
if @params[:search].present?
records = records.where_attributes_like(%w(experiments.name experiments.description), @params[:search])
end
if @params[:created_on_from].present?
records = records.where('experiments.created_at > ?', @params[:created_on_from])
end
records = records.where('experiments.created_at < ?', @params[:created_on_to]) if @params[:created_on_to].present?
if @params[:archived_on_from].present?
records = records.where('COALESCE(experiments.archived_on, projects.archived_on) > ?', @params[:archived_on_from])
end
if @params[:archived_on_to].present?
records = records.where('COALESCE(experiments.archived_on, projects.archived_on) < ?', @params[:archived_on_to])
end
records
end
def sort_records(records)
case @sort
when 'new'
records.order(created_at: :desc)
when 'old'
records.order(:created_at)
when 'atoz'
records.order(:name)
when 'ztoa'
records.order(name: :desc)
when 'archived_old'
records.order(Arel.sql('COALESCE(experiments.archived_on, projects.archived_on) ASC'))
when 'archived_new'
records.order(Arel.sql('COALESCE(experiments.archived_on, projects.archived_on) DESC'))
else
records
end
end
end

View file

@ -110,8 +110,9 @@ class ProjectsOverviewService
records = records.archived if @view_mode == 'archived'
records = records.active if @view_mode == 'active'
records = records.where_attributes_like('projects.name', @params[:search]) if @params[:search].present?
records = records.where_attributes_like('projects.name', @params[:search]) if @params[:search].present?
records = records.joins(:user_projects).where('user_projects.user_id IN (?)', @params[:members]) if @params[:members]&.any?
if @params[:members].present?
records = records.joins(:user_projects).where('user_projects.user_id IN (?)', @params[:members])
end
records = records.where('projects.created_at > ?', @params[:created_on_from]) if @params[:created_on_from].present?
records = records.where('projects.created_at < ?', @params[:created_on_to]) if @params[:created_on_to].present?
records = records.where('projects.archived_on < ?', @params[:archived_on_to]) if @params[:archived_on_to].present?

View file

@ -0,0 +1,170 @@
# frozen_string_literal: true
require 'rails_helper'
describe ExperimentsOverviewService do
EXPERIMENTS_CNT = 26
time = Time.new(2019, 8, 1, 14, 35, 0)
let!(:user) { create :user }
let!(:project) { create :project }
before do
@experiments_overview = ExperimentsOverviewService.new(project, user, params)
end
let!(:experiment_1) do
create :experiment, name: 'test experiment D', project: project,
archived: false, created_at: time.advance(hours: 2)
end
let!(:experiment_2) do
create :experiment, name: 'test experiment B', project: project,
archived: true, archived_on: time.advance(hours: 9), archived_by: user, created_at: time
end
let!(:experiment_3) do
create :experiment, name: 'test experiment C', project: project,
archived: false, created_at: time.advance(hours: 3)
end
let!(:experiment_4) do
create :experiment, name: 'test experiment A', project: project,
archived: true, archived_on: time.advance(hours: 8), archived_by: user,
created_at: time.advance(hours: 1)
end
let!(:experiment_5) do
create :experiment, name: 'test experiment E', project: project,
archived: true, archived_on: time.advance(hours: 10), archived_by: user,
created_at: time.advance(hours: 5)
end
let!(:experiment_6) do
create :experiment, name: 'test experiment F', project: project,
archived: false, created_at: time.advance(hours: 4)
end
(7..EXPERIMENTS_CNT).each do |i|
let!("experiment_#{i}") do
create :experiment, name: "test experiment #{(64 + i).chr}",
project: project, created_at: time.advance(hours: 6, minutes: i)
end
end
describe '#experiments' do
let(:params) { {} }
context "with request parameters { filter: 'active' }" do
let(:params) { { view_mode: 'active' } }
it 'returns all active experiments' do
experiments = @experiments_overview.experiments
expect(experiments.length).to eq EXPERIMENTS_CNT - 3
expect(experiments.uniq.length).to eq experiments.length
expect(experiments).to include(experiment_1, experiment_3)
expect(experiments).not_to include(experiment_2, experiment_4, experiment_5)
end
context "with request parameters { sort: 'old' }" do
let(:params) { super().merge(sort: 'old') }
it 'returns all active experiments, sorted by ascending creation time attribute' do
experiments = @experiments_overview.experiments
expect(experiments.length).to eq EXPERIMENTS_CNT - 3
expect(experiments.uniq.length).to eq experiments.length
expect(experiments.first(2)).to eq [experiment_1, experiment_3]
expect(experiments).not_to include(experiment_2, experiment_4, experiment_5)
end
end
context "with request parameters { sort: 'new' }" do
let(:params) { super().merge(sort: 'new') }
it 'returns all active experiments, sorted by descending creation time attribute' do
experiments = @experiments_overview.experiments
expect(experiments.length).to eq EXPERIMENTS_CNT - 3
expect(experiments.uniq.length).to eq experiments.length
expect(experiments.last(2)).to eq [experiment_3, experiment_1]
expect(experiments).not_to include(experiment_2, experiment_4, experiment_5)
end
end
context "with request parameters { sort: 'atoz' }" do
let(:params) { super().merge(sort: 'atoz') }
it 'returns all active experiments, sorted by ascending name attribute' do
experiments = @experiments_overview.experiments
expect(experiments.length).to eq EXPERIMENTS_CNT - 3
expect(experiments.uniq.length).to eq experiments.length
expect(experiments.first(2)).to eq [experiment_3, experiment_1]
expect(experiments).not_to include(experiment_2, experiment_4, experiment_5)
end
end
context "with request parameters { sort: 'ztoa' }" do
let(:params) { super().merge(sort: 'ztoa') }
it 'returns all active experiments, sorted by descending name attribute' do
experiments = @experiments_overview.experiments
expect(experiments.length).to eq EXPERIMENTS_CNT - 3
expect(experiments.uniq.length).to eq experiments.length
expect(experiments.last(2)).to eq [experiment_1, experiment_3]
expect(experiments).not_to include(experiment_2, experiment_4, experiment_5)
end
end
end
context "with request parameters { filter: 'archived' }" do
let(:params) { super().merge(view_mode: 'archived') }
it 'returns all archived experiments' do
experiments = @experiments_overview.experiments
expect(experiments.length).to eq 3
expect(experiments.uniq.length).to eq experiments.length
expect(experiments).to include(experiment_2, experiment_4, experiment_5)
expect(experiments).not_to include(experiment_1, experiment_3)
end
context "with request parameters { sort: 'old' }" do
let(:params) { super().merge(sort: 'old') }
it 'returns all archived experiments, sorted by ascending creation time attribute' do
experiments = @experiments_overview.experiments
expect(experiments.length).to eq 3
expect(experiments.uniq.length).to eq experiments.length
expect(experiments.first(3)).to eq [experiment_2, experiment_4, experiment_5]
expect(experiments).not_to include(experiment_1, experiment_3, experiment_6)
end
end
context "with request parameters { sort: 'new' }" do
let(:params) { super().merge(sort: 'new') }
it 'returns all archived experiments, sorted by descending creation time attribute' do
experiments = @experiments_overview.experiments
expect(experiments.length).to eq 3
expect(experiments.uniq.length).to eq experiments.length
expect(experiments.last(3)).to eq [experiment_5, experiment_4, experiment_2]
expect(experiments).not_to include(experiment_1, experiment_3, experiment_6)
end
end
context "with request parameters { sort: 'atoz' }" do
let(:params) { super().merge(sort: 'atoz') }
it 'returns all archived experiments, sorted by ascending name attribute' do
experiments = @experiments_overview.experiments
expect(experiments.length).to eq 3
expect(experiments.uniq.length).to eq experiments.length
expect(experiments.first(3)).to eq [experiment_4, experiment_2, experiment_5]
expect(experiments).not_to include(experiment_1, experiment_3, experiment_6)
end
end
context "with request parameters { sort: 'ztoa' }" do
let(:params) { super().merge(sort: 'ztoa') }
it 'returns all archived experiments, sorted by descending name attribute' do
experiments = @experiments_overview.experiments
expect(experiments.length).to eq 3
expect(experiments.uniq.length).to eq experiments.length
expect(experiments.last(3)).to eq [experiment_5, experiment_2, experiment_4]
expect(experiments).not_to include(experiment_1, experiment_3, experiment_6)
end
end
end
end
end