diff --git a/.overcommit.yml b/.overcommit.yml new file mode 100644 index 000000000..ae6a4ce07 --- /dev/null +++ b/.overcommit.yml @@ -0,0 +1,5 @@ +PreCommit: + RuboCop: + enabled: true + on_warn: fail + problem_on_unmodified_line: ignore diff --git a/Gemfile b/Gemfile index 2fe9e3e77..2dfd5cee3 100644 --- a/Gemfile +++ b/Gemfile @@ -109,6 +109,7 @@ group :development, :test do gem 'byebug' gem 'factory_bot_rails' gem 'listen', '~> 3.0' + gem 'overcommit' gem 'pry' gem 'pry-byebug' gem 'pry-rails' diff --git a/Gemfile.lock b/Gemfile.lock index 12680e154..76978c064 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -156,6 +156,8 @@ GEM mail case_transform (0.2) activesupport + childprocess (0.9.0) + ffi (~> 1.0, >= 1.0.11) climate_control (0.2.0) cliver (0.3.2) coderay (1.1.2) @@ -241,6 +243,7 @@ GEM concurrent-ruby (~> 1.0) i18n-js (3.0.3) i18n (~> 0.6, >= 0.6.6) + iniparse (1.4.4) jaro_winkler (1.5.1) jbuilder (2.7.0) activesupport (>= 4.2.0) @@ -330,6 +333,9 @@ GEM oauth2 (~> 1.1) omniauth (~> 1.2) orm_adapter (0.5.0) + overcommit (0.46.0) + childprocess (~> 0.6, >= 0.6.3) + iniparse (~> 1.4) paperclip (5.3.0) activemodel (>= 4.2.0) activesupport (>= 4.2.0) @@ -590,6 +596,7 @@ DEPENDENCIES nokogiri (~> 1.8.1) omniauth omniauth-linkedin-oauth2 + overcommit paperclip (~> 5.3) pg (~> 0.18) phantomjs @@ -637,4 +644,4 @@ RUBY VERSION ruby 2.4.4p296 BUNDLED WITH - 1.16.6 + 1.17.1 diff --git a/app/models/experiment.rb b/app/models/experiment.rb index 69d2926af..851372781 100644 --- a/app/models/experiment.rb +++ b/app/models/experiment.rb @@ -98,12 +98,6 @@ class Experiment < ApplicationRecord end end - def modules_without_group - MyModule.where(experiment_id: id) - .where(my_module_group: nil) - .where(archived: false) - end - def active_module_groups my_module_groups.joins(:my_modules) .where('my_modules.archived = ?', false) @@ -227,125 +221,8 @@ class Experiment < ApplicationRecord true end - # This method generate the workflow image and saves it as - # experiment attachment def generate_workflow_img - require 'graphviz' - - graph = GraphViz.new(:G, - type: :digraph, - use: :neato) - - graph[:size] = '4,4' - graph.node[color: Constants::COLOR_ALTO, - style: :filled, - fontcolor: Constants::COLOR_EMPEROR, - shape: 'circle', - fontname: 'Arial', - fontsize: '16.0'] - - graph.edge[color: Constants::COLOR_ALTO] - - label = '' - subg = {} - - # Draw orphan modules - if modules_without_group - modules_without_group.each do |my_module| - graph - .subgraph(rank: 'same') - .add_nodes("Orphan-#{my_module.id}", - label: label, - pos: "#{my_module.x / 10},-#{my_module.y / 10}!") - end - end - - # Draw grouped modules - if my_module_groups.many? - my_module_groups.each_with_index do |group, gindex| - subgraph_name = "cluster-#{gindex}" - subg[subgraph_name] = graph.subgraph(rank: 'same') - group.ordered_modules.each_with_index do |my_module, index| - if my_module.outputs.any? - parent = subg[subgraph_name] - .add_nodes("#{subgraph_name}-#{index}", - label: label, - pos: "#{my_module.x / 10},-#{my_module.y / 10}!") - - my_module.outputs.each_with_index do |output, i| - child_mod = MyModule.find_by_id(output.input_id) - child_node = subg[subgraph_name] - .add_nodes("#{subgraph_name}-O#{child_mod.id}-#{i}", - label: label, - pos: "#{child_mod.x / 10},-#{child_mod.y / 10}!") - - subg[subgraph_name].add_edges(parent, child_node) - end - elsif my_module.inputs.any? - parent = subg[subgraph_name] - .add_nodes("#{subgraph_name}-#{index}", - label: label, - pos: "#{my_module.x / 10},-#{my_module.y / 10}!") - - my_module.inputs.each_with_index do |input, i| - child_mod = MyModule.find_by_id(input.output_id) - child_node = subg[subgraph_name] - .add_nodes("#{subgraph_name}-I#{child_mod.id}-#{i}", - label: label, - pos: "#{child_mod.x / 10},-#{child_mod.y / 10}!") - - subg[subgraph_name].add_edges(child_node, parent) - end - end - end - end - else - my_module_groups.each do |group| - group.ordered_modules.each_with_index do |my_module, index| - if my_module.outputs.any? - parent = graph.add_nodes("N-#{index}", - label: label, - pos: "#{my_module.x / 10},-#{ my_module.y / 10}!") - - my_module.outputs.each_with_index do |output, i| - child_mod = MyModule.find_by_id(output.input_id) - child_node = graph - .add_nodes("N-O#{child_mod.id}-#{i}", - label: label, - pos: "#{child_mod.x / 10},-#{child_mod.y / 10}!") - graph.add_edges(parent, child_node) - end - elsif my_module.inputs.any? - parent = graph.add_nodes("N-#{index}", - label: label, - pos: "#{my_module.x / 10},-#{my_module.y / 10}!") - my_module.inputs.each_with_index do |input, i| - child_mod = MyModule.find_by_id(input.output_id) - child_node = graph - .add_nodes("N-I#{child_mod.id}-#{i}", - label: label, - pos: "#{child_mod.x / 10},-#{child_mod.y / 10}!") - graph.add_edges(child_node, parent) - end - end - end - end - end - - file_location = Tempfile.open(['wimg', '.png'], - Rails.root.join('tmp')) - - graph.output(png: file_location.path) - - begin - file = File.open(file_location) - self.workflowimg = file - file.close - save - touch(:workflowimg_updated_at) - rescue => ex - logger.error ex.message - end + Experiments::GenerateWorkflowImageService.call(experiment_id: id) end # Clone this experiment to given project @@ -371,7 +248,7 @@ class Experiment < ApplicationRecord end # Copy modules without group - clone.my_modules << modules_without_group.map do |m| + clone.my_modules << my_modules.without_group.map do |m| m.deep_clone_to_experiment(current_user, clone) end clone.save diff --git a/app/models/my_module.rb b/app/models/my_module.rb index 28936dc8f..9991aeaa9 100644 --- a/app/models/my_module.rb +++ b/app/models/my_module.rb @@ -68,11 +68,13 @@ class MyModule < ApplicationRecord scope :is_archived, ->(is_archived) { where('archived = ?', is_archived) } scope :active, -> { where(archived: false) } scope :overdue, -> { where('my_modules.due_date < ?', Time.current.utc) } + scope :without_group, -> { active.where(my_module_group: nil) } scope :one_day_prior, (lambda do where('my_modules.due_date > ? AND my_modules.due_date < ?', Time.current.utc, Time.current.utc + 1.day) end) + scope :workflow_ordered, -> { order(workflow_order: :asc) } # A module takes this much space in canvas (x, y) in database WIDTH = 30 diff --git a/app/models/my_module_group.rb b/app/models/my_module_group.rb index bfe4493af..37c36adc9 100644 --- a/app/models/my_module_group.rb +++ b/app/models/my_module_group.rb @@ -10,10 +10,6 @@ class MyModuleGroup < ApplicationRecord optional: true has_many :my_modules, inverse_of: :my_module_group, dependent: :nullify - def ordered_modules - my_modules.order(workflow_order: :asc) - end - def deep_clone_to_experiment(current_user, experiment) clone = MyModuleGroup.new( created_by: created_by, @@ -21,12 +17,12 @@ class MyModuleGroup < ApplicationRecord ) # Get clones of modules from this group, save them as hash - cloned_modules = ordered_modules.each_with_object({}) do |m, hash| - hash[m.id] = m.deep_clone_to_experiment(current_user, experiment) - hash + cloned_modules = my_modules.workflow_ordered.each_with_object({}) do |m, h| + h[m.id] = m.deep_clone_to_experiment(current_user, experiment) + h end - ordered_modules.each do |m| + my_modules.workflow_ordered.each do |m| # Copy connections m.inputs.each do |inp| Connection.create( diff --git a/app/services/experiments/generate_workflow_image_service.rb b/app/services/experiments/generate_workflow_image_service.rb new file mode 100644 index 000000000..71ca146dc --- /dev/null +++ b/app/services/experiments/generate_workflow_image_service.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +module Experiments + class GenerateWorkflowImageService + extend Service + require 'graphviz' + + attr_reader :errors + + def initialize(experiment_id:) + @exp = Experiment.find experiment_id + @graph = GraphViz.new(:G, type: :digraph, use: :neato) + + @graph[:size] = '4,4' + @graph.node[color: Constants::COLOR_ALTO, + style: :filled, + fontcolor: Constants::COLOR_EMPEROR, + shape: 'circle', + fontname: 'Arial', + fontsize: '16.0'] + + @graph.edge[color: Constants::COLOR_ALTO] + @errors = [] + end + + def call + draw_diagram + save_file + self + end + + def succeed? + @errors.none? + end + + private + + def draw_diagram + # Draw orphan nodes + @exp.my_modules.without_group.each do |my_module| + @graph.subgraph(rank: 'same').add_nodes( + "Orphan-#{my_module.id}", + label: '', + pos: "#{my_module.x / 10},-#{my_module.y / 10}!" + ) + end + + # Draw grouped modules + subg = {} + @exp.my_module_groups.each_with_index do |group, gindex| + subgraph_id = "cluster-#{gindex}" + subg[subgraph_id] = @graph.subgraph(rank: 'same') + nodes = {} + + group.my_modules.workflow_ordered.each_with_index do |my_module, index| + # draw nodes + node = subg[subgraph_id].add_nodes( + "#{subgraph_id}-#{index}", + label: '', + pos: "#{my_module.x / 10},-#{my_module.y / 10}!" + ) + nodes[my_module.id] = node + end + + # draw edges + group.my_modules.workflow_ordered.each do |m| + m.outputs.each do |output| + parent_node = nodes[m.id] + child_node = nodes[output.input_id] + subg[subgraph_id].add_edges(parent_node, child_node) + end + end + end + end + + def save_file + file = Tempfile.open(%w(wimg .png), Rails.root.join('tmp')) + @graph.output(png: file.path) + @exp.workflowimg = file + file.close + @exp.save + @exp.touch(:workflowimg_updated_at) + rescue StandardError => ex + logger.error ex.message + end + end +end diff --git a/app/services/service.rb b/app/services/service.rb new file mode 100644 index 000000000..3485ed0a2 --- /dev/null +++ b/app/services/service.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Service + def call(*args) + new(*args).call + end +end diff --git a/app/views/reports/new/modal/_experiment_contents_inner.html.erb b/app/views/reports/new/modal/_experiment_contents_inner.html.erb index 8ce2c1384..71c7ff6ee 100644 --- a/app/views/reports/new/modal/_experiment_contents_inner.html.erb +++ b/app/views/reports/new/modal/_experiment_contents_inner.html.erb @@ -13,7 +13,7 @@ <% experiment.my_module_groups.each do |my_module_group| %> <% if my_module_group.my_modules.exists? then %> - <% my_module_group.ordered_modules.each do |my_module| %> + <% my_module_group.my_modules.workflow_ordered.each do |my_module| %>