From bdcc7939ada792a3924168416d56323d4e08be3d Mon Sep 17 00:00:00 2001
From: aignatov-bio <aignatov@biosistemika.com>
Date: Fri, 16 Oct 2020 18:25:01 +0200
Subject: [PATCH] Update export all generator

---
 app/models/report.rb           | 137 +++++----------------------------
 config/initializers/extends.rb | 102 ++++++++++++++++++++----
 2 files changed, 105 insertions(+), 134 deletions(-)

diff --git a/app/models/report.rb b/app/models/report.rb
index 688c71e74..1686fa725 100644
--- a/app/models/report.rb
+++ b/app/models/report.rb
@@ -92,10 +92,7 @@ class Report < ApplicationRecord
   end
 
   def self.generate_whole_project_report(project, current_user, current_team)
-    report_contents = gen_element_content(project, nil, 'project_header', true)
-    Extends::EXPORT_ALL_PROJECT_ELEMENTS.each do |report_element, children|
-      report_contents += public_send("generate_#{report_element}_element", project, children)
-    end
+    report_contents = gen_element_content(project, Extends::EXPORT_ALL_PROJECT_ELEMENTS)
 
     report = Report.new
     report.name = loop do
@@ -110,126 +107,32 @@ class Report < ApplicationRecord
     report
   end
 
-  def self.gen_element_content(parent_obj, association_objs, type_of,
-                               use_parent_id = false, sort_order = nil,
-                               children = nil)
-    parent_type = parent_obj.class.name.underscore
-    type = type_of.split('_').last.singularize
-    extra_id_needed = use_parent_id && !association_objs.nil?
+  def self.gen_element_content(parent, children)
     elements = []
 
-    association_objs ||= [nil]
-    association_objs.each do |obj|
-      elements << {
-        'type_of' => type_of,
-        'id' => {}.tap do |ids_hash|
-                  if use_parent_id
-                    ids_hash["#{parent_type}_id"] = parent_obj.id
-                  else
-                    ids_hash["#{type}_id"] = obj.id
-                  end
-                  ids_hash["#{type}_id"] = obj.id if extra_id_needed
-                end,
-        'sort_order' => sort_order.presence,
-        'children' => children.presence || []
+    children.each do |element|
+      element_hash = lambda { |object|
+        hash_object = {
+          'type_of' => element[:type_of] || element[:type_of_lambda].call(object),
+          'id' => { element[:id_key] => object.id },
+          'sort_order' => element[:sort_order],
+          'children' => gen_element_content(object, element[:children])
+        }
+        hash_object['id'][element[:parent_id_key]] = parent.id if element[:parent_id_key]
+        hash_object
       }
-    end
 
+      if element[:relation]
+        (element[:relation].inject(parent) { |p, method| p.public_send(method) }).each do |child|
+          elements.push(element_hash.call(child))
+        end
+      else
+        elements.push(element_hash.call(parent))
+      end
+    end
     elements
   end
 
-  def self.generate_experiments_element(project, elements)
-    experiments = []
-    project.experiments.each do |experiment|
-      experiment_contents = []
-      elements.each do |report_element, children|
-        experiment_contents += public_send("generate_#{report_element}_element", experiment, children)
-      end
-      experiments += gen_element_content(experiment, nil, 'experiment', true, nil, experiment_contents)
-    end
-    experiments
-  end
-
-  def self.generate_my_modules_element(experiment, elements)
-    my_modules = []
-    experiment.my_modules.each do |my_module|
-      my_module_contents = []
-      elements.each do |report_element, children|
-        my_module_contents += public_send("generate_#{report_element}_element", my_module, children)
-      end
-      my_modules += gen_element_content(my_module, nil, 'my_module', true, nil, my_module_contents)
-    end
-    my_modules
-  end
-
-  def self.generate_my_module_protocol_element(my_module, elements)
-    protcol_contents = []
-    elements.each do |report_element, children|
-      protcol_contents += public_send("generate_#{report_element}_element", my_module, children)
-    end
-    gen_element_content(my_module, nil, 'my_module_protocol', true, nil, protcol_contents)
-  end
-
-  def self.generate_my_module_steps_element(my_module, elements)
-    steps = []
-    my_module.protocol.steps.each do |step|
-      step_contents = []
-      elements.each do |report_element, children|
-        step_contents += public_send("generate_#{report_element}_element", step, children)
-      end
-      steps += gen_element_content(step, nil, 'step', true, nil, step_contents)
-    end
-    steps
-  end
-
-  def self.generate_step_assets_element(step, _elements)
-    gen_element_content(step, step.assets, 'step_asset')
-  end
-
-  def self.generate_step_tables_element(step, _elements)
-    gen_element_content(step, step.tables, 'step_table')
-  end
-
-  def self.generate_step_checklists_element(step, _elements)
-    gen_element_content(step, step.checklists, 'step_checklist')
-  end
-
-  def self.generate_step_comments_element(step, _elements)
-    gen_element_content(step, nil, 'step_comments', true, 'asc')
-  end
-
-  def self.generate_my_module_results_element(my_module, elements)
-    results = []
-    my_module.results.each do |result|
-      result_contents = []
-      elements.each do |report_element, children|
-        result_contents += public_send("generate_#{report_element}_element", result, children)
-      end
-      result_type = if result.asset
-                      'result_asset'
-                    elsif result.table
-                      'result_table'
-                    elsif result.result_text
-                      'result_text'
-                    end
-      results += gen_element_content(result, nil, result_type, true, nil, result_contents)
-    end
-    results
-  end
-
-  def self.generate_result_comments_element(result, _elements)
-    gen_element_content(result, nil, 'result_comments', true, 'asc')
-  end
-
-  def self.generate_my_module_activities_element(my_module, _elements)
-    gen_element_content(my_module, nil, 'my_module_activity', true, 'asc')
-  end
-
-  def self.generate_my_module_repositories_element(my_module, _elements)
-    repositories = my_module.experiment.project.assigned_repositories_and_snapshots
-    gen_element_content(my_module, repositories, 'my_module_repository', true, 'asc')
-  end
-
   private
 
   # Recursively save a single JSON element
diff --git a/config/initializers/extends.rb b/config/initializers/extends.rb
index 284f94648..15519fc2a 100644
--- a/config/initializers/extends.rb
+++ b/config/initializers/extends.rb
@@ -41,24 +41,92 @@ class Extends
                            my_module_repository: 17,
                            my_module_protocol: 18 }
 
-  EXPORT_ALL_PROJECT_ELEMENTS = {
-    experiments: {
-      my_modules: {
-        my_module_protocol: {},
-        my_module_steps: {
-          step_assets: {},
-          step_tables: {},
-          step_checklists: {},
-          step_comments: {}
-        },
-        my_module_results: {
-          result_comments: {}
-        },
-        my_module_activities: {},
-        my_module_repositories: {}
-      }
+  EXPORT_ALL_PROJECT_ELEMENTS = [
+    {
+      type_of: 'project_header',
+      id_key: 'project_id',
+      children: []
+    },
+    {
+      type_of: 'experiment',
+      id_key: 'experiment_id',
+      relation: %w(experiments),
+      children: [
+        {
+          type_of: 'my_module',
+          id_key: 'my_module_id',
+          relation: %w(my_modules),
+          children: [
+            {
+              type_of: 'my_module_protocol',
+              id_key: 'my_module_id',
+              children: []
+            },
+            {
+              type_of: 'step',
+              relation: %w(protocol steps),
+              id_key: 'step_id',
+              children: [
+                {
+                  type_of: 'step_asset',
+                  relation: %w(assets),
+                  id_key: 'asset_id',
+                  children: []
+                },
+                {
+                  type_of: 'step_table',
+                  relation: %w(tables),
+                  id_key: 'table_id',
+                  children: []
+                },
+                {
+                  type_of: 'step_checklist',
+                  relation: %w(checklists),
+                  id_key: 'checklist_id',
+                  children: []
+                },
+                {
+                  type_of: 'step_comments',
+                  id_key: 'step_id',
+                  sort_order: 'asc',
+                  children: []
+                }
+              ]
+            },
+            {
+              type_of_lambda: lambda { |result|
+                (result.result_asset ||
+                 result.result_table ||
+                 result.result_text).class.to_s.underscore
+              },
+              relation: %w(results),
+              id_key: 'result_id',
+              children: [{
+                type_of: 'result_comments',
+                id_key: 'result_id',
+                sort_order: 'asc',
+                children: []
+              }]
+            },
+            {
+              type_of: 'my_module_activity',
+              id_key: 'my_module_id',
+              sort_order: 'asc',
+              children: []
+            },
+            {
+              type_of: 'my_module_repository',
+              relation: %w(experiment project assigned_repositories_and_snapshots),
+              id_key: 'repository_id',
+              parent_id_key: 'my_module_id',
+              sort_order: 'asc',
+              children: []
+            }
+          ]
+        }
+      ]
     }
-  }
+  ]
 
   # Data type name should match corresponding model's name
   REPOSITORY_DATA_TYPES = { RepositoryTextValue: 0,