From 986e2717e8af8c42bafc3fa54809401a3279d8fe Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Fri, 10 Jul 2020 10:13:18 -0400 Subject: [PATCH 01/38] MMT-2313: Added loss reporting route, added a display action to the collections controller --- app/controllers/collections_controller.rb | 8 ++++++-- config/routes.rb | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/controllers/collections_controller.rb b/app/controllers/collections_controller.rb index 76a925f80..6df58c4b6 100644 --- a/app/controllers/collections_controller.rb +++ b/app/controllers/collections_controller.rb @@ -23,6 +23,10 @@ def show end end + def loss_report + render 'errors/internal_server_error' + end + def edit draft = CollectionDraft.create_from_collection(@collection, current_user, @native_id) Rails.logger.info("Audit Log: Collection Draft for #{draft.entry_title} was created by #{current_user.urs_uid} in provider #{current_user.provider_id}") @@ -149,7 +153,7 @@ def set_collection @download_xml_options.each do |download_option| # gsub here is needed because of the iso-smap and application/iso:smap+xml format options if native_format.gsub(':','').include?(download_option[:format].gsub('-', '')) - download_option[:title].concat(' (Native)') + download_option[:title].concat(' (Native)') @download_xml_options.delete(download_option) @download_xml_options.unshift(download_option) break @@ -201,7 +205,7 @@ def proposal_mode_enabled? super end end - + def select_revision selected = @revisions.select {|r| r.fetch('meta')['revision-id'] && r.fetch('meta')['deleted'] == false && r.fetch('meta')['revision-id'].to_i < @revision_id.to_i}.first selected.blank? ? nil : selected.fetch('meta')['revision-id'] diff --git a/config/routes.rb b/config/routes.rb index 4b61880ee..23cd27dff 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -69,6 +69,7 @@ get '/collections/:id/download_xml/:format(/:revision_id)' => 'collections#download_xml', as: 'download_collection_xml' get '/collections/:id/create_delete_proposal' => 'collections#create_delete_proposal', as: 'create_delete_proposal_collection' get '/collections/:id/create_update_proposal' => 'collections#create_update_proposal', as: 'create_update_proposal_collection' + get 'collections/:id/loss' => 'collections#loss_report' resource :variable_generation_processes_search, only: [:new] From 57b82a3537348a96065fda65039b1c7fcbc53e92 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Mon, 13 Jul 2020 11:30:07 -0400 Subject: [PATCH 02/38] MMT-2313: added nokogiri/diff to gemfile, added hidden url endpoint for loss report --- .gitignore | 3 + Gemfile | 2 + Gemfile.lock | 5 + app/controllers/collections_controller.rb | 2 +- app/helpers/loss_report_helper.rb | 241 ++++++++++++++++++ .../collection_drafts/loss_report.html.erb | 23 ++ 6 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 app/helpers/loss_report_helper.rb create mode 100644 app/views/collection_drafts/loss_report.html.erb diff --git a/.gitignore b/.gitignore index 79593f013..25b8f5332 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,6 @@ nohup.out #backup files *~ + +#ignore package +package.json diff --git a/Gemfile b/Gemfile index f7b917df1..4198d27fc 100644 --- a/Gemfile +++ b/Gemfile @@ -73,6 +73,8 @@ gem 'aasm' gem 'browser' +gem 'nokogiri-diff', '~> 0.2.0' # for comparing xml documents + # collections metadata preview # run this command to work from a local copy of the gem's repo # bundle config local.cmr_metadata_preview /path/to/local/git/repository diff --git a/Gemfile.lock b/Gemfile.lock index 61ce28ff4..493f42aa4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -195,6 +195,9 @@ GEM nio4r (2.5.2) nokogiri (1.10.9) mini_portile2 (~> 2.4.0) + nokogiri-diff (0.2.0) + nokogiri (~> 1.5) + tdiff (~> 0.3, >= 0.3.2) parallel (1.19.1) parser (2.7.1.2) ast (~> 2.4.0) @@ -297,6 +300,7 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) sqlite3 (1.4.2) + tdiff (0.3.4) thor (1.0.1) thread_safe (0.3.6) tilt (2.0.10) @@ -365,6 +369,7 @@ DEPENDENCIES mini_racer momentjs-rails multi_xml + nokogiri-diff (~> 0.2.0) pg pundit rack_session_access diff --git a/app/controllers/collections_controller.rb b/app/controllers/collections_controller.rb index 6df58c4b6..4da86ea50 100644 --- a/app/controllers/collections_controller.rb +++ b/app/controllers/collections_controller.rb @@ -24,7 +24,7 @@ def show end def loss_report - render 'errors/internal_server_error' + render 'collection_drafts/loss_report' end def edit diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb new file mode 100644 index 000000000..604f6a383 --- /dev/null +++ b/app/helpers/loss_report_helper.rb @@ -0,0 +1,241 @@ +module LossReportHelper + + def prepare_collections(concept_id, format, umm_c_version) + # TODO: need to add exception handling for get_concept, translate_collection + original_collection_xml = cmr_client.get_concept(concept_id,token, {}) + original_collection_hash = Hash.from_xml(original_collection_xml.body) + translated_collection_umm = cmr_client.translate_collection(original_collection_xml.body, "application/#{format}+xml", "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", skip_validation=true) + translated_collection_xml = cmr_client.translate_collection(translated_collection_umm.body.to_json, "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", "application/#{format}+xml", skip_validation=true) + translated_collection_hash = Hash.from_xml(translated_collection_xml.body) + return original_collection_xml.body, translated_collection_xml.body + end + + def path_leads_to_list?(path, org_hash, conv_hash) + org_hash_path = hash_navigation(path, org_hash) + conv_hash_path = hash_navigation(path, conv_hash) + + if org_hash_path == 'flag' || conv_hash_path == 'flag' + return false + end + + if path.include?("[") && path.include?("]") + bool = true + elsif org_hash_path.is_a?(Hash) && conv_hash_path.is_a?(Hash) + org_hash_path.keys.each { |key| bool = true; break if org_hash_path[key].is_a?(Array) } + conv_hash_path.keys.each { |key| bool = true; break if conv_hash_path[key].is_a?(Array) } + elsif org_hash_path.is_a?(Array) || conv_hash_path.is_a?(Array) + bool = true + else + bool = false + end + bool + end + + def hash_navigation(dir, hash) + dir = dir.split("/") + if dir.is_a? Array + dir.each do |key| + if !key.empty? && hash.is_a?(Hash) + hash = hash[key] + elsif hash.is_a? Array + return 'flag' + end + end + else + hash = hash[dir] + end + hash + end + + def get_list_paths(dif_hash, original, converted) + values_list = hash_to_list_of_values(dif_hash) + paths = Array.new + + for item in values_list + org_path = get_dir(item, original) + conv_path = get_dir(item, converted) + + if org_path.include? "[]" + path = org_path + elsif conv_path.include? "[]" + path = conv_path + else + path = org_path #arbitrary + end + + if path.include? "[]" + path = path.split "[]" + paths << path[0] unless paths.any? { |p| p.eql? path[0] } + elsif path_leads_to_list?(path, original, converted) + paths << path unless paths.any? { |p| p.eql? path } + end + end + paths + end + + def compare_arrays(diff_hash, original_hash, converted_hash) + dif_hash = diff_hash.clone + original = original_hash.clone + converted = converted_hash.clone + paths = get_list_paths(dif_hash, original, converted) + + paths.each do |path| + org_array = hash_navigation(path, original) + org_arr = org_array.clone + conv_array = hash_navigation(path, converted) + conv_arr = conv_array.clone + + org_arr = Array.wrap(org_arr) unless org_arr.is_a?(Array) + org_array = Array.wrap(org_array) unless org_array.is_a?(Array) + conv_arr = Array.wrap(conv_arr) unless conv_arr.is_a?(Array) + conv_array = Array.wrap(conv_array) unless conv_array.is_a?(Array) + + for conv_item in conv_array + for org_item in org_array + if org_item.eql? conv_item + org_arr.delete(org_item) + break + end + end + end + + for org_item in org_array + for conv_item in conv_array + if org_item.eql? conv_item + conv_arr.delete(conv_item) + break + end + end + end + + org_arr.each do |item| + path_with_index = path + "[#{org_array.index(item)}]" + puts "-: ".ljust(60) + path_with_index + end + + conv_arr.each do |item| + path_with_index = path + "[#{conv_array.index(item)}]" + puts "+: ".ljust(60) + path_with_index #THIS INDEX DOESN'T MAKE SENSE + end + end + end + + def find_difference_bt_hash_arrays(org_arr, conv_arr) + org = org_arr.clone + conv = conv_arr.clone + missing = Array.new + if org.eql? conv + return missing + else + for conv_item in conv + for org_item in org + if org_item.eql? conv_item + org.delete(conv_item) + break + end + end + end + missing += org + end + missing + end + + def find_difference_bt_hashes(org, conv) + missing = Hash.new + if org.eql? conv + return missing + else + org.each do |org_key,org_value| + conv_value = conv[org_key] + if conv.key? org_key + if conv_value.eql? org_value + next + elsif org_value.is_a?(Hash) && conv_value.is_a?(Hash) + missing_value = find_difference_bt_hashes(org_value, conv_value) + unless missing_value.empty? + missing[org_key] = missing_value + end + elsif org_value.is_a?(Array) && conv_value.is_a?(Array) + missing_value = find_difference_bt_hash_arrays(org_value, conv_value) + unless missing_value.empty? + missing[org_key] = missing_value + end + else + missing[org_key] = org_value + end + else + missing[org_key] = org_value + end + end + end + missing + end + + def get_dir(value, hash_or_arr) + iterable = hash_or_arr.clone + dir = String.new + if iterable.is_a? Hash + unless iterable.key(value).nil? + matching_key = iterable.key(value) + dir += '/' + matching_key + iterable.delete(matching_key) + return dir + else + iterable.each do |key,val| + if val.is_a?(Hash) && hash_to_list_of_values(val).include?(value) + dir += '/' + key + dir += get_dir(value, val) + return dir + elsif val.is_a?(Array) && array_to_list_of_values(val).include?(value) + dir += '/' + key + "[]" + dir += get_dir(value, val) + return dir + elsif val.eql? value + dir += '/' + key + iterable.delete(key) + return dir + end + end + end + elsif iterable.is_a? Array + iterable.each do |item| + if item.is_a?(Hash) && hash_to_list_of_values(item).include?(value) + dir += get_dir(value,item) + return dir + elsif item.is_a?(Array) && array_to_list_of_values(item).include?(value) + dir += get_dir(value,item) + "[]" + return dir + end + end + end + dir + end + + def hash_to_list_of_values(hash) + list = Array.new + for val in hash.values + if val.is_a? Hash + list += hash_to_list_of_values(val) + elsif val.is_a? Array + list += array_to_list_of_values(val) + else + list << val + end + end + list + end + + def array_to_list_of_values(array) + ls = Array.new + for item in array + if item.is_a? Hash + ls += hash_to_list_of_values(item) + elsif item.is_a? Array + ls += array_to_list_of_values(item) + else + ls << item + end + end + ls + end +end diff --git a/app/views/collection_drafts/loss_report.html.erb b/app/views/collection_drafts/loss_report.html.erb new file mode 100644 index 000000000..d659e036f --- /dev/null +++ b/app/views/collection_drafts/loss_report.html.erb @@ -0,0 +1,23 @@ + + + + + + + + + <% orig,conv = prepare_collections('C1200000085-NSIDC_ECS', 'echo10', '1.15.3') %> + <% orig = Nokogiri::XML(orig) { |config| config.strict.noblanks } %> + <% conv = Nokogiri::XML(conv) { |config| config.strict.noblanks } %> + <% orig.diff(conv, {:added => true, :removed => true}) do |change,node| %> + + + + + <% end %> + +
<%= 'Alteration' %><%= 'Path' %>
+ <%= change %> + + <%= node.parent.path %> +
From 49c7dd962110e24a6fcd1f3a3ece3260ef4137d0 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Mon, 13 Jul 2020 13:14:08 -0400 Subject: [PATCH 03/38] MMT-2313: added loss reporting for arrays --- app/helpers/loss_report_helper.rb | 13 ++++++++++--- .../collection_drafts/loss_report.html.erb | 17 ++++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index 604f6a383..fc006c1a3 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -7,7 +7,7 @@ def prepare_collections(concept_id, format, umm_c_version) translated_collection_umm = cmr_client.translate_collection(original_collection_xml.body, "application/#{format}+xml", "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", skip_validation=true) translated_collection_xml = cmr_client.translate_collection(translated_collection_umm.body.to_json, "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", "application/#{format}+xml", skip_validation=true) translated_collection_hash = Hash.from_xml(translated_collection_xml.body) - return original_collection_xml.body, translated_collection_xml.body + return original_collection_xml.body, translated_collection_xml.body, original_collection_hash, translated_collection_hash end def path_leads_to_list?(path, org_hash, conv_hash) @@ -73,12 +73,14 @@ def get_list_paths(dif_hash, original, converted) paths end - def compare_arrays(diff_hash, original_hash, converted_hash) - dif_hash = diff_hash.clone + def compare_arrays(original_hash, converted_hash, dh=false) + dh ? dif_hash = dh.clone : dif_hash = find_difference_bt_hashes(original_hash, converted_hash).clone original = original_hash.clone converted = converted_hash.clone paths = get_list_paths(dif_hash, original, converted) + output = Array.new + paths.each do |path| org_array = hash_navigation(path, original) org_arr = org_array.clone @@ -111,13 +113,18 @@ def compare_arrays(diff_hash, original_hash, converted_hash) org_arr.each do |item| path_with_index = path + "[#{org_array.index(item)}]" puts "-: ".ljust(60) + path_with_index + loss_item = ['-', path_with_index] + output << loss_item end conv_arr.each do |item| path_with_index = path + "[#{conv_array.index(item)}]" puts "+: ".ljust(60) + path_with_index #THIS INDEX DOESN'T MAKE SENSE + loss_item = ['+', path_with_index] + output << loss_item end end + output end def find_difference_bt_hash_arrays(org_arr, conv_arr) diff --git a/app/views/collection_drafts/loss_report.html.erb b/app/views/collection_drafts/loss_report.html.erb index d659e036f..702b54024 100644 --- a/app/views/collection_drafts/loss_report.html.erb +++ b/app/views/collection_drafts/loss_report.html.erb @@ -1,15 +1,16 @@ - - + + - <% orig,conv = prepare_collections('C1200000085-NSIDC_ECS', 'echo10', '1.15.3') %> + <% orig,conv,orig_h,conv_h = prepare_collections('C1200000085-NSIDC_ECS', 'echo10', '1.15.3') %> <% orig = Nokogiri::XML(orig) { |config| config.strict.noblanks } %> <% conv = Nokogiri::XML(conv) { |config| config.strict.noblanks } %> <% orig.diff(conv, {:added => true, :removed => true}) do |change,node| %> + <% next if path_leads_to_list?(node.parent.path, orig_h, conv_h) %> <% end %> + <% compare_arrays(orig_h, conv_h).each do |item| %> + + + + + <% end %>
<%= 'Alteration' %><%= 'Path' %><%= 'Alteration' %><%= 'Path' %>
<%= change %> @@ -19,5 +20,15 @@
+ <%= item[0] %> + + <%= item[1] %> +
From 67676fcfcbc7914241a5c8a0747896ac654dab46 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Mon, 13 Jul 2020 14:14:07 -0400 Subject: [PATCH 04/38] MMT-2313: Added comment to collections_controller --- app/controllers/collections_controller.rb | 10 ++++++---- .../loss_report.html.erb | 0 2 files changed, 6 insertions(+), 4 deletions(-) rename app/views/{collection_drafts => collections}/loss_report.html.erb (100%) diff --git a/app/controllers/collections_controller.rb b/app/controllers/collections_controller.rb index 4da86ea50..da1c79d88 100644 --- a/app/controllers/collections_controller.rb +++ b/app/controllers/collections_controller.rb @@ -23,10 +23,6 @@ def show end end - def loss_report - render 'collection_drafts/loss_report' - end - def edit draft = CollectionDraft.create_from_collection(@collection, current_user, @native_id) Rails.logger.info("Audit Log: Collection Draft for #{draft.entry_title} was created by #{current_user.urs_uid} in provider #{current_user.provider_id}") @@ -119,6 +115,12 @@ def create_update_proposal redirect_to collection_draft_proposal_path(proposal) end + def loss_report + # When a user wants to use MMT to edit metadata that currently exists in a non-UMM form, + # it's important that they're able to see if any data loss occurs in the translation to umm. + # This method is needed to reference the appropriate helper and view for the lossiness report + end + private def ensure_correct_collection_provider diff --git a/app/views/collection_drafts/loss_report.html.erb b/app/views/collections/loss_report.html.erb similarity index 100% rename from app/views/collection_drafts/loss_report.html.erb rename to app/views/collections/loss_report.html.erb From d767d9ff1f6f6cf3ced88f199d2a7074a4806fe5 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Tue, 14 Jul 2020 10:35:59 -0400 Subject: [PATCH 05/38] MMT-2313: made changes as per MMT-2311-1 PR change requests --- .gitignore | 2 +- app/helpers/loss_report_helper.rb | 63 +++--- lib/tasks/translate_collections.rake | 281 +++++++++++++++++++++++++++ 3 files changed, 318 insertions(+), 28 deletions(-) create mode 100644 lib/tasks/translate_collections.rake diff --git a/.gitignore b/.gitignore index 25b8f5332..03dd71f16 100644 --- a/.gitignore +++ b/.gitignore @@ -59,4 +59,4 @@ nohup.out *~ #ignore package -package.json +package-lock.json diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index fc006c1a3..7992d2da1 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -2,21 +2,20 @@ module LossReportHelper def prepare_collections(concept_id, format, umm_c_version) # TODO: need to add exception handling for get_concept, translate_collection - original_collection_xml = cmr_client.get_concept(concept_id,token, {}) - original_collection_hash = Hash.from_xml(original_collection_xml.body) - translated_collection_umm = cmr_client.translate_collection(original_collection_xml.body, "application/#{format}+xml", "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", skip_validation=true) - translated_collection_xml = cmr_client.translate_collection(translated_collection_umm.body.to_json, "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", "application/#{format}+xml", skip_validation=true) - translated_collection_hash = Hash.from_xml(translated_collection_xml.body) - return original_collection_xml.body, translated_collection_xml.body, original_collection_hash, translated_collection_hash + original_collection_native_xml = cmr_client.get_concept(concept_id,token, {}) + original_collection_native_hash = Hash.from_xml(original_collection_native_xml.body) + translated_collection_umm_json = cmr_client.translate_collection(original_collection_native_xml.body, "application/#{format}+xml", "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", skip_validation=true) + translated_collection_native_xml = cmr_client.translate_collection(translated_collection_umm_json.body.to_json, "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", "application/#{format}+xml", skip_validation=true) + translated_collection_native_hash = Hash.from_xml(translated_collection_native_xml.body) + return original_collection_native_xml.body, translated_collection_native_xml.body, original_collection_native_hash, translated_collection_native_hash end def path_leads_to_list?(path, org_hash, conv_hash) + # this method takes a path string (and the full original and converted hashes) and outputs true if the path string contains a list; else false org_hash_path = hash_navigation(path, org_hash) conv_hash_path = hash_navigation(path, conv_hash) - if org_hash_path == 'flag' || conv_hash_path == 'flag' - return false - end + return false if org_hash_path == false || conv_hash_path == false if path.include?("[") && path.include?("]") bool = true @@ -32,13 +31,15 @@ def path_leads_to_list?(path, org_hash, conv_hash) end def hash_navigation(dir, hash) + # Passed a path string and the hash being navigated. This method parses the path string and + # returns the hash at the end of the path dir = dir.split("/") if dir.is_a? Array dir.each do |key| if !key.empty? && hash.is_a?(Hash) hash = hash[key] elsif hash.is_a? Array - return 'flag' + return false end end else @@ -48,6 +49,8 @@ def hash_navigation(dir, hash) end def get_list_paths(dif_hash, original, converted) + # arguments: differences hash, the original hash, and converted hash + # Using these 3 hashses, all paths that lead to a list are returned as an array of path strings values_list = hash_to_list_of_values(dif_hash) paths = Array.new @@ -60,9 +63,13 @@ def get_list_paths(dif_hash, original, converted) elsif conv_path.include? "[]" path = conv_path else - path = org_path #arbitrary + path = org_path end + # the get_dir method includes a clause that 'tags' array-containing fields with '[]' + # eg. '/Collection/Contacts/Contact[]/OrganizationEmails/Email' + # the following lines show how this 'tagging' is used to identify an array in a given directory + if path.include? "[]" path = path.split "[]" paths << path[0] unless paths.any? { |p| p.eql? path[0] } @@ -74,6 +81,11 @@ def get_list_paths(dif_hash, original, converted) end def compare_arrays(original_hash, converted_hash, dh=false) + # arguments: differences hash, the original hash, and converted hash + # each path that leads to an array is used to navigate to that array and + # subsequently compare the arrays in the original and converted hashes. + # there is no usable ouput; there is printing to the terminal + dh ? dif_hash = dh.clone : dif_hash = find_difference_bt_hashes(original_hash, converted_hash).clone original = original_hash.clone converted = converted_hash.clone @@ -83,27 +95,17 @@ def compare_arrays(original_hash, converted_hash, dh=false) paths.each do |path| org_array = hash_navigation(path, original) - org_arr = org_array.clone conv_array = hash_navigation(path, converted) - conv_arr = conv_array.clone - org_arr = Array.wrap(org_arr) unless org_arr.is_a?(Array) + org_array.is_a?(Array) ? org_arr = Array.wrap(org_array) : org_arr = org_array.clone org_array = Array.wrap(org_array) unless org_array.is_a?(Array) - conv_arr = Array.wrap(conv_arr) unless conv_arr.is_a?(Array) + conv_array.is_a?(Array) ? conv_arr = Array.wrap(conv_array) : conv_arr = conv_array.clone conv_array = Array.wrap(conv_array) unless conv_array.is_a?(Array) for conv_item in conv_array for org_item in org_array if org_item.eql? conv_item org_arr.delete(org_item) - break - end - end - end - - for org_item in org_array - for conv_item in conv_array - if org_item.eql? conv_item conv_arr.delete(conv_item) break end @@ -128,6 +130,8 @@ def compare_arrays(original_hash, converted_hash, dh=false) end def find_difference_bt_hash_arrays(org_arr, conv_arr) + # array inputs; the output is an array that contains the items in the original array + # that were not found in the converted array org = org_arr.clone conv = conv_arr.clone missing = Array.new @@ -148,13 +152,15 @@ def find_difference_bt_hash_arrays(org_arr, conv_arr) end def find_difference_bt_hashes(org, conv) + # input is the original hash and the converted hash; the output is the + # 'differences hash' which represents the items in the original hash that were + # not found in the converted hash missing = Hash.new if org.eql? conv return missing else org.each do |org_key,org_value| - conv_value = conv[org_key] - if conv.key? org_key + if (conv_value = conv[org_key]) if conv_value.eql? org_value next elsif org_value.is_a?(Hash) && conv_value.is_a?(Hash) @@ -179,11 +185,12 @@ def find_difference_bt_hashes(org, conv) end def get_dir(value, hash_or_arr) + # passing the sought-after value and the hash or array being parsed + # output: a single string representing the path to the value arg passed to this method iterable = hash_or_arr.clone dir = String.new if iterable.is_a? Hash - unless iterable.key(value).nil? - matching_key = iterable.key(value) + if (matching_key = iterable.key(value)) dir += '/' + matching_key iterable.delete(matching_key) return dir @@ -219,6 +226,7 @@ def get_dir(value, hash_or_arr) end def hash_to_list_of_values(hash) + # converts a highly nested hash to a list of all its values list = Array.new for val in hash.values if val.is_a? Hash @@ -233,6 +241,7 @@ def hash_to_list_of_values(hash) end def array_to_list_of_values(array) + #converts a highly nested array to a list of all its values ls = Array.new for item in array if item.is_a? Hash diff --git a/lib/tasks/translate_collections.rake b/lib/tasks/translate_collections.rake new file mode 100644 index 000000000..ec0ae90e8 --- /dev/null +++ b/lib/tasks/translate_collections.rake @@ -0,0 +1,281 @@ +require 'libxml_to_hash' + +namespace :collection do + desc 'Translate a collection from native format to UMM JSON and back to native format' + task :translate, [:file, :format, :disp, :version] => :environment do |_task, args| + args.with_defaults(:version => '1.15.3') + args.with_defaults(:disp => 'show') + + abort 'FORMAT INVALID' unless args.format.eql? ('echo10' || 'dif10' || 'iso19115') + + filename = args.file.split('/')[-1] + puts "\nTranslating #{filename} to UMM JSON..." + + native_original_xml = File.read(args.file) + native_original_hash = Hash.from_xml(native_original_xml) + + #translate to UMM + umm_response = cmr_client.translate_collection(native_original_xml, "application/#{args.format}+xml", "application/vnd.nasa.cmr.umm+json;version=#{args.version}", skip_validation=true ) + umm_json = umm_response.body.to_json + umm_response.success? ? puts("\nsuccessful translation to UMM") : abort("\nUMM translation failure") + + # translate back to native + back_to_native = cmr_client.translate_collection(umm_json, "application/vnd.nasa.cmr.umm+json;version=#{args.version}", "application/#{args.format}+xml", skip_validation=true ) + native_converted_hash = Hash.from_xml(back_to_native.body) + native_converted_xml = back_to_native.body + back_to_native.success? ? puts("successful translation to native format \n\n") : abort("Native format translation failure \n\n") + + # nokogiri output + nokogiri_original = Nokogiri::XML(native_original_xml) { |config| config.strict.noblanks } + nokogiri_converted = Nokogiri::XML(native_converted_xml) { |config| config.strict.noblanks } + + nokogiri_original.diff(nokogiri_converted, {:added => true, :removed => true}) do |change,node| + next if path_leads_to_list?(node.parent.path, native_original_hash, native_converted_hash) + puts("#{change}: #{node.to_xml}".ljust(60) + node.parent.path) if args.disp.eql? 'show' + puts("#{change}: ". + node.parent.path) if args.disp.eql? 'hide' + end + + # find differences + dif_hash = find_difference_bt_hashes(native_original_hash, native_converted_hash) + compare_arrays(dif_hash, native_original_hash, native_converted_hash) + + end + + def path_leads_to_list?(path, org_hash, conv_hash) + # this method takes a path string (and the full original and converted hashes) and outputs true if the path string contains a list; else false + org_hash_path = hash_navigation(path, org_hash) + conv_hash_path = hash_navigation(path, conv_hash) + + return false if org_hash_path == false || conv_hash_path == false + + if path.include?("[") && path.include?("]") + bool = true + elsif org_hash_path.is_a?(Hash) && conv_hash_path.is_a?(Hash) + org_hash_path.keys.each { |key| bool = true; break if org_hash_path[key].is_a?(Array) } + conv_hash_path.keys.each { |key| bool = true; break if conv_hash_path[key].is_a?(Array) } + elsif org_hash_path.is_a?(Array) || conv_hash_path.is_a?(Array) + bool = true + else + bool = false + end + bool + end + + def hash_navigation(dir, hash) + # Passed a path string and the hash being navigated. This method parses the path string and + # returns the hash at the end of the path + dir = dir.split("/") + if dir.is_a? Array + dir.each do |key| + if !key.empty? && hash.is_a?(Hash) + hash = hash[key] + elsif hash.is_a? Array + return false + end + end + else + hash = hash[dir] + end + hash + end + + def get_list_paths(dif_hash, original, converted) + # arguments: differences hash, the original hash, and converted hash + # Using these 3 hashses, all paths that lead to a list are returned as an array of path strings + values_list = hash_to_list_of_values(dif_hash) + paths = Array.new + + for item in values_list + org_path = get_dir(item, original) + conv_path = get_dir(item, converted) + + if org_path.include? "[]" + path = org_path + elsif conv_path.include? "[]" + path = conv_path + else + path = org_path + end + + # the get_dir method includes a clause that 'tags' array-containing fields with '[]' + # eg. '/Collection/Contacts/Contact[]/OrganizationEmails/Email' + # the following lines show how this 'tagging' is used to identify an array in a given directory + + if path.include? "[]" + path = path.split "[]" + paths << path[0] unless paths.any? { |p| p.eql? path[0] } + elsif path_leads_to_list?(path, original, converted) + paths << path unless paths.any? { |p| p.eql? path } + end + end + paths + end + + def compare_arrays(dif_hash, original, converted) + # arguments: differences hash, the original hash, and converted hash + # each path that leads to an array is used to navigate to that array and + # subsequently compare the arrays in the original and converted hashes. + # there is no usable ouput; there is printing to the terminal + paths = get_list_paths(dif_hash, original, converted) + + paths.each do |path| + org_array = hash_navigation(path, original) + conv_array = hash_navigation(path, converted) + + org_array.is_a?(Array) ? org_arr = Array.wrap(org_array) : org_arr = org_array.clone + org_array = Array.wrap(org_array) unless org_array.is_a?(Array) + conv_array.is_a?(Array) ? conv_arr = Array.wrap(conv_array) : conv_arr = conv_array.clone + conv_array = Array.wrap(conv_array) unless conv_array.is_a?(Array) + + for conv_item in conv_array + for org_item in org_array + if org_item.eql? conv_item + org_arr.delete(org_item) + conv_arr.delete(conv_item) + break + end + end + end + + org_arr.each do |item| + path_with_index = path + "[#{org_array.index(item)}]" + puts "-: ". + path_with_index + end + + conv_arr.each do |item| + path_with_index = path + "[#{conv_array.index(item)}]" + puts "+: " + path_with_index + end + end + end + + def find_difference_bt_hash_arrays(org_arr, conv_arr) + # array inputs; the output is an array that contains the items in the original array + # that were not found in the converted array + org = org_arr.clone + conv = conv_arr.clone + missing = Array.new + if org.eql? conv + return missing + else + for conv_item in conv + for org_item in org + if org_item.eql? conv_item + org.delete(conv_item) + break + end + end + end + missing += org + end + missing + end + + def find_difference_bt_hashes(org, conv) + # input is the original hash and the converted hash; the output is the + # 'differences hash' which represents the items in the original hash that were + # not found in the converted hash + missing = Hash.new + if org.eql? conv + return missing + else + org.each do |org_key,org_value| + if (conv_value = conv[org_key]) + if conv_value.eql? org_value + next + elsif org_value.is_a?(Hash) && conv_value.is_a?(Hash) + missing_value = find_difference_bt_hashes(org_value, conv_value) + unless missing_value.empty? + missing[org_key] = missing_value + end + elsif org_value.is_a?(Array) && conv_value.is_a?(Array) + missing_value = find_difference_bt_hash_arrays(org_value, conv_value) + unless missing_value.empty? + missing[org_key] = missing_value + end + else + missing[org_key] = org_value + end + else + missing[org_key] = org_value + end + end + end + missing + end + + def get_dir(value, hash_or_arr) + # passing the sought-after value and the hash or array being parsed + # output: a single string representing the path to the value arg passed to this method + iterable = hash_or_arr.clone + dir = String.new + if iterable.is_a? Hash + if (matching_key = iterable.key(value)) + dir += '/' + matching_key + iterable.delete(matching_key) + return dir + else + iterable.each do |key,val| + if val.is_a?(Hash) && hash_to_list_of_values(val).include?(value) + dir += '/' + key + dir += get_dir(value, val) + return dir + elsif val.is_a?(Array) && array_to_list_of_values(val).include?(value) + dir += '/' + key + "[]" + dir += get_dir(value, val) + return dir + elsif val.eql? value + dir += '/' + key + iterable.delete(key) + return dir + end + end + end + elsif iterable.is_a? Array + iterable.each do |item| + if item.is_a?(Hash) && hash_to_list_of_values(item).include?(value) + dir += get_dir(value,item) + return dir + elsif item.is_a?(Array) && array_to_list_of_values(item).include?(value) + dir += get_dir(value,item) + "[]" + return dir + end + end + end + dir + end + + def hash_to_list_of_values(hash) + # converts a highly nested hash to a list of all its values + list = Array.new + for val in hash.values + if val.is_a? Hash + list += hash_to_list_of_values(val) + elsif val.is_a? Array + list += array_to_list_of_values(val) + else + list << val + end + end + list + end + + def array_to_list_of_values(array) + #converts a highly nested array to a list of all its values + ls = Array.new + for item in array + if item.is_a? Hash + ls += hash_to_list_of_values(item) + elsif item.is_a? Array + ls += array_to_list_of_values(item) + else + ls << item + end + end + ls + end + + def cmr_client + @cmr_client ||= Cmr::Client.client_for_environment(Rails.configuration.cmr_env, Rails.configuration.services) + end +end From 83f50570a518480c636d456433bfce0416750d71 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Wed, 15 Jul 2020 13:33:26 -0400 Subject: [PATCH 06/38] MMT-2313: Updated comparison logic to use nokogiri for array paths --- .gitignore | 1 + app/helpers/loss_report_helper.rb | 63 +++++++++--- app/views/collections/loss_report.html.erb | 109 ++++++++++++++++++++- config/routes.rb | 2 +- 4 files changed, 160 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 03dd71f16..e4ef9f124 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,4 @@ nohup.out #ignore package package-lock.json +package.json diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index 7992d2da1..80aa95096 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -3,10 +3,15 @@ module LossReportHelper def prepare_collections(concept_id, format, umm_c_version) # TODO: need to add exception handling for get_concept, translate_collection original_collection_native_xml = cmr_client.get_concept(concept_id,token, {}) + # concept ID and format can be scalped from headers etc original_collection_native_hash = Hash.from_xml(original_collection_native_xml.body) translated_collection_umm_json = cmr_client.translate_collection(original_collection_native_xml.body, "application/#{format}+xml", "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", skip_validation=true) translated_collection_native_xml = cmr_client.translate_collection(translated_collection_umm_json.body.to_json, "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", "application/#{format}+xml", skip_validation=true) translated_collection_native_hash = Hash.from_xml(translated_collection_native_xml.body) + # File.write('/Users/ctrummer/Documents/devtesting/o_'+concept_id+'.json', JSON.pretty_generate(original_collection_native_hash)) + # File.write('/Users/ctrummer/Documents/devtesting/c_'+concept_id+'.json', JSON.pretty_generate(translated_collection_native_hash)) + # File.write('/Users/ctrummer/Documents/devtesting/o_'+concept_id+'.xml', original_collection_native_xml.body) + # File.write('/Users/ctrummer/Documents/devtesting/c_'+concept_id+'.xml', translated_collection_native_xml.body) return original_collection_native_xml.body, translated_collection_native_xml.body, original_collection_native_hash, translated_collection_native_hash end @@ -15,13 +20,11 @@ def path_leads_to_list?(path, org_hash, conv_hash) org_hash_path = hash_navigation(path, org_hash) conv_hash_path = hash_navigation(path, conv_hash) - return false if org_hash_path == false || conv_hash_path == false - if path.include?("[") && path.include?("]") bool = true elsif org_hash_path.is_a?(Hash) && conv_hash_path.is_a?(Hash) - org_hash_path.keys.each { |key| bool = true; break if org_hash_path[key].is_a?(Array) } - conv_hash_path.keys.each { |key| bool = true; break if conv_hash_path[key].is_a?(Array) } + bool = true if org_hash_path.keys.length == 1 && org_hash_path[org_hash_path.keys[0]].is_a?(Array) + bool = true if conv_hash_path.keys.length == 1 && conv_hash_path[conv_hash_path.keys[0]].is_a?(Array) elsif org_hash_path.is_a?(Array) || conv_hash_path.is_a?(Array) bool = true else @@ -33,13 +36,13 @@ def path_leads_to_list?(path, org_hash, conv_hash) def hash_navigation(dir, hash) # Passed a path string and the hash being navigated. This method parses the path string and # returns the hash at the end of the path - dir = dir.split("/") + dir = dir.split '/' if dir.is_a? Array dir.each do |key| if !key.empty? && hash.is_a?(Hash) hash = hash[key] elsif hash.is_a? Array - return false + return hash end end else @@ -80,6 +83,44 @@ def get_list_paths(dif_hash, original, converted) paths end + def array_comparison(path, original_hash, converted_hash) + # this is a 'less iterative' version of compare_arrays. Args: a single path, the original hash, and the converted hash. + # Rather than finding all the array paths and using those to find the array differences, the array paths are individually + # supplied by the nokogiri gem; this reduces redundancy + org_array = hash_navigation(path, original_hash) + conv_array = hash_navigation(path, converted_hash) + + org_array.is_a?(Array) ? org_arr = org_array.clone : org_arr = Array.wrap(org_array) + org_array = Array.wrap(org_array) unless org_array.is_a?(Array) + conv_array.is_a?(Array) ? conv_arr = conv_array.clone : conv_arr = Array.wrap(conv_array) + conv_array = Array.wrap(conv_array) unless conv_array.is_a?(Array) + + for conv_item in conv_array + for org_item in org_array + if org_item.eql? conv_item + org_arr.delete(org_item) + conv_arr.delete(conv_item) + break + end + end + end + + output = Array.new + org_arr.each do |item| + path_with_index = path + "[#{org_array.index(item)}]" + loss_item = ['-', item, path_with_index] + output << loss_item + end + + + conv_arr.each do |item| + path_with_index = path + "[#{conv_array.index(item)}]" + loss_item = ['+', item, path_with_index] + output << loss_item + end + output + end + def compare_arrays(original_hash, converted_hash, dh=false) # arguments: differences hash, the original hash, and converted hash # each path that leads to an array is used to navigate to that array and @@ -94,12 +135,12 @@ def compare_arrays(original_hash, converted_hash, dh=false) output = Array.new paths.each do |path| - org_array = hash_navigation(path, original) - conv_array = hash_navigation(path, converted) + org_array = hash_navigation(path, original_hash) + conv_array = hash_navigation(path, converted_hash) - org_array.is_a?(Array) ? org_arr = Array.wrap(org_array) : org_arr = org_array.clone + org_array.is_a?(Array) ? org_arr = org_array.clone : org_arr = Array.wrap(org_array) org_array = Array.wrap(org_array) unless org_array.is_a?(Array) - conv_array.is_a?(Array) ? conv_arr = Array.wrap(conv_array) : conv_arr = conv_array.clone + conv_array.is_a?(Array) ? conv_arr = conv_array.clone : conv_arr = Array.wrap(conv_array) conv_array = Array.wrap(conv_array) unless conv_array.is_a?(Array) for conv_item in conv_array @@ -121,7 +162,7 @@ def compare_arrays(original_hash, converted_hash, dh=false) conv_arr.each do |item| path_with_index = path + "[#{conv_array.index(item)}]" - puts "+: ".ljust(60) + path_with_index #THIS INDEX DOESN'T MAKE SENSE + puts "+: ".ljust(60) + path_with_index loss_item = ['+', path_with_index] output << loss_item end diff --git a/app/views/collections/loss_report.html.erb b/app/views/collections/loss_report.html.erb index 702b54024..7e19bdfdf 100644 --- a/app/views/collections/loss_report.html.erb +++ b/app/views/collections/loss_report.html.erb @@ -1,34 +1,137 @@ - +
- + + - <% orig,conv,orig_h,conv_h = prepare_collections('C1200000085-NSIDC_ECS', 'echo10', '1.15.3') %> + <% orig,conv,orig_h,conv_h = prepare_collections('C1200000063-LARC', 'echo10', '1.15.3') %> <% orig = Nokogiri::XML(orig) { |config| config.strict.noblanks } %> <% conv = Nokogiri::XML(conv) { |config| config.strict.noblanks } %> + <% ignored_paths = Array.new %> + + + + + + + + <% counter = 0 %> + <% orig.diff(conv, {:added => true, :removed => true}) do |change,node| %> + <% if node.parent.path.include?('[') && !ignored_paths.include?(node.parent.path.split('[')[0]) %> + <% ignored_paths << node.parent.path.split('[')[0] %> + <% array_comparison(node.parent.path.split('[')[0], orig_h, conv_h).each do |item| %> + + + + + + <% counter += 1%> + <% end %> + <% elsif !ignored_paths.include?(node.parent.path.split('[')[0]) && !path_leads_to_list?(node.parent.path, orig_h, conv_h) %> + + + + + + <% counter += 1%> + <% end %> + <% end %> + + + + + + + + <% counter = 0 %> <% orig.diff(conv, {:added => true, :removed => true}) do |change,node| %> <% next if path_leads_to_list?(node.parent.path, orig_h, conv_h) %> + + <% counter += 1%> <% end %> <% compare_arrays(orig_h, conv_h).each do |item| %> + + <% counter += 1%> + <% end %> + + + + + + + + <% counter = 0 %> + <% orig.diff(conv, {:added => true, :removed => true}) do |change,node| %> + <% next if path_leads_to_list?(node.parent.path, orig_h, conv_h) %> + + + + + + <% counter += 1%> <% end %>
<%= 'Alteration' %><%= 'Path' %><%= 'Node' %><%= 'Path' %>
+ <%= 'NOKOGIRI + FAST COMPARISON ' %> + + <%= ' ' %> + + <%= ' ' %> +
+ <%= item[0] %> + + <%= counter %> + <%= item[1] %> + + <%= item[2] %> +
+ <%= change %> + + <%= counter %> + <%= node.to_html %> + + <%= node.parent.path %> +
+ <%= 'NOKOGIRI + EXPENSIVE COMPARISON ' %> + + <%= ' ' %> + + <%= ' ' %> +
<%= change %> + <%= counter %> + <%= node %> + <%= node.parent.path %>
<%= item[0] %> + <%= counter %> + <%= item[1] %>
+ <%= 'ONLY NOKOGIRI ' %> + + <%= ' ' %> + + <%= ' ' %> +
+ <%= change %> + + <%= counter %> + <%= node.to_html %> + + <%= node.parent.path %> +
diff --git a/config/routes.rb b/config/routes.rb index 23cd27dff..74e5b20ac 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -69,7 +69,7 @@ get '/collections/:id/download_xml/:format(/:revision_id)' => 'collections#download_xml', as: 'download_collection_xml' get '/collections/:id/create_delete_proposal' => 'collections#create_delete_proposal', as: 'create_delete_proposal_collection' get '/collections/:id/create_update_proposal' => 'collections#create_update_proposal', as: 'create_update_proposal_collection' - get 'collections/:id/loss' => 'collections#loss_report' + get '/collections/:id/loss' => 'collections#loss_report' resource :variable_generation_processes_search, only: [:new] From affd9ab38443497a41354bec1f5bd25c981a99af Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Wed, 15 Jul 2020 14:11:33 -0400 Subject: [PATCH 07/38] MMT-2313: removed some comments and test code --- app/controllers/collections_controller.rb | 3 + app/views/collections/loss_report.html.erb | 87 ---------------------- 2 files changed, 3 insertions(+), 87 deletions(-) diff --git a/app/controllers/collections_controller.rb b/app/controllers/collections_controller.rb index da1c79d88..63a4210cf 100644 --- a/app/controllers/collections_controller.rb +++ b/app/controllers/collections_controller.rb @@ -119,6 +119,9 @@ def loss_report # When a user wants to use MMT to edit metadata that currently exists in a non-UMM form, # it's important that they're able to see if any data loss occurs in the translation to umm. # This method is needed to reference the appropriate helper and view for the lossiness report + concept_id = params[:id] + collection_response = cmr_client.get_concept(concept_id, token, {}) + render txt: collection_response.body end private diff --git a/app/views/collections/loss_report.html.erb b/app/views/collections/loss_report.html.erb index 7e19bdfdf..8716ce780 100644 --- a/app/views/collections/loss_report.html.erb +++ b/app/views/collections/loss_report.html.erb @@ -11,19 +11,6 @@ <% orig = Nokogiri::XML(orig) { |config| config.strict.noblanks } %> <% conv = Nokogiri::XML(conv) { |config| config.strict.noblanks } %> <% ignored_paths = Array.new %> - - - - <%= 'NOKOGIRI + FAST COMPARISON ' %> - - - <%= ' ' %> - - - <%= ' ' %> - - - <% counter = 0 %> <% orig.diff(conv, {:added => true, :removed => true}) do |change,node| %> <% if node.parent.path.include?('[') && !ignored_paths.include?(node.parent.path.split('[')[0]) %> @@ -59,79 +46,5 @@ <% counter += 1%> <% end %> <% end %> - - - - <%= 'NOKOGIRI + EXPENSIVE COMPARISON ' %> - - - <%= ' ' %> - - - <%= ' ' %> - - - - <% counter = 0 %> - <% orig.diff(conv, {:added => true, :removed => true}) do |change,node| %> - <% next if path_leads_to_list?(node.parent.path, orig_h, conv_h) %> - - - <%= change %> - - - <%= counter %> - <%= node %> - - - <%= node.parent.path %> - - - <% counter += 1%> - <% end %> - <% compare_arrays(orig_h, conv_h).each do |item| %> - - - <%= item[0] %> - - - <%= counter %> - - - <%= item[1] %> - - - <% counter += 1%> - <% end %> - - - - <%= 'ONLY NOKOGIRI ' %> - - - <%= ' ' %> - - - <%= ' ' %> - - - - <% counter = 0 %> - <% orig.diff(conv, {:added => true, :removed => true}) do |change,node| %> - <% next if path_leads_to_list?(node.parent.path, orig_h, conv_h) %> - - - <%= change %> - - - <%= counter %> - <%= node.to_html %> - - - <%= node.parent.path %> - - - <% counter += 1%> - <% end %> From 7cae6d6a78948dcdeb0cfe8341dca9a631f86724 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Thu, 16 Jul 2020 15:10:01 -0400 Subject: [PATCH 08/38] MMT-2313: Added custom function for hash mapping, finds the path for every value in a hash --- app/controllers/collections_controller.rb | 7 +- app/helpers/loss_report_helper.rb | 308 ++++++--------------- app/views/collections/loss_report.html.erb | 1 + lib/tasks/compare_xml_collections.rake | 124 +++++++++ lib/tasks/translate_collections.rake | 281 ------------------- 5 files changed, 219 insertions(+), 502 deletions(-) create mode 100644 lib/tasks/compare_xml_collections.rake delete mode 100644 lib/tasks/translate_collections.rake diff --git a/app/controllers/collections_controller.rb b/app/controllers/collections_controller.rb index 63a4210cf..5cc0fb3fd 100644 --- a/app/controllers/collections_controller.rb +++ b/app/controllers/collections_controller.rb @@ -2,6 +2,7 @@ class CollectionsController < ManageCollectionsController include ManageMetadataHelper include CMRCollectionsHelper include CollectionsHelper + include LossReportHelper before_action :set_collection before_action :ensure_correct_collection_provider, only: [:edit, :clone, :revert, :destroy] @@ -120,8 +121,10 @@ def loss_report # it's important that they're able to see if any data loss occurs in the translation to umm. # This method is needed to reference the appropriate helper and view for the lossiness report concept_id = params[:id] - collection_response = cmr_client.get_concept(concept_id, token, {}) - render txt: collection_response.body + respond_to do |format| + format.any {render plain: loss_report_output(concept_id, hide_items=false, disp='text') } + format.json { render json: JSON.pretty_generate(loss_report_output(concept_id, hide_items=false, disp='json')) } + end end private diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index 80aa95096..fd946c95d 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -1,5 +1,80 @@ module LossReportHelper + def loss_report_output(concept_id, hide_items=true, disp='text') + orig,conv,orig_h,conv_h = prepare_collections(concept_id, 'echo10', '1.15.3') + orig = Nokogiri::XML(orig) { |config| config.strict.noblanks } + conv = Nokogiri::XML(conv) { |config| config.strict.noblanks } + + ignored_paths = Array.new + comparison_string = String.new if disp == 'text' + comparison_hash = Hash.new if disp == 'json' + + counter = 1 + orig.diff(conv, {:added => true, :removed => true}) do |change,node| + split_path = node.parent.path.split('[') + if node.parent.path.include?('[') && !ignored_paths.include?(split_path[0]) + ignored_paths << split_path[0] + array_comparison(split_path[0], orig_h, conv_h).each do |item| + if disp == 'text' + comparison_string += "#{counter}. #{item[0]}: #{item[1]}".ljust(60) + item[2] + "\n" if hide_items == false + comparison_string += "#{counter}. #{item[0]}: ".ljust(2) + item[2] + "\n" if hide_items == true + elsif disp == 'json' + comparison_hash["#{counter}. #{item[0]}: #{item[2]}"] = item[1] + end + counter += 1 + end + elsif !ignored_paths.include?(split_path[0]) && !path_leads_to_list?(node.parent.path, orig_h, conv_h) + element = node.to_xml + path = node.parent.path + if disp == 'text' + if element.include?('<' && '') + element = Hash.from_xml(element) + hash_map(element).each do |item| + comparison_string += "#{counter}. #{change}: #{item['value']}".ljust(60) + path + '/' + item['path'] + "\n" if hide_items == false + comparison_string += "#{counter}. #{change}: ".ljust(2) + path + '/' + item['path'] + "\n" if hide_items == true + counter += 1 + end + else + comparison_string += "#{counter}. #{change}: #{element}".ljust(60) + path + "\n" if hide_items == false + comparison_string += "#{counter}. #{change}: ".ljust(2) + path + "\n" if hide_items == true + counter += 1 + end + elsif disp == 'json' + if element.include?('<' && '') + element = Hash.from_xml(element) + hash_map(element).each do |item| + comparison_hash["#{counter}. #{change}: #{path + '/' + item['path']}"] = item['value'] + counter += 1 + end + else + comparison_hash["#{counter}. #{change}: #{path}"] = element + counter += 1 + end + end + end + end + if disp == 'text' + return comparison_string + elsif disp == 'json' + return comparison_hash + end + end + + def hash_map(hash) + buckets = Array.new + hash.each do |key,val| + if val.is_a? Hash + hash_map(val).each do |item| + item['path'] = key + '/' + item['path'] + buckets << item + end + else + buckets << {'path'=> key, 'value'=> val} + end + end + buckets + end + def prepare_collections(concept_id, format, umm_c_version) # TODO: need to add exception handling for get_concept, translate_collection original_collection_native_xml = cmr_client.get_concept(concept_id,token, {}) @@ -23,6 +98,10 @@ def path_leads_to_list?(path, org_hash, conv_hash) if path.include?("[") && path.include?("]") bool = true elsif org_hash_path.is_a?(Hash) && conv_hash_path.is_a?(Hash) + # the number of keys must be 1 because all arrays in echo10, dif10, and iso19115 are tagged similar to: + # contact and so all array-containing tags will be the plural + # of the array name. This clause serves to idenitfy array-containing tags when their paths aren't properly + # displayed by nokogiri bool = true if org_hash_path.keys.length == 1 && org_hash_path[org_hash_path.keys[0]].is_a?(Array) bool = true if conv_hash_path.keys.length == 1 && conv_hash_path[conv_hash_path.keys[0]].is_a?(Array) elsif org_hash_path.is_a?(Array) || conv_hash_path.is_a?(Array) @@ -37,56 +116,18 @@ def hash_navigation(dir, hash) # Passed a path string and the hash being navigated. This method parses the path string and # returns the hash at the end of the path dir = dir.split '/' - if dir.is_a? Array - dir.each do |key| - if !key.empty? && hash.is_a?(Hash) - hash = hash[key] - elsif hash.is_a? Array - return hash - end + dir.each do |key| + if !key.empty? && hash.is_a?(Hash) + hash = hash[key] + elsif hash.is_a? Array + return hash end - else - hash = hash[dir] end hash end - def get_list_paths(dif_hash, original, converted) - # arguments: differences hash, the original hash, and converted hash - # Using these 3 hashses, all paths that lead to a list are returned as an array of path strings - values_list = hash_to_list_of_values(dif_hash) - paths = Array.new - - for item in values_list - org_path = get_dir(item, original) - conv_path = get_dir(item, converted) - - if org_path.include? "[]" - path = org_path - elsif conv_path.include? "[]" - path = conv_path - else - path = org_path - end - - # the get_dir method includes a clause that 'tags' array-containing fields with '[]' - # eg. '/Collection/Contacts/Contact[]/OrganizationEmails/Email' - # the following lines show how this 'tagging' is used to identify an array in a given directory - - if path.include? "[]" - path = path.split "[]" - paths << path[0] unless paths.any? { |p| p.eql? path[0] } - elsif path_leads_to_list?(path, original, converted) - paths << path unless paths.any? { |p| p.eql? path } - end - end - paths - end - def array_comparison(path, original_hash, converted_hash) - # this is a 'less iterative' version of compare_arrays. Args: a single path, the original hash, and the converted hash. - # Rather than finding all the array paths and using those to find the array differences, the array paths are individually - # supplied by the nokogiri gem; this reduces redundancy + org_array = hash_navigation(path, original_hash) conv_array = hash_navigation(path, converted_hash) @@ -95,6 +136,10 @@ def array_comparison(path, original_hash, converted_hash) conv_array.is_a?(Array) ? conv_arr = conv_array.clone : conv_arr = Array.wrap(conv_array) conv_array = Array.wrap(conv_array) unless conv_array.is_a?(Array) + # org_arr and conv_arr are copies of org_array and conv_array, respectively. + # The *_arr values are edited during the comparison between the org_array and conv_array arrays + # and so the *_array arrays are used to maintained a full version of each array for indexing the items in the following lines. + for conv_item in conv_array for org_item in org_array if org_item.eql? conv_item @@ -120,179 +165,4 @@ def array_comparison(path, original_hash, converted_hash) end output end - - def compare_arrays(original_hash, converted_hash, dh=false) - # arguments: differences hash, the original hash, and converted hash - # each path that leads to an array is used to navigate to that array and - # subsequently compare the arrays in the original and converted hashes. - # there is no usable ouput; there is printing to the terminal - - dh ? dif_hash = dh.clone : dif_hash = find_difference_bt_hashes(original_hash, converted_hash).clone - original = original_hash.clone - converted = converted_hash.clone - paths = get_list_paths(dif_hash, original, converted) - - output = Array.new - - paths.each do |path| - org_array = hash_navigation(path, original_hash) - conv_array = hash_navigation(path, converted_hash) - - org_array.is_a?(Array) ? org_arr = org_array.clone : org_arr = Array.wrap(org_array) - org_array = Array.wrap(org_array) unless org_array.is_a?(Array) - conv_array.is_a?(Array) ? conv_arr = conv_array.clone : conv_arr = Array.wrap(conv_array) - conv_array = Array.wrap(conv_array) unless conv_array.is_a?(Array) - - for conv_item in conv_array - for org_item in org_array - if org_item.eql? conv_item - org_arr.delete(org_item) - conv_arr.delete(conv_item) - break - end - end - end - - org_arr.each do |item| - path_with_index = path + "[#{org_array.index(item)}]" - puts "-: ".ljust(60) + path_with_index - loss_item = ['-', path_with_index] - output << loss_item - end - - conv_arr.each do |item| - path_with_index = path + "[#{conv_array.index(item)}]" - puts "+: ".ljust(60) + path_with_index - loss_item = ['+', path_with_index] - output << loss_item - end - end - output - end - - def find_difference_bt_hash_arrays(org_arr, conv_arr) - # array inputs; the output is an array that contains the items in the original array - # that were not found in the converted array - org = org_arr.clone - conv = conv_arr.clone - missing = Array.new - if org.eql? conv - return missing - else - for conv_item in conv - for org_item in org - if org_item.eql? conv_item - org.delete(conv_item) - break - end - end - end - missing += org - end - missing - end - - def find_difference_bt_hashes(org, conv) - # input is the original hash and the converted hash; the output is the - # 'differences hash' which represents the items in the original hash that were - # not found in the converted hash - missing = Hash.new - if org.eql? conv - return missing - else - org.each do |org_key,org_value| - if (conv_value = conv[org_key]) - if conv_value.eql? org_value - next - elsif org_value.is_a?(Hash) && conv_value.is_a?(Hash) - missing_value = find_difference_bt_hashes(org_value, conv_value) - unless missing_value.empty? - missing[org_key] = missing_value - end - elsif org_value.is_a?(Array) && conv_value.is_a?(Array) - missing_value = find_difference_bt_hash_arrays(org_value, conv_value) - unless missing_value.empty? - missing[org_key] = missing_value - end - else - missing[org_key] = org_value - end - else - missing[org_key] = org_value - end - end - end - missing - end - - def get_dir(value, hash_or_arr) - # passing the sought-after value and the hash or array being parsed - # output: a single string representing the path to the value arg passed to this method - iterable = hash_or_arr.clone - dir = String.new - if iterable.is_a? Hash - if (matching_key = iterable.key(value)) - dir += '/' + matching_key - iterable.delete(matching_key) - return dir - else - iterable.each do |key,val| - if val.is_a?(Hash) && hash_to_list_of_values(val).include?(value) - dir += '/' + key - dir += get_dir(value, val) - return dir - elsif val.is_a?(Array) && array_to_list_of_values(val).include?(value) - dir += '/' + key + "[]" - dir += get_dir(value, val) - return dir - elsif val.eql? value - dir += '/' + key - iterable.delete(key) - return dir - end - end - end - elsif iterable.is_a? Array - iterable.each do |item| - if item.is_a?(Hash) && hash_to_list_of_values(item).include?(value) - dir += get_dir(value,item) - return dir - elsif item.is_a?(Array) && array_to_list_of_values(item).include?(value) - dir += get_dir(value,item) + "[]" - return dir - end - end - end - dir - end - - def hash_to_list_of_values(hash) - # converts a highly nested hash to a list of all its values - list = Array.new - for val in hash.values - if val.is_a? Hash - list += hash_to_list_of_values(val) - elsif val.is_a? Array - list += array_to_list_of_values(val) - else - list << val - end - end - list - end - - def array_to_list_of_values(array) - #converts a highly nested array to a list of all its values - ls = Array.new - for item in array - if item.is_a? Hash - ls += hash_to_list_of_values(item) - elsif item.is_a? Array - ls += array_to_list_of_values(item) - else - ls << item - end - end - ls - end end diff --git a/app/views/collections/loss_report.html.erb b/app/views/collections/loss_report.html.erb index 8716ce780..f27bbf9f0 100644 --- a/app/views/collections/loss_report.html.erb +++ b/app/views/collections/loss_report.html.erb @@ -6,6 +6,7 @@ <%= 'Path' %> + <% orig,conv,orig_h,conv_h = prepare_collections('C1200000063-LARC', 'echo10', '1.15.3') %> <% orig = Nokogiri::XML(orig) { |config| config.strict.noblanks } %> diff --git a/lib/tasks/compare_xml_collections.rake b/lib/tasks/compare_xml_collections.rake new file mode 100644 index 000000000..27ca858b3 --- /dev/null +++ b/lib/tasks/compare_xml_collections.rake @@ -0,0 +1,124 @@ +require 'libxml_to_hash' + +namespace :collection do + desc 'Translate a collection from native format to UMM JSON and back to native format' + task :loss, [:file, :format, :disp, :version] => :environment do |_task, args| + args.with_defaults(:version => '1.15.3') + args.with_defaults(:disp => 'show') + + abort 'FORMAT INVALID' unless args.format.eql? ('echo10' || 'dif10' || 'iso19115') + + filename = args.file.split('/')[-1] + puts "\nTranslating #{filename} to UMM JSON..." + + native_original_xml = File.read(args.file) + native_original_hash = Hash.from_xml(native_original_xml) + + #translate to UMM + umm_response = cmr_client.translate_collection(native_original_xml, "application/#{args.format}+xml", "application/vnd.nasa.cmr.umm+json;version=#{args.version}", skip_validation=true ) + umm_json = umm_response.body.to_json + umm_response.success? ? puts("\nsuccessful translation to UMM") : abort("\nUMM translation failure") + + # translate back to native + back_to_native = cmr_client.translate_collection(umm_json, "application/vnd.nasa.cmr.umm+json;version=#{args.version}", "application/#{args.format}+xml", skip_validation=true ) + native_converted_hash = Hash.from_xml(back_to_native.body) + native_converted_xml = back_to_native.body + back_to_native.success? ? puts("successful translation to native format \n\n") : abort("Native format translation failure \n\n") + + # nokogiri output + nokogiri_original = Nokogiri::XML(native_original_xml) { |config| config.strict.noblanks } + nokogiri_converted = Nokogiri::XML(native_converted_xml) { |config| config.strict.noblanks } + + ignored_paths = Array.new + + nokogiri_original.diff(nokogiri_converted, {:added => true, :removed => true}) do |change,node| + split_path = node.parent.path.split('[') + if node.parent.path.include?('[') && !ignored_paths.include?(split_path[0]) + ignored_paths << split_path[0] + array_comparison(split_path[0], native_original_hash, native_converted_hash).each do |item| + puts("#{item[0]}: #{item[1]}".ljust(60) + item[2]) if args.disp.eql? 'show' + puts("#{item[0]}: ". + item[2]) if args.disp.eql? 'hide' + end + elsif !ignored_paths.include?(split_path[0]) && !path_leads_to_list?(node.parent.path, native_original_hash, native_converted_hash) + puts("#{change}: #{node.to_xml}".ljust(60) + node.parent.path) if args.disp.eql? 'show' + puts("#{change}: ". + node.parent.path) if args.disp.eql? 'hide' + end + end + end + + def path_leads_to_list?(path, org_hash, conv_hash) + # this method takes a path string (and the full original and converted hashes) and outputs true if the path string contains a list; else false + org_hash_path = hash_navigation(path, org_hash) + conv_hash_path = hash_navigation(path, conv_hash) + + if path.include?("[") && path.include?("]") + bool = true + elsif org_hash_path.is_a?(Hash) && conv_hash_path.is_a?(Hash) + # the number of keys must be 1 because all arrays in echo10, dif10, and iso19115 are tagged similar to: + # contact and so all array-containing tags will be the plural + # of the array name. This clause serves to idenitfy array-containing tags when their paths aren't properly + # displayed by nokogiri + bool = true if org_hash_path.keys.length == 1 && org_hash_path[org_hash_path.keys[0]].is_a?(Array) + bool = true if conv_hash_path.keys.length == 1 && conv_hash_path[conv_hash_path.keys[0]].is_a?(Array) + elsif org_hash_path.is_a?(Array) || conv_hash_path.is_a?(Array) + bool = true + else + bool = false + end + bool + end + + def hash_navigation(dir, hash) + # Passed a path string and the hash being navigated. This method parses the path string and + # returns the hash at the end of the path + dir = dir.split '/' + dir.each do |key| + if !key.empty? && hash.is_a?(Hash) + hash = hash[key] + elsif hash.is_a? Array + return hash + end + end + hash + end + + def array_comparison(path, original_hash, converted_hash) + + org_array = hash_navigation(path, original_hash) + conv_array = hash_navigation(path, converted_hash) + + org_array.is_a?(Array) ? org_arr = org_array.clone : org_arr = Array.wrap(org_array) + org_array = Array.wrap(org_array) unless org_array.is_a?(Array) + conv_array.is_a?(Array) ? conv_arr = conv_array.clone : conv_arr = Array.wrap(conv_array) + conv_array = Array.wrap(conv_array) unless conv_array.is_a?(Array) + + # org_arr and conv_arr are copies of org_array and conv_array, respectively. + # The *_arr values are edited during the comparison between the org_array and conv_array arrays + # and so the *_array arrays are used to maintained a full version of each array for indexing the items in the following lines. + + for conv_item in conv_array + for org_item in org_array + if org_item.eql? conv_item + org_arr.delete(org_item) + conv_arr.delete(conv_item) + break + end + end + end + + output = Array.new + org_arr.each do |item| + path_with_index = path + "[#{org_array.index(item)}]" + loss_item = ['-', item, path_with_index] + output << loss_item + end + + + conv_arr.each do |item| + path_with_index = path + "[#{conv_array.index(item)}]" + loss_item = ['+', item, path_with_index] + output << loss_item + end + output + end +end diff --git a/lib/tasks/translate_collections.rake b/lib/tasks/translate_collections.rake deleted file mode 100644 index ec0ae90e8..000000000 --- a/lib/tasks/translate_collections.rake +++ /dev/null @@ -1,281 +0,0 @@ -require 'libxml_to_hash' - -namespace :collection do - desc 'Translate a collection from native format to UMM JSON and back to native format' - task :translate, [:file, :format, :disp, :version] => :environment do |_task, args| - args.with_defaults(:version => '1.15.3') - args.with_defaults(:disp => 'show') - - abort 'FORMAT INVALID' unless args.format.eql? ('echo10' || 'dif10' || 'iso19115') - - filename = args.file.split('/')[-1] - puts "\nTranslating #{filename} to UMM JSON..." - - native_original_xml = File.read(args.file) - native_original_hash = Hash.from_xml(native_original_xml) - - #translate to UMM - umm_response = cmr_client.translate_collection(native_original_xml, "application/#{args.format}+xml", "application/vnd.nasa.cmr.umm+json;version=#{args.version}", skip_validation=true ) - umm_json = umm_response.body.to_json - umm_response.success? ? puts("\nsuccessful translation to UMM") : abort("\nUMM translation failure") - - # translate back to native - back_to_native = cmr_client.translate_collection(umm_json, "application/vnd.nasa.cmr.umm+json;version=#{args.version}", "application/#{args.format}+xml", skip_validation=true ) - native_converted_hash = Hash.from_xml(back_to_native.body) - native_converted_xml = back_to_native.body - back_to_native.success? ? puts("successful translation to native format \n\n") : abort("Native format translation failure \n\n") - - # nokogiri output - nokogiri_original = Nokogiri::XML(native_original_xml) { |config| config.strict.noblanks } - nokogiri_converted = Nokogiri::XML(native_converted_xml) { |config| config.strict.noblanks } - - nokogiri_original.diff(nokogiri_converted, {:added => true, :removed => true}) do |change,node| - next if path_leads_to_list?(node.parent.path, native_original_hash, native_converted_hash) - puts("#{change}: #{node.to_xml}".ljust(60) + node.parent.path) if args.disp.eql? 'show' - puts("#{change}: ". + node.parent.path) if args.disp.eql? 'hide' - end - - # find differences - dif_hash = find_difference_bt_hashes(native_original_hash, native_converted_hash) - compare_arrays(dif_hash, native_original_hash, native_converted_hash) - - end - - def path_leads_to_list?(path, org_hash, conv_hash) - # this method takes a path string (and the full original and converted hashes) and outputs true if the path string contains a list; else false - org_hash_path = hash_navigation(path, org_hash) - conv_hash_path = hash_navigation(path, conv_hash) - - return false if org_hash_path == false || conv_hash_path == false - - if path.include?("[") && path.include?("]") - bool = true - elsif org_hash_path.is_a?(Hash) && conv_hash_path.is_a?(Hash) - org_hash_path.keys.each { |key| bool = true; break if org_hash_path[key].is_a?(Array) } - conv_hash_path.keys.each { |key| bool = true; break if conv_hash_path[key].is_a?(Array) } - elsif org_hash_path.is_a?(Array) || conv_hash_path.is_a?(Array) - bool = true - else - bool = false - end - bool - end - - def hash_navigation(dir, hash) - # Passed a path string and the hash being navigated. This method parses the path string and - # returns the hash at the end of the path - dir = dir.split("/") - if dir.is_a? Array - dir.each do |key| - if !key.empty? && hash.is_a?(Hash) - hash = hash[key] - elsif hash.is_a? Array - return false - end - end - else - hash = hash[dir] - end - hash - end - - def get_list_paths(dif_hash, original, converted) - # arguments: differences hash, the original hash, and converted hash - # Using these 3 hashses, all paths that lead to a list are returned as an array of path strings - values_list = hash_to_list_of_values(dif_hash) - paths = Array.new - - for item in values_list - org_path = get_dir(item, original) - conv_path = get_dir(item, converted) - - if org_path.include? "[]" - path = org_path - elsif conv_path.include? "[]" - path = conv_path - else - path = org_path - end - - # the get_dir method includes a clause that 'tags' array-containing fields with '[]' - # eg. '/Collection/Contacts/Contact[]/OrganizationEmails/Email' - # the following lines show how this 'tagging' is used to identify an array in a given directory - - if path.include? "[]" - path = path.split "[]" - paths << path[0] unless paths.any? { |p| p.eql? path[0] } - elsif path_leads_to_list?(path, original, converted) - paths << path unless paths.any? { |p| p.eql? path } - end - end - paths - end - - def compare_arrays(dif_hash, original, converted) - # arguments: differences hash, the original hash, and converted hash - # each path that leads to an array is used to navigate to that array and - # subsequently compare the arrays in the original and converted hashes. - # there is no usable ouput; there is printing to the terminal - paths = get_list_paths(dif_hash, original, converted) - - paths.each do |path| - org_array = hash_navigation(path, original) - conv_array = hash_navigation(path, converted) - - org_array.is_a?(Array) ? org_arr = Array.wrap(org_array) : org_arr = org_array.clone - org_array = Array.wrap(org_array) unless org_array.is_a?(Array) - conv_array.is_a?(Array) ? conv_arr = Array.wrap(conv_array) : conv_arr = conv_array.clone - conv_array = Array.wrap(conv_array) unless conv_array.is_a?(Array) - - for conv_item in conv_array - for org_item in org_array - if org_item.eql? conv_item - org_arr.delete(org_item) - conv_arr.delete(conv_item) - break - end - end - end - - org_arr.each do |item| - path_with_index = path + "[#{org_array.index(item)}]" - puts "-: ". + path_with_index - end - - conv_arr.each do |item| - path_with_index = path + "[#{conv_array.index(item)}]" - puts "+: " + path_with_index - end - end - end - - def find_difference_bt_hash_arrays(org_arr, conv_arr) - # array inputs; the output is an array that contains the items in the original array - # that were not found in the converted array - org = org_arr.clone - conv = conv_arr.clone - missing = Array.new - if org.eql? conv - return missing - else - for conv_item in conv - for org_item in org - if org_item.eql? conv_item - org.delete(conv_item) - break - end - end - end - missing += org - end - missing - end - - def find_difference_bt_hashes(org, conv) - # input is the original hash and the converted hash; the output is the - # 'differences hash' which represents the items in the original hash that were - # not found in the converted hash - missing = Hash.new - if org.eql? conv - return missing - else - org.each do |org_key,org_value| - if (conv_value = conv[org_key]) - if conv_value.eql? org_value - next - elsif org_value.is_a?(Hash) && conv_value.is_a?(Hash) - missing_value = find_difference_bt_hashes(org_value, conv_value) - unless missing_value.empty? - missing[org_key] = missing_value - end - elsif org_value.is_a?(Array) && conv_value.is_a?(Array) - missing_value = find_difference_bt_hash_arrays(org_value, conv_value) - unless missing_value.empty? - missing[org_key] = missing_value - end - else - missing[org_key] = org_value - end - else - missing[org_key] = org_value - end - end - end - missing - end - - def get_dir(value, hash_or_arr) - # passing the sought-after value and the hash or array being parsed - # output: a single string representing the path to the value arg passed to this method - iterable = hash_or_arr.clone - dir = String.new - if iterable.is_a? Hash - if (matching_key = iterable.key(value)) - dir += '/' + matching_key - iterable.delete(matching_key) - return dir - else - iterable.each do |key,val| - if val.is_a?(Hash) && hash_to_list_of_values(val).include?(value) - dir += '/' + key - dir += get_dir(value, val) - return dir - elsif val.is_a?(Array) && array_to_list_of_values(val).include?(value) - dir += '/' + key + "[]" - dir += get_dir(value, val) - return dir - elsif val.eql? value - dir += '/' + key - iterable.delete(key) - return dir - end - end - end - elsif iterable.is_a? Array - iterable.each do |item| - if item.is_a?(Hash) && hash_to_list_of_values(item).include?(value) - dir += get_dir(value,item) - return dir - elsif item.is_a?(Array) && array_to_list_of_values(item).include?(value) - dir += get_dir(value,item) + "[]" - return dir - end - end - end - dir - end - - def hash_to_list_of_values(hash) - # converts a highly nested hash to a list of all its values - list = Array.new - for val in hash.values - if val.is_a? Hash - list += hash_to_list_of_values(val) - elsif val.is_a? Array - list += array_to_list_of_values(val) - else - list << val - end - end - list - end - - def array_to_list_of_values(array) - #converts a highly nested array to a list of all its values - ls = Array.new - for item in array - if item.is_a? Hash - ls += hash_to_list_of_values(item) - elsif item.is_a? Array - ls += array_to_list_of_values(item) - else - ls << item - end - end - ls - end - - def cmr_client - @cmr_client ||= Cmr::Client.client_for_environment(Rails.configuration.cmr_env, Rails.configuration.services) - end -end From e23bb6a681d40a9ecc2436dfceebfcdfe939d133 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Thu, 16 Jul 2020 15:12:21 -0400 Subject: [PATCH 09/38] MMT-2313: adjusted whitespace --- app/helpers/loss_report_helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index fd946c95d..369c62d8a 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -53,11 +53,13 @@ def loss_report_output(concept_id, hide_items=true, disp='text') end end end + if disp == 'text' return comparison_string elsif disp == 'json' return comparison_hash end + end def hash_map(hash) From 50c0c0f46e497d2f37b4971cf43b0b68c31c3c75 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Fri, 17 Jul 2020 09:05:19 -0400 Subject: [PATCH 10/38] MMT-2313: added some comments --- app/controllers/collections_controller.rb | 3 ++- app/helpers/loss_report_helper.rb | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/controllers/collections_controller.rb b/app/controllers/collections_controller.rb index 5cc0fb3fd..b613d674f 100644 --- a/app/controllers/collections_controller.rb +++ b/app/controllers/collections_controller.rb @@ -121,8 +121,9 @@ def loss_report # it's important that they're able to see if any data loss occurs in the translation to umm. # This method is needed to reference the appropriate helper and view for the lossiness report concept_id = params[:id] + ft = params[:format] respond_to do |format| - format.any {render plain: loss_report_output(concept_id, hide_items=false, disp='text') } + format.text {render plain: loss_report_output(concept_id, hide_items=true, disp='text') } format.json { render json: JSON.pretty_generate(loss_report_output(concept_id, hide_items=false, disp='json')) } end end diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index 369c62d8a..cd24898b7 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -5,14 +5,14 @@ def loss_report_output(concept_id, hide_items=true, disp='text') orig = Nokogiri::XML(orig) { |config| config.strict.noblanks } conv = Nokogiri::XML(conv) { |config| config.strict.noblanks } - ignored_paths = Array.new + ignored_paths = Array.new # This array is used to keep track of the paths that lead to arrays that have already been mapped comparison_string = String.new if disp == 'text' comparison_hash = Hash.new if disp == 'json' counter = 1 - orig.diff(conv, {:added => true, :removed => true}) do |change,node| + orig.diff(conv, {:added => true, :removed => true}) do |change,node| split_path = node.parent.path.split('[') - if node.parent.path.include?('[') && !ignored_paths.include?(split_path[0]) + if node.parent.path.include?('[') && !ignored_paths.include?(split_path[0]) # ignored_paths << split_path[0] array_comparison(split_path[0], orig_h, conv_h).each do |item| if disp == 'text' @@ -59,7 +59,7 @@ def loss_report_output(concept_id, hide_items=true, disp='text') elsif disp == 'json' return comparison_hash end - + end def hash_map(hash) From 748631e6bfbf07ad1398185d8594926f59a675fd Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Mon, 20 Jul 2020 09:07:14 -0400 Subject: [PATCH 11/38] MMT-2313: increased readability of loss_report_output --- app/controllers/collections_controller.rb | 1 - app/helpers/loss_report_helper.rb | 152 ++++++++++------------ 2 files changed, 69 insertions(+), 84 deletions(-) diff --git a/app/controllers/collections_controller.rb b/app/controllers/collections_controller.rb index b613d674f..e148bed83 100644 --- a/app/controllers/collections_controller.rb +++ b/app/controllers/collections_controller.rb @@ -121,7 +121,6 @@ def loss_report # it's important that they're able to see if any data loss occurs in the translation to umm. # This method is needed to reference the appropriate helper and view for the lossiness report concept_id = params[:id] - ft = params[:format] respond_to do |format| format.text {render plain: loss_report_output(concept_id, hide_items=true, disp='text') } format.json { render json: JSON.pretty_generate(loss_report_output(concept_id, hide_items=false, disp='json')) } diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index cd24898b7..4fc92a00b 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -1,65 +1,53 @@ module LossReportHelper def loss_report_output(concept_id, hide_items=true, disp='text') - orig,conv,orig_h,conv_h = prepare_collections(concept_id, 'echo10', '1.15.3') + # depending on the input selection (json or text) a comparison string/hash is created and displayed in-browser + orig,conv,orig_h,conv_h,ct = prepare_collections(concept_id, '1.15.3') + orig = Nokogiri::XML(orig) { |config| config.strict.noblanks } conv = Nokogiri::XML(conv) { |config| config.strict.noblanks } ignored_paths = Array.new # This array is used to keep track of the paths that lead to arrays that have already been mapped comparison_string = String.new if disp == 'text' comparison_hash = Hash.new if disp == 'json' + comparison_string += (ct + "\n\n") counter = 1 - orig.diff(conv, {:added => true, :removed => true}) do |change,node| - split_path = node.parent.path.split('[') - if node.parent.path.include?('[') && !ignored_paths.include?(split_path[0]) # - ignored_paths << split_path[0] - array_comparison(split_path[0], orig_h, conv_h).each do |item| - if disp == 'text' - comparison_string += "#{counter}. #{item[0]}: #{item[1]}".ljust(60) + item[2] + "\n" if hide_items == false - comparison_string += "#{counter}. #{item[0]}: ".ljust(2) + item[2] + "\n" if hide_items == true - elsif disp == 'json' - comparison_hash["#{counter}. #{item[0]}: #{item[2]}"] = item[1] - end + orig.diff(conv, {:added => true, :removed => true}) do |change,node| + element = node.to_xml + path = node.parent.path + split_path = path.split('[')[0] + + if path.include?('[') && !ignored_paths.include?(split_path) + ignored_paths << split_path + array_comparison(split_path, orig_h, conv_h).each do |item| + add_to_report(counter, item[0], item[1], item[2], hide_items, disp, comparison_hash, comparison_string) counter += 1 end - elsif !ignored_paths.include?(split_path[0]) && !path_leads_to_list?(node.parent.path, orig_h, conv_h) - element = node.to_xml - path = node.parent.path - if disp == 'text' - if element.include?('<' && '') - element = Hash.from_xml(element) - hash_map(element).each do |item| - comparison_string += "#{counter}. #{change}: #{item['value']}".ljust(60) + path + '/' + item['path'] + "\n" if hide_items == false - comparison_string += "#{counter}. #{change}: ".ljust(2) + path + '/' + item['path'] + "\n" if hide_items == true - counter += 1 - end - else - comparison_string += "#{counter}. #{change}: #{element}".ljust(60) + path + "\n" if hide_items == false - comparison_string += "#{counter}. #{change}: ".ljust(2) + path + "\n" if hide_items == true - counter += 1 - end - elsif disp == 'json' - if element.include?('<' && '') - element = Hash.from_xml(element) - hash_map(element).each do |item| - comparison_hash["#{counter}. #{change}: #{path + '/' + item['path']}"] = item['value'] - counter += 1 - end - else - comparison_hash["#{counter}. #{change}: #{path}"] = element + elsif !ignored_paths.include?(split_path) && !path_leads_to_list?(path, orig_h, conv_h) + if is_xml? node + element = Hash.from_xml(element) + hash_map(element).each do |item| + add_to_report(counter, change, item['value'], path +'/'+ item['path'], hide_items, disp, comparison_hash, comparison_string) counter += 1 end + else + add_to_report(counter, change, element, path, hide_items, disp, comparison_hash, comparison_string) + counter += 1 end end - end - if disp == 'text' - return comparison_string - elsif disp == 'json' - return comparison_hash end + if disp == 'text' then return comparison_string + elsif disp == 'json' then return comparison_hash end + end + def add_to_report(counter, change, element, path, hide_items, disp, comparison_hash, comparison_string) + # this function serves to preclude complex nests from forming in loss_report_output the + # following 'if' structure is intended to increase readability by eliminating nests + return comparison_string.concat("#{counter}. #{change}: #{element}".ljust(60) + path + "\n") if hide_items == false && disp == 'text' + return comparison_string.concat("#{counter}. #{change}: ".ljust(2) + path + "\n") if hide_items == true && disp == 'text' + return comparison_hash["#{counter}. #{change}: #{path}"] = element if disp == 'json' end def hash_map(hash) @@ -77,36 +65,37 @@ def hash_map(hash) buckets end - def prepare_collections(concept_id, format, umm_c_version) + def is_xml?(node) + if node.to_xml.include?('<' && '') then return true + else return false end + end + + def prepare_collections(concept_id, umm_c_version) # TODO: need to add exception handling for get_concept, translate_collection original_collection_native_xml = cmr_client.get_concept(concept_id,token, {}) - # concept ID and format can be scalped from headers etc + content_type = original_collection_native_xml.headers.fetch('content-type').split(';')[0] original_collection_native_hash = Hash.from_xml(original_collection_native_xml.body) - translated_collection_umm_json = cmr_client.translate_collection(original_collection_native_xml.body, "application/#{format}+xml", "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", skip_validation=true) - translated_collection_native_xml = cmr_client.translate_collection(translated_collection_umm_json.body.to_json, "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", "application/#{format}+xml", skip_validation=true) + translated_collection_umm_json = cmr_client.translate_collection(original_collection_native_xml.body, content_type, "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", skip_validation=true) + translated_collection_native_xml = cmr_client.translate_collection(translated_collection_umm_json.body.to_json, "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", content_type, skip_validation=true) translated_collection_native_hash = Hash.from_xml(translated_collection_native_xml.body) - # File.write('/Users/ctrummer/Documents/devtesting/o_'+concept_id+'.json', JSON.pretty_generate(original_collection_native_hash)) - # File.write('/Users/ctrummer/Documents/devtesting/c_'+concept_id+'.json', JSON.pretty_generate(translated_collection_native_hash)) - # File.write('/Users/ctrummer/Documents/devtesting/o_'+concept_id+'.xml', original_collection_native_xml.body) - # File.write('/Users/ctrummer/Documents/devtesting/c_'+concept_id+'.xml', translated_collection_native_xml.body) - return original_collection_native_xml.body, translated_collection_native_xml.body, original_collection_native_hash, translated_collection_native_hash + return original_collection_native_xml.body, translated_collection_native_xml.body, original_collection_native_hash, translated_collection_native_hash, content_type end def path_leads_to_list?(path, org_hash, conv_hash) # this method takes a path string (and the full original and converted hashes) and outputs true if the path string contains a list; else false - org_hash_path = hash_navigation(path, org_hash) - conv_hash_path = hash_navigation(path, conv_hash) + org_hash = hash_navigation(path, org_hash) + conv_hash = hash_navigation(path, conv_hash) if path.include?("[") && path.include?("]") bool = true - elsif org_hash_path.is_a?(Hash) && conv_hash_path.is_a?(Hash) + elsif org_hash.is_a?(Hash) && conv_hash.is_a?(Hash) # the number of keys must be 1 because all arrays in echo10, dif10, and iso19115 are tagged similar to: # contact and so all array-containing tags will be the plural - # of the array name. This clause serves to idenitfy array-containing tags when their paths aren't properly + # of the array name. This clause serves to identify array-containing tags when their paths aren't properly # displayed by nokogiri - bool = true if org_hash_path.keys.length == 1 && org_hash_path[org_hash_path.keys[0]].is_a?(Array) - bool = true if conv_hash_path.keys.length == 1 && conv_hash_path[conv_hash_path.keys[0]].is_a?(Array) - elsif org_hash_path.is_a?(Array) || conv_hash_path.is_a?(Array) + bool = true if org_hash.keys.length == 1 && org_hash[org_hash.keys[0]].is_a?(Array) + bool = true if conv_hash.keys.length == 1 && conv_hash[conv_hash.keys[0]].is_a?(Array) + elsif org_hash.is_a?(Array) || conv_hash.is_a?(Array) bool = true else bool = false @@ -114,15 +103,14 @@ def path_leads_to_list?(path, org_hash, conv_hash) bool end - def hash_navigation(dir, hash) + def hash_navigation(path, hash) # Passed a path string and the hash being navigated. This method parses the path string and - # returns the hash at the end of the path - dir = dir.split '/' - dir.each do |key| - if !key.empty? && hash.is_a?(Hash) - hash = hash[key] - elsif hash.is_a? Array + # returns the hash/value at the end of the path + path.split('/').each do |key| + if hash.is_a? Array return hash + elsif hash.key?(key) && hash.is_a?(Hash) + hash = hash[key] end end hash @@ -130,40 +118,38 @@ def hash_navigation(dir, hash) def array_comparison(path, original_hash, converted_hash) - org_array = hash_navigation(path, original_hash) - conv_array = hash_navigation(path, converted_hash) + pretranslation_array = hash_navigation(path, original_hash) + post_translation_array = hash_navigation(path, converted_hash) - org_array.is_a?(Array) ? org_arr = org_array.clone : org_arr = Array.wrap(org_array) - org_array = Array.wrap(org_array) unless org_array.is_a?(Array) - conv_array.is_a?(Array) ? conv_arr = conv_array.clone : conv_arr = Array.wrap(conv_array) - conv_array = Array.wrap(conv_array) unless conv_array.is_a?(Array) + pretranslation_array.is_a?(Array) ? lost_items_arr = pretranslation_array.clone : lost_items_arr = Array.wrap(pretranslation_array) + pretranslation_array = Array.wrap(pretranslation_array) unless pretranslation_array.is_a?(Array) + post_translation_array.is_a?(Array) ? added_itmes_arr = post_translation_array.clone : added_itmes_arr = Array.wrap(post_translation_array) + post_translation_array = Array.wrap(post_translation_array) unless post_translation_array.is_a?(Array) # org_arr and conv_arr are copies of org_array and conv_array, respectively. # The *_arr values are edited during the comparison between the org_array and conv_array arrays # and so the *_array arrays are used to maintained a full version of each array for indexing the items in the following lines. - for conv_item in conv_array - for org_item in org_array + for conv_item in post_translation_array + for org_item in pretranslation_array if org_item.eql? conv_item - org_arr.delete(org_item) - conv_arr.delete(conv_item) + lost_items_arr.delete(org_item) + added_itmes_arr.delete(conv_item) break end end end output = Array.new - org_arr.each do |item| - path_with_index = path + "[#{org_array.index(item)}]" - loss_item = ['-', item, path_with_index] - output << loss_item + lost_items_arr.each do |item| + path_with_index = path + "[#{pretranslation_array.index(item)}]" + output << ['-', item, path_with_index] end - conv_arr.each do |item| - path_with_index = path + "[#{conv_array.index(item)}]" - loss_item = ['+', item, path_with_index] - output << loss_item + added_itmes_arr.each do |item| + path_with_index = path + "[#{post_translation_array.index(item)}]" + output << ['+', item, path_with_index] end output end From d6d5077bcc8ae54f8af119139c89081678ffec0e Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Mon, 20 Jul 2020 11:00:27 -0400 Subject: [PATCH 12/38] MMT-2313: improved readability --- app/helpers/loss_report_helper.rb | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index 4fc92a00b..31561b749 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -10,7 +10,8 @@ def loss_report_output(concept_id, hide_items=true, disp='text') ignored_paths = Array.new # This array is used to keep track of the paths that lead to arrays that have already been mapped comparison_string = String.new if disp == 'text' comparison_hash = Hash.new if disp == 'json' - comparison_string += (ct + "\n\n") + comparison_hash['format'] = ct + comparison_string += (ct + "\n\n") if disp == 'text' counter = 1 orig.diff(conv, {:added => true, :removed => true}) do |change,node| @@ -25,7 +26,7 @@ def loss_report_output(concept_id, hide_items=true, disp='text') counter += 1 end elsif !ignored_paths.include?(split_path) && !path_leads_to_list?(path, orig_h, conv_h) - if is_xml? node + if is_xml?(node) element = Hash.from_xml(element) hash_map(element).each do |item| add_to_report(counter, change, item['value'], path +'/'+ item['path'], hide_items, disp, comparison_hash, comparison_string) @@ -105,9 +106,9 @@ def path_leads_to_list?(path, org_hash, conv_hash) def hash_navigation(path, hash) # Passed a path string and the hash being navigated. This method parses the path string and - # returns the hash/value at the end of the path + # returns the array/value at the end of the path path.split('/').each do |key| - if hash.is_a? Array + if hash.is_a?(Array) return hash elsif hash.key?(key) && hash.is_a?(Hash) hash = hash[key] @@ -118,21 +119,23 @@ def hash_navigation(path, hash) def array_comparison(path, original_hash, converted_hash) - pretranslation_array = hash_navigation(path, original_hash) + pre_translation_array = hash_navigation(path, original_hash) post_translation_array = hash_navigation(path, converted_hash) - pretranslation_array.is_a?(Array) ? lost_items_arr = pretranslation_array.clone : lost_items_arr = Array.wrap(pretranslation_array) - pretranslation_array = Array.wrap(pretranslation_array) unless pretranslation_array.is_a?(Array) + # in the case that a one-item array is parsed as a regular key-value pair instead of an array, an Array wrapper is placed around key-val pair + # so that the following for loops can be executed without error + pre_translation_array.is_a?(Array) ? lost_items_arr = pre_translation_array.clone : lost_items_arr = Array.wrap(pre_translation_array) + pre_translation_array = Array.wrap(pre_translation_array) post_translation_array.is_a?(Array) ? added_itmes_arr = post_translation_array.clone : added_itmes_arr = Array.wrap(post_translation_array) - post_translation_array = Array.wrap(post_translation_array) unless post_translation_array.is_a?(Array) + post_translation_array = Array.wrap(post_translation_array) - # org_arr and conv_arr are copies of org_array and conv_array, respectively. - # The *_arr values are edited during the comparison between the org_array and conv_array arrays - # and so the *_array arrays are used to maintained a full version of each array for indexing the items in the following lines. + # as defined above, the lost_items_arr and added_itmes_arr are copies of pre_translation_array and post_translation_array, respectively. + # The *_arr values are edited during the comparison between the pre_translation_array and post_translation_array arrays + # and so the *_array arrays are used to maintain a full version of each array for indexing the items in the following lines. for conv_item in post_translation_array - for org_item in pretranslation_array - if org_item.eql? conv_item + for org_item in pre_translation_array + if org_item == conv_item lost_items_arr.delete(org_item) added_itmes_arr.delete(conv_item) break @@ -142,7 +145,7 @@ def array_comparison(path, original_hash, converted_hash) output = Array.new lost_items_arr.each do |item| - path_with_index = path + "[#{pretranslation_array.index(item)}]" + path_with_index = path + "[#{pre_translation_array.index(item)}]" output << ['-', item, path_with_index] end From db730a000cd2c2d18d5997ce499d1881a7541930 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Tue, 21 Jul 2020 15:22:14 -0400 Subject: [PATCH 13/38] MMT-2313: attempting to fix asterisk problem --- app/helpers/loss_report_helper.rb | 89 ++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 26 deletions(-) diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index 31561b749..5d44853f0 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -2,16 +2,37 @@ module LossReportHelper def loss_report_output(concept_id, hide_items=true, disp='text') # depending on the input selection (json or text) a comparison string/hash is created and displayed in-browser - orig,conv,orig_h,conv_h,ct = prepare_collections(concept_id, '1.15.3') + orig_xml,conv_xml,orig_h,conv_h,content_type = prepare_collections(concept_id, '1.15.3') + + # if content_type.include?('dif10') + # orig = Nokogiri::XML(orig_h.to_xml({dasherize: false, skip_types: true, skip_instruct: true})) { |config| config.strict.noblanks } .search('DIF').first.dup + # conv = Nokogiri::XML(conv_h.to_xml({dasherize: false, skip_types: true, skip_instruct: true})) { |config| config.strict.noblanks } .search('DIF').first.dup + # else + # orig = Nokogiri::XML(orig_xml) { |config| config.strict.noblanks } + # conv = Nokogiri::XML(conv_xml) { |config| config.strict.noblanks } + # end + # orig_xml = orig_xml.split('')[-1] + # conv_xml = conv_xml.split('')[-1] + + orig = Nokogiri::XML(orig_xml) { |config| config.strict.noblanks } + conv = Nokogiri::XML(conv_xml) { |config| config.strict.noblanks } + - orig = Nokogiri::XML(orig) { |config| config.strict.noblanks } - conv = Nokogiri::XML(conv) { |config| config.strict.noblanks } ignored_paths = Array.new # This array is used to keep track of the paths that lead to arrays that have already been mapped comparison_string = String.new if disp == 'text' comparison_hash = Hash.new if disp == 'json' - comparison_hash['format'] = ct - comparison_string += (ct + "\n\n") if disp == 'text' + + # comparison_hash['orig'] = hash_map(orig_h) if disp == 'json' + # comparison_hash['orig'] = orig_h if disp == 'json' + # comparison_hash['conv'] = conv_h if disp == 'json' + comparison_string += orig_xml +"\n\n\n\n\n\n" + orig_h.to_xml({dasherize: false, skip_types: true, skip_instruct: true}) if disp == 'text' + + comparison_hash['format'] = content_type if disp == 'json' + comparison_string += (content_type + "\n\n") if disp == 'text' + + # p = '/DIF/Related_RL/Related_UR' + # comparison_string += (p+': path_leads_to_list? => ' + path_leads_to_list?(p, orig_h, conv_h).to_s + "\n") counter = 1 orig.diff(conv, {:added => true, :removed => true}) do |change,node| @@ -19,25 +40,32 @@ def loss_report_output(concept_id, hide_items=true, disp='text') path = node.parent.path split_path = path.split('[')[0] - if path.include?('[') && !ignored_paths.include?(split_path) - ignored_paths << split_path - array_comparison(split_path, orig_h, conv_h).each do |item| - add_to_report(counter, item[0], item[1], item[2], hide_items, disp, comparison_hash, comparison_string) - counter += 1 - end - elsif !ignored_paths.include?(split_path) && !path_leads_to_list?(path, orig_h, conv_h) - if is_xml?(node) - element = Hash.from_xml(element) - hash_map(element).each do |item| - add_to_report(counter, change, item['value'], path +'/'+ item['path'], hide_items, disp, comparison_hash, comparison_string) - counter += 1 - end - else - add_to_report(counter, change, element, path, hide_items, disp, comparison_hash, comparison_string) - counter += 1 - end - end - + # comparison_string += (path + "\n") if disp == 'text' + + # if path_leads_to_list?(path, orig_h, conv_h) && !ignored_paths.include?(split_path) + # ignored_paths << split_path + # array_comparison(split_path, orig_h, conv_h).each do |item| + # add_to_report(counter, 'c'+item[0], item[1], item[2], hide_items, disp, comparison_hash, comparison_string) + # counter += 1 + # end + # # path += ' identified as a list' + # elsif !ignored_paths.include?(split_path) + # if is_xml?(node) #Possibly use the nokogiri #xml? method + # # path += ' needs hash mapping' + # element = Hash.from_xml(element) + # hash_map(element).each do |item| + # add_to_report(counter, 'ct'+change, item['value'], path +'/'+ item['path'], hide_items, disp, comparison_hash, comparison_string) + # counter += 1 + # end + # else + # # path += ' pure nokogiri' + # add_to_report(counter, change, element, path, hide_items, disp, comparison_hash, comparison_string) + # counter += 1 + # end + # end + + add_to_report(counter, change, element, path, hide_items, disp, comparison_hash, comparison_string) + counter += 1 end if disp == 'text' then return comparison_string elsif disp == 'json' then return comparison_hash end @@ -46,11 +74,17 @@ def loss_report_output(concept_id, hide_items=true, disp='text') def add_to_report(counter, change, element, path, hide_items, disp, comparison_hash, comparison_string) # this function serves to preclude complex nests from forming in loss_report_output the # following 'if' structure is intended to increase readability by eliminating nests - return comparison_string.concat("#{counter}. #{change}: #{element}".ljust(60) + path + "\n") if hide_items == false && disp == 'text' - return comparison_string.concat("#{counter}. #{change}: ".ljust(2) + path + "\n") if hide_items == true && disp == 'text' + return comparison_string.concat("#{counter}.".ljust(4)+"#{change}: #{element}".ljust(60) + path + "\n") if hide_items == false && disp == 'text' + return comparison_string.concat("#{counter}.".ljust(4)+"#{change}: ".ljust(3) + path + "\n") if hide_items == true && disp == 'text' return comparison_hash["#{counter}. #{change}: #{path}"] = element if disp == 'json' end + def change_path(path) + arr = path.split('/*') + arr[0] = '/DIF' + arr.join + end + def hash_map(hash) buckets = Array.new hash.each do |key,val| @@ -87,6 +121,8 @@ def path_leads_to_list?(path, org_hash, conv_hash) org_hash = hash_navigation(path, org_hash) conv_hash = hash_navigation(path, conv_hash) + # if path == '/DIF/Related-URL' then byebug end + if path.include?("[") && path.include?("]") bool = true elsif org_hash.is_a?(Hash) && conv_hash.is_a?(Hash) @@ -101,6 +137,7 @@ def path_leads_to_list?(path, org_hash, conv_hash) else bool = false end + # if bool == nil then bool = 'flag' end #THIS NEEDS TO BE EVALUATED bool end From d1854c8d4d6a798ff31652e7ddb45aafa312acc4 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Tue, 21 Jul 2020 15:27:44 -0400 Subject: [PATCH 14/38] MMT-2313 attempting to fix asterisk problem --- app/helpers/loss_report_helper.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index 5d44853f0..d8c2e1fb5 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -11,6 +11,7 @@ def loss_report_output(concept_id, hide_items=true, disp='text') # orig = Nokogiri::XML(orig_xml) { |config| config.strict.noblanks } # conv = Nokogiri::XML(conv_xml) { |config| config.strict.noblanks } # end + # orig_xml = orig_xml.split('')[-1] # conv_xml = conv_xml.split('')[-1] From 1bc3d15b26db413e64e79432fec9899c0ac17090 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Wed, 22 Jul 2020 13:29:55 -0400 Subject: [PATCH 15/38] MMT-2313: fix nokogiri problems --- app/helpers/loss_report_helper.rb | 99 ++++++++++++++++--------------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index d8c2e1fb5..58bdb7b73 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -4,70 +4,75 @@ def loss_report_output(concept_id, hide_items=true, disp='text') # depending on the input selection (json or text) a comparison string/hash is created and displayed in-browser orig_xml,conv_xml,orig_h,conv_h,content_type = prepare_collections(concept_id, '1.15.3') - # if content_type.include?('dif10') - # orig = Nokogiri::XML(orig_h.to_xml({dasherize: false, skip_types: true, skip_instruct: true})) { |config| config.strict.noblanks } .search('DIF').first.dup - # conv = Nokogiri::XML(conv_h.to_xml({dasherize: false, skip_types: true, skip_instruct: true})) { |config| config.strict.noblanks } .search('DIF').first.dup - # else - # orig = Nokogiri::XML(orig_xml) { |config| config.strict.noblanks } - # conv = Nokogiri::XML(conv_xml) { |config| config.strict.noblanks } - # end - - # orig_xml = orig_xml.split('')[-1] - # conv_xml = conv_xml.split('')[-1] - - orig = Nokogiri::XML(orig_xml) { |config| config.strict.noblanks } - conv = Nokogiri::XML(conv_xml) { |config| config.strict.noblanks } - - + if content_type.include?('dif10') + orig = Nokogiri::XML(orig_xml) { |config| config.strict.noblanks } .remove_namespaces! + conv = Nokogiri::XML(conv_xml) { |config| config.strict.noblanks } .remove_namespaces! + else + orig = Nokogiri::XML(orig_xml) { |config| config.strict.noblanks } + conv = Nokogiri::XML(conv_xml) { |config| config.strict.noblanks } + end ignored_paths = Array.new # This array is used to keep track of the paths that lead to arrays that have already been mapped comparison_string = String.new if disp == 'text' comparison_hash = Hash.new if disp == 'json' # comparison_hash['orig'] = hash_map(orig_h) if disp == 'json' - # comparison_hash['orig'] = orig_h if disp == 'json' + comparison_hash['orig'] = orig_h if disp == 'json' # comparison_hash['conv'] = conv_h if disp == 'json' - comparison_string += orig_xml +"\n\n\n\n\n\n" + orig_h.to_xml({dasherize: false, skip_types: true, skip_instruct: true}) if disp == 'text' + comparison_string += orig_xml if disp == 'text' + + # p = '/DIF' + # comparison_string += path_leads_to_list?(p, orig_h, conv_h).to_s + "\n\n" if disp == 'text' comparison_hash['format'] = content_type if disp == 'json' comparison_string += (content_type + "\n\n") if disp == 'text' - # p = '/DIF/Related_RL/Related_UR' - # comparison_string += (p+': path_leads_to_list? => ' + path_leads_to_list?(p, orig_h, conv_h).to_s + "\n") - counter = 1 orig.diff(conv, {:added => true, :removed => true}) do |change,node| element = node.to_xml - path = node.parent.path - split_path = path.split('[')[0] - + path = node.parent.path.split('[')[0] # comparison_string += (path + "\n") if disp == 'text' - # if path_leads_to_list?(path, orig_h, conv_h) && !ignored_paths.include?(split_path) - # ignored_paths << split_path - # array_comparison(split_path, orig_h, conv_h).each do |item| - # add_to_report(counter, 'c'+item[0], item[1], item[2], hide_items, disp, comparison_hash, comparison_string) - # counter += 1 - # end - # # path += ' identified as a list' - # elsif !ignored_paths.include?(split_path) - # if is_xml?(node) #Possibly use the nokogiri #xml? method - # # path += ' needs hash mapping' - # element = Hash.from_xml(element) - # hash_map(element).each do |item| - # add_to_report(counter, 'ct'+change, item['value'], path +'/'+ item['path'], hide_items, disp, comparison_hash, comparison_string) - # counter += 1 - # end - # else - # # path += ' pure nokogiri' - # add_to_report(counter, change, element, path, hide_items, disp, comparison_hash, comparison_string) - # counter += 1 - # end - # end - - add_to_report(counter, change, element, path, hide_items, disp, comparison_hash, comparison_string) + # needs to check for lists that pass the first if condition but should be evaluated by the elsif (ie. Related_URL) + # figure out why some elements are not evaluating true at the first if (ie. Extended_Metadata) + + if path_leads_to_list?(path, orig_h, conv_h) && !ignored_paths.include?(path) # all lists + ignored_paths << path + array_comparison(path, orig_h, conv_h).each do |item| + add_to_report(counter, 'c'+item[0], item[1], item[2], hide_items, disp, comparison_hash, comparison_string) + counter += 1 + end + elsif !ignored_paths.include?(path) # nokogiri + if is_xml?(node) #Possibly use the nokogiri #xml? method + element = Hash.from_xml(element) + + hash_map(element).each do |item| + if path_leads_to_list?(path +'/'+ item['path'], orig_h, conv_h) && !ignored_paths.include?(path +'/'+ item['path']) # all lists + ignored_paths << path +'/'+ item['path'] + array_comparison(path +'/'+ item['path'], orig_h, conv_h).each do |item| + add_to_report(counter, 'cc'+item[0], item[1], item[2], hide_items, disp, comparison_hash, comparison_string) + counter += 1 + end + else + add_to_report(counter, 'ct'+change, item['value'], path +'/'+ item['path'], hide_items, disp, comparison_hash, comparison_string) + counter += 1 + end + + end + else + add_to_report(counter, change, element, path, hide_items, disp, comparison_hash, comparison_string) + counter += 1 + end + end + end + + counter = 0 + comparison_string += "\n\n\n\n" if disp == 'text' + orig.diff(conv, {:added => true, :removed => true}) do |change,node| + add_to_report(counter, change, node.to_xml, node.parent.path, false, disp, comparison_hash, comparison_string) counter += 1 end + if disp == 'text' then return comparison_string elsif disp == 'json' then return comparison_hash end end @@ -123,7 +128,7 @@ def path_leads_to_list?(path, org_hash, conv_hash) conv_hash = hash_navigation(path, conv_hash) # if path == '/DIF/Related-URL' then byebug end - + bool = false if path.include?("[") && path.include?("]") bool = true elsif org_hash.is_a?(Hash) && conv_hash.is_a?(Hash) From be69b303276aa4885fc5a3de7ae5b545e74b2740 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Wed, 22 Jul 2020 16:38:37 -0400 Subject: [PATCH 16/38] MMT-2313: cleaned up inefficiencies, fixed bugs --- app/helpers/loss_report_helper.rb | 86 +++++++++++++------------------ 1 file changed, 37 insertions(+), 49 deletions(-) diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index 58bdb7b73..dfdd70cd9 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -17,12 +17,12 @@ def loss_report_output(concept_id, hide_items=true, disp='text') comparison_hash = Hash.new if disp == 'json' # comparison_hash['orig'] = hash_map(orig_h) if disp == 'json' - comparison_hash['orig'] = orig_h if disp == 'json' + # comparison_hash['orig'] = orig_h if disp == 'json' # comparison_hash['conv'] = conv_h if disp == 'json' - comparison_string += orig_xml if disp == 'text' + # comparison_string += orig_xml if disp == 'text' # p = '/DIF' - # comparison_string += path_leads_to_list?(p, orig_h, conv_h).to_s + "\n\n" if disp == 'text' + # comparison_string += path_leads_to_array?(p, orig_h, conv_h).to_s + "\n\n" if disp == 'text' comparison_hash['format'] = content_type if disp == 'json' comparison_string += (content_type + "\n\n") if disp == 'text' @@ -33,10 +33,10 @@ def loss_report_output(concept_id, hide_items=true, disp='text') path = node.parent.path.split('[')[0] # comparison_string += (path + "\n") if disp == 'text' - # needs to check for lists that pass the first if condition but should be evaluated by the elsif (ie. Related_URL) - # figure out why some elements are not evaluating true at the first if (ie. Extended_Metadata) + # need to check for lists in hash_map obj + # need to solve problem where noko cherry picks an item out of a list (giving no indication it is a list; ie. Extended_metadata, related_URL) - if path_leads_to_list?(path, orig_h, conv_h) && !ignored_paths.include?(path) # all lists + if path_leads_to_array?(path, orig_h, conv_h) && !ignored_paths.include?(path) # all lists ignored_paths << path array_comparison(path, orig_h, conv_h).each do |item| add_to_report(counter, 'c'+item[0], item[1], item[2], hide_items, disp, comparison_hash, comparison_string) @@ -47,12 +47,19 @@ def loss_report_output(concept_id, hide_items=true, disp='text') element = Hash.from_xml(element) hash_map(element).each do |item| - if path_leads_to_list?(path +'/'+ item['path'], orig_h, conv_h) && !ignored_paths.include?(path +'/'+ item['path']) # all lists - ignored_paths << path +'/'+ item['path'] - array_comparison(path +'/'+ item['path'], orig_h, conv_h).each do |item| - add_to_report(counter, 'cc'+item[0], item[1], item[2], hide_items, disp, comparison_hash, comparison_string) - counter += 1 + if path_leads_to_array?(path +'/'+ item['path'], orig_h, conv_h) && !ignored_paths.include?(path +'/'+ item['path']) # all lists + # hash_navigation(path+'/'+ item['path'], orig_h).is_a?(Array) ? arr_path = hash_navigation(path+'/'+ item['path'], orig_h, return_path=true) : arr_path = hash_navigation(path+'/'+ item['path'], conv_h, return_path=true) + arr_path = hash_navigation(path +'/'+ item['path'],orig_h,return_path=true) if hash_navigation(path +'/'+ item['path'], orig_h).is_a?(Array) + arr_path = hash_navigation(path +'/'+ item['path'],conv_h,return_path=true) if hash_navigation(path +'/'+ item['path'], conv_h).is_a?(Array) + + if !ignored_paths.include?(arr_path) + ignored_paths << arr_path + array_comparison(arr_path, orig_h, conv_h).each do |item| + add_to_report(counter, 'cc'+item[0], item[1], item[2], hide_items, disp, comparison_hash, comparison_string) + counter += 1 + end end + else add_to_report(counter, 'ct'+change, item['value'], path +'/'+ item['path'], hide_items, disp, comparison_hash, comparison_string) counter += 1 @@ -67,11 +74,11 @@ def loss_report_output(concept_id, hide_items=true, disp='text') end counter = 0 - comparison_string += "\n\n\n\n" if disp == 'text' - orig.diff(conv, {:added => true, :removed => true}) do |change,node| - add_to_report(counter, change, node.to_xml, node.parent.path, false, disp, comparison_hash, comparison_string) - counter += 1 - end + comparison_string += "\n\n\n\n#{JSON.pretty_generate(orig_h)}\n\n\n\n#{JSON.pretty_generate(conv_h)}" if disp == 'text' + # orig.diff(conv, {:added => true, :removed => true}) do |change,node| + # add_to_report(counter, change, node.to_xml, node.parent.path, false, disp, comparison_hash, comparison_string) + # counter += 1 + # end if disp == 'text' then return comparison_string elsif disp == 'json' then return comparison_hash end @@ -85,12 +92,6 @@ def add_to_report(counter, change, element, path, hide_items, disp, comparison_h return comparison_hash["#{counter}. #{change}: #{path}"] = element if disp == 'json' end - def change_path(path) - arr = path.split('/*') - arr[0] = '/DIF' - arr.join - end - def hash_map(hash) buckets = Array.new hash.each do |key,val| @@ -122,7 +123,7 @@ def prepare_collections(concept_id, umm_c_version) return original_collection_native_xml.body, translated_collection_native_xml.body, original_collection_native_hash, translated_collection_native_hash, content_type end - def path_leads_to_list?(path, org_hash, conv_hash) + def path_leads_to_array?(path, org_hash, conv_hash) # this method takes a path string (and the full original and converted hashes) and outputs true if the path string contains a list; else false org_hash = hash_navigation(path, org_hash) conv_hash = hash_navigation(path, conv_hash) @@ -147,17 +148,23 @@ def path_leads_to_list?(path, org_hash, conv_hash) bool end - def hash_navigation(path, hash) + def hash_navigation(path, hash, return_path=false) # Passed a path string and the hash being navigated. This method parses the path string and # returns the array/value at the end of the path + current_path = String.new path.split('/').each do |key| if hash.is_a?(Array) - return hash + return hash if return_path == false + return current_path if return_path == true elsif hash.key?(key) && hash.is_a?(Hash) + current_path += "/#{key}" hash = hash[key] + elsif !hash.key?(key) && key != '' + return path_exists = false end end - hash + return hash if return_path == false + return current_path if return_path == true end def array_comparison(path, original_hash, converted_hash) @@ -165,35 +172,16 @@ def array_comparison(path, original_hash, converted_hash) pre_translation_array = hash_navigation(path, original_hash) post_translation_array = hash_navigation(path, converted_hash) - # in the case that a one-item array is parsed as a regular key-value pair instead of an array, an Array wrapper is placed around key-val pair - # so that the following for loops can be executed without error - pre_translation_array.is_a?(Array) ? lost_items_arr = pre_translation_array.clone : lost_items_arr = Array.wrap(pre_translation_array) - pre_translation_array = Array.wrap(pre_translation_array) - post_translation_array.is_a?(Array) ? added_itmes_arr = post_translation_array.clone : added_itmes_arr = Array.wrap(post_translation_array) - post_translation_array = Array.wrap(post_translation_array) - - # as defined above, the lost_items_arr and added_itmes_arr are copies of pre_translation_array and post_translation_array, respectively. - # The *_arr values are edited during the comparison between the pre_translation_array and post_translation_array arrays - # and so the *_array arrays are used to maintain a full version of each array for indexing the items in the following lines. - - for conv_item in post_translation_array - for org_item in pre_translation_array - if org_item == conv_item - lost_items_arr.delete(org_item) - added_itmes_arr.delete(conv_item) - break - end - end - end + pre_translation_array == false ? pre_translation_array = Array.new : pre_translation_array = Array.wrap(pre_translation_array) + post_translation_array == false ? post_translation_array = Array.new : post_translation_array = Array.wrap(post_translation_array) output = Array.new - lost_items_arr.each do |item| + (pre_translation_array - post_translation_array).each do |item| path_with_index = path + "[#{pre_translation_array.index(item)}]" output << ['-', item, path_with_index] end - - added_itmes_arr.each do |item| + (post_translation_array - pre_translation_array).each do |item| path_with_index = path + "[#{post_translation_array.index(item)}]" output << ['+', item, path_with_index] end From 34bb5fe53b658deeda10b17fc58302e0742407cf Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Fri, 24 Jul 2020 14:15:21 -0400 Subject: [PATCH 17/38] MMT-2313: fixed bugs, improved accuracy --- app/helpers/loss_report_helper.rb | 214 +++++++++++++++---------- lib/tasks/compare_xml_collections.rake | 124 -------------- 2 files changed, 125 insertions(+), 213 deletions(-) delete mode 100644 lib/tasks/compare_xml_collections.rake diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index dfdd70cd9..d40f1aa55 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -4,7 +4,7 @@ def loss_report_output(concept_id, hide_items=true, disp='text') # depending on the input selection (json or text) a comparison string/hash is created and displayed in-browser orig_xml,conv_xml,orig_h,conv_h,content_type = prepare_collections(concept_id, '1.15.3') - if content_type.include?('dif10') + if content_type.include?('iso') || content_type.include?('dif') orig = Nokogiri::XML(orig_xml) { |config| config.strict.noblanks } .remove_namespaces! conv = Nokogiri::XML(conv_xml) { |config| config.strict.noblanks } .remove_namespaces! else @@ -12,94 +12,156 @@ def loss_report_output(concept_id, hide_items=true, disp='text') conv = Nokogiri::XML(conv_xml) { |config| config.strict.noblanks } end - ignored_paths = Array.new # This array is used to keep track of the paths that lead to arrays that have already been mapped - comparison_string = String.new if disp == 'text' - comparison_hash = Hash.new if disp == 'json' + #write files to test that all changes are being found with opendiff + dir = '/Users/ctrummer/Documents/devtesting' + o = Nokogiri::XML(orig_xml) { |config| config.strict.noblanks } .remove_namespaces! + c = Nokogiri::XML(conv_xml) { |config| config.strict.noblanks } .remove_namespaces! + File.write("#{dir}/o_#{concept_id}.xml", o.to_xml) + File.write("#{dir}/c_#{concept_id}.xml", c.to_xml) - # comparison_hash['orig'] = hash_map(orig_h) if disp == 'json' - # comparison_hash['orig'] = orig_h if disp == 'json' - # comparison_hash['conv'] = conv_h if disp == 'json' - # comparison_string += orig_xml if disp == 'text' + arr_paths = Array.new # This array is used to keep track of the paths that lead to arrays that have already been mapped + text_output = String.new if disp == 'text' + json_output = Hash.new if disp == 'json' - # p = '/DIF' - # comparison_string += path_leads_to_array?(p, orig_h, conv_h).to_s + "\n\n" if disp == 'text' + # json_output['orig'] = hash_map(orig_h) if disp == 'json' + # json_output['orig'] = orig_h if disp == 'json' + # json_output['conv'] = conv_h if disp == 'json' + # text_output += orig_xml if disp == 'text' - comparison_hash['format'] = content_type if disp == 'json' - comparison_string += (content_type + "\n\n") if disp == 'text' + json_output['format'] = content_type if disp == 'json' + text_output += (content_type + "\n\n") if disp == 'text' + + # text_output += top_level_arr_path('/Collection/OnlineResources/OnlineResource', orig_h, conv_h).to_s+"\n" - counter = 1 orig.diff(conv, {:added => true, :removed => true}) do |change,node| element = node.to_xml path = node.parent.path.split('[')[0] - # comparison_string += (path + "\n") if disp == 'text' + arr_path = top_level_arr_path(path, orig_h, conv_h) + + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + puts "---------------------------------------------------------------------------------" + puts "arr_path: #{arr_path} ... node.parent.path: #{node.parent.path} ... path: #{path}" + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + + if arr_path && path_not_checked?(arr_path, arr_paths) - # need to check for lists in hash_map obj - # need to solve problem where noko cherry picks an item out of a list (giving no indication it is a list; ie. Extended_metadata, related_URL) + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + puts "** path 1" + puts "ar path_not_checked?(arr_path,arr_paths): #{path_not_checked?(arr_path,arr_paths).to_s}" + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - if path_leads_to_array?(path, orig_h, conv_h) && !ignored_paths.include?(path) # all lists - ignored_paths << path - array_comparison(path, orig_h, conv_h).each do |item| - add_to_report(counter, 'c'+item[0], item[1], item[2], hide_items, disp, comparison_hash, comparison_string) - counter += 1 + arr_paths << arr_path + array_comparison(arr_path, orig_h, conv_h).each do |item| # all lists + add_to_report('ar'+item[0], item[1], item[2], hide_items, disp, json_output, text_output) end - elsif !ignored_paths.include?(path) # nokogiri - if is_xml?(node) #Possibly use the nokogiri #xml? method - element = Hash.from_xml(element) + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + puts "arr_paths: #{arr_paths}" + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + + elsif path_not_checked?(path, arr_paths) # nokogiri + + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + puts "** path 2" + puts "path_not_checked?(path,arr_paths): #{path_not_checked?(path,arr_paths).to_s}" + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + + if is_xml?(node) + element = Hash.from_xml(element) hash_map(element).each do |item| - if path_leads_to_array?(path +'/'+ item['path'], orig_h, conv_h) && !ignored_paths.include?(path +'/'+ item['path']) # all lists - # hash_navigation(path+'/'+ item['path'], orig_h).is_a?(Array) ? arr_path = hash_navigation(path+'/'+ item['path'], orig_h, return_path=true) : arr_path = hash_navigation(path+'/'+ item['path'], conv_h, return_path=true) - arr_path = hash_navigation(path +'/'+ item['path'],orig_h,return_path=true) if hash_navigation(path +'/'+ item['path'], orig_h).is_a?(Array) - arr_path = hash_navigation(path +'/'+ item['path'],conv_h,return_path=true) if hash_navigation(path +'/'+ item['path'], conv_h).is_a?(Array) + arr_path = top_level_arr_path("#{path}/#{item['path']}", orig_h, conv_h) + + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + puts "path_not_checked?('path/item['path']}, arr_paths): #{path_not_checked?("#{path}/#{item['path']}", arr_paths)}" + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - if !ignored_paths.include?(arr_path) - ignored_paths << arr_path + if arr_path && path_not_checked?("#{path}/#{item['path']}", arr_paths) # all list + if path_not_checked?(arr_path, arr_paths) + + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + puts "na path_not_checked?(arr_path, arr_paths): #{path_not_checked?(arr_path, arr_paths)}" + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + + arr_paths << arr_path array_comparison(arr_path, orig_h, conv_h).each do |item| - add_to_report(counter, 'cc'+item[0], item[1], item[2], hide_items, disp, comparison_hash, comparison_string) - counter += 1 + add_to_report('na'+item[0], item[1], item[2], hide_items, disp, json_output, text_output) end - end - else - add_to_report(counter, 'ct'+change, item['value'], path +'/'+ item['path'], hide_items, disp, comparison_hash, comparison_string) - counter += 1 + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + puts "arr_paths: #{arr_paths}" + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + + end + elsif path_not_checked?("#{path}/#{item['path']}", arr_paths) + add_to_report('hn'+change, item['value'], "#{path}/#{item['path']}", hide_items, disp, json_output, text_output) end + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + puts "arr_paths: #{arr_paths}" + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + end else - add_to_report(counter, change, element, path, hide_items, disp, comparison_hash, comparison_string) - counter += 1 + add_to_report('ng'+change, element, path, hide_items, disp, json_output, text_output) + + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + puts "arr_paths: #{arr_paths}" + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + end end end + if disp == 'text' then return text_output + elsif disp == 'json' then return json_output end + end - counter = 0 - comparison_string += "\n\n\n\n#{JSON.pretty_generate(orig_h)}\n\n\n\n#{JSON.pretty_generate(conv_h)}" if disp == 'text' - # orig.diff(conv, {:added => true, :removed => true}) do |change,node| - # add_to_report(counter, change, node.to_xml, node.parent.path, false, disp, comparison_hash, comparison_string) - # counter += 1 - # end + def path_not_checked?(arr_path, arr_paths) + arr_paths.each do |path| + if arr_path.include?(path) + return false + end + end + true + end + + def top_level_arr_path(path, orig_h, conv_h) + pre_translation_array, pre_translation_path = hash_navigation(path, orig_h) + post_translation_array, post_translation_path = hash_navigation(path, conv_h) + + return false if pre_translation_array == false && post_translation_array == false - if disp == 'text' then return comparison_string - elsif disp == 'json' then return comparison_hash end + return pre_translation_path if pre_translation_array.is_a?(Array) + return post_translation_path if post_translation_array.is_a?(Array) + + # the number of keys must be 1 because all arrays in echo10, dif10, and iso19115 are tagged similar to: + # contact and so all array-containing tags will be the plural + # of the array name. This clause serves to identify array-containing tags when their paths aren't properly + # displayed by nokogiri + if pre_translation_array.is_a?(Hash) && pre_translation_array.keys.length == 1 && pre_translation_array[pre_translation_array.keys[0]].is_a?(Array) + return pre_translation_path + "/#{pre_translation_array.keys[0]}" + elsif post_translation_array.is_a?(Hash) && post_translation_array.keys.length == 1 && post_translation_array[post_translation_array.keys[0]].is_a?(Array) + return post_translation_path + "/#{post_translation_array.keys[0]}" + end + + path_contains_array = false end - def add_to_report(counter, change, element, path, hide_items, disp, comparison_hash, comparison_string) + def add_to_report(change, element, path, hide_items, disp, json_output, text_output) + @counter ||= 0 and @counter += 1 + # this function serves to preclude complex nests from forming in loss_report_output the # following 'if' structure is intended to increase readability by eliminating nests - return comparison_string.concat("#{counter}.".ljust(4)+"#{change}: #{element}".ljust(60) + path + "\n") if hide_items == false && disp == 'text' - return comparison_string.concat("#{counter}.".ljust(4)+"#{change}: ".ljust(3) + path + "\n") if hide_items == true && disp == 'text' - return comparison_hash["#{counter}. #{change}: #{path}"] = element if disp == 'json' + return text_output.concat("#{@counter}.".ljust(4)+"#{change}: #{element}".ljust(60) + path + "\n") if hide_items == false && disp == 'text' + puts "#{@counter}.".ljust(4)+"#{change}: ".ljust(3) + path; return text_output.concat("#{@counter}.".ljust(4)+"#{change}: ".ljust(3) + path + "\n") if hide_items == true && disp == 'text' + return json_output["#{@counter}. #{change}: #{path}"] = element if disp == 'json' end def hash_map(hash) buckets = Array.new hash.each do |key,val| - if val.is_a? Hash - hash_map(val).each do |item| - item['path'] = key + '/' + item['path'] - buckets << item - end + if val.is_a? Hash then hash_map(val).each do |item| + item['path'] = key + '/' + item['path'] + buckets << item end else buckets << {'path'=> key, 'value'=> val} end @@ -123,59 +185,32 @@ def prepare_collections(concept_id, umm_c_version) return original_collection_native_xml.body, translated_collection_native_xml.body, original_collection_native_hash, translated_collection_native_hash, content_type end - def path_leads_to_array?(path, org_hash, conv_hash) - # this method takes a path string (and the full original and converted hashes) and outputs true if the path string contains a list; else false - org_hash = hash_navigation(path, org_hash) - conv_hash = hash_navigation(path, conv_hash) - - # if path == '/DIF/Related-URL' then byebug end - bool = false - if path.include?("[") && path.include?("]") - bool = true - elsif org_hash.is_a?(Hash) && conv_hash.is_a?(Hash) - # the number of keys must be 1 because all arrays in echo10, dif10, and iso19115 are tagged similar to: - # contact and so all array-containing tags will be the plural - # of the array name. This clause serves to identify array-containing tags when their paths aren't properly - # displayed by nokogiri - bool = true if org_hash.keys.length == 1 && org_hash[org_hash.keys[0]].is_a?(Array) - bool = true if conv_hash.keys.length == 1 && conv_hash[conv_hash.keys[0]].is_a?(Array) - elsif org_hash.is_a?(Array) || conv_hash.is_a?(Array) - bool = true - else - bool = false - end - # if bool == nil then bool = 'flag' end #THIS NEEDS TO BE EVALUATED - bool - end - - def hash_navigation(path, hash, return_path=false) + def hash_navigation(path, hash) # Passed a path string and the hash being navigated. This method parses the path string and # returns the array/value at the end of the path current_path = String.new path.split('/').each do |key| if hash.is_a?(Array) - return hash if return_path == false - return current_path if return_path == true + return hash, current_path elsif hash.key?(key) && hash.is_a?(Hash) current_path += "/#{key}" hash = hash[key] elsif !hash.key?(key) && key != '' - return path_exists = false + return path_exists = false, "#{current_path}/#{key}" end end - return hash if return_path == false - return current_path if return_path == true + return hash, current_path end def array_comparison(path, original_hash, converted_hash) - - pre_translation_array = hash_navigation(path, original_hash) - post_translation_array = hash_navigation(path, converted_hash) + pre_translation_array = hash_navigation(path, original_hash)[0] + post_translation_array = hash_navigation(path, converted_hash)[0] pre_translation_array == false ? pre_translation_array = Array.new : pre_translation_array = Array.wrap(pre_translation_array) post_translation_array == false ? post_translation_array = Array.new : post_translation_array = Array.wrap(post_translation_array) output = Array.new + (pre_translation_array - post_translation_array).each do |item| path_with_index = path + "[#{pre_translation_array.index(item)}]" output << ['-', item, path_with_index] @@ -187,4 +222,5 @@ def array_comparison(path, original_hash, converted_hash) end output end + end diff --git a/lib/tasks/compare_xml_collections.rake b/lib/tasks/compare_xml_collections.rake deleted file mode 100644 index 27ca858b3..000000000 --- a/lib/tasks/compare_xml_collections.rake +++ /dev/null @@ -1,124 +0,0 @@ -require 'libxml_to_hash' - -namespace :collection do - desc 'Translate a collection from native format to UMM JSON and back to native format' - task :loss, [:file, :format, :disp, :version] => :environment do |_task, args| - args.with_defaults(:version => '1.15.3') - args.with_defaults(:disp => 'show') - - abort 'FORMAT INVALID' unless args.format.eql? ('echo10' || 'dif10' || 'iso19115') - - filename = args.file.split('/')[-1] - puts "\nTranslating #{filename} to UMM JSON..." - - native_original_xml = File.read(args.file) - native_original_hash = Hash.from_xml(native_original_xml) - - #translate to UMM - umm_response = cmr_client.translate_collection(native_original_xml, "application/#{args.format}+xml", "application/vnd.nasa.cmr.umm+json;version=#{args.version}", skip_validation=true ) - umm_json = umm_response.body.to_json - umm_response.success? ? puts("\nsuccessful translation to UMM") : abort("\nUMM translation failure") - - # translate back to native - back_to_native = cmr_client.translate_collection(umm_json, "application/vnd.nasa.cmr.umm+json;version=#{args.version}", "application/#{args.format}+xml", skip_validation=true ) - native_converted_hash = Hash.from_xml(back_to_native.body) - native_converted_xml = back_to_native.body - back_to_native.success? ? puts("successful translation to native format \n\n") : abort("Native format translation failure \n\n") - - # nokogiri output - nokogiri_original = Nokogiri::XML(native_original_xml) { |config| config.strict.noblanks } - nokogiri_converted = Nokogiri::XML(native_converted_xml) { |config| config.strict.noblanks } - - ignored_paths = Array.new - - nokogiri_original.diff(nokogiri_converted, {:added => true, :removed => true}) do |change,node| - split_path = node.parent.path.split('[') - if node.parent.path.include?('[') && !ignored_paths.include?(split_path[0]) - ignored_paths << split_path[0] - array_comparison(split_path[0], native_original_hash, native_converted_hash).each do |item| - puts("#{item[0]}: #{item[1]}".ljust(60) + item[2]) if args.disp.eql? 'show' - puts("#{item[0]}: ". + item[2]) if args.disp.eql? 'hide' - end - elsif !ignored_paths.include?(split_path[0]) && !path_leads_to_list?(node.parent.path, native_original_hash, native_converted_hash) - puts("#{change}: #{node.to_xml}".ljust(60) + node.parent.path) if args.disp.eql? 'show' - puts("#{change}: ". + node.parent.path) if args.disp.eql? 'hide' - end - end - end - - def path_leads_to_list?(path, org_hash, conv_hash) - # this method takes a path string (and the full original and converted hashes) and outputs true if the path string contains a list; else false - org_hash_path = hash_navigation(path, org_hash) - conv_hash_path = hash_navigation(path, conv_hash) - - if path.include?("[") && path.include?("]") - bool = true - elsif org_hash_path.is_a?(Hash) && conv_hash_path.is_a?(Hash) - # the number of keys must be 1 because all arrays in echo10, dif10, and iso19115 are tagged similar to: - # contact and so all array-containing tags will be the plural - # of the array name. This clause serves to idenitfy array-containing tags when their paths aren't properly - # displayed by nokogiri - bool = true if org_hash_path.keys.length == 1 && org_hash_path[org_hash_path.keys[0]].is_a?(Array) - bool = true if conv_hash_path.keys.length == 1 && conv_hash_path[conv_hash_path.keys[0]].is_a?(Array) - elsif org_hash_path.is_a?(Array) || conv_hash_path.is_a?(Array) - bool = true - else - bool = false - end - bool - end - - def hash_navigation(dir, hash) - # Passed a path string and the hash being navigated. This method parses the path string and - # returns the hash at the end of the path - dir = dir.split '/' - dir.each do |key| - if !key.empty? && hash.is_a?(Hash) - hash = hash[key] - elsif hash.is_a? Array - return hash - end - end - hash - end - - def array_comparison(path, original_hash, converted_hash) - - org_array = hash_navigation(path, original_hash) - conv_array = hash_navigation(path, converted_hash) - - org_array.is_a?(Array) ? org_arr = org_array.clone : org_arr = Array.wrap(org_array) - org_array = Array.wrap(org_array) unless org_array.is_a?(Array) - conv_array.is_a?(Array) ? conv_arr = conv_array.clone : conv_arr = Array.wrap(conv_array) - conv_array = Array.wrap(conv_array) unless conv_array.is_a?(Array) - - # org_arr and conv_arr are copies of org_array and conv_array, respectively. - # The *_arr values are edited during the comparison between the org_array and conv_array arrays - # and so the *_array arrays are used to maintained a full version of each array for indexing the items in the following lines. - - for conv_item in conv_array - for org_item in org_array - if org_item.eql? conv_item - org_arr.delete(org_item) - conv_arr.delete(conv_item) - break - end - end - end - - output = Array.new - org_arr.each do |item| - path_with_index = path + "[#{org_array.index(item)}]" - loss_item = ['-', item, path_with_index] - output << loss_item - end - - - conv_arr.each do |item| - path_with_index = path + "[#{conv_array.index(item)}]" - loss_item = ['+', item, path_with_index] - output << loss_item - end - output - end -end From a267e4cc6aefd79324aaa06768a04d22a6f0b93d Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Mon, 27 Jul 2020 09:14:56 -0400 Subject: [PATCH 18/38] MMT-2313: added some test code to be removed later --- app/helpers/loss_report_helper.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index d40f1aa55..bea6def45 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -14,10 +14,8 @@ def loss_report_output(concept_id, hide_items=true, disp='text') #write files to test that all changes are being found with opendiff dir = '/Users/ctrummer/Documents/devtesting' - o = Nokogiri::XML(orig_xml) { |config| config.strict.noblanks } .remove_namespaces! - c = Nokogiri::XML(conv_xml) { |config| config.strict.noblanks } .remove_namespaces! - File.write("#{dir}/o_#{concept_id}.xml", o.to_xml) - File.write("#{dir}/c_#{concept_id}.xml", c.to_xml) + File.write("#{dir}/o_#{concept_id}.xml", orig.to_xml) + File.write("#{dir}/c_#{concept_id}.xml", conv.to_xml) arr_paths = Array.new # This array is used to keep track of the paths that lead to arrays that have already been mapped text_output = String.new if disp == 'text' @@ -185,6 +183,7 @@ def prepare_collections(concept_id, umm_c_version) return original_collection_native_xml.body, translated_collection_native_xml.body, original_collection_native_hash, translated_collection_native_hash, content_type end + def hash_navigation(path, hash) # Passed a path string and the hash being navigated. This method parses the path string and # returns the array/value at the end of the path @@ -210,14 +209,17 @@ def array_comparison(path, original_hash, converted_hash) post_translation_array == false ? post_translation_array = Array.new : post_translation_array = Array.wrap(post_translation_array) output = Array.new - (pre_translation_array - post_translation_array).each do |item| path_with_index = path + "[#{pre_translation_array.index(item)}]" + # the following line is used to eliminate indexing confusion when there is more than one occurrence of a particular item in an array + pre_translation_array[pre_translation_array.index(item)] = item.to_s + 'item indexed' output << ['-', item, path_with_index] end (post_translation_array - pre_translation_array).each do |item| path_with_index = path + "[#{post_translation_array.index(item)}]" + # the following line is used to eliminate indexing confusion when there is more than one occurrence of a particular item in an array + post_translation_array[post_translation_array.index(item)] = item.to_s + 'item indexed' output << ['+', item, path_with_index] end output From 937342dea65a6b22f315052cc0e613ae2d3fa513 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Tue, 28 Jul 2020 09:14:12 -0400 Subject: [PATCH 19/38] MMT-2313: Added some exception handling --- app/helpers/loss_report_helper.rb | 43 +++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index bea6def45..e28429c75 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -2,7 +2,14 @@ module LossReportHelper def loss_report_output(concept_id, hide_items=true, disp='text') # depending on the input selection (json or text) a comparison string/hash is created and displayed in-browser - orig_xml,conv_xml,orig_h,conv_h,content_type = prepare_collections(concept_id, '1.15.3') + + # prepare_collections returns false when the cmr_client endpoints are unsuccessfully executed + if (collections = prepare_collections(concept_id, '1.15.3')) + orig_xml,conv_xml,orig_h,conv_h,content_type = collections + else + return "Failure to get_concept or translate_collection" if disp == 'text' + return {"error"=>"Failure to get_concept or translate_collection"} if disp == 'json' + end if content_type.include?('iso') || content_type.include?('dif') orig = Nokogiri::XML(orig_xml) { |config| config.strict.noblanks } .remove_namespaces! @@ -29,8 +36,6 @@ def loss_report_output(concept_id, hide_items=true, disp='text') json_output['format'] = content_type if disp == 'json' text_output += (content_type + "\n\n") if disp == 'text' - # text_output += top_level_arr_path('/Collection/OnlineResources/OnlineResource', orig_h, conv_h).to_s+"\n" - orig.diff(conv, {:added => true, :removed => true}) do |change,node| element = node.to_xml path = node.parent.path.split('[')[0] @@ -50,7 +55,7 @@ def loss_report_output(concept_id, hide_items=true, disp='text') arr_paths << arr_path array_comparison(arr_path, orig_h, conv_h).each do |item| # all lists - add_to_report('ar'+item[0], item[1], item[2], hide_items, disp, json_output, text_output) + add_to_report(item[0], item[1], item[2], hide_items, disp, json_output, text_output) end # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- @@ -82,7 +87,7 @@ def loss_report_output(concept_id, hide_items=true, disp='text') arr_paths << arr_path array_comparison(arr_path, orig_h, conv_h).each do |item| - add_to_report('na'+item[0], item[1], item[2], hide_items, disp, json_output, text_output) + add_to_report(item[0], item[1], item[2], hide_items, disp, json_output, text_output) end # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- @@ -91,7 +96,7 @@ def loss_report_output(concept_id, hide_items=true, disp='text') end elsif path_not_checked?("#{path}/#{item['path']}", arr_paths) - add_to_report('hn'+change, item['value'], "#{path}/#{item['path']}", hide_items, disp, json_output, text_output) + add_to_report(change, item['value'], "#{path}/#{item['path']}", hide_items, disp, json_output, text_output) end # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- @@ -99,8 +104,10 @@ def loss_report_output(concept_id, hide_items=true, disp='text') # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- end + elsif (attr,val = is_attribute?(node)) + add_to_report(change, val, "#{path}/#{attr}" , hide_items, disp, json_output, text_output) else - add_to_report('ng'+change, element, path, hide_items, disp, json_output, text_output) + add_to_report(change, element, path, hide_items, disp, json_output, text_output) # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- puts "arr_paths: #{arr_paths}" @@ -109,8 +116,18 @@ def loss_report_output(concept_id, hide_items=true, disp='text') end end end - if disp == 'text' then return text_output - elsif disp == 'json' then return json_output end + return text_output if disp == 'text' + return json_output if disp == 'json' + end + + def is_attribute?(node) + if node.to_xml.include?('=') && !node.to_xml.include?(' = ') + attr_val = Array.new + node.to_xml.split('=').each {|item| attr_val << item.strip.delete('\\"')} + attr_val + else + false + end end def path_not_checked?(arr_path, arr_paths) @@ -146,11 +163,12 @@ def top_level_arr_path(path, orig_h, conv_h) def add_to_report(change, element, path, hide_items, disp, json_output, text_output) @counter ||= 0 and @counter += 1 - # this function serves to preclude complex nests from forming in loss_report_output the # following 'if' structure is intended to increase readability by eliminating nests return text_output.concat("#{@counter}.".ljust(4)+"#{change}: #{element}".ljust(60) + path + "\n") if hide_items == false && disp == 'text' + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- puts "#{@counter}.".ljust(4)+"#{change}: ".ljust(3) + path; return text_output.concat("#{@counter}.".ljust(4)+"#{change}: ".ljust(3) + path + "\n") if hide_items == true && disp == 'text' + # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- return json_output["#{@counter}. #{change}: #{path}"] = element if disp == 'json' end @@ -175,10 +193,13 @@ def is_xml?(node) def prepare_collections(concept_id, umm_c_version) # TODO: need to add exception handling for get_concept, translate_collection original_collection_native_xml = cmr_client.get_concept(concept_id,token, {}) + return false if !original_collection_native_xml.success? content_type = original_collection_native_xml.headers.fetch('content-type').split(';')[0] original_collection_native_hash = Hash.from_xml(original_collection_native_xml.body) translated_collection_umm_json = cmr_client.translate_collection(original_collection_native_xml.body, content_type, "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", skip_validation=true) - translated_collection_native_xml = cmr_client.translate_collection(translated_collection_umm_json.body.to_json, "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", content_type, skip_validation=true) + return false if !translated_collection_umm_json.success? + translated_collection_native_xml = cmr_client.translate_collection(JSON.pretty_generate(translated_collection_umm_json.body), "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", content_type, skip_validation=true) + return false if !translated_collection_native_xml.success? translated_collection_native_hash = Hash.from_xml(translated_collection_native_xml.body) return original_collection_native_xml.body, translated_collection_native_xml.body, original_collection_native_hash, translated_collection_native_hash, content_type end From fa56d22dfed8cffad2b282f2f3447dbf623d9f6d Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Wed, 29 Jul 2020 14:21:29 -0400 Subject: [PATCH 20/38] MMT-2313: created specs and stored some sample reports --- app/controllers/collections_controller.rb | 2 +- app/helpers/loss_report_helper.rb | 106 +-- config/routes.rb | 2 +- spec/factories/loss_report_factory_data.rb | 831 ++++++++++++++++++ spec/features/collections/loss_report_spec.rb | 41 + spec/helpers/loss_report_helper_spec.rb | 49 ++ 6 files changed, 957 insertions(+), 74 deletions(-) create mode 100644 spec/factories/loss_report_factory_data.rb create mode 100644 spec/features/collections/loss_report_spec.rb create mode 100644 spec/helpers/loss_report_helper_spec.rb diff --git a/app/controllers/collections_controller.rb b/app/controllers/collections_controller.rb index e148bed83..78e9cfaf5 100644 --- a/app/controllers/collections_controller.rb +++ b/app/controllers/collections_controller.rb @@ -122,7 +122,7 @@ def loss_report # This method is needed to reference the appropriate helper and view for the lossiness report concept_id = params[:id] respond_to do |format| - format.text {render plain: loss_report_output(concept_id, hide_items=true, disp='text') } + format.text { render plain: loss_report_output(concept_id, hide_items=true, disp='text') } format.json { render json: JSON.pretty_generate(loss_report_output(concept_id, hide_items=false, disp='json')) } end end diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index e28429c75..e17a00e80 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -1,5 +1,17 @@ module LossReportHelper + def cmr_client + Cmr::Client.client_for_environment(Rails.configuration.cmr_env, Rails.configuration.services) + end + + def token + if session[:login_method] == 'launchpad' + session[:launchpad_cookie] + elsif session[:login_method] == 'urs' + session[:access_token] + end + end + def loss_report_output(concept_id, hide_items=true, disp='text') # depending on the input selection (json or text) a comparison string/hash is created and displayed in-browser @@ -7,7 +19,7 @@ def loss_report_output(concept_id, hide_items=true, disp='text') if (collections = prepare_collections(concept_id, '1.15.3')) orig_xml,conv_xml,orig_h,conv_h,content_type = collections else - return "Failure to get_concept or translate_collection" if disp == 'text' + return 'Failure to get_concept or translate_collection' if disp == 'text' return {"error"=>"Failure to get_concept or translate_collection"} if disp == 'json' end @@ -41,78 +53,27 @@ def loss_report_output(concept_id, hide_items=true, disp='text') path = node.parent.path.split('[')[0] arr_path = top_level_arr_path(path, orig_h, conv_h) - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - puts "---------------------------------------------------------------------------------" - puts "arr_path: #{arr_path} ... node.parent.path: #{node.parent.path} ... path: #{path}" - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - if arr_path && path_not_checked?(arr_path, arr_paths) - - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - puts "** path 1" - puts "ar path_not_checked?(arr_path,arr_paths): #{path_not_checked?(arr_path,arr_paths).to_s}" - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - arr_paths << arr_path - array_comparison(arr_path, orig_h, conv_h).each do |item| # all lists - add_to_report(item[0], item[1], item[2], hide_items, disp, json_output, text_output) - end - - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - puts "arr_paths: #{arr_paths}" - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - + array_comparison(arr_path, orig_h, conv_h).each { |item| add_to_report(item[0], item[1], item[2], hide_items, disp, json_output, text_output) } elsif path_not_checked?(path, arr_paths) # nokogiri - - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - puts "** path 2" - puts "path_not_checked?(path,arr_paths): #{path_not_checked?(path,arr_paths).to_s}" - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - if is_xml?(node) element = Hash.from_xml(element) hash_map(element).each do |item| arr_path = top_level_arr_path("#{path}/#{item['path']}", orig_h, conv_h) - - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - puts "path_not_checked?('path/item['path']}, arr_paths): #{path_not_checked?("#{path}/#{item['path']}", arr_paths)}" - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - if arr_path && path_not_checked?("#{path}/#{item['path']}", arr_paths) # all list if path_not_checked?(arr_path, arr_paths) - - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - puts "na path_not_checked?(arr_path, arr_paths): #{path_not_checked?(arr_path, arr_paths)}" - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - arr_paths << arr_path - array_comparison(arr_path, orig_h, conv_h).each do |item| - add_to_report(item[0], item[1], item[2], hide_items, disp, json_output, text_output) - end - - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - puts "arr_paths: #{arr_paths}" - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - + array_comparison(arr_path, orig_h, conv_h).each { |item| add_to_report(item[0], item[1], item[2], hide_items, disp, json_output, text_output) } end elsif path_not_checked?("#{path}/#{item['path']}", arr_paths) add_to_report(change, item['value'], "#{path}/#{item['path']}", hide_items, disp, json_output, text_output) end - - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - puts "arr_paths: #{arr_paths}" - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - end elsif (attr,val = is_attribute?(node)) add_to_report(change, val, "#{path}/#{attr}" , hide_items, disp, json_output, text_output) else add_to_report(change, element, path, hide_items, disp, json_output, text_output) - - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - puts "arr_paths: #{arr_paths}" - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - end end end @@ -120,7 +81,15 @@ def loss_report_output(concept_id, hide_items=true, disp='text') return json_output if disp == 'json' end + def is_xml?(node) + # checks if the node being passed is xml + # may be beneficial to add more checks + node.to_xml.include?('<' && '') ? true : false + end + def is_attribute?(node) + # this method checks if the node being passed is an attribute change; + # TODO: it may be beneficial to add more conditions to improve accuracy if node.to_xml.include?('=') && !node.to_xml.include?(' = ') attr_val = Array.new node.to_xml.split('=').each {|item| attr_val << item.strip.delete('\\"')} @@ -131,20 +100,19 @@ def is_attribute?(node) end def path_not_checked?(arr_path, arr_paths) - arr_paths.each do |path| - if arr_path.include?(path) - return false - end - end + # this method checks the arr_paths array to see if the path being added to + # the report has already been previously evaluated and added + arr_paths.each { |path| return false if arr_path.include?(path) } true end def top_level_arr_path(path, orig_h, conv_h) + # if an array is passed that passes through an array ie. /Contacts/Contact[0]/Role/Name + # this method would return /Contacts/Contact because Contact is the outermost array (or false if the path doesn't contain an array) pre_translation_array, pre_translation_path = hash_navigation(path, orig_h) post_translation_array, post_translation_path = hash_navigation(path, conv_h) return false if pre_translation_array == false && post_translation_array == false - return pre_translation_path if pre_translation_array.is_a?(Array) return post_translation_path if post_translation_array.is_a?(Array) @@ -153,11 +121,10 @@ def top_level_arr_path(path, orig_h, conv_h) # of the array name. This clause serves to identify array-containing tags when their paths aren't properly # displayed by nokogiri if pre_translation_array.is_a?(Hash) && pre_translation_array.keys.length == 1 && pre_translation_array[pre_translation_array.keys[0]].is_a?(Array) - return pre_translation_path + "/#{pre_translation_array.keys[0]}" + return "#{pre_translation_path}/#{pre_translation_array.keys[0]}" elsif post_translation_array.is_a?(Hash) && post_translation_array.keys.length == 1 && post_translation_array[post_translation_array.keys[0]].is_a?(Array) - return post_translation_path + "/#{post_translation_array.keys[0]}" + return "#{post_translation_path}/#{post_translation_array.keys[0]}" end - path_contains_array = false end @@ -166,9 +133,7 @@ def add_to_report(change, element, path, hide_items, disp, json_output, text_out # this function serves to preclude complex nests from forming in loss_report_output the # following 'if' structure is intended to increase readability by eliminating nests return text_output.concat("#{@counter}.".ljust(4)+"#{change}: #{element}".ljust(60) + path + "\n") if hide_items == false && disp == 'text' - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- - puts "#{@counter}.".ljust(4)+"#{change}: ".ljust(3) + path; return text_output.concat("#{@counter}.".ljust(4)+"#{change}: ".ljust(3) + path + "\n") if hide_items == true && disp == 'text' - # FOR TROUBLESHOOTING ------------------------------------------------------------------------------------- + return text_output.concat("#{@counter}.".ljust(4)+"#{change}: ".ljust(3) + path + "\n") if hide_items == true && disp == 'text' return json_output["#{@counter}. #{change}: #{path}"] = element if disp == 'json' end @@ -185,21 +150,18 @@ def hash_map(hash) buckets end - def is_xml?(node) - if node.to_xml.include?('<' && '') then return true - else return false end - end - def prepare_collections(concept_id, umm_c_version) - # TODO: need to add exception handling for get_concept, translate_collection original_collection_native_xml = cmr_client.get_concept(concept_id,token, {}) return false if !original_collection_native_xml.success? + content_type = original_collection_native_xml.headers.fetch('content-type').split(';')[0] original_collection_native_hash = Hash.from_xml(original_collection_native_xml.body) translated_collection_umm_json = cmr_client.translate_collection(original_collection_native_xml.body, content_type, "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", skip_validation=true) return false if !translated_collection_umm_json.success? + translated_collection_native_xml = cmr_client.translate_collection(JSON.pretty_generate(translated_collection_umm_json.body), "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", content_type, skip_validation=true) return false if !translated_collection_native_xml.success? + translated_collection_native_hash = Hash.from_xml(translated_collection_native_xml.body) return original_collection_native_xml.body, translated_collection_native_xml.body, original_collection_native_hash, translated_collection_native_hash, content_type end diff --git a/config/routes.rb b/config/routes.rb index 74e5b20ac..b5e17c668 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -69,7 +69,7 @@ get '/collections/:id/download_xml/:format(/:revision_id)' => 'collections#download_xml', as: 'download_collection_xml' get '/collections/:id/create_delete_proposal' => 'collections#create_delete_proposal', as: 'create_delete_proposal_collection' get '/collections/:id/create_update_proposal' => 'collections#create_update_proposal', as: 'create_update_proposal_collection' - get '/collections/:id/loss' => 'collections#loss_report' + get '/collections/:id/loss' => 'collections#loss_report', as: 'loss_report_collections' resource :variable_generation_processes_search, only: [:new] diff --git a/spec/factories/loss_report_factory_data.rb b/spec/factories/loss_report_factory_data.rb new file mode 100644 index 000000000..517da5094 --- /dev/null +++ b/spec/factories/loss_report_factory_data.rb @@ -0,0 +1,831 @@ + +def dif_id + 'C1200000031-SEDAC' +end + +def iso_id + 'C1200000089-LARC' +end + +def echo_id + 'C1200000040-SEDAC' +end + +def iso_json_report + { + "format" => "application/iso:smap+xml", + "1. -: /DS_Series/schemaLocation" => "http://www.isotc211.org/2005/gmi http://cdn.earthdata.nasa.gov/iso/schema/1.0/ISO19115-2_EOS.xsd", + "2. -: /DS_Series/seriesMetadata/MI_Metadata/fileIdentifier/FileName" => "L4_SM_aup", + "3. -: /DS_Series/seriesMetadata/MI_Metadata/characterSet/MD_CharacterSetCode" => "utf8", + "4. -: /DS_Series/seriesMetadata/MI_Metadata/hierarchyLevel/MD_ScopeCode" => "series", + "5. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/attributeDescription" => nil, + "6. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/contentType" => nil, + "7. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/code/CharacterString" => "Not provided", + "8. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/codeSpace/CharacterString" => "gov.nasa.esdis.umm.processinglevelid", + "9. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardName/CharacterString" => "ISO 19115-2 Geographic information - Metadata - Part 2: Extensions for imagery and gridded data", + "10. +: /DS_Series/seriesMetadata/MI_Metadata/dataQualityInfo/DQ_DataQuality/scope/DQ_Scope/level/MD_ScopeCode" => "series", + "11. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/id" => "dba588298-ef6b-4e0f-9092-d1bfe87001ea", + "12. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/code/CharacterString" => "Not provided", + "13. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/codeSpace/CharacterString" => "gov.nasa.esdis.umm.platformshortname", + "14. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/description/CharacterString" => "Not provided", + "15. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/instrument/nilReason" => "inapplicable", + "16. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardVersion/CharacterString" => "ISO 19115-2:2009-02-15", + "17. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update" + }, "date" => { + "CI_Date" => { + "date" => { + "Date" => "2016-04-29" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + }, "edition" => { + "CharacterString" => "Vv2010" + }, "identifier" => [{ + "MD_Identifier" => { + "code" => { + "CharacterString" => "SPL4SMAU" + }, "codeSpace" => { + "CharacterString" => "http://gmao.gsfc.nasa.gov" + }, "description" => { + "CharacterString" => "The ECS Short Name" + } + } + }, { + "MD_Identifier" => { + "code" => { + "CharacterString" => "002" + }, "codeSpace" => { + "CharacterString" => "gov.nasa.esdis" + }, "description" => { + "CharacterString" => "The ECS Version ID" + } + } + }, { + "MD_Identifier" => { + "code" => { + "Anchor" => "doi:10.5067/JJY2V0GJNFRZ" + }, "codeSpace" => { + "CharacterString" => "gov.nasa.esdis" + }, "description" => { + "CharacterString" => "A Digital Object Identifier (DOI) that provides a persistent interoperable means to locate the SMAP Level 4 Radar data product." + } + } + }], "citedResponsibleParty" => [{ + "CI_ResponsibleParty" => { + "organisationName" => { + "CharacterString" => "National Aeronautics and Space Administration" + }, "role" => { + "CI_RoleCode" => "resourceProvider" + } + } + }, { + "CI_ResponsibleParty" => { + "organisationName" => { + "CharacterString" => "Global Modeling and Assimilation Office" + }, "role" => { + "CI_RoleCode" => "originator" + } + } + }], "presentationForm" => { + "CI_PresentationFormCode" => "documentDigital" + }, "series" => { + "CI_Series" => { + "name" => { + "CharacterString" => "L4_SM" + } + } + }, "otherCitationDetails" => { + "CharacterString" => "The first Validated Release of the SMAP Level 4 Science Processing Software." + } + } + }, "abstract" => { + "CharacterString" => "The SMAP L4_SM data product provides global, 3-hourly surface and root zone soil moisture at 9 km resolution. The L4_SM data product consists of three Collections: geophysical, analysis update and land-model-constants." + }, "purpose" => { + "CharacterString" => "The SMAP L4_SM data product provides spatially and temporally complete surface and root zone soil moisture information for science and applications users." + }, "credit" => { + "CharacterString" => "The software that generates the L4_SM data product and the data system that automates its production were designed and implemented at the NASA Global Modeling and Assimilation Office, Goddard Space Flight Center, Greenbelt, Maryland, USA." + }, "status" => { + "MD_ProgressCode" => "onGoing" + }, "pointOfContact" => { + "CI_ResponsibleParty" => { + "organisationName" => { + "CharacterString" => "PVC" + }, "role" => { + "CI_RoleCode" => "distributor" + } + } + }, "resourceMaintenance" => { + "MD_MaintenanceInformation" => { + "maintenanceAndUpdateFrequency" => { + "MD_MaintenanceFrequencyCode" => "As Needed" + }, "dateOfNextUpdate" => { + "Date" => "2016-11-01" + }, "updateScope" => { + "MD_ScopeCode" => "series" + } + } + }, "resourceFormat" => { + "MD_Format" => { + "name" => { + "CharacterString" => "HDF5" + }, "version" => { + "CharacterString" => "Version 1.8.9" + } + } + }, "descriptiveKeywords" => [{ + "MD_Keywords" => { + "keyword" => [{ + "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT" + }, { + "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > SURFACE SOIL MOISTURE" + }, { + "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > ROOT ZONE SOIL MOISTURE" + }], "type" => { + "MD_KeywordTypeCode" => "theme" + }, "thesaurusName" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "NASA/GCMD Earth Science Keywords" + }, "date" => { + "gco:nilReason" => "missing" + } + } + } + } + }, { + "MD_Keywords" => { + "keyword" => { + "CharacterString" => "Earth Remote Sensing Instruments > Active Remote Sensing > NONE > SMAP L-BAND RADAR > SMAP L-Band Radar" + }, "type" => { + "MD_KeywordTypeCode" => "theme" + }, "thesaurusName" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "NASA/GCMD Earth Science Keywords" + }, "date" => { + "gco:nilReason" => "missing" + } + } + } + } + }, { + "MD_Keywords" => { + "keyword" => { + "CharacterString" => "Earth Observation Satellites > NASA Decadal Survey > SMAP > Soil Moisture Active and Passive Observatory" + }, "type" => { + "MD_KeywordTypeCode" => "theme" + }, "thesaurusName" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "NASA/GCMD Earth Science Keywords" + }, "date" => { + "gco:nilReason" => "missing" + } + } + } + } + }, { + "MD_Keywords" => { + "keyword" => { + "CharacterString" => "GEOGRAPHIC REGION > GLOBAL" + }, "type" => { + "MD_KeywordTypeCode" => "theme" + }, "thesaurusName" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "NASA/GCMD Earth Science Keywords" + }, "date" => { + "gco:nilReason" => "missing" + } + } + } + } + }], "aggregationInfo" => { + "MD_AggregateInformation" => { + "aggregateDataSetIdentifier" => { + "MD_Identifier" => { + "code" => { + "CharacterString" => "SMAP" + } + } + }, "associationType" => { + "DS_AssociationTypeCode" => "largerWorkCitation" + }, "initiativeType" => { + "DS_InitiativeTypeCode" => "mission" + } + } + }, "language" => { + "CharacterString" => "eng" + }, "characterSet" => { + "MD_CharacterSetCode" => "utf8" + }, "topicCategory" => { + "MD_TopicCategoryCode" => "geoscientificInformation" + }, "environmentDescription" => { + "CharacterString" => "Data product generated by the SMAP mission in HDF5 format with metadata that conforms to the ISO 19115 model." + }, "extent" => { + "EX_Extent" => { + "description" => { + "CharacterString" => "Global land excluding inland water and permanent ice." + }, "geographicElement" => { + "EX_GeographicBoundingBox" => { + "extentTypeCode" => { + "Boolean" => "1" + }, "westBoundLongitude" => { + "Decimal" => "-180" + }, "eastBoundLongitude" => { + "Decimal" => "180" + }, "southBoundLatitude" => { + "Decimal" => "-85.04456" + }, "northBoundLatitude" => { + "Decimal" => "85.04456" + } + } + }, "temporalElement" => { + "EX_TemporalExtent" => { + "extent" => { + "TimePeriod" => { + "gml:id" => "swathTemporalExtent", "beginPosition" => "2015-03-31T01:30:00.000Z", "endPosition" => "2021-01-01T01:29:59.999Z" + } + } + } + } + } + } + } + }, + "18. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "Soil Moisture Active Passive Mission Level 4 Surface and Root Zone Soil Moisture (L4_SM) Product Specification Document" + }, "date" => { + "CI_Date" => { + "date" => { + "Date" => "2015-10-31" + }, "dateType" => { + "CI_DateTypeCode" => "publication" + } + } + }, "edition" => { + "CharacterString" => "1.4" + }, "identifier" => { + "MD_Identifier" => { + "code" => { + "CharacterString" => "L4_SM" + }, "codeSpace" => { + "CharacterString" => "http://gmao.gsfc.nasa.gov" + }, "description" => { + "CharacterString" => "A short name used by the Soil Moisture Active Passive (SMAP) mission to identify the Level 4 Radar product." + } + } + }, "presentationForm" => { + "CI_PresentationFormCode" => "documentDigital" + }, "series" => { + "CI_Series" => { + "name" => { + "CharacterString" => "L4_SM" + } + } + } + } + }, "abstract" => { + "CharacterString" => "The SMAP L4_SM data product provides global, 3-hourly surface and root zone soil moisture at 9 km resolution. The L4_SM data product consists of three Collections: geophysical, analysis update and land-model-constants." + }, "language" => { + "CharacterString" => "eng" + } + } + }, + "19. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[2]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "DataSetId" + }, "date" => { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-12T11:50:19.050Z" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + } + } + }, "abstract" => { + "CharacterString" => "DataSetId" + }, "aggregationInfo" => { + "MD_AggregateInformation" => { + "aggregateDataSetIdentifier" => { + "MD_Identifier" => { + "code" => { + "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update V002" + } + } + }, "associationType" => nil + } + }, "language" => { + "CharacterString" => "eng" + } + } + }, + "20. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[3]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "InsertTime" + }, "date" => { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-08T09:16:24.835Z" + }, "dateType" => { + "CI_DateTypeCode" => "creation" + } + } + } + } + }, "abstract" => { + "CharacterString" => "InsertTime" + }, "purpose" => { + "CharacterString" => "InsertTime" + }, "language" => { + "CharacterString" => "eng" + } + } + }, + "21. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[4]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "UpdateTime" + }, "date" => { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-12T11:50:19.050Z" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + } + } + }, "abstract" => { + "CharacterString" => "UpdateTime" + }, "purpose" => { + "CharacterString" => "UpdateTime" + }, "language" => { + "CharacterString" => "eng" + } + } + }, + "22. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[5]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "DIFID" + }, "date" => { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-12T11:50:19.050Z" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + }, "identifier" => { + "MD_Identifier" => { + "code" => { + "CharacterString" => "SPL4SMAU" + } + } + } + } + }, "abstract" => { + "CharacterString" => "DIFID" + }, "purpose" => { + "CharacterString" => "DIFID" + }, "language" => { + "CharacterString" => "eng" + } + } + }, + "23. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update" + }, "date" => [{ + "CI_Date" => { + "date" => { + "DateTime" => "2016-04-29T00:00:00.000Z" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + }, { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-12T11:50:19.050Z" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + }, { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-08T09:16:24.835Z" + }, "dateType" => { + "CI_DateTypeCode" => "creation" + } + } + }], "edition" => { + "CharacterString" => "Vv2010" + }, "identifier" => [{ + "MD_Identifier" => { + "code" => { + "CharacterString" => "SPL4SMAU" + }, "description" => { + "CharacterString" => "The ECS Short Name" + } + } + }, { + "MD_Identifier" => { + "code" => { + "CharacterString" => "002" + }, "description" => { + "CharacterString" => "The ECS Version ID" + } + } + }, { + "MD_Identifier" => { + "code" => { + "CharacterString" => "doi:10.5067/JJY2V0GJNFRZ" + }, "codeSpace" => { + "CharacterString" => "gov.nasa.esdis.umm.doi" + }, "description" => { + "CharacterString" => "DOI" + } + } + }], "presentationForm" => { + "CI_PresentationFormCode" => "documentDigital" + }, "series" => { + "CI_Series" => { + "name" => { + "CharacterString" => "L4_SM" + } + } + }, "otherCitationDetails" => { + "CharacterString" => "The first Validated Release of the SMAP Level 4 Science Processing Software." + } + } + }, "abstract" => { + "CharacterString" => "The SMAP L4_SM data product provides global, 3-hourly surface and root zone soil moisture at 9 km resolution. The L4_SM data product consists of three Collections: geophysical, analysis update and land-model-constants." + }, "purpose" => { + "gco:nilReason" => "missing", "CharacterString" => "The SMAP L4_SM data product provides spatially and temporally complete surface and root zone soil moisture information for science and applications users." + }, "status" => { + "MD_ProgressCode" => "onGoing" + }, "pointOfContact" => { + "CI_ResponsibleParty" => { + "organisationName" => { + "CharacterString" => "PVC" + }, "role" => { + "CI_RoleCode" => "distributor" + } + } + }, "descriptiveKeywords" => [{ + "MD_Keywords" => { + "keyword" => [{ + "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > NONE" + }, { + "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > SURFACE SOIL MOISTURE" + }, { + "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > ROOT ZONE SOIL MOISTURE" + }], "type" => { + "MD_KeywordTypeCode" => "theme" + }, "thesaurusName" => { + "gco:nilReason" => "unknown" + } + } + }, { + "MD_Keywords" => { + "keyword" => { + "CharacterString" => "Aircraft > Not provided > Not provided > " + } + } + }], "language" => { + "CharacterString" => "eng" + }, "topicCategory" => { + "MD_TopicCategoryCode" => "geoscientificInformation" + }, "extent" => { + "EX_Extent" => { + "geographicElement" => { + "EX_GeographicBoundingBox" => { + "extentTypeCode" => { + "Boolean" => "1" + }, "westBoundLongitude" => { + "Decimal" => "-180.0" + }, "eastBoundLongitude" => { + "Decimal" => "180.0" + }, "southBoundLatitude" => { + "Decimal" => "-85.04456" + }, "northBoundLatitude" => { + "Decimal" => "85.04456" + } + } + }, "temporalElement" => { + "EX_TemporalExtent" => { + "extent" => { + "TimePeriod" => { + "gml:id" => "dc46625fa-ae1e-4c95-a6ae-b15dd90fe8d3", "beginPosition" => "2015-03-31T01:30:00.000Z", "endPosition" => "2021-01-01T01:29:59.999Z" + } + } + } + } + } + }, "processingLevel" => { + "MD_Identifier" => { + "code" => { + "CharacterString" => "Not provided" + }, "codeSpace" => { + "CharacterString" => "gov.nasa.esdis.umm.processinglevelid" + } + } + } + } + }, + "24. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "DataSetId" + }, "date" => [{ + "CI_Date" => { + "date" => { + "DateTime" => "2016-04-29T00:00:00.000Z" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + }, { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-12T11:50:19.050Z" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + }, { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-08T09:16:24.835Z" + }, "dateType" => { + "CI_DateTypeCode" => "creation" + } + } + }], "citedResponsibleParty" => { + "CI_ResponsibleParty" => { + "organisationName" => { + "CharacterString" => "Global Modeling and Assimilation Office" + }, "role" => { + "CI_RoleCode" => "originator" + } + } + } + } + }, "abstract" => { + "CharacterString" => "DataSetId" + }, "resourceFormat" => { + "MD_Format" => { + "name" => { + "CharacterString" => "HDF5" + }, "version" => { + "gco:nilReason" => "unknown" + } + } + }, "aggregationInfo" => { + "MD_AggregateInformation" => { + "aggregateDataSetIdentifier" => { + "MD_Identifier" => { + "code" => { + "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update V002" + } + } + }, "associationType" => { + "DS_AssociationTypeCode" => "largerWorkCitation" + } + } + }, "language" => { + "CharacterString" => "eng" + } + } + }, + "25. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/organisationName/CharacterString" => "NSIDC DAAC > National Snow and Ice Data Center DAAC", "26. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/address/CI_Address/electronicMailAddress/CharacterString" => "nsidc@nsidc.org", "27. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/onlineResource/CI_OnlineResource/linkage/URL" => "http://nsidc.org/daac/", "28. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/role/CI_RoleCode" => "pointOfContact", "29. +: /DS_Series/seriesMetadata/MI_Metadata/contact/href" => "#alaskaSARContact", "30. -: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date" => "2016-04-29", "31. +: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date" => "2013-01-02" + } +end + +def dif_json_report + { + "format" => "application/dif10+xml", + "1. -: /DIF/Temporal_Coverage/Temporal_Range_Type" => "Long Range", + "2. -: /DIF/Related_URL[1]" => { + "URL_Content_Type" => { + "Type" => "VIEW DATA SET LANDING PAGE" + }, "URL" => "http://dx.doi.org/10.7927/H4NK3BZJ", "Description" => "data set DOI and homepage" + }, + "3. +: /DIF/Related_URL[1]" => { + "URL_Content_Type" => { + "Type" => "DATA SET LANDING PAGE" + }, "URL" => "http://dx.doi.org/10.7927/H4NK3BZJ", "Description" => "data set DOI and homepage" + } +} +end + +def echo_json_report + { + "format" => "application/echo10+xml", + "1. -: /Collection/Orderable" => "true", + "2. -: /Collection/Visible" => "true", + "3. -: /Collection/MaintenanceAndUpdateFrequency" => "As needed", + "4. +: /Collection/Temporal/EndsAtPresentFlag" => "false", + "5. +: /Collection/Temporal/RangeDateTime/BeginningDateTime" => "1970-01-01T00:00:00.000Z", + "6. +: /Collection/Platforms/Platform/ShortName" => "Not provided", + "7. +: /Collection/Platforms/Platform/LongName" => "Not provided", + "8. +: /Collection/Platforms/Platform/Type" => "Not provided", + "9. -: /Collection/AssociatedDIFs/DIF/EntryId" => "CIESIN_SEDAC_ANTHROMES_v2_1700", + "10. -: /Collection/InsertTime" => "2014-05-13T00:00:00Z", + "11. +: /Collection/InsertTime" => "2014-05-13T00:00:00.000Z", + "12. -: /Collection/LastUpdate" => "2015-08-04T00:00:00Z", + "13. +: /Collection/LastUpdate" => "2015-08-04T00:00:00.000Z", + "14. -: /Collection/LongName" => "Anthropogenic Biomes of the World, Version 2: 1700", + "15. +: /Collection/LongName" => "Not provided", + "16. -: /Collection/CollectionState" => "Final", + "17. +: /Collection/CollectionState" => "NOT PROVIDED", + "18. -: /Collection/Price" => "0", + "19. +: /Collection/Price" => " 0.00", + "20. -: /Collection/SpatialKeywords/Keyword[0]" => "Africa", + "21. -: /Collection/SpatialKeywords/Keyword[1]" => "Asia", + "22. +: /Collection/SpatialKeywords/Keyword[0]" => "AFRICA", + "23. +: /Collection/SpatialKeywords/Keyword[1]" => "GAZA STRIP", + "24. -: /Collection/Contacts/Contact[0]" => { + "Role" => "Archive", "HoursOfService" => "9:00 A.M. to 5:00 P.M., Monday to Friday", "OrganizationName" => "Socioeconomic Data and Applications Center (SEDAC)", "OrganizationAddresses" => { + "Address" => { + "StreetAddress" => "CIESIN, Columbia University, 61 Route 9W, P.O. Box 1000", "City" => "Palisades", "StateProvince" => "NY", "PostalCode" => "10964", "Country" => "USA" + } + }, "OrganizationPhones" => { + "Phone" => [{ + "Number" => "+1 845-365-8920", + "Type" => "Telephone" + }, { + "Number" => "+1 845-365-8922", + "Type" => "Fax" + }] + }, "OrganizationEmails" => { + "Email" => "ciesin.info@ciesin.columbia.edu" + }, "ContactPersons" => { + "ContactPerson" => { + "FirstName" => "SEDAC", "MiddleName" => "User", "LastName" => "Services" + } + } + }, + "25. +: /Collection/Contacts/Contact[0]" => { + "Role" => "PROCESSOR", "OrganizationName" => "SEDAC" + }, + "26. +: /Collection/Contacts/Contact[1]" => { + "Role" => "ARCHIVER", "OrganizationName" => "SEDAC" + }, + "27. +: /Collection/Contacts/Contact[2]" => { + "Role" => "ARCHIVER", "HoursOfService" => "9:00 A.M. to 5:00 P.M., Monday to Friday", "OrganizationName" => "Socioeconomic Data and Applications Center (SEDAC)", "OrganizationAddresses" => { + "Address" => { + "StreetAddress" => "CIESIN, Columbia University, 61 Route 9W, P.O. Box 1000", "City" => "Palisades", "StateProvince" => "NY", "PostalCode" => "10964", "Country" => "USA" + } + }, "OrganizationPhones" => { + "Phone" => [{ + "Number" => "+1 845-365-8920", + "Type" => "Telephone" + }, { + "Number" => "+1 845-365-8922", + "Type" => "Fax" + }] + }, "OrganizationEmails" => { + "Email" => "ciesin.info@ciesin.columbia.edu" + }, "ContactPersons" => { + "ContactPerson" => { + "FirstName" => "SEDAC", "MiddleName" => "User", "LastName" => "Services", "JobPosition" => "TECHNICAL CONTACT" + } + } + }, + "28. -: /Collection/SpatialInfo/SpatialCoverageType" => "Horizontal", + "29. +: /Collection/SpatialInfo/SpatialCoverageType" => "HORIZONTAL", + "30. -: /Collection/OnlineResources/OnlineResource/Type" => "DOI URL", + "31. +: /Collection/OnlineResources/OnlineResource/Type" => "CollectionURL : DATA SET LANDING PAGE", + "32. -: /Collection/Spatial/SpatialCoverageType" => "Horizontal", + "33. +: /Collection/Spatial/SpatialCoverageType" => "HORIZONTAL", + "34. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate" => "-180.000000", + "35. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate" => "-180.0", + "36. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate" => "90.000000", + "37. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate" => "90.0", + "38. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate" => "180.000000", + "39. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate" => "180.0", + "40. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate" => "-90.000000", + "41. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate" => "-90.0" +} +end + +def iso_text_report + 'application/iso:smap+xml + + 1. -: /DS_Series/schemaLocation + 2. -: /DS_Series/seriesMetadata/MI_Metadata/fileIdentifier/FileName + 3. -: /DS_Series/seriesMetadata/MI_Metadata/characterSet/MD_CharacterSetCode + 4. -: /DS_Series/seriesMetadata/MI_Metadata/hierarchyLevel/MD_ScopeCode + 5. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/attributeDescription + 6. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/contentType + 7. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/code/CharacterString + 8. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/codeSpace/CharacterString + 9. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardName/CharacterString + 10. +: /DS_Series/seriesMetadata/MI_Metadata/dataQualityInfo/DQ_DataQuality/scope/DQ_Scope/level/MD_ScopeCode + 11. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/id + 12. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/code/CharacterString + 13. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/codeSpace/CharacterString + 14. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/description/CharacterString + 15. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/instrument/nilReason + 16. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardVersion/CharacterString + 17. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0] + 18. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1] + 19. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[2] + 20. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[3] + 21. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[4] + 22. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[5] + 23. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0] + 24. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1] + 25. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/organisationName/CharacterString + 26. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/address/CI_Address/electronicMailAddress/CharacterString + 27. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/onlineResource/CI_OnlineResource/linkage/URL + 28. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/role/CI_RoleCode + 29. +: /DS_Series/seriesMetadata/MI_Metadata/contact/href + 30. -: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date + 31. +: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date' +end + +def echo_text_report + 'application/echo10+xml + + 1. -: /Collection/Orderable + 2. -: /Collection/Visible + 3. -: /Collection/MaintenanceAndUpdateFrequency + 4. +: /Collection/Temporal/EndsAtPresentFlag + 5. +: /Collection/Temporal/RangeDateTime/BeginningDateTime + 6. +: /Collection/Platforms/Platform/ShortName + 7. +: /Collection/Platforms/Platform/LongName + 8. +: /Collection/Platforms/Platform/Type + 9. -: /Collection/AssociatedDIFs/DIF/EntryId + 10. -: /Collection/InsertTime + 11. +: /Collection/InsertTime + 12. -: /Collection/LastUpdate + 13. +: /Collection/LastUpdate + 14. -: /Collection/LongName + 15. +: /Collection/LongName + 16. -: /Collection/CollectionState + 17. +: /Collection/CollectionState + 18. -: /Collection/Price + 19. +: /Collection/Price + 20. -: /Collection/SpatialKeywords/Keyword[0] + 21. -: /Collection/SpatialKeywords/Keyword[1] + 22. +: /Collection/SpatialKeywords/Keyword[0] + 23. +: /Collection/SpatialKeywords/Keyword[1] + 24. -: /Collection/Contacts/Contact[0] + 25. +: /Collection/Contacts/Contact[0] + 26. +: /Collection/Contacts/Contact[1] + 27. +: /Collection/Contacts/Contact[2] + 28. -: /Collection/SpatialInfo/SpatialCoverageType + 29. +: /Collection/SpatialInfo/SpatialCoverageType + 30. -: /Collection/OnlineResources/OnlineResource/Type + 31. +: /Collection/OnlineResources/OnlineResource/Type + 32. -: /Collection/Spatial/SpatialCoverageType + 33. +: /Collection/Spatial/SpatialCoverageType + 34. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate + 35. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate + 36. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate + 37. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate + 38. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate + 39. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate + 40. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate + 41. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate' +end + +def dif_text_report + 'application/dif10+xml + + 1. -: /DIF/Temporal_Coverage/Temporal_Range_Type + 2. -: /DIF/Related_URL[1] + 3. +: /DIF/Related_URL[1]' +end diff --git a/spec/features/collections/loss_report_spec.rb b/spec/features/collections/loss_report_spec.rb new file mode 100644 index 000000000..14e503ff3 --- /dev/null +++ b/spec/features/collections/loss_report_spec.rb @@ -0,0 +1,41 @@ +require 'rails_helper' + +describe 'Displaying the comparison report in browser', js: true do + + context 'when accessing the comparison report' do + + before do + login + end + + context 'when displaying json' do + it 'properly displays the echo json report' do + visit loss_report_collections_path(echo_id, format:'json') + expect(page).to have_content('application/echo') + end + it 'properly displays the iso json report' do + visit loss_report_collections_path(iso_id, format:'json') + expect(page).to have_content('application/iso') + end + it 'properly displays the dif json report' do + visit loss_report_collections_path(dif_id, format:'json') + expect(page).to have_content('application/dif') + end + end + + context 'when displaying text' do + it 'properly displays the echo text report' do + visit loss_report_collections_path(echo_id, format:'text') + expect(page).to have_content('application/echo') + end + it 'properly displays the iso text report' do + visit loss_report_collections_path(iso_id, format:'text') + expect(page).to have_content('application/iso') + end + it 'properly displays the dif text report' do + visit loss_report_collections_path(dif_id, format:'text') + expect(page).to have_content('application/dif') + end + end + end +end diff --git a/spec/helpers/loss_report_helper_spec.rb b/spec/helpers/loss_report_helper_spec.rb new file mode 100644 index 000000000..a48655938 --- /dev/null +++ b/spec/helpers/loss_report_helper_spec.rb @@ -0,0 +1,49 @@ +require 'rails_helper' + +describe 'Loss Report Helper', js: true do + let(:umm_c_version) { '1.15.3' } + + context '#prepare_collections' do + context 'when using cmr endpoints' do + it 'successfully retrieves and translates the dif collection' do + expect(helper.prepare_collections(dif_id, umm_c_version)).to be_truthy + end + it 'successfully retrieves and translates the iso collection' do + expect(helper.prepare_collections(iso_id, umm_c_version)).to be_truthy + end + it 'successfully retrieves and translates the echo collection' do + expect(helper.prepare_collections(echo_id, umm_c_version)).to be_truthy + end + end + + context '#loss_report_output' + context 'when processing a dif collection' do + it 'successfully produces a text loss report' do + expect(helper.loss_report_output(dif_id).gsub(/\s+/, "")).to eql(dif_text_report.gsub(/\s+/, "")) + end + it 'successfully produces a json loss report' do + expect(helper.loss_report_output(dif_id, hide_items=false, disp='json')).to eql(dif_json_report) + end + end + context 'when processing an echo collection' do + it 'successfully produces a text loss report' do + expect(helper.loss_report_output(echo_id).gsub(/\s+/, "")).to eql(echo_text_report.gsub(/\s+/, "")) + end + it 'successfully produces a json loss report' do + expect(helper.loss_report_output(echo_id, hide_items=false, disp='json')).to eql(echo_json_report) + end + end + context 'when processing an iso collection' do + it 'successfully produces a text loss report' do + expect(helper.loss_report_output(iso_id).gsub(/\s+/, "")).to eql(iso_text_report.gsub(/\s+/, "")) + end + it 'successfully produces a json loss report' do + expect(helper.loss_report_output(iso_id, hide_items=false, disp='json')).to have_key("31. +: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date") + end + end + # the reason the iso example only checks the last key (instead of verifying the full report) is that cmr adds/updates an 'id' attribute + # in the actual collection (every time it is translated) and therefore the the comparison report will always include this change + # except with a different value for the 'id' attribute. This would cause the equality between the hashes to evaluate false and fail the + # test every time. Checking the last change is a comparible solution because it + end +end From 20fd9e2966342ea347685800948ac7bead2beea6 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Wed, 29 Jul 2020 14:44:31 -0400 Subject: [PATCH 21/38] MMT-2313: moved loss_report_samples_helper.rb and added it to the rails_helper.rb --- spec/factories/loss_report_factory_data.rb | 831 -------------------- spec/rails_helper.rb | 2 + spec/support/loss_report_samples_helper.rb | 834 +++++++++++++++++++++ 3 files changed, 836 insertions(+), 831 deletions(-) delete mode 100644 spec/factories/loss_report_factory_data.rb create mode 100644 spec/support/loss_report_samples_helper.rb diff --git a/spec/factories/loss_report_factory_data.rb b/spec/factories/loss_report_factory_data.rb deleted file mode 100644 index 517da5094..000000000 --- a/spec/factories/loss_report_factory_data.rb +++ /dev/null @@ -1,831 +0,0 @@ - -def dif_id - 'C1200000031-SEDAC' -end - -def iso_id - 'C1200000089-LARC' -end - -def echo_id - 'C1200000040-SEDAC' -end - -def iso_json_report - { - "format" => "application/iso:smap+xml", - "1. -: /DS_Series/schemaLocation" => "http://www.isotc211.org/2005/gmi http://cdn.earthdata.nasa.gov/iso/schema/1.0/ISO19115-2_EOS.xsd", - "2. -: /DS_Series/seriesMetadata/MI_Metadata/fileIdentifier/FileName" => "L4_SM_aup", - "3. -: /DS_Series/seriesMetadata/MI_Metadata/characterSet/MD_CharacterSetCode" => "utf8", - "4. -: /DS_Series/seriesMetadata/MI_Metadata/hierarchyLevel/MD_ScopeCode" => "series", - "5. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/attributeDescription" => nil, - "6. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/contentType" => nil, - "7. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/code/CharacterString" => "Not provided", - "8. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/codeSpace/CharacterString" => "gov.nasa.esdis.umm.processinglevelid", - "9. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardName/CharacterString" => "ISO 19115-2 Geographic information - Metadata - Part 2: Extensions for imagery and gridded data", - "10. +: /DS_Series/seriesMetadata/MI_Metadata/dataQualityInfo/DQ_DataQuality/scope/DQ_Scope/level/MD_ScopeCode" => "series", - "11. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/id" => "dba588298-ef6b-4e0f-9092-d1bfe87001ea", - "12. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/code/CharacterString" => "Not provided", - "13. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/codeSpace/CharacterString" => "gov.nasa.esdis.umm.platformshortname", - "14. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/description/CharacterString" => "Not provided", - "15. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/instrument/nilReason" => "inapplicable", - "16. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardVersion/CharacterString" => "ISO 19115-2:2009-02-15", - "17. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update" - }, "date" => { - "CI_Date" => { - "date" => { - "Date" => "2016-04-29" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - }, "edition" => { - "CharacterString" => "Vv2010" - }, "identifier" => [{ - "MD_Identifier" => { - "code" => { - "CharacterString" => "SPL4SMAU" - }, "codeSpace" => { - "CharacterString" => "http://gmao.gsfc.nasa.gov" - }, "description" => { - "CharacterString" => "The ECS Short Name" - } - } - }, { - "MD_Identifier" => { - "code" => { - "CharacterString" => "002" - }, "codeSpace" => { - "CharacterString" => "gov.nasa.esdis" - }, "description" => { - "CharacterString" => "The ECS Version ID" - } - } - }, { - "MD_Identifier" => { - "code" => { - "Anchor" => "doi:10.5067/JJY2V0GJNFRZ" - }, "codeSpace" => { - "CharacterString" => "gov.nasa.esdis" - }, "description" => { - "CharacterString" => "A Digital Object Identifier (DOI) that provides a persistent interoperable means to locate the SMAP Level 4 Radar data product." - } - } - }], "citedResponsibleParty" => [{ - "CI_ResponsibleParty" => { - "organisationName" => { - "CharacterString" => "National Aeronautics and Space Administration" - }, "role" => { - "CI_RoleCode" => "resourceProvider" - } - } - }, { - "CI_ResponsibleParty" => { - "organisationName" => { - "CharacterString" => "Global Modeling and Assimilation Office" - }, "role" => { - "CI_RoleCode" => "originator" - } - } - }], "presentationForm" => { - "CI_PresentationFormCode" => "documentDigital" - }, "series" => { - "CI_Series" => { - "name" => { - "CharacterString" => "L4_SM" - } - } - }, "otherCitationDetails" => { - "CharacterString" => "The first Validated Release of the SMAP Level 4 Science Processing Software." - } - } - }, "abstract" => { - "CharacterString" => "The SMAP L4_SM data product provides global, 3-hourly surface and root zone soil moisture at 9 km resolution. The L4_SM data product consists of three Collections: geophysical, analysis update and land-model-constants." - }, "purpose" => { - "CharacterString" => "The SMAP L4_SM data product provides spatially and temporally complete surface and root zone soil moisture information for science and applications users." - }, "credit" => { - "CharacterString" => "The software that generates the L4_SM data product and the data system that automates its production were designed and implemented at the NASA Global Modeling and Assimilation Office, Goddard Space Flight Center, Greenbelt, Maryland, USA." - }, "status" => { - "MD_ProgressCode" => "onGoing" - }, "pointOfContact" => { - "CI_ResponsibleParty" => { - "organisationName" => { - "CharacterString" => "PVC" - }, "role" => { - "CI_RoleCode" => "distributor" - } - } - }, "resourceMaintenance" => { - "MD_MaintenanceInformation" => { - "maintenanceAndUpdateFrequency" => { - "MD_MaintenanceFrequencyCode" => "As Needed" - }, "dateOfNextUpdate" => { - "Date" => "2016-11-01" - }, "updateScope" => { - "MD_ScopeCode" => "series" - } - } - }, "resourceFormat" => { - "MD_Format" => { - "name" => { - "CharacterString" => "HDF5" - }, "version" => { - "CharacterString" => "Version 1.8.9" - } - } - }, "descriptiveKeywords" => [{ - "MD_Keywords" => { - "keyword" => [{ - "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT" - }, { - "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > SURFACE SOIL MOISTURE" - }, { - "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > ROOT ZONE SOIL MOISTURE" - }], "type" => { - "MD_KeywordTypeCode" => "theme" - }, "thesaurusName" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "NASA/GCMD Earth Science Keywords" - }, "date" => { - "gco:nilReason" => "missing" - } - } - } - } - }, { - "MD_Keywords" => { - "keyword" => { - "CharacterString" => "Earth Remote Sensing Instruments > Active Remote Sensing > NONE > SMAP L-BAND RADAR > SMAP L-Band Radar" - }, "type" => { - "MD_KeywordTypeCode" => "theme" - }, "thesaurusName" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "NASA/GCMD Earth Science Keywords" - }, "date" => { - "gco:nilReason" => "missing" - } - } - } - } - }, { - "MD_Keywords" => { - "keyword" => { - "CharacterString" => "Earth Observation Satellites > NASA Decadal Survey > SMAP > Soil Moisture Active and Passive Observatory" - }, "type" => { - "MD_KeywordTypeCode" => "theme" - }, "thesaurusName" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "NASA/GCMD Earth Science Keywords" - }, "date" => { - "gco:nilReason" => "missing" - } - } - } - } - }, { - "MD_Keywords" => { - "keyword" => { - "CharacterString" => "GEOGRAPHIC REGION > GLOBAL" - }, "type" => { - "MD_KeywordTypeCode" => "theme" - }, "thesaurusName" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "NASA/GCMD Earth Science Keywords" - }, "date" => { - "gco:nilReason" => "missing" - } - } - } - } - }], "aggregationInfo" => { - "MD_AggregateInformation" => { - "aggregateDataSetIdentifier" => { - "MD_Identifier" => { - "code" => { - "CharacterString" => "SMAP" - } - } - }, "associationType" => { - "DS_AssociationTypeCode" => "largerWorkCitation" - }, "initiativeType" => { - "DS_InitiativeTypeCode" => "mission" - } - } - }, "language" => { - "CharacterString" => "eng" - }, "characterSet" => { - "MD_CharacterSetCode" => "utf8" - }, "topicCategory" => { - "MD_TopicCategoryCode" => "geoscientificInformation" - }, "environmentDescription" => { - "CharacterString" => "Data product generated by the SMAP mission in HDF5 format with metadata that conforms to the ISO 19115 model." - }, "extent" => { - "EX_Extent" => { - "description" => { - "CharacterString" => "Global land excluding inland water and permanent ice." - }, "geographicElement" => { - "EX_GeographicBoundingBox" => { - "extentTypeCode" => { - "Boolean" => "1" - }, "westBoundLongitude" => { - "Decimal" => "-180" - }, "eastBoundLongitude" => { - "Decimal" => "180" - }, "southBoundLatitude" => { - "Decimal" => "-85.04456" - }, "northBoundLatitude" => { - "Decimal" => "85.04456" - } - } - }, "temporalElement" => { - "EX_TemporalExtent" => { - "extent" => { - "TimePeriod" => { - "gml:id" => "swathTemporalExtent", "beginPosition" => "2015-03-31T01:30:00.000Z", "endPosition" => "2021-01-01T01:29:59.999Z" - } - } - } - } - } - } - } - }, - "18. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "Soil Moisture Active Passive Mission Level 4 Surface and Root Zone Soil Moisture (L4_SM) Product Specification Document" - }, "date" => { - "CI_Date" => { - "date" => { - "Date" => "2015-10-31" - }, "dateType" => { - "CI_DateTypeCode" => "publication" - } - } - }, "edition" => { - "CharacterString" => "1.4" - }, "identifier" => { - "MD_Identifier" => { - "code" => { - "CharacterString" => "L4_SM" - }, "codeSpace" => { - "CharacterString" => "http://gmao.gsfc.nasa.gov" - }, "description" => { - "CharacterString" => "A short name used by the Soil Moisture Active Passive (SMAP) mission to identify the Level 4 Radar product." - } - } - }, "presentationForm" => { - "CI_PresentationFormCode" => "documentDigital" - }, "series" => { - "CI_Series" => { - "name" => { - "CharacterString" => "L4_SM" - } - } - } - } - }, "abstract" => { - "CharacterString" => "The SMAP L4_SM data product provides global, 3-hourly surface and root zone soil moisture at 9 km resolution. The L4_SM data product consists of three Collections: geophysical, analysis update and land-model-constants." - }, "language" => { - "CharacterString" => "eng" - } - } - }, - "19. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[2]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "DataSetId" - }, "date" => { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-12T11:50:19.050Z" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - } - } - }, "abstract" => { - "CharacterString" => "DataSetId" - }, "aggregationInfo" => { - "MD_AggregateInformation" => { - "aggregateDataSetIdentifier" => { - "MD_Identifier" => { - "code" => { - "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update V002" - } - } - }, "associationType" => nil - } - }, "language" => { - "CharacterString" => "eng" - } - } - }, - "20. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[3]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "InsertTime" - }, "date" => { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-08T09:16:24.835Z" - }, "dateType" => { - "CI_DateTypeCode" => "creation" - } - } - } - } - }, "abstract" => { - "CharacterString" => "InsertTime" - }, "purpose" => { - "CharacterString" => "InsertTime" - }, "language" => { - "CharacterString" => "eng" - } - } - }, - "21. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[4]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "UpdateTime" - }, "date" => { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-12T11:50:19.050Z" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - } - } - }, "abstract" => { - "CharacterString" => "UpdateTime" - }, "purpose" => { - "CharacterString" => "UpdateTime" - }, "language" => { - "CharacterString" => "eng" - } - } - }, - "22. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[5]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "DIFID" - }, "date" => { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-12T11:50:19.050Z" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - }, "identifier" => { - "MD_Identifier" => { - "code" => { - "CharacterString" => "SPL4SMAU" - } - } - } - } - }, "abstract" => { - "CharacterString" => "DIFID" - }, "purpose" => { - "CharacterString" => "DIFID" - }, "language" => { - "CharacterString" => "eng" - } - } - }, - "23. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update" - }, "date" => [{ - "CI_Date" => { - "date" => { - "DateTime" => "2016-04-29T00:00:00.000Z" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - }, { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-12T11:50:19.050Z" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - }, { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-08T09:16:24.835Z" - }, "dateType" => { - "CI_DateTypeCode" => "creation" - } - } - }], "edition" => { - "CharacterString" => "Vv2010" - }, "identifier" => [{ - "MD_Identifier" => { - "code" => { - "CharacterString" => "SPL4SMAU" - }, "description" => { - "CharacterString" => "The ECS Short Name" - } - } - }, { - "MD_Identifier" => { - "code" => { - "CharacterString" => "002" - }, "description" => { - "CharacterString" => "The ECS Version ID" - } - } - }, { - "MD_Identifier" => { - "code" => { - "CharacterString" => "doi:10.5067/JJY2V0GJNFRZ" - }, "codeSpace" => { - "CharacterString" => "gov.nasa.esdis.umm.doi" - }, "description" => { - "CharacterString" => "DOI" - } - } - }], "presentationForm" => { - "CI_PresentationFormCode" => "documentDigital" - }, "series" => { - "CI_Series" => { - "name" => { - "CharacterString" => "L4_SM" - } - } - }, "otherCitationDetails" => { - "CharacterString" => "The first Validated Release of the SMAP Level 4 Science Processing Software." - } - } - }, "abstract" => { - "CharacterString" => "The SMAP L4_SM data product provides global, 3-hourly surface and root zone soil moisture at 9 km resolution. The L4_SM data product consists of three Collections: geophysical, analysis update and land-model-constants." - }, "purpose" => { - "gco:nilReason" => "missing", "CharacterString" => "The SMAP L4_SM data product provides spatially and temporally complete surface and root zone soil moisture information for science and applications users." - }, "status" => { - "MD_ProgressCode" => "onGoing" - }, "pointOfContact" => { - "CI_ResponsibleParty" => { - "organisationName" => { - "CharacterString" => "PVC" - }, "role" => { - "CI_RoleCode" => "distributor" - } - } - }, "descriptiveKeywords" => [{ - "MD_Keywords" => { - "keyword" => [{ - "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > NONE" - }, { - "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > SURFACE SOIL MOISTURE" - }, { - "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > ROOT ZONE SOIL MOISTURE" - }], "type" => { - "MD_KeywordTypeCode" => "theme" - }, "thesaurusName" => { - "gco:nilReason" => "unknown" - } - } - }, { - "MD_Keywords" => { - "keyword" => { - "CharacterString" => "Aircraft > Not provided > Not provided > " - } - } - }], "language" => { - "CharacterString" => "eng" - }, "topicCategory" => { - "MD_TopicCategoryCode" => "geoscientificInformation" - }, "extent" => { - "EX_Extent" => { - "geographicElement" => { - "EX_GeographicBoundingBox" => { - "extentTypeCode" => { - "Boolean" => "1" - }, "westBoundLongitude" => { - "Decimal" => "-180.0" - }, "eastBoundLongitude" => { - "Decimal" => "180.0" - }, "southBoundLatitude" => { - "Decimal" => "-85.04456" - }, "northBoundLatitude" => { - "Decimal" => "85.04456" - } - } - }, "temporalElement" => { - "EX_TemporalExtent" => { - "extent" => { - "TimePeriod" => { - "gml:id" => "dc46625fa-ae1e-4c95-a6ae-b15dd90fe8d3", "beginPosition" => "2015-03-31T01:30:00.000Z", "endPosition" => "2021-01-01T01:29:59.999Z" - } - } - } - } - } - }, "processingLevel" => { - "MD_Identifier" => { - "code" => { - "CharacterString" => "Not provided" - }, "codeSpace" => { - "CharacterString" => "gov.nasa.esdis.umm.processinglevelid" - } - } - } - } - }, - "24. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "DataSetId" - }, "date" => [{ - "CI_Date" => { - "date" => { - "DateTime" => "2016-04-29T00:00:00.000Z" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - }, { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-12T11:50:19.050Z" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - }, { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-08T09:16:24.835Z" - }, "dateType" => { - "CI_DateTypeCode" => "creation" - } - } - }], "citedResponsibleParty" => { - "CI_ResponsibleParty" => { - "organisationName" => { - "CharacterString" => "Global Modeling and Assimilation Office" - }, "role" => { - "CI_RoleCode" => "originator" - } - } - } - } - }, "abstract" => { - "CharacterString" => "DataSetId" - }, "resourceFormat" => { - "MD_Format" => { - "name" => { - "CharacterString" => "HDF5" - }, "version" => { - "gco:nilReason" => "unknown" - } - } - }, "aggregationInfo" => { - "MD_AggregateInformation" => { - "aggregateDataSetIdentifier" => { - "MD_Identifier" => { - "code" => { - "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update V002" - } - } - }, "associationType" => { - "DS_AssociationTypeCode" => "largerWorkCitation" - } - } - }, "language" => { - "CharacterString" => "eng" - } - } - }, - "25. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/organisationName/CharacterString" => "NSIDC DAAC > National Snow and Ice Data Center DAAC", "26. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/address/CI_Address/electronicMailAddress/CharacterString" => "nsidc@nsidc.org", "27. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/onlineResource/CI_OnlineResource/linkage/URL" => "http://nsidc.org/daac/", "28. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/role/CI_RoleCode" => "pointOfContact", "29. +: /DS_Series/seriesMetadata/MI_Metadata/contact/href" => "#alaskaSARContact", "30. -: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date" => "2016-04-29", "31. +: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date" => "2013-01-02" - } -end - -def dif_json_report - { - "format" => "application/dif10+xml", - "1. -: /DIF/Temporal_Coverage/Temporal_Range_Type" => "Long Range", - "2. -: /DIF/Related_URL[1]" => { - "URL_Content_Type" => { - "Type" => "VIEW DATA SET LANDING PAGE" - }, "URL" => "http://dx.doi.org/10.7927/H4NK3BZJ", "Description" => "data set DOI and homepage" - }, - "3. +: /DIF/Related_URL[1]" => { - "URL_Content_Type" => { - "Type" => "DATA SET LANDING PAGE" - }, "URL" => "http://dx.doi.org/10.7927/H4NK3BZJ", "Description" => "data set DOI and homepage" - } -} -end - -def echo_json_report - { - "format" => "application/echo10+xml", - "1. -: /Collection/Orderable" => "true", - "2. -: /Collection/Visible" => "true", - "3. -: /Collection/MaintenanceAndUpdateFrequency" => "As needed", - "4. +: /Collection/Temporal/EndsAtPresentFlag" => "false", - "5. +: /Collection/Temporal/RangeDateTime/BeginningDateTime" => "1970-01-01T00:00:00.000Z", - "6. +: /Collection/Platforms/Platform/ShortName" => "Not provided", - "7. +: /Collection/Platforms/Platform/LongName" => "Not provided", - "8. +: /Collection/Platforms/Platform/Type" => "Not provided", - "9. -: /Collection/AssociatedDIFs/DIF/EntryId" => "CIESIN_SEDAC_ANTHROMES_v2_1700", - "10. -: /Collection/InsertTime" => "2014-05-13T00:00:00Z", - "11. +: /Collection/InsertTime" => "2014-05-13T00:00:00.000Z", - "12. -: /Collection/LastUpdate" => "2015-08-04T00:00:00Z", - "13. +: /Collection/LastUpdate" => "2015-08-04T00:00:00.000Z", - "14. -: /Collection/LongName" => "Anthropogenic Biomes of the World, Version 2: 1700", - "15. +: /Collection/LongName" => "Not provided", - "16. -: /Collection/CollectionState" => "Final", - "17. +: /Collection/CollectionState" => "NOT PROVIDED", - "18. -: /Collection/Price" => "0", - "19. +: /Collection/Price" => " 0.00", - "20. -: /Collection/SpatialKeywords/Keyword[0]" => "Africa", - "21. -: /Collection/SpatialKeywords/Keyword[1]" => "Asia", - "22. +: /Collection/SpatialKeywords/Keyword[0]" => "AFRICA", - "23. +: /Collection/SpatialKeywords/Keyword[1]" => "GAZA STRIP", - "24. -: /Collection/Contacts/Contact[0]" => { - "Role" => "Archive", "HoursOfService" => "9:00 A.M. to 5:00 P.M., Monday to Friday", "OrganizationName" => "Socioeconomic Data and Applications Center (SEDAC)", "OrganizationAddresses" => { - "Address" => { - "StreetAddress" => "CIESIN, Columbia University, 61 Route 9W, P.O. Box 1000", "City" => "Palisades", "StateProvince" => "NY", "PostalCode" => "10964", "Country" => "USA" - } - }, "OrganizationPhones" => { - "Phone" => [{ - "Number" => "+1 845-365-8920", - "Type" => "Telephone" - }, { - "Number" => "+1 845-365-8922", - "Type" => "Fax" - }] - }, "OrganizationEmails" => { - "Email" => "ciesin.info@ciesin.columbia.edu" - }, "ContactPersons" => { - "ContactPerson" => { - "FirstName" => "SEDAC", "MiddleName" => "User", "LastName" => "Services" - } - } - }, - "25. +: /Collection/Contacts/Contact[0]" => { - "Role" => "PROCESSOR", "OrganizationName" => "SEDAC" - }, - "26. +: /Collection/Contacts/Contact[1]" => { - "Role" => "ARCHIVER", "OrganizationName" => "SEDAC" - }, - "27. +: /Collection/Contacts/Contact[2]" => { - "Role" => "ARCHIVER", "HoursOfService" => "9:00 A.M. to 5:00 P.M., Monday to Friday", "OrganizationName" => "Socioeconomic Data and Applications Center (SEDAC)", "OrganizationAddresses" => { - "Address" => { - "StreetAddress" => "CIESIN, Columbia University, 61 Route 9W, P.O. Box 1000", "City" => "Palisades", "StateProvince" => "NY", "PostalCode" => "10964", "Country" => "USA" - } - }, "OrganizationPhones" => { - "Phone" => [{ - "Number" => "+1 845-365-8920", - "Type" => "Telephone" - }, { - "Number" => "+1 845-365-8922", - "Type" => "Fax" - }] - }, "OrganizationEmails" => { - "Email" => "ciesin.info@ciesin.columbia.edu" - }, "ContactPersons" => { - "ContactPerson" => { - "FirstName" => "SEDAC", "MiddleName" => "User", "LastName" => "Services", "JobPosition" => "TECHNICAL CONTACT" - } - } - }, - "28. -: /Collection/SpatialInfo/SpatialCoverageType" => "Horizontal", - "29. +: /Collection/SpatialInfo/SpatialCoverageType" => "HORIZONTAL", - "30. -: /Collection/OnlineResources/OnlineResource/Type" => "DOI URL", - "31. +: /Collection/OnlineResources/OnlineResource/Type" => "CollectionURL : DATA SET LANDING PAGE", - "32. -: /Collection/Spatial/SpatialCoverageType" => "Horizontal", - "33. +: /Collection/Spatial/SpatialCoverageType" => "HORIZONTAL", - "34. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate" => "-180.000000", - "35. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate" => "-180.0", - "36. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate" => "90.000000", - "37. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate" => "90.0", - "38. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate" => "180.000000", - "39. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate" => "180.0", - "40. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate" => "-90.000000", - "41. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate" => "-90.0" -} -end - -def iso_text_report - 'application/iso:smap+xml - - 1. -: /DS_Series/schemaLocation - 2. -: /DS_Series/seriesMetadata/MI_Metadata/fileIdentifier/FileName - 3. -: /DS_Series/seriesMetadata/MI_Metadata/characterSet/MD_CharacterSetCode - 4. -: /DS_Series/seriesMetadata/MI_Metadata/hierarchyLevel/MD_ScopeCode - 5. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/attributeDescription - 6. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/contentType - 7. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/code/CharacterString - 8. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/codeSpace/CharacterString - 9. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardName/CharacterString - 10. +: /DS_Series/seriesMetadata/MI_Metadata/dataQualityInfo/DQ_DataQuality/scope/DQ_Scope/level/MD_ScopeCode - 11. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/id - 12. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/code/CharacterString - 13. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/codeSpace/CharacterString - 14. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/description/CharacterString - 15. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/instrument/nilReason - 16. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardVersion/CharacterString - 17. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0] - 18. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1] - 19. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[2] - 20. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[3] - 21. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[4] - 22. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[5] - 23. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0] - 24. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1] - 25. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/organisationName/CharacterString - 26. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/address/CI_Address/electronicMailAddress/CharacterString - 27. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/onlineResource/CI_OnlineResource/linkage/URL - 28. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/role/CI_RoleCode - 29. +: /DS_Series/seriesMetadata/MI_Metadata/contact/href - 30. -: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date - 31. +: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date' -end - -def echo_text_report - 'application/echo10+xml - - 1. -: /Collection/Orderable - 2. -: /Collection/Visible - 3. -: /Collection/MaintenanceAndUpdateFrequency - 4. +: /Collection/Temporal/EndsAtPresentFlag - 5. +: /Collection/Temporal/RangeDateTime/BeginningDateTime - 6. +: /Collection/Platforms/Platform/ShortName - 7. +: /Collection/Platforms/Platform/LongName - 8. +: /Collection/Platforms/Platform/Type - 9. -: /Collection/AssociatedDIFs/DIF/EntryId - 10. -: /Collection/InsertTime - 11. +: /Collection/InsertTime - 12. -: /Collection/LastUpdate - 13. +: /Collection/LastUpdate - 14. -: /Collection/LongName - 15. +: /Collection/LongName - 16. -: /Collection/CollectionState - 17. +: /Collection/CollectionState - 18. -: /Collection/Price - 19. +: /Collection/Price - 20. -: /Collection/SpatialKeywords/Keyword[0] - 21. -: /Collection/SpatialKeywords/Keyword[1] - 22. +: /Collection/SpatialKeywords/Keyword[0] - 23. +: /Collection/SpatialKeywords/Keyword[1] - 24. -: /Collection/Contacts/Contact[0] - 25. +: /Collection/Contacts/Contact[0] - 26. +: /Collection/Contacts/Contact[1] - 27. +: /Collection/Contacts/Contact[2] - 28. -: /Collection/SpatialInfo/SpatialCoverageType - 29. +: /Collection/SpatialInfo/SpatialCoverageType - 30. -: /Collection/OnlineResources/OnlineResource/Type - 31. +: /Collection/OnlineResources/OnlineResource/Type - 32. -: /Collection/Spatial/SpatialCoverageType - 33. +: /Collection/Spatial/SpatialCoverageType - 34. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate - 35. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate - 36. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate - 37. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate - 38. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate - 39. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate - 40. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate - 41. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate' -end - -def dif_text_report - 'application/dif10+xml - - 1. -: /DIF/Temporal_Coverage/Temporal_Range_Type - 2. -: /DIF/Related_URL[1] - 3. +: /DIF/Related_URL[1]' -end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 91abc19b6..8a1e89ccb 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -189,6 +189,7 @@ config.include Helpers::GroupHelper config.include Helpers::IngestHelpers config.include Helpers::Instrumentation + config.include Helpers::LossReportSamplesHelper config.include Helpers::ProposalStatusHelper config.include Helpers::SearchHelpers config.include Helpers::SubscriptionHelpers @@ -196,6 +197,7 @@ config.include Helpers::UmmTDraftHelpers config.include Helpers::UserHelpers + # Precompile assets before running the test suite # config.before(:suite) do # Rails.application.load_tasks diff --git a/spec/support/loss_report_samples_helper.rb b/spec/support/loss_report_samples_helper.rb new file mode 100644 index 000000000..46865f046 --- /dev/null +++ b/spec/support/loss_report_samples_helper.rb @@ -0,0 +1,834 @@ +module Helpers + module LossReportSamplesHelper + def dif_id + 'C1200000031-SEDAC' + end + + def iso_id + 'C1200000089-LARC' + end + + def echo_id + 'C1200000040-SEDAC' + end + + def iso_json_report + { + "format" => "application/iso:smap+xml", + "1. -: /DS_Series/schemaLocation" => "http://www.isotc211.org/2005/gmi http://cdn.earthdata.nasa.gov/iso/schema/1.0/ISO19115-2_EOS.xsd", + "2. -: /DS_Series/seriesMetadata/MI_Metadata/fileIdentifier/FileName" => "L4_SM_aup", + "3. -: /DS_Series/seriesMetadata/MI_Metadata/characterSet/MD_CharacterSetCode" => "utf8", + "4. -: /DS_Series/seriesMetadata/MI_Metadata/hierarchyLevel/MD_ScopeCode" => "series", + "5. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/attributeDescription" => nil, + "6. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/contentType" => nil, + "7. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/code/CharacterString" => "Not provided", + "8. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/codeSpace/CharacterString" => "gov.nasa.esdis.umm.processinglevelid", + "9. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardName/CharacterString" => "ISO 19115-2 Geographic information - Metadata - Part 2: Extensions for imagery and gridded data", + "10. +: /DS_Series/seriesMetadata/MI_Metadata/dataQualityInfo/DQ_DataQuality/scope/DQ_Scope/level/MD_ScopeCode" => "series", + "11. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/id" => "dba588298-ef6b-4e0f-9092-d1bfe87001ea", + "12. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/code/CharacterString" => "Not provided", + "13. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/codeSpace/CharacterString" => "gov.nasa.esdis.umm.platformshortname", + "14. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/description/CharacterString" => "Not provided", + "15. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/instrument/nilReason" => "inapplicable", + "16. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardVersion/CharacterString" => "ISO 19115-2:2009-02-15", + "17. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update" + }, "date" => { + "CI_Date" => { + "date" => { + "Date" => "2016-04-29" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + }, "edition" => { + "CharacterString" => "Vv2010" + }, "identifier" => [{ + "MD_Identifier" => { + "code" => { + "CharacterString" => "SPL4SMAU" + }, "codeSpace" => { + "CharacterString" => "http://gmao.gsfc.nasa.gov" + }, "description" => { + "CharacterString" => "The ECS Short Name" + } + } + }, { + "MD_Identifier" => { + "code" => { + "CharacterString" => "002" + }, "codeSpace" => { + "CharacterString" => "gov.nasa.esdis" + }, "description" => { + "CharacterString" => "The ECS Version ID" + } + } + }, { + "MD_Identifier" => { + "code" => { + "Anchor" => "doi:10.5067/JJY2V0GJNFRZ" + }, "codeSpace" => { + "CharacterString" => "gov.nasa.esdis" + }, "description" => { + "CharacterString" => "A Digital Object Identifier (DOI) that provides a persistent interoperable means to locate the SMAP Level 4 Radar data product." + } + } + }], "citedResponsibleParty" => [{ + "CI_ResponsibleParty" => { + "organisationName" => { + "CharacterString" => "National Aeronautics and Space Administration" + }, "role" => { + "CI_RoleCode" => "resourceProvider" + } + } + }, { + "CI_ResponsibleParty" => { + "organisationName" => { + "CharacterString" => "Global Modeling and Assimilation Office" + }, "role" => { + "CI_RoleCode" => "originator" + } + } + }], "presentationForm" => { + "CI_PresentationFormCode" => "documentDigital" + }, "series" => { + "CI_Series" => { + "name" => { + "CharacterString" => "L4_SM" + } + } + }, "otherCitationDetails" => { + "CharacterString" => "The first Validated Release of the SMAP Level 4 Science Processing Software." + } + } + }, "abstract" => { + "CharacterString" => "The SMAP L4_SM data product provides global, 3-hourly surface and root zone soil moisture at 9 km resolution. The L4_SM data product consists of three Collections: geophysical, analysis update and land-model-constants." + }, "purpose" => { + "CharacterString" => "The SMAP L4_SM data product provides spatially and temporally complete surface and root zone soil moisture information for science and applications users." + }, "credit" => { + "CharacterString" => "The software that generates the L4_SM data product and the data system that automates its production were designed and implemented at the NASA Global Modeling and Assimilation Office, Goddard Space Flight Center, Greenbelt, Maryland, USA." + }, "status" => { + "MD_ProgressCode" => "onGoing" + }, "pointOfContact" => { + "CI_ResponsibleParty" => { + "organisationName" => { + "CharacterString" => "PVC" + }, "role" => { + "CI_RoleCode" => "distributor" + } + } + }, "resourceMaintenance" => { + "MD_MaintenanceInformation" => { + "maintenanceAndUpdateFrequency" => { + "MD_MaintenanceFrequencyCode" => "As Needed" + }, "dateOfNextUpdate" => { + "Date" => "2016-11-01" + }, "updateScope" => { + "MD_ScopeCode" => "series" + } + } + }, "resourceFormat" => { + "MD_Format" => { + "name" => { + "CharacterString" => "HDF5" + }, "version" => { + "CharacterString" => "Version 1.8.9" + } + } + }, "descriptiveKeywords" => [{ + "MD_Keywords" => { + "keyword" => [{ + "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT" + }, { + "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > SURFACE SOIL MOISTURE" + }, { + "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > ROOT ZONE SOIL MOISTURE" + }], "type" => { + "MD_KeywordTypeCode" => "theme" + }, "thesaurusName" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "NASA/GCMD Earth Science Keywords" + }, "date" => { + "gco:nilReason" => "missing" + } + } + } + } + }, { + "MD_Keywords" => { + "keyword" => { + "CharacterString" => "Earth Remote Sensing Instruments > Active Remote Sensing > NONE > SMAP L-BAND RADAR > SMAP L-Band Radar" + }, "type" => { + "MD_KeywordTypeCode" => "theme" + }, "thesaurusName" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "NASA/GCMD Earth Science Keywords" + }, "date" => { + "gco:nilReason" => "missing" + } + } + } + } + }, { + "MD_Keywords" => { + "keyword" => { + "CharacterString" => "Earth Observation Satellites > NASA Decadal Survey > SMAP > Soil Moisture Active and Passive Observatory" + }, "type" => { + "MD_KeywordTypeCode" => "theme" + }, "thesaurusName" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "NASA/GCMD Earth Science Keywords" + }, "date" => { + "gco:nilReason" => "missing" + } + } + } + } + }, { + "MD_Keywords" => { + "keyword" => { + "CharacterString" => "GEOGRAPHIC REGION > GLOBAL" + }, "type" => { + "MD_KeywordTypeCode" => "theme" + }, "thesaurusName" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "NASA/GCMD Earth Science Keywords" + }, "date" => { + "gco:nilReason" => "missing" + } + } + } + } + }], "aggregationInfo" => { + "MD_AggregateInformation" => { + "aggregateDataSetIdentifier" => { + "MD_Identifier" => { + "code" => { + "CharacterString" => "SMAP" + } + } + }, "associationType" => { + "DS_AssociationTypeCode" => "largerWorkCitation" + }, "initiativeType" => { + "DS_InitiativeTypeCode" => "mission" + } + } + }, "language" => { + "CharacterString" => "eng" + }, "characterSet" => { + "MD_CharacterSetCode" => "utf8" + }, "topicCategory" => { + "MD_TopicCategoryCode" => "geoscientificInformation" + }, "environmentDescription" => { + "CharacterString" => "Data product generated by the SMAP mission in HDF5 format with metadata that conforms to the ISO 19115 model." + }, "extent" => { + "EX_Extent" => { + "description" => { + "CharacterString" => "Global land excluding inland water and permanent ice." + }, "geographicElement" => { + "EX_GeographicBoundingBox" => { + "extentTypeCode" => { + "Boolean" => "1" + }, "westBoundLongitude" => { + "Decimal" => "-180" + }, "eastBoundLongitude" => { + "Decimal" => "180" + }, "southBoundLatitude" => { + "Decimal" => "-85.04456" + }, "northBoundLatitude" => { + "Decimal" => "85.04456" + } + } + }, "temporalElement" => { + "EX_TemporalExtent" => { + "extent" => { + "TimePeriod" => { + "gml:id" => "swathTemporalExtent", "beginPosition" => "2015-03-31T01:30:00.000Z", "endPosition" => "2021-01-01T01:29:59.999Z" + } + } + } + } + } + } + } + }, + "18. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "Soil Moisture Active Passive Mission Level 4 Surface and Root Zone Soil Moisture (L4_SM) Product Specification Document" + }, "date" => { + "CI_Date" => { + "date" => { + "Date" => "2015-10-31" + }, "dateType" => { + "CI_DateTypeCode" => "publication" + } + } + }, "edition" => { + "CharacterString" => "1.4" + }, "identifier" => { + "MD_Identifier" => { + "code" => { + "CharacterString" => "L4_SM" + }, "codeSpace" => { + "CharacterString" => "http://gmao.gsfc.nasa.gov" + }, "description" => { + "CharacterString" => "A short name used by the Soil Moisture Active Passive (SMAP) mission to identify the Level 4 Radar product." + } + } + }, "presentationForm" => { + "CI_PresentationFormCode" => "documentDigital" + }, "series" => { + "CI_Series" => { + "name" => { + "CharacterString" => "L4_SM" + } + } + } + } + }, "abstract" => { + "CharacterString" => "The SMAP L4_SM data product provides global, 3-hourly surface and root zone soil moisture at 9 km resolution. The L4_SM data product consists of three Collections: geophysical, analysis update and land-model-constants." + }, "language" => { + "CharacterString" => "eng" + } + } + }, + "19. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[2]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "DataSetId" + }, "date" => { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-12T11:50:19.050Z" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + } + } + }, "abstract" => { + "CharacterString" => "DataSetId" + }, "aggregationInfo" => { + "MD_AggregateInformation" => { + "aggregateDataSetIdentifier" => { + "MD_Identifier" => { + "code" => { + "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update V002" + } + } + }, "associationType" => nil + } + }, "language" => { + "CharacterString" => "eng" + } + } + }, + "20. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[3]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "InsertTime" + }, "date" => { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-08T09:16:24.835Z" + }, "dateType" => { + "CI_DateTypeCode" => "creation" + } + } + } + } + }, "abstract" => { + "CharacterString" => "InsertTime" + }, "purpose" => { + "CharacterString" => "InsertTime" + }, "language" => { + "CharacterString" => "eng" + } + } + }, + "21. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[4]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "UpdateTime" + }, "date" => { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-12T11:50:19.050Z" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + } + } + }, "abstract" => { + "CharacterString" => "UpdateTime" + }, "purpose" => { + "CharacterString" => "UpdateTime" + }, "language" => { + "CharacterString" => "eng" + } + } + }, + "22. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[5]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "DIFID" + }, "date" => { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-12T11:50:19.050Z" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + }, "identifier" => { + "MD_Identifier" => { + "code" => { + "CharacterString" => "SPL4SMAU" + } + } + } + } + }, "abstract" => { + "CharacterString" => "DIFID" + }, "purpose" => { + "CharacterString" => "DIFID" + }, "language" => { + "CharacterString" => "eng" + } + } + }, + "23. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update" + }, "date" => [{ + "CI_Date" => { + "date" => { + "DateTime" => "2016-04-29T00:00:00.000Z" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + }, { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-12T11:50:19.050Z" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + }, { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-08T09:16:24.835Z" + }, "dateType" => { + "CI_DateTypeCode" => "creation" + } + } + }], "edition" => { + "CharacterString" => "Vv2010" + }, "identifier" => [{ + "MD_Identifier" => { + "code" => { + "CharacterString" => "SPL4SMAU" + }, "description" => { + "CharacterString" => "The ECS Short Name" + } + } + }, { + "MD_Identifier" => { + "code" => { + "CharacterString" => "002" + }, "description" => { + "CharacterString" => "The ECS Version ID" + } + } + }, { + "MD_Identifier" => { + "code" => { + "CharacterString" => "doi:10.5067/JJY2V0GJNFRZ" + }, "codeSpace" => { + "CharacterString" => "gov.nasa.esdis.umm.doi" + }, "description" => { + "CharacterString" => "DOI" + } + } + }], "presentationForm" => { + "CI_PresentationFormCode" => "documentDigital" + }, "series" => { + "CI_Series" => { + "name" => { + "CharacterString" => "L4_SM" + } + } + }, "otherCitationDetails" => { + "CharacterString" => "The first Validated Release of the SMAP Level 4 Science Processing Software." + } + } + }, "abstract" => { + "CharacterString" => "The SMAP L4_SM data product provides global, 3-hourly surface and root zone soil moisture at 9 km resolution. The L4_SM data product consists of three Collections: geophysical, analysis update and land-model-constants." + }, "purpose" => { + "gco:nilReason" => "missing", "CharacterString" => "The SMAP L4_SM data product provides spatially and temporally complete surface and root zone soil moisture information for science and applications users." + }, "status" => { + "MD_ProgressCode" => "onGoing" + }, "pointOfContact" => { + "CI_ResponsibleParty" => { + "organisationName" => { + "CharacterString" => "PVC" + }, "role" => { + "CI_RoleCode" => "distributor" + } + } + }, "descriptiveKeywords" => [{ + "MD_Keywords" => { + "keyword" => [{ + "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > NONE" + }, { + "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > SURFACE SOIL MOISTURE" + }, { + "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > ROOT ZONE SOIL MOISTURE" + }], "type" => { + "MD_KeywordTypeCode" => "theme" + }, "thesaurusName" => { + "gco:nilReason" => "unknown" + } + } + }, { + "MD_Keywords" => { + "keyword" => { + "CharacterString" => "Aircraft > Not provided > Not provided > " + } + } + }], "language" => { + "CharacterString" => "eng" + }, "topicCategory" => { + "MD_TopicCategoryCode" => "geoscientificInformation" + }, "extent" => { + "EX_Extent" => { + "geographicElement" => { + "EX_GeographicBoundingBox" => { + "extentTypeCode" => { + "Boolean" => "1" + }, "westBoundLongitude" => { + "Decimal" => "-180.0" + }, "eastBoundLongitude" => { + "Decimal" => "180.0" + }, "southBoundLatitude" => { + "Decimal" => "-85.04456" + }, "northBoundLatitude" => { + "Decimal" => "85.04456" + } + } + }, "temporalElement" => { + "EX_TemporalExtent" => { + "extent" => { + "TimePeriod" => { + "gml:id" => "dc46625fa-ae1e-4c95-a6ae-b15dd90fe8d3", "beginPosition" => "2015-03-31T01:30:00.000Z", "endPosition" => "2021-01-01T01:29:59.999Z" + } + } + } + } + } + }, "processingLevel" => { + "MD_Identifier" => { + "code" => { + "CharacterString" => "Not provided" + }, "codeSpace" => { + "CharacterString" => "gov.nasa.esdis.umm.processinglevelid" + } + } + } + } + }, + "24. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1]" => { + "MD_DataIdentification" => { + "citation" => { + "CI_Citation" => { + "title" => { + "CharacterString" => "DataSetId" + }, "date" => [{ + "CI_Date" => { + "date" => { + "DateTime" => "2016-04-29T00:00:00.000Z" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + }, { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-12T11:50:19.050Z" + }, "dateType" => { + "CI_DateTypeCode" => "revision" + } + } + }, { + "CI_Date" => { + "date" => { + "DateTime" => "2016-09-08T09:16:24.835Z" + }, "dateType" => { + "CI_DateTypeCode" => "creation" + } + } + }], "citedResponsibleParty" => { + "CI_ResponsibleParty" => { + "organisationName" => { + "CharacterString" => "Global Modeling and Assimilation Office" + }, "role" => { + "CI_RoleCode" => "originator" + } + } + } + } + }, "abstract" => { + "CharacterString" => "DataSetId" + }, "resourceFormat" => { + "MD_Format" => { + "name" => { + "CharacterString" => "HDF5" + }, "version" => { + "gco:nilReason" => "unknown" + } + } + }, "aggregationInfo" => { + "MD_AggregateInformation" => { + "aggregateDataSetIdentifier" => { + "MD_Identifier" => { + "code" => { + "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update V002" + } + } + }, "associationType" => { + "DS_AssociationTypeCode" => "largerWorkCitation" + } + } + }, "language" => { + "CharacterString" => "eng" + } + } + }, + "25. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/organisationName/CharacterString" => "NSIDC DAAC > National Snow and Ice Data Center DAAC", "26. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/address/CI_Address/electronicMailAddress/CharacterString" => "nsidc@nsidc.org", "27. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/onlineResource/CI_OnlineResource/linkage/URL" => "http://nsidc.org/daac/", "28. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/role/CI_RoleCode" => "pointOfContact", "29. +: /DS_Series/seriesMetadata/MI_Metadata/contact/href" => "#alaskaSARContact", "30. -: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date" => "2016-04-29", "31. +: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date" => "2013-01-02" + } + end + + def dif_json_report + { + "format" => "application/dif10+xml", + "1. -: /DIF/Temporal_Coverage/Temporal_Range_Type" => "Long Range", + "2. -: /DIF/Related_URL[1]" => { + "URL_Content_Type" => { + "Type" => "VIEW DATA SET LANDING PAGE" + }, "URL" => "http://dx.doi.org/10.7927/H4NK3BZJ", "Description" => "data set DOI and homepage" + }, + "3. +: /DIF/Related_URL[1]" => { + "URL_Content_Type" => { + "Type" => "DATA SET LANDING PAGE" + }, "URL" => "http://dx.doi.org/10.7927/H4NK3BZJ", "Description" => "data set DOI and homepage" + } + } + end + + def echo_json_report + { + "format" => "application/echo10+xml", + "1. -: /Collection/Orderable" => "true", + "2. -: /Collection/Visible" => "true", + "3. -: /Collection/MaintenanceAndUpdateFrequency" => "As needed", + "4. +: /Collection/Temporal/EndsAtPresentFlag" => "false", + "5. +: /Collection/Temporal/RangeDateTime/BeginningDateTime" => "1970-01-01T00:00:00.000Z", + "6. +: /Collection/Platforms/Platform/ShortName" => "Not provided", + "7. +: /Collection/Platforms/Platform/LongName" => "Not provided", + "8. +: /Collection/Platforms/Platform/Type" => "Not provided", + "9. -: /Collection/AssociatedDIFs/DIF/EntryId" => "CIESIN_SEDAC_ANTHROMES_v2_1700", + "10. -: /Collection/InsertTime" => "2014-05-13T00:00:00Z", + "11. +: /Collection/InsertTime" => "2014-05-13T00:00:00.000Z", + "12. -: /Collection/LastUpdate" => "2015-08-04T00:00:00Z", + "13. +: /Collection/LastUpdate" => "2015-08-04T00:00:00.000Z", + "14. -: /Collection/LongName" => "Anthropogenic Biomes of the World, Version 2: 1700", + "15. +: /Collection/LongName" => "Not provided", + "16. -: /Collection/CollectionState" => "Final", + "17. +: /Collection/CollectionState" => "NOT PROVIDED", + "18. -: /Collection/Price" => "0", + "19. +: /Collection/Price" => " 0.00", + "20. -: /Collection/SpatialKeywords/Keyword[0]" => "Africa", + "21. -: /Collection/SpatialKeywords/Keyword[1]" => "Asia", + "22. +: /Collection/SpatialKeywords/Keyword[0]" => "AFRICA", + "23. +: /Collection/SpatialKeywords/Keyword[1]" => "GAZA STRIP", + "24. -: /Collection/Contacts/Contact[0]" => { + "Role" => "Archive", "HoursOfService" => "9:00 A.M. to 5:00 P.M., Monday to Friday", "OrganizationName" => "Socioeconomic Data and Applications Center (SEDAC)", "OrganizationAddresses" => { + "Address" => { + "StreetAddress" => "CIESIN, Columbia University, 61 Route 9W, P.O. Box 1000", "City" => "Palisades", "StateProvince" => "NY", "PostalCode" => "10964", "Country" => "USA" + } + }, "OrganizationPhones" => { + "Phone" => [{ + "Number" => "+1 845-365-8920", + "Type" => "Telephone" + }, { + "Number" => "+1 845-365-8922", + "Type" => "Fax" + }] + }, "OrganizationEmails" => { + "Email" => "ciesin.info@ciesin.columbia.edu" + }, "ContactPersons" => { + "ContactPerson" => { + "FirstName" => "SEDAC", "MiddleName" => "User", "LastName" => "Services" + } + } + }, + "25. +: /Collection/Contacts/Contact[0]" => { + "Role" => "PROCESSOR", "OrganizationName" => "SEDAC" + }, + "26. +: /Collection/Contacts/Contact[1]" => { + "Role" => "ARCHIVER", "OrganizationName" => "SEDAC" + }, + "27. +: /Collection/Contacts/Contact[2]" => { + "Role" => "ARCHIVER", "HoursOfService" => "9:00 A.M. to 5:00 P.M., Monday to Friday", "OrganizationName" => "Socioeconomic Data and Applications Center (SEDAC)", "OrganizationAddresses" => { + "Address" => { + "StreetAddress" => "CIESIN, Columbia University, 61 Route 9W, P.O. Box 1000", "City" => "Palisades", "StateProvince" => "NY", "PostalCode" => "10964", "Country" => "USA" + } + }, "OrganizationPhones" => { + "Phone" => [{ + "Number" => "+1 845-365-8920", + "Type" => "Telephone" + }, { + "Number" => "+1 845-365-8922", + "Type" => "Fax" + }] + }, "OrganizationEmails" => { + "Email" => "ciesin.info@ciesin.columbia.edu" + }, "ContactPersons" => { + "ContactPerson" => { + "FirstName" => "SEDAC", "MiddleName" => "User", "LastName" => "Services", "JobPosition" => "TECHNICAL CONTACT" + } + } + }, + "28. -: /Collection/SpatialInfo/SpatialCoverageType" => "Horizontal", + "29. +: /Collection/SpatialInfo/SpatialCoverageType" => "HORIZONTAL", + "30. -: /Collection/OnlineResources/OnlineResource/Type" => "DOI URL", + "31. +: /Collection/OnlineResources/OnlineResource/Type" => "CollectionURL : DATA SET LANDING PAGE", + "32. -: /Collection/Spatial/SpatialCoverageType" => "Horizontal", + "33. +: /Collection/Spatial/SpatialCoverageType" => "HORIZONTAL", + "34. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate" => "-180.000000", + "35. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate" => "-180.0", + "36. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate" => "90.000000", + "37. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate" => "90.0", + "38. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate" => "180.000000", + "39. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate" => "180.0", + "40. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate" => "-90.000000", + "41. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate" => "-90.0" + } + end + + def iso_text_report + 'application/iso:smap+xml + + 1. -: /DS_Series/schemaLocation + 2. -: /DS_Series/seriesMetadata/MI_Metadata/fileIdentifier/FileName + 3. -: /DS_Series/seriesMetadata/MI_Metadata/characterSet/MD_CharacterSetCode + 4. -: /DS_Series/seriesMetadata/MI_Metadata/hierarchyLevel/MD_ScopeCode + 5. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/attributeDescription + 6. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/contentType + 7. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/code/CharacterString + 8. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/codeSpace/CharacterString + 9. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardName/CharacterString + 10. +: /DS_Series/seriesMetadata/MI_Metadata/dataQualityInfo/DQ_DataQuality/scope/DQ_Scope/level/MD_ScopeCode + 11. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/id + 12. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/code/CharacterString + 13. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/codeSpace/CharacterString + 14. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/description/CharacterString + 15. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/instrument/nilReason + 16. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardVersion/CharacterString + 17. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0] + 18. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1] + 19. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[2] + 20. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[3] + 21. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[4] + 22. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[5] + 23. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0] + 24. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1] + 25. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/organisationName/CharacterString + 26. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/address/CI_Address/electronicMailAddress/CharacterString + 27. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/onlineResource/CI_OnlineResource/linkage/URL + 28. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/role/CI_RoleCode + 29. +: /DS_Series/seriesMetadata/MI_Metadata/contact/href + 30. -: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date + 31. +: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date' + end + + def echo_text_report + 'application/echo10+xml + + 1. -: /Collection/Orderable + 2. -: /Collection/Visible + 3. -: /Collection/MaintenanceAndUpdateFrequency + 4. +: /Collection/Temporal/EndsAtPresentFlag + 5. +: /Collection/Temporal/RangeDateTime/BeginningDateTime + 6. +: /Collection/Platforms/Platform/ShortName + 7. +: /Collection/Platforms/Platform/LongName + 8. +: /Collection/Platforms/Platform/Type + 9. -: /Collection/AssociatedDIFs/DIF/EntryId + 10. -: /Collection/InsertTime + 11. +: /Collection/InsertTime + 12. -: /Collection/LastUpdate + 13. +: /Collection/LastUpdate + 14. -: /Collection/LongName + 15. +: /Collection/LongName + 16. -: /Collection/CollectionState + 17. +: /Collection/CollectionState + 18. -: /Collection/Price + 19. +: /Collection/Price + 20. -: /Collection/SpatialKeywords/Keyword[0] + 21. -: /Collection/SpatialKeywords/Keyword[1] + 22. +: /Collection/SpatialKeywords/Keyword[0] + 23. +: /Collection/SpatialKeywords/Keyword[1] + 24. -: /Collection/Contacts/Contact[0] + 25. +: /Collection/Contacts/Contact[0] + 26. +: /Collection/Contacts/Contact[1] + 27. +: /Collection/Contacts/Contact[2] + 28. -: /Collection/SpatialInfo/SpatialCoverageType + 29. +: /Collection/SpatialInfo/SpatialCoverageType + 30. -: /Collection/OnlineResources/OnlineResource/Type + 31. +: /Collection/OnlineResources/OnlineResource/Type + 32. -: /Collection/Spatial/SpatialCoverageType + 33. +: /Collection/Spatial/SpatialCoverageType + 34. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate + 35. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate + 36. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate + 37. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate + 38. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate + 39. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate + 40. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate + 41. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate' + end + + def dif_text_report + 'application/dif10+xml + + 1. -: /DIF/Temporal_Coverage/Temporal_Range_Type + 2. -: /DIF/Related_URL[1] + 3. +: /DIF/Related_URL[1]' + end + end +end From 34891ecf57d32ea222a90c1df67f3df8bfb51dd3 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Fri, 31 Jul 2020 13:45:59 -0400 Subject: [PATCH 22/38] MMT-2313: made changes per PR change requests --- app/controllers/collections_controller.rb | 27 ++++++++-- app/helpers/loss_report_helper.rb | 41 +++++---------- app/views/collections/loss_report.html.erb | 51 ------------------- spec/features/collections/loss_report_spec.rb | 3 +- spec/helpers/loss_report_helper_spec.rb | 13 +++-- spec/support/loss_report_samples_helper.rb | 8 ++- 6 files changed, 53 insertions(+), 90 deletions(-) delete mode 100644 app/views/collections/loss_report.html.erb diff --git a/app/controllers/collections_controller.rb b/app/controllers/collections_controller.rb index 78e9cfaf5..f0c884d5a 100644 --- a/app/controllers/collections_controller.rb +++ b/app/controllers/collections_controller.rb @@ -5,6 +5,7 @@ class CollectionsController < ManageCollectionsController include LossReportHelper before_action :set_collection + before_action :prepare_translated_collections before_action :ensure_correct_collection_provider, only: [:edit, :clone, :revert, :destroy] layout 'collection_preview', only: [:show] @@ -120,10 +121,9 @@ def loss_report # When a user wants to use MMT to edit metadata that currently exists in a non-UMM form, # it's important that they're able to see if any data loss occurs in the translation to umm. # This method is needed to reference the appropriate helper and view for the lossiness report - concept_id = params[:id] respond_to do |format| - format.text { render plain: loss_report_output(concept_id, hide_items=true, disp='text') } - format.json { render json: JSON.pretty_generate(loss_report_output(concept_id, hide_items=false, disp='json')) } + format.text { render plain: loss_report_output(hide_items=true, disp='text') } + format.json { render json: JSON.pretty_generate(loss_report_output(hide_items=false, disp='json')) } end end @@ -139,6 +139,27 @@ def ensure_correct_collection_provider render :show end + def prepare_translated_collections + original_collection_native_xml = cmr_client.get_concept(params[:id],token, {}) + original_collection_native_xml.success? ? @collection_error = false : @collection_error = true + + @content_type = original_collection_native_xml.headers.fetch('content-type').split(';')[0] + @collection_error = true if @content_type.include?('application/vnd.nasa.cmr.umm+json;version=') + + @original_collection_native_hash = Hash.from_xml(original_collection_native_xml.body) + + translated_collection_umm_json = cmr_client.translate_collection(original_collection_native_xml.body, @content_type, "application/#{Rails.configuration.umm_c_version}; charset=utf-8", skip_validation=true) + @collection_error = true if !translated_collection_umm_json.success? + + translated_collection_native_xml = cmr_client.translate_collection(JSON.pretty_generate(translated_collection_umm_json.body), "application/#{Rails.configuration.umm_c_version}; charset=utf-8", @content_type, skip_validation=true) + @collection_error = true if !translated_collection_native_xml.success? + + @translated_collection_native_hash = Hash.from_xml(translated_collection_native_xml.body) + + @original_collection_native_xml = original_collection_native_xml.body + @translated_collection_native_xml = translated_collection_native_xml.body + end + def set_collection @concept_id = params[:id] @revision_id = params[:revision_id] diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index e17a00e80..39b3acfdd 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -12,18 +12,23 @@ def token end end - def loss_report_output(concept_id, hide_items=true, disp='text') + def loss_report_output(hide_items=true, disp='text') # depending on the input selection (json or text) a comparison string/hash is created and displayed in-browser - # prepare_collections returns false when the cmr_client endpoints are unsuccessfully executed - if (collections = prepare_collections(concept_id, '1.15.3')) - orig_xml,conv_xml,orig_h,conv_h,content_type = collections + # @collection_error is true if there is an error in the translation that is performed by prepare_collections in the collections_controller + if !@collection_error + orig_xml,conv_xml = @original_collection_native_xml, @translated_collection_native_xml + orig_h,conv_h = @original_collection_native_hash, @translated_collection_native_hash else return 'Failure to get_concept or translate_collection' if disp == 'text' return {"error"=>"Failure to get_concept or translate_collection"} if disp == 'json' end - if content_type.include?('iso') || content_type.include?('dif') + # ISO and DIF collections (in XML form) contain namespaces that cause errors in the below comparison. + # Specifically, when nodes are evaluated individually, (their namespace definitions remaining at the top of the xml) + # their prefixes are undefined in the scope of the evaluation and therefore raise errors. Removing the namespaces + # eliminates this issue. + if @content_type.include?('iso') || @content_type.include?('dif') orig = Nokogiri::XML(orig_xml) { |config| config.strict.noblanks } .remove_namespaces! conv = Nokogiri::XML(conv_xml) { |config| config.strict.noblanks } .remove_namespaces! else @@ -31,11 +36,6 @@ def loss_report_output(concept_id, hide_items=true, disp='text') conv = Nokogiri::XML(conv_xml) { |config| config.strict.noblanks } end - #write files to test that all changes are being found with opendiff - dir = '/Users/ctrummer/Documents/devtesting' - File.write("#{dir}/o_#{concept_id}.xml", orig.to_xml) - File.write("#{dir}/c_#{concept_id}.xml", conv.to_xml) - arr_paths = Array.new # This array is used to keep track of the paths that lead to arrays that have already been mapped text_output = String.new if disp == 'text' json_output = Hash.new if disp == 'json' @@ -45,8 +45,8 @@ def loss_report_output(concept_id, hide_items=true, disp='text') # json_output['conv'] = conv_h if disp == 'json' # text_output += orig_xml if disp == 'text' - json_output['format'] = content_type if disp == 'json' - text_output += (content_type + "\n\n") if disp == 'text' + json_output['format'] = @content_type if disp == 'json' + text_output += (@content_type + "\n\n") if disp == 'text' orig.diff(conv, {:added => true, :removed => true}) do |change,node| element = node.to_xml @@ -150,23 +150,6 @@ def hash_map(hash) buckets end - def prepare_collections(concept_id, umm_c_version) - original_collection_native_xml = cmr_client.get_concept(concept_id,token, {}) - return false if !original_collection_native_xml.success? - - content_type = original_collection_native_xml.headers.fetch('content-type').split(';')[0] - original_collection_native_hash = Hash.from_xml(original_collection_native_xml.body) - translated_collection_umm_json = cmr_client.translate_collection(original_collection_native_xml.body, content_type, "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", skip_validation=true) - return false if !translated_collection_umm_json.success? - - translated_collection_native_xml = cmr_client.translate_collection(JSON.pretty_generate(translated_collection_umm_json.body), "application/vnd.nasa.cmr.umm+json;version=#{umm_c_version}", content_type, skip_validation=true) - return false if !translated_collection_native_xml.success? - - translated_collection_native_hash = Hash.from_xml(translated_collection_native_xml.body) - return original_collection_native_xml.body, translated_collection_native_xml.body, original_collection_native_hash, translated_collection_native_hash, content_type - end - - def hash_navigation(path, hash) # Passed a path string and the hash being navigated. This method parses the path string and # returns the array/value at the end of the path diff --git a/app/views/collections/loss_report.html.erb b/app/views/collections/loss_report.html.erb deleted file mode 100644 index f27bbf9f0..000000000 --- a/app/views/collections/loss_report.html.erb +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - <% orig,conv,orig_h,conv_h = prepare_collections('C1200000063-LARC', 'echo10', '1.15.3') %> - <% orig = Nokogiri::XML(orig) { |config| config.strict.noblanks } %> - <% conv = Nokogiri::XML(conv) { |config| config.strict.noblanks } %> - <% ignored_paths = Array.new %> - <% counter = 0 %> - <% orig.diff(conv, {:added => true, :removed => true}) do |change,node| %> - <% if node.parent.path.include?('[') && !ignored_paths.include?(node.parent.path.split('[')[0]) %> - <% ignored_paths << node.parent.path.split('[')[0] %> - <% array_comparison(node.parent.path.split('[')[0], orig_h, conv_h).each do |item| %> - - - - - - <% counter += 1%> - <% end %> - <% elsif !ignored_paths.include?(node.parent.path.split('[')[0]) && !path_leads_to_list?(node.parent.path, orig_h, conv_h) %> - - - - - - <% counter += 1%> - <% end %> - <% end %> - -
<%= 'Alteration' %><%= 'Node' %><%= 'Path' %>
- <%= item[0] %> - - <%= counter %> - <%= item[1] %> - - <%= item[2] %> -
- <%= change %> - - <%= counter %> - <%= node.to_html %> - - <%= node.parent.path %> -
diff --git a/spec/features/collections/loss_report_spec.rb b/spec/features/collections/loss_report_spec.rb index 14e503ff3..6c84110af 100644 --- a/spec/features/collections/loss_report_spec.rb +++ b/spec/features/collections/loss_report_spec.rb @@ -1,6 +1,5 @@ -require 'rails_helper' -describe 'Displaying the comparison report in browser', js: true do +describe 'Displaying the comparison report in browser' do context 'when accessing the comparison report' do diff --git a/spec/helpers/loss_report_helper_spec.rb b/spec/helpers/loss_report_helper_spec.rb index a48655938..79b1a0e9c 100644 --- a/spec/helpers/loss_report_helper_spec.rb +++ b/spec/helpers/loss_report_helper_spec.rb @@ -1,6 +1,5 @@ -require 'rails_helper' -describe 'Loss Report Helper', js: true do +describe 'Loss Report Helper' do let(:umm_c_version) { '1.15.3' } context '#prepare_collections' do @@ -38,12 +37,18 @@ expect(helper.loss_report_output(iso_id).gsub(/\s+/, "")).to eql(iso_text_report.gsub(/\s+/, "")) end it 'successfully produces a json loss report' do - expect(helper.loss_report_output(iso_id, hide_items=false, disp='json')).to have_key("31. +: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date") + report = helper.loss_report_output(iso_id, hide_items=false, disp='json') + expect(report.keys.length).to be(32) + expect(report).to have_key('8. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/codeSpace/CharacterString') + expect(report).to have_key('21. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[4]') + expect(report).to have_key('24. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1]') + expect(report).to have_key('31. +: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date') end end # the reason the iso example only checks the last key (instead of verifying the full report) is that cmr adds/updates an 'id' attribute # in the actual collection (every time it is translated) and therefore the the comparison report will always include this change # except with a different value for the 'id' attribute. This would cause the equality between the hashes to evaluate false and fail the - # test every time. Checking the last change is a comparible solution because it + # test every time. Spot checking the output is a comparable solution because any small addition/removal should throw off the numbering system, + # +/- symbol, or path, and this test will fail. end end diff --git a/spec/support/loss_report_samples_helper.rb b/spec/support/loss_report_samples_helper.rb index 46865f046..de5a38c4e 100644 --- a/spec/support/loss_report_samples_helper.rb +++ b/spec/support/loss_report_samples_helper.rb @@ -629,7 +629,13 @@ def iso_json_report } } }, - "25. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/organisationName/CharacterString" => "NSIDC DAAC > National Snow and Ice Data Center DAAC", "26. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/address/CI_Address/electronicMailAddress/CharacterString" => "nsidc@nsidc.org", "27. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/onlineResource/CI_OnlineResource/linkage/URL" => "http://nsidc.org/daac/", "28. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/role/CI_RoleCode" => "pointOfContact", "29. +: /DS_Series/seriesMetadata/MI_Metadata/contact/href" => "#alaskaSARContact", "30. -: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date" => "2016-04-29", "31. +: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date" => "2013-01-02" + "25. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/organisationName/CharacterString" => "NSIDC DAAC > National Snow and Ice Data Center DAAC", + "26. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/address/CI_Address/electronicMailAddress/CharacterString" => "nsidc@nsidc.org", + "27. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/onlineResource/CI_OnlineResource/linkage/URL" => "http://nsidc.org/daac/", + "28. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/role/CI_RoleCode" => "pointOfContact", + "29. +: /DS_Series/seriesMetadata/MI_Metadata/contact/href" => "#alaskaSARContact", + "30. -: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date" => "2016-04-29", + "31. +: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date" => "2013-01-02" } end From 758e8fa24c0eddc6713bd7ad395d2967cfeecbd6 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Fri, 31 Jul 2020 14:19:22 -0400 Subject: [PATCH 23/38] MMT-2313: removed unneeded comment --- app/helpers/loss_report_helper.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index 39b3acfdd..3dba501ac 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -40,11 +40,6 @@ def loss_report_output(hide_items=true, disp='text') text_output = String.new if disp == 'text' json_output = Hash.new if disp == 'json' - # json_output['orig'] = hash_map(orig_h) if disp == 'json' - # json_output['orig'] = orig_h if disp == 'json' - # json_output['conv'] = conv_h if disp == 'json' - # text_output += orig_xml if disp == 'text' - json_output['format'] = @content_type if disp == 'json' text_output += (@content_type + "\n\n") if disp == 'text' From aa3e768a4b2dca2f7ee0b973b2e966d50160e88e Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Fri, 31 Jul 2020 16:47:33 -0400 Subject: [PATCH 24/38] MMT-2313: removed an unecessary cmr call --- app/controllers/collections_controller.rb | 7 +------ app/helpers/loss_report_helper.rb | 4 ++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/app/controllers/collections_controller.rb b/app/controllers/collections_controller.rb index f0c884d5a..adc03faf1 100644 --- a/app/controllers/collections_controller.rb +++ b/app/controllers/collections_controller.rb @@ -145,15 +145,10 @@ def prepare_translated_collections @content_type = original_collection_native_xml.headers.fetch('content-type').split(';')[0] @collection_error = true if @content_type.include?('application/vnd.nasa.cmr.umm+json;version=') - @original_collection_native_hash = Hash.from_xml(original_collection_native_xml.body) - translated_collection_umm_json = cmr_client.translate_collection(original_collection_native_xml.body, @content_type, "application/#{Rails.configuration.umm_c_version}; charset=utf-8", skip_validation=true) - @collection_error = true if !translated_collection_umm_json.success? - - translated_collection_native_xml = cmr_client.translate_collection(JSON.pretty_generate(translated_collection_umm_json.body), "application/#{Rails.configuration.umm_c_version}; charset=utf-8", @content_type, skip_validation=true) + translated_collection_native_xml = cmr_client.translate_collection(JSON.pretty_generate(@collection), "application/#{Rails.configuration.umm_c_version}; charset=utf-8", @content_type, skip_validation=true) @collection_error = true if !translated_collection_native_xml.success? - @translated_collection_native_hash = Hash.from_xml(translated_collection_native_xml.body) @original_collection_native_xml = original_collection_native_xml.body diff --git a/app/helpers/loss_report_helper.rb b/app/helpers/loss_report_helper.rb index 3dba501ac..be531dc22 100644 --- a/app/helpers/loss_report_helper.rb +++ b/app/helpers/loss_report_helper.rb @@ -51,12 +51,12 @@ def loss_report_output(hide_items=true, disp='text') if arr_path && path_not_checked?(arr_path, arr_paths) arr_paths << arr_path array_comparison(arr_path, orig_h, conv_h).each { |item| add_to_report(item[0], item[1], item[2], hide_items, disp, json_output, text_output) } - elsif path_not_checked?(path, arr_paths) # nokogiri + elsif path_not_checked?(path, arr_paths) if is_xml?(node) element = Hash.from_xml(element) hash_map(element).each do |item| arr_path = top_level_arr_path("#{path}/#{item['path']}", orig_h, conv_h) - if arr_path && path_not_checked?("#{path}/#{item['path']}", arr_paths) # all list + if arr_path && path_not_checked?("#{path}/#{item['path']}", arr_paths) if path_not_checked?(arr_path, arr_paths) arr_paths << arr_path array_comparison(arr_path, orig_h, conv_h).each { |item| add_to_report(item[0], item[1], item[2], hide_items, disp, json_output, text_output) } From 3a7591c6f567d5765d8fea184b5e1062634fba87 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Mon, 3 Aug 2020 15:23:12 -0400 Subject: [PATCH 25/38] MMT-2346: added loss-report modal --- .../change_current_provider.coffee | 2 +- app/assets/stylesheets/components/_modal.scss | 1 + .../stylesheets/components/_search.scss | 10 +++--- app/views/collections/show.html.erb | 31 +++++++++++++++---- app/views/shared/_loss_report_modal.html.erb | 16 ++++++++++ 5 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 app/views/shared/_loss_report_modal.html.erb diff --git a/app/assets/javascripts/change_current_provider.coffee b/app/assets/javascripts/change_current_provider.coffee index 540b4124c..e5fc52188 100644 --- a/app/assets/javascripts/change_current_provider.coffee +++ b/app/assets/javascripts/change_current_provider.coffee @@ -96,7 +96,7 @@ $(document).ready -> # Click the link that the user needs if linkType == 'delete-collection' && $('.collection-granule-count').text() != 'Granules (0)' $('#display-granules-modal').click() - else + else $(link)[0].click() # Change current provider diff --git a/app/assets/stylesheets/components/_modal.scss b/app/assets/stylesheets/components/_modal.scss index 785e24d93..682715c15 100644 --- a/app/assets/stylesheets/components/_modal.scss +++ b/app/assets/stylesheets/components/_modal.scss @@ -15,6 +15,7 @@ display: none; } +#loss-report-modal, #provider-context, #help-modal, #download-xml-modal, diff --git a/app/assets/stylesheets/components/_search.scss b/app/assets/stylesheets/components/_search.scss index 09b7679c3..04071974b 100644 --- a/app/assets/stylesheets/components/_search.scss +++ b/app/assets/stylesheets/components/_search.scss @@ -64,12 +64,12 @@ } } } - .search-dropdown-short { /* smaller dropdown if radio buttons are hidden in DMMT */ + .search-dropdown-short { /* smaller dropdown if radio buttons are hidden in DMMT */ width: 100%; height: 45px; } - - .search-dropdown-wide { /* wider dropdown if UMM-T is enabled */ + + .search-dropdown-wide { /* wider dropdown if UMM-T is enabled */ right: 0px; // causes dropdown width to extend to the left max-width: 440px; } @@ -79,12 +79,12 @@ border-right: 1px solid $dolphin-grey; border-bottom: 1px solid $steel-blue; } - + #search-submit-button { text-align: left; width: 172px; // 11.92em } - + .search-field-wide { width: 241px; } diff --git a/app/views/collections/show.html.erb b/app/views/collections/show.html.erb index 208a3c432..512c51eaa 100644 --- a/app/views/collections/show.html.erb +++ b/app/views/collections/show.html.erb @@ -57,12 +57,23 @@

- <% if current_provider?(@provider_id) %> - <%= link_to 'Edit Collection Record', edit_collection_path(revision_id: @revision_id), class: 'eui-btn--link bar-after' %> - <% elsif available_provider?(@provider_id) %> - <%= link_to 'Edit Collection Record', '#not-current-provider-modal', class: 'display-modal not-current-provider eui-btn--link bar-after', data: { provider: @provider_id, record_action: 'edit-collection' } %> + + <% if current_provider?(@provider_id) %> <%= link_to 'Clone Collection Record', clone_collection_path, class: 'eui-btn--link bar-after' %> <% elsif available_provider?(@provider_id) %> @@ -112,12 +123,20 @@ draft: @draft, draft_id: @draft.try(:id) } } %> + <%= render partial: 'shared/loss_report_modal', locals: { + options: { + collection: @collection, + concept_id: @concept_id, + revision_id: @revision_id, + draft: @draft, + draft_id: @draft.try(:id) } + } %>

class="modal-close float-r"> -

This collection has <%= @num_granules %> associated <%= 'granule'.pluralize(@num_granules) %>.

-

Deleting this collection will delete all associated granules.

+

This collection has <%= @num_granules %> associated <%= 'granule'.pluralize(@num_granules) %>.

+

Deleting this collection will delete all associated granules.

Please confirm that you wish to continue by entering '<%= CollectionsHelper::DELETE_CONFIRMATION_TEXT %>' below.

<%= form_tag(collection_path, method: :delete, class: 'granules-confirmation-form') do %> <%= text_field_tag('confirmation-text', nil, class: 'full-width') %> diff --git a/app/views/shared/_loss_report_modal.html.erb b/app/views/shared/_loss_report_modal.html.erb new file mode 100644 index 000000000..88d569406 --- /dev/null +++ b/app/views/shared/_loss_report_modal.html.erb @@ -0,0 +1,16 @@ + +
+ +
+

Data Loss Warning

+

This collection possesses a non-UMM native format and could experience data loss while being translated to UMM. Would you like to view a loss report before you begin editing?

+

+ <%= link_to 'Yes', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--blue' %> + <% if false#current_provider?(@provider_id) %> + <%= link_to 'No', edit_collection_path(revision_id: @revision_id), class: 'eui-btn' %> + <% elsif true #available_provider?(@provider_id) %> + <%= link_to 'No', '#not-current-provider-modal', class: 'display-modal not-current-provider eui-btn', data: { provider: @provider_id, record_action: 'edit-collection' } %> + <% end %> +

+
+
From c5be6027eb3d6cd1e5ea4e31d936f1294988d07b Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Mon, 3 Aug 2020 18:33:15 -0400 Subject: [PATCH 26/38] MMT-2346: loss reporting - link and modal options --- app/views/collections/show.html.erb | 9 +++++---- app/views/shared/_loss_report_modal.html.erb | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/views/collections/show.html.erb b/app/views/collections/show.html.erb index 512c51eaa..36189b178 100644 --- a/app/views/collections/show.html.erb +++ b/app/views/collections/show.html.erb @@ -57,13 +57,14 @@

- <% if !@revisions.first.fetch('meta', {})['format'].include?('umm') && current_provider?(@provider_id) %> - <%= link_to 'View Loss', "#delete-record-modal", class: 'display-modal delete-collection delete-collection-in-current-provider eui-btn--link bar-after' %> + <%= link_to 'View Data Loss Report', loss_report_collections_path(@concept_id, format: 'json'), class: 'eui-btn--link bar-after' %> <% elsif !@revisions.first.fetch('meta', {})['format'].include?('umm') && available_provider?(@provider_id) %> - <%= link_to 'Delete Collection Record', '#not-current-provider-modal', class: 'display-modal delete-collection not-current-provider eui-btn--link bar-after', data: { provider: @provider_id, record_action: 'delete-collection' } %> + <%= link_to 'View Data Loss Report', '#not-current-provider-modal', class: 'display-modal delete-collection not-current-provider eui-btn--link bar-after', data: { provider: @provider_id, record_action: 'delete-collection' } %> <% end %> + <% if !@revisions.first.fetch('meta', {})['format'].include?('umm') %> <%= link_to 'Edit Collection Record', '#loss-report-modal', class: 'display-modal loss-report eui-btn--link bar-after' %> <% else %> @@ -72,7 +73,7 @@ <% elsif available_provider?(@provider_id) %> <%= link_to 'Edit Collection Record', '#not-current-provider-modal', class: 'display-modal not-current-provider eui-btn--link bar-after', data: { provider: @provider_id, record_action: 'edit-collection' } %> <% end %> - <% end %> --> + <% end %> <% if current_provider?(@provider_id) %> <%= link_to 'Clone Collection Record', clone_collection_path, class: 'eui-btn--link bar-after' %> diff --git a/app/views/shared/_loss_report_modal.html.erb b/app/views/shared/_loss_report_modal.html.erb index 88d569406..e884cfb8c 100644 --- a/app/views/shared/_loss_report_modal.html.erb +++ b/app/views/shared/_loss_report_modal.html.erb @@ -6,9 +6,9 @@

This collection possesses a non-UMM native format and could experience data loss while being translated to UMM. Would you like to view a loss report before you begin editing?

<%= link_to 'Yes', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--blue' %> - <% if false#current_provider?(@provider_id) %> + <% if current_provider?(@provider_id) %> <%= link_to 'No', edit_collection_path(revision_id: @revision_id), class: 'eui-btn' %> - <% elsif true #available_provider?(@provider_id) %> + <% elsif available_provider?(@provider_id) %> <%= link_to 'No', '#not-current-provider-modal', class: 'display-modal not-current-provider eui-btn', data: { provider: @provider_id, record_action: 'edit-collection' } %> <% end %>

From 91103d3caa54220cfbf9bc3c73419355bda108e0 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Wed, 5 Aug 2020 13:45:03 -0400 Subject: [PATCH 27/38] MMT-2346: MVP modal access to loss report --- .../javascripts/change_current_provider.coffee | 4 ++++ app/views/collections/show.html.erb | 11 ++--------- app/views/shared/_loss_report_modal.html.erb | 15 ++++++++++----- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/change_current_provider.coffee b/app/assets/javascripts/change_current_provider.coffee index e5fc52188..5c1b626c9 100644 --- a/app/assets/javascripts/change_current_provider.coffee +++ b/app/assets/javascripts/change_current_provider.coffee @@ -4,6 +4,10 @@ $(document).ready -> overlay: 0.6 closeButton: '.modal-close' + $('a.loss-report').on 'click', (element) -> + $('#loss-report-modal').hide() + $('#loss-report-to-edit-collection').click() + # Handle not-current-provider-modal $('a.not-current-provider').on 'click', (element) -> provider = $(element.target).data('provider') diff --git a/app/views/collections/show.html.erb b/app/views/collections/show.html.erb index 36189b178..bad1c05c2 100644 --- a/app/views/collections/show.html.erb +++ b/app/views/collections/show.html.erb @@ -57,16 +57,8 @@

- - <% if !@revisions.first.fetch('meta', {})['format'].include?('umm') && current_provider?(@provider_id) %> - <%= link_to 'View Data Loss Report', loss_report_collections_path(@concept_id, format: 'json'), class: 'eui-btn--link bar-after' %> - <% elsif !@revisions.first.fetch('meta', {})['format'].include?('umm') && available_provider?(@provider_id) %> - <%= link_to 'View Data Loss Report', '#not-current-provider-modal', class: 'display-modal delete-collection not-current-provider eui-btn--link bar-after', data: { provider: @provider_id, record_action: 'delete-collection' } %> - <% end %> - - <% if !@revisions.first.fetch('meta', {})['format'].include?('umm') %> - <%= link_to 'Edit Collection Record', '#loss-report-modal', class: 'display-modal loss-report eui-btn--link bar-after' %> + <%= link_to 'Edit Collection Record', '#loss-report-modal', class: 'display-modal eui-btn--link bar-after' %> <% else %> <% if current_provider?(@provider_id) %> <%= link_to 'Edit Collection Record', edit_collection_path(revision_id: @revision_id), class: 'eui-btn--link bar-after' %> @@ -126,6 +118,7 @@ } %> <%= render partial: 'shared/loss_report_modal', locals: { options: { + provider_id: @provider_id, collection: @collection, concept_id: @concept_id, revision_id: @revision_id, diff --git a/app/views/shared/_loss_report_modal.html.erb b/app/views/shared/_loss_report_modal.html.erb index e884cfb8c..911d88903 100644 --- a/app/views/shared/_loss_report_modal.html.erb +++ b/app/views/shared/_loss_report_modal.html.erb @@ -5,12 +5,17 @@

Data Loss Warning

This collection possesses a non-UMM native format and could experience data loss while being translated to UMM. Would you like to view a loss report before you begin editing?

- <%= link_to 'Yes', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--blue' %> - <% if current_provider?(@provider_id) %> - <%= link_to 'No', edit_collection_path(revision_id: @revision_id), class: 'eui-btn' %> - <% elsif available_provider?(@provider_id) %> - <%= link_to 'No', '#not-current-provider-modal', class: 'display-modal not-current-provider eui-btn', data: { provider: @provider_id, record_action: 'edit-collection' } %> + <% if current_provider?(options[:provider_id]) %> + <%= link_to 'Yes', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'modal-close eui-btn--blue', target: :_blank %> + <%= link_to 'No', edit_collection_path(revision_id: options[:revision_id]), class: 'modal-close eui-btn' %> + <% elsif available_provider?(options[:provider_id]) %> + <%= link_to 'Yes', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'loss-report eui-btn--blue', target: :_blank %> + <%= link_to 'No', '#', class: 'loss-report eui-btn', data: {}%> + <% else %> + <%= link_to 'Yes', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'modal-close eui-btn--blue', target: :_blank %> + No <% end %> + <%= link_to 'Invisible Link', '#not-current-provider-modal', class: 'display-modal not-current-provider eui-btn is-invisible', id: 'loss-report-to-edit-collection', data: { provider: @provider_id, record_action: 'edit-collection' } %>

From a242f661056fb022e74b499f86078232a7261843 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Mon, 10 Aug 2020 07:51:55 -0400 Subject: [PATCH 28/38] MMT-2346: completed modal specs --- app/controllers/collections_controller.rb | 2 +- app/views/shared/_loss_report_modal.html.erb | 2 +- .../collections/loss_report_modal_spec.rb | 92 ++ spec/features/collections/loss_report_spec.rb | 40 - spec/helpers/loss_report_helper_spec.rb | 54 -- spec/rails_helper.rb | 1 - spec/support/loss_report_samples_helper.rb | 840 ------------------ 7 files changed, 94 insertions(+), 937 deletions(-) create mode 100644 spec/features/collections/loss_report_modal_spec.rb delete mode 100644 spec/features/collections/loss_report_spec.rb delete mode 100644 spec/helpers/loss_report_helper_spec.rb delete mode 100644 spec/support/loss_report_samples_helper.rb diff --git a/app/controllers/collections_controller.rb b/app/controllers/collections_controller.rb index adc03faf1..a35f931d0 100644 --- a/app/controllers/collections_controller.rb +++ b/app/controllers/collections_controller.rb @@ -5,7 +5,7 @@ class CollectionsController < ManageCollectionsController include LossReportHelper before_action :set_collection - before_action :prepare_translated_collections + before_action :prepare_translated_collections, only: [:loss_report] before_action :ensure_correct_collection_provider, only: [:edit, :clone, :revert, :destroy] layout 'collection_preview', only: [:show] diff --git a/app/views/shared/_loss_report_modal.html.erb b/app/views/shared/_loss_report_modal.html.erb index 911d88903..617abd15e 100644 --- a/app/views/shared/_loss_report_modal.html.erb +++ b/app/views/shared/_loss_report_modal.html.erb @@ -1,5 +1,5 @@ -
+

Data Loss Warning

diff --git a/spec/features/collections/loss_report_modal_spec.rb b/spec/features/collections/loss_report_modal_spec.rb new file mode 100644 index 000000000..678970494 --- /dev/null +++ b/spec/features/collections/loss_report_modal_spec.rb @@ -0,0 +1,92 @@ + +# need to make sure the change current provider modal specs don't fail +describe 'loss report modal', js: true do + # this is an echo collection (SEDAC provider) + let(:cmr_response) { cmr_client.get_collections({'EntryTitle': 'Anthropogenic Biomes of the World, Version 2: 1700'}) } + let(:concept_id) { cmr_response.body.dig('items',0,'meta','concept-id') } + + context 'when user clicks Edit Collection Record for a non-UMM collection' do + context 'when provider context does not need to be changed' do + + before do + login(provider: 'SEDAC', providers: %w[SEDAC LARC]) + visit collection_path(concept_id) + click_on 'Edit Collection Record' + end + + it 'displays the loss-report-modal' do + within '#loss-report-modal' do + expect(page).to have_link('Yes', href: loss_report_collections_path(concept_id, format: 'json')) + expect(page).to have_link('No', href: edit_collection_path(id: concept_id)) + end + end + + context 'when the "No" button is clicked' do + it 'does not display the not-current-provider-modal' do + within '#loss-report-modal' do + click_on 'No' + end + + expect(page).to have_no_css('#not-current-provider-modal') + end + end + + context 'when the "Yes" button is clicked' do + it 'does not display display the not-current-provider-modal' do + within '#loss-report-modal' do + click_on 'Yes' + end + + expect(page).to have_no_css('#not-current-provider-modal') + end + end + end + + context 'when provider context needs to be changed and the required provider context is available' do + + before do + login(provider: 'LARC', providers: %w[SEDAC LARC]) + visit collection_path(concept_id) + click_on 'Edit Collection Record' + end + + it 'displays the loss-report-modal' do + within '#loss-report-modal' do + expect(page).to have_link('Yes', href: loss_report_collections_path(concept_id, format: 'json')) + expect(page).to have_link('No', href: '#') + end + end + + context 'when the "No" button is clicked' do + it 'displays the not-current-provider-modal' do + within '#loss-report-modal' do + click_on 'No' + end + + expect(page).to have_css('#not-current-provider-modal') + + within '#not-current-provider-modal' do + expect(page).to have_link('Yes', href: '#') + expect(page).to have_link('No', href: 'javascript:void(0)') + end + end + end + + context 'when the "Yes" button is clicked' do + it 'displays the not-current-provider-modal' do + within '#loss-report-modal' do + click_on 'Yes' + end + + expect(page).to have_css('#not-current-provider-modal') + + within '#not-current-provider-modal' do + expect(page).to have_link('Yes', href: '#') + expect(page).to have_link('No', href: 'javascript:void(0)') + end + end + end + + end + end +end diff --git a/spec/features/collections/loss_report_spec.rb b/spec/features/collections/loss_report_spec.rb deleted file mode 100644 index 6c84110af..000000000 --- a/spec/features/collections/loss_report_spec.rb +++ /dev/null @@ -1,40 +0,0 @@ - -describe 'Displaying the comparison report in browser' do - - context 'when accessing the comparison report' do - - before do - login - end - - context 'when displaying json' do - it 'properly displays the echo json report' do - visit loss_report_collections_path(echo_id, format:'json') - expect(page).to have_content('application/echo') - end - it 'properly displays the iso json report' do - visit loss_report_collections_path(iso_id, format:'json') - expect(page).to have_content('application/iso') - end - it 'properly displays the dif json report' do - visit loss_report_collections_path(dif_id, format:'json') - expect(page).to have_content('application/dif') - end - end - - context 'when displaying text' do - it 'properly displays the echo text report' do - visit loss_report_collections_path(echo_id, format:'text') - expect(page).to have_content('application/echo') - end - it 'properly displays the iso text report' do - visit loss_report_collections_path(iso_id, format:'text') - expect(page).to have_content('application/iso') - end - it 'properly displays the dif text report' do - visit loss_report_collections_path(dif_id, format:'text') - expect(page).to have_content('application/dif') - end - end - end -end diff --git a/spec/helpers/loss_report_helper_spec.rb b/spec/helpers/loss_report_helper_spec.rb deleted file mode 100644 index 79b1a0e9c..000000000 --- a/spec/helpers/loss_report_helper_spec.rb +++ /dev/null @@ -1,54 +0,0 @@ - -describe 'Loss Report Helper' do - let(:umm_c_version) { '1.15.3' } - - context '#prepare_collections' do - context 'when using cmr endpoints' do - it 'successfully retrieves and translates the dif collection' do - expect(helper.prepare_collections(dif_id, umm_c_version)).to be_truthy - end - it 'successfully retrieves and translates the iso collection' do - expect(helper.prepare_collections(iso_id, umm_c_version)).to be_truthy - end - it 'successfully retrieves and translates the echo collection' do - expect(helper.prepare_collections(echo_id, umm_c_version)).to be_truthy - end - end - - context '#loss_report_output' - context 'when processing a dif collection' do - it 'successfully produces a text loss report' do - expect(helper.loss_report_output(dif_id).gsub(/\s+/, "")).to eql(dif_text_report.gsub(/\s+/, "")) - end - it 'successfully produces a json loss report' do - expect(helper.loss_report_output(dif_id, hide_items=false, disp='json')).to eql(dif_json_report) - end - end - context 'when processing an echo collection' do - it 'successfully produces a text loss report' do - expect(helper.loss_report_output(echo_id).gsub(/\s+/, "")).to eql(echo_text_report.gsub(/\s+/, "")) - end - it 'successfully produces a json loss report' do - expect(helper.loss_report_output(echo_id, hide_items=false, disp='json')).to eql(echo_json_report) - end - end - context 'when processing an iso collection' do - it 'successfully produces a text loss report' do - expect(helper.loss_report_output(iso_id).gsub(/\s+/, "")).to eql(iso_text_report.gsub(/\s+/, "")) - end - it 'successfully produces a json loss report' do - report = helper.loss_report_output(iso_id, hide_items=false, disp='json') - expect(report.keys.length).to be(32) - expect(report).to have_key('8. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/codeSpace/CharacterString') - expect(report).to have_key('21. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[4]') - expect(report).to have_key('24. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1]') - expect(report).to have_key('31. +: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date') - end - end - # the reason the iso example only checks the last key (instead of verifying the full report) is that cmr adds/updates an 'id' attribute - # in the actual collection (every time it is translated) and therefore the the comparison report will always include this change - # except with a different value for the 'id' attribute. This would cause the equality between the hashes to evaluate false and fail the - # test every time. Spot checking the output is a comparable solution because any small addition/removal should throw off the numbering system, - # +/- symbol, or path, and this test will fail. - end -end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 8a1e89ccb..9c81587a1 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -189,7 +189,6 @@ config.include Helpers::GroupHelper config.include Helpers::IngestHelpers config.include Helpers::Instrumentation - config.include Helpers::LossReportSamplesHelper config.include Helpers::ProposalStatusHelper config.include Helpers::SearchHelpers config.include Helpers::SubscriptionHelpers diff --git a/spec/support/loss_report_samples_helper.rb b/spec/support/loss_report_samples_helper.rb deleted file mode 100644 index de5a38c4e..000000000 --- a/spec/support/loss_report_samples_helper.rb +++ /dev/null @@ -1,840 +0,0 @@ -module Helpers - module LossReportSamplesHelper - def dif_id - 'C1200000031-SEDAC' - end - - def iso_id - 'C1200000089-LARC' - end - - def echo_id - 'C1200000040-SEDAC' - end - - def iso_json_report - { - "format" => "application/iso:smap+xml", - "1. -: /DS_Series/schemaLocation" => "http://www.isotc211.org/2005/gmi http://cdn.earthdata.nasa.gov/iso/schema/1.0/ISO19115-2_EOS.xsd", - "2. -: /DS_Series/seriesMetadata/MI_Metadata/fileIdentifier/FileName" => "L4_SM_aup", - "3. -: /DS_Series/seriesMetadata/MI_Metadata/characterSet/MD_CharacterSetCode" => "utf8", - "4. -: /DS_Series/seriesMetadata/MI_Metadata/hierarchyLevel/MD_ScopeCode" => "series", - "5. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/attributeDescription" => nil, - "6. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/contentType" => nil, - "7. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/code/CharacterString" => "Not provided", - "8. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/codeSpace/CharacterString" => "gov.nasa.esdis.umm.processinglevelid", - "9. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardName/CharacterString" => "ISO 19115-2 Geographic information - Metadata - Part 2: Extensions for imagery and gridded data", - "10. +: /DS_Series/seriesMetadata/MI_Metadata/dataQualityInfo/DQ_DataQuality/scope/DQ_Scope/level/MD_ScopeCode" => "series", - "11. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/id" => "dba588298-ef6b-4e0f-9092-d1bfe87001ea", - "12. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/code/CharacterString" => "Not provided", - "13. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/codeSpace/CharacterString" => "gov.nasa.esdis.umm.platformshortname", - "14. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/description/CharacterString" => "Not provided", - "15. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/instrument/nilReason" => "inapplicable", - "16. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardVersion/CharacterString" => "ISO 19115-2:2009-02-15", - "17. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update" - }, "date" => { - "CI_Date" => { - "date" => { - "Date" => "2016-04-29" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - }, "edition" => { - "CharacterString" => "Vv2010" - }, "identifier" => [{ - "MD_Identifier" => { - "code" => { - "CharacterString" => "SPL4SMAU" - }, "codeSpace" => { - "CharacterString" => "http://gmao.gsfc.nasa.gov" - }, "description" => { - "CharacterString" => "The ECS Short Name" - } - } - }, { - "MD_Identifier" => { - "code" => { - "CharacterString" => "002" - }, "codeSpace" => { - "CharacterString" => "gov.nasa.esdis" - }, "description" => { - "CharacterString" => "The ECS Version ID" - } - } - }, { - "MD_Identifier" => { - "code" => { - "Anchor" => "doi:10.5067/JJY2V0GJNFRZ" - }, "codeSpace" => { - "CharacterString" => "gov.nasa.esdis" - }, "description" => { - "CharacterString" => "A Digital Object Identifier (DOI) that provides a persistent interoperable means to locate the SMAP Level 4 Radar data product." - } - } - }], "citedResponsibleParty" => [{ - "CI_ResponsibleParty" => { - "organisationName" => { - "CharacterString" => "National Aeronautics and Space Administration" - }, "role" => { - "CI_RoleCode" => "resourceProvider" - } - } - }, { - "CI_ResponsibleParty" => { - "organisationName" => { - "CharacterString" => "Global Modeling and Assimilation Office" - }, "role" => { - "CI_RoleCode" => "originator" - } - } - }], "presentationForm" => { - "CI_PresentationFormCode" => "documentDigital" - }, "series" => { - "CI_Series" => { - "name" => { - "CharacterString" => "L4_SM" - } - } - }, "otherCitationDetails" => { - "CharacterString" => "The first Validated Release of the SMAP Level 4 Science Processing Software." - } - } - }, "abstract" => { - "CharacterString" => "The SMAP L4_SM data product provides global, 3-hourly surface and root zone soil moisture at 9 km resolution. The L4_SM data product consists of three Collections: geophysical, analysis update and land-model-constants." - }, "purpose" => { - "CharacterString" => "The SMAP L4_SM data product provides spatially and temporally complete surface and root zone soil moisture information for science and applications users." - }, "credit" => { - "CharacterString" => "The software that generates the L4_SM data product and the data system that automates its production were designed and implemented at the NASA Global Modeling and Assimilation Office, Goddard Space Flight Center, Greenbelt, Maryland, USA." - }, "status" => { - "MD_ProgressCode" => "onGoing" - }, "pointOfContact" => { - "CI_ResponsibleParty" => { - "organisationName" => { - "CharacterString" => "PVC" - }, "role" => { - "CI_RoleCode" => "distributor" - } - } - }, "resourceMaintenance" => { - "MD_MaintenanceInformation" => { - "maintenanceAndUpdateFrequency" => { - "MD_MaintenanceFrequencyCode" => "As Needed" - }, "dateOfNextUpdate" => { - "Date" => "2016-11-01" - }, "updateScope" => { - "MD_ScopeCode" => "series" - } - } - }, "resourceFormat" => { - "MD_Format" => { - "name" => { - "CharacterString" => "HDF5" - }, "version" => { - "CharacterString" => "Version 1.8.9" - } - } - }, "descriptiveKeywords" => [{ - "MD_Keywords" => { - "keyword" => [{ - "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT" - }, { - "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > SURFACE SOIL MOISTURE" - }, { - "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > ROOT ZONE SOIL MOISTURE" - }], "type" => { - "MD_KeywordTypeCode" => "theme" - }, "thesaurusName" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "NASA/GCMD Earth Science Keywords" - }, "date" => { - "gco:nilReason" => "missing" - } - } - } - } - }, { - "MD_Keywords" => { - "keyword" => { - "CharacterString" => "Earth Remote Sensing Instruments > Active Remote Sensing > NONE > SMAP L-BAND RADAR > SMAP L-Band Radar" - }, "type" => { - "MD_KeywordTypeCode" => "theme" - }, "thesaurusName" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "NASA/GCMD Earth Science Keywords" - }, "date" => { - "gco:nilReason" => "missing" - } - } - } - } - }, { - "MD_Keywords" => { - "keyword" => { - "CharacterString" => "Earth Observation Satellites > NASA Decadal Survey > SMAP > Soil Moisture Active and Passive Observatory" - }, "type" => { - "MD_KeywordTypeCode" => "theme" - }, "thesaurusName" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "NASA/GCMD Earth Science Keywords" - }, "date" => { - "gco:nilReason" => "missing" - } - } - } - } - }, { - "MD_Keywords" => { - "keyword" => { - "CharacterString" => "GEOGRAPHIC REGION > GLOBAL" - }, "type" => { - "MD_KeywordTypeCode" => "theme" - }, "thesaurusName" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "NASA/GCMD Earth Science Keywords" - }, "date" => { - "gco:nilReason" => "missing" - } - } - } - } - }], "aggregationInfo" => { - "MD_AggregateInformation" => { - "aggregateDataSetIdentifier" => { - "MD_Identifier" => { - "code" => { - "CharacterString" => "SMAP" - } - } - }, "associationType" => { - "DS_AssociationTypeCode" => "largerWorkCitation" - }, "initiativeType" => { - "DS_InitiativeTypeCode" => "mission" - } - } - }, "language" => { - "CharacterString" => "eng" - }, "characterSet" => { - "MD_CharacterSetCode" => "utf8" - }, "topicCategory" => { - "MD_TopicCategoryCode" => "geoscientificInformation" - }, "environmentDescription" => { - "CharacterString" => "Data product generated by the SMAP mission in HDF5 format with metadata that conforms to the ISO 19115 model." - }, "extent" => { - "EX_Extent" => { - "description" => { - "CharacterString" => "Global land excluding inland water and permanent ice." - }, "geographicElement" => { - "EX_GeographicBoundingBox" => { - "extentTypeCode" => { - "Boolean" => "1" - }, "westBoundLongitude" => { - "Decimal" => "-180" - }, "eastBoundLongitude" => { - "Decimal" => "180" - }, "southBoundLatitude" => { - "Decimal" => "-85.04456" - }, "northBoundLatitude" => { - "Decimal" => "85.04456" - } - } - }, "temporalElement" => { - "EX_TemporalExtent" => { - "extent" => { - "TimePeriod" => { - "gml:id" => "swathTemporalExtent", "beginPosition" => "2015-03-31T01:30:00.000Z", "endPosition" => "2021-01-01T01:29:59.999Z" - } - } - } - } - } - } - } - }, - "18. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "Soil Moisture Active Passive Mission Level 4 Surface and Root Zone Soil Moisture (L4_SM) Product Specification Document" - }, "date" => { - "CI_Date" => { - "date" => { - "Date" => "2015-10-31" - }, "dateType" => { - "CI_DateTypeCode" => "publication" - } - } - }, "edition" => { - "CharacterString" => "1.4" - }, "identifier" => { - "MD_Identifier" => { - "code" => { - "CharacterString" => "L4_SM" - }, "codeSpace" => { - "CharacterString" => "http://gmao.gsfc.nasa.gov" - }, "description" => { - "CharacterString" => "A short name used by the Soil Moisture Active Passive (SMAP) mission to identify the Level 4 Radar product." - } - } - }, "presentationForm" => { - "CI_PresentationFormCode" => "documentDigital" - }, "series" => { - "CI_Series" => { - "name" => { - "CharacterString" => "L4_SM" - } - } - } - } - }, "abstract" => { - "CharacterString" => "The SMAP L4_SM data product provides global, 3-hourly surface and root zone soil moisture at 9 km resolution. The L4_SM data product consists of three Collections: geophysical, analysis update and land-model-constants." - }, "language" => { - "CharacterString" => "eng" - } - } - }, - "19. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[2]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "DataSetId" - }, "date" => { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-12T11:50:19.050Z" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - } - } - }, "abstract" => { - "CharacterString" => "DataSetId" - }, "aggregationInfo" => { - "MD_AggregateInformation" => { - "aggregateDataSetIdentifier" => { - "MD_Identifier" => { - "code" => { - "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update V002" - } - } - }, "associationType" => nil - } - }, "language" => { - "CharacterString" => "eng" - } - } - }, - "20. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[3]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "InsertTime" - }, "date" => { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-08T09:16:24.835Z" - }, "dateType" => { - "CI_DateTypeCode" => "creation" - } - } - } - } - }, "abstract" => { - "CharacterString" => "InsertTime" - }, "purpose" => { - "CharacterString" => "InsertTime" - }, "language" => { - "CharacterString" => "eng" - } - } - }, - "21. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[4]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "UpdateTime" - }, "date" => { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-12T11:50:19.050Z" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - } - } - }, "abstract" => { - "CharacterString" => "UpdateTime" - }, "purpose" => { - "CharacterString" => "UpdateTime" - }, "language" => { - "CharacterString" => "eng" - } - } - }, - "22. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[5]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "DIFID" - }, "date" => { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-12T11:50:19.050Z" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - }, "identifier" => { - "MD_Identifier" => { - "code" => { - "CharacterString" => "SPL4SMAU" - } - } - } - } - }, "abstract" => { - "CharacterString" => "DIFID" - }, "purpose" => { - "CharacterString" => "DIFID" - }, "language" => { - "CharacterString" => "eng" - } - } - }, - "23. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update" - }, "date" => [{ - "CI_Date" => { - "date" => { - "DateTime" => "2016-04-29T00:00:00.000Z" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - }, { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-12T11:50:19.050Z" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - }, { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-08T09:16:24.835Z" - }, "dateType" => { - "CI_DateTypeCode" => "creation" - } - } - }], "edition" => { - "CharacterString" => "Vv2010" - }, "identifier" => [{ - "MD_Identifier" => { - "code" => { - "CharacterString" => "SPL4SMAU" - }, "description" => { - "CharacterString" => "The ECS Short Name" - } - } - }, { - "MD_Identifier" => { - "code" => { - "CharacterString" => "002" - }, "description" => { - "CharacterString" => "The ECS Version ID" - } - } - }, { - "MD_Identifier" => { - "code" => { - "CharacterString" => "doi:10.5067/JJY2V0GJNFRZ" - }, "codeSpace" => { - "CharacterString" => "gov.nasa.esdis.umm.doi" - }, "description" => { - "CharacterString" => "DOI" - } - } - }], "presentationForm" => { - "CI_PresentationFormCode" => "documentDigital" - }, "series" => { - "CI_Series" => { - "name" => { - "CharacterString" => "L4_SM" - } - } - }, "otherCitationDetails" => { - "CharacterString" => "The first Validated Release of the SMAP Level 4 Science Processing Software." - } - } - }, "abstract" => { - "CharacterString" => "The SMAP L4_SM data product provides global, 3-hourly surface and root zone soil moisture at 9 km resolution. The L4_SM data product consists of three Collections: geophysical, analysis update and land-model-constants." - }, "purpose" => { - "gco:nilReason" => "missing", "CharacterString" => "The SMAP L4_SM data product provides spatially and temporally complete surface and root zone soil moisture information for science and applications users." - }, "status" => { - "MD_ProgressCode" => "onGoing" - }, "pointOfContact" => { - "CI_ResponsibleParty" => { - "organisationName" => { - "CharacterString" => "PVC" - }, "role" => { - "CI_RoleCode" => "distributor" - } - } - }, "descriptiveKeywords" => [{ - "MD_Keywords" => { - "keyword" => [{ - "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > NONE" - }, { - "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > SURFACE SOIL MOISTURE" - }, { - "CharacterString" => "EARTH SCIENCE > LAND SURFACE > SOILS > SOIL MOISTURE/WATER CONTENT > NONE > NONE > ROOT ZONE SOIL MOISTURE" - }], "type" => { - "MD_KeywordTypeCode" => "theme" - }, "thesaurusName" => { - "gco:nilReason" => "unknown" - } - } - }, { - "MD_Keywords" => { - "keyword" => { - "CharacterString" => "Aircraft > Not provided > Not provided > " - } - } - }], "language" => { - "CharacterString" => "eng" - }, "topicCategory" => { - "MD_TopicCategoryCode" => "geoscientificInformation" - }, "extent" => { - "EX_Extent" => { - "geographicElement" => { - "EX_GeographicBoundingBox" => { - "extentTypeCode" => { - "Boolean" => "1" - }, "westBoundLongitude" => { - "Decimal" => "-180.0" - }, "eastBoundLongitude" => { - "Decimal" => "180.0" - }, "southBoundLatitude" => { - "Decimal" => "-85.04456" - }, "northBoundLatitude" => { - "Decimal" => "85.04456" - } - } - }, "temporalElement" => { - "EX_TemporalExtent" => { - "extent" => { - "TimePeriod" => { - "gml:id" => "dc46625fa-ae1e-4c95-a6ae-b15dd90fe8d3", "beginPosition" => "2015-03-31T01:30:00.000Z", "endPosition" => "2021-01-01T01:29:59.999Z" - } - } - } - } - } - }, "processingLevel" => { - "MD_Identifier" => { - "code" => { - "CharacterString" => "Not provided" - }, "codeSpace" => { - "CharacterString" => "gov.nasa.esdis.umm.processinglevelid" - } - } - } - } - }, - "24. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1]" => { - "MD_DataIdentification" => { - "citation" => { - "CI_Citation" => { - "title" => { - "CharacterString" => "DataSetId" - }, "date" => [{ - "CI_Date" => { - "date" => { - "DateTime" => "2016-04-29T00:00:00.000Z" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - }, { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-12T11:50:19.050Z" - }, "dateType" => { - "CI_DateTypeCode" => "revision" - } - } - }, { - "CI_Date" => { - "date" => { - "DateTime" => "2016-09-08T09:16:24.835Z" - }, "dateType" => { - "CI_DateTypeCode" => "creation" - } - } - }], "citedResponsibleParty" => { - "CI_ResponsibleParty" => { - "organisationName" => { - "CharacterString" => "Global Modeling and Assimilation Office" - }, "role" => { - "CI_RoleCode" => "originator" - } - } - } - } - }, "abstract" => { - "CharacterString" => "DataSetId" - }, "resourceFormat" => { - "MD_Format" => { - "name" => { - "CharacterString" => "HDF5" - }, "version" => { - "gco:nilReason" => "unknown" - } - } - }, "aggregationInfo" => { - "MD_AggregateInformation" => { - "aggregateDataSetIdentifier" => { - "MD_Identifier" => { - "code" => { - "CharacterString" => "SMAP L4 Global 3-hourly 9 km Surface and Rootzone Soil Moisture Analysis Update V002" - } - } - }, "associationType" => { - "DS_AssociationTypeCode" => "largerWorkCitation" - } - } - }, "language" => { - "CharacterString" => "eng" - } - } - }, - "25. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/organisationName/CharacterString" => "NSIDC DAAC > National Snow and Ice Data Center DAAC", - "26. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/address/CI_Address/electronicMailAddress/CharacterString" => "nsidc@nsidc.org", - "27. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/onlineResource/CI_OnlineResource/linkage/URL" => "http://nsidc.org/daac/", - "28. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/role/CI_RoleCode" => "pointOfContact", - "29. +: /DS_Series/seriesMetadata/MI_Metadata/contact/href" => "#alaskaSARContact", - "30. -: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date" => "2016-04-29", - "31. +: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date" => "2013-01-02" - } - end - - def dif_json_report - { - "format" => "application/dif10+xml", - "1. -: /DIF/Temporal_Coverage/Temporal_Range_Type" => "Long Range", - "2. -: /DIF/Related_URL[1]" => { - "URL_Content_Type" => { - "Type" => "VIEW DATA SET LANDING PAGE" - }, "URL" => "http://dx.doi.org/10.7927/H4NK3BZJ", "Description" => "data set DOI and homepage" - }, - "3. +: /DIF/Related_URL[1]" => { - "URL_Content_Type" => { - "Type" => "DATA SET LANDING PAGE" - }, "URL" => "http://dx.doi.org/10.7927/H4NK3BZJ", "Description" => "data set DOI and homepage" - } - } - end - - def echo_json_report - { - "format" => "application/echo10+xml", - "1. -: /Collection/Orderable" => "true", - "2. -: /Collection/Visible" => "true", - "3. -: /Collection/MaintenanceAndUpdateFrequency" => "As needed", - "4. +: /Collection/Temporal/EndsAtPresentFlag" => "false", - "5. +: /Collection/Temporal/RangeDateTime/BeginningDateTime" => "1970-01-01T00:00:00.000Z", - "6. +: /Collection/Platforms/Platform/ShortName" => "Not provided", - "7. +: /Collection/Platforms/Platform/LongName" => "Not provided", - "8. +: /Collection/Platforms/Platform/Type" => "Not provided", - "9. -: /Collection/AssociatedDIFs/DIF/EntryId" => "CIESIN_SEDAC_ANTHROMES_v2_1700", - "10. -: /Collection/InsertTime" => "2014-05-13T00:00:00Z", - "11. +: /Collection/InsertTime" => "2014-05-13T00:00:00.000Z", - "12. -: /Collection/LastUpdate" => "2015-08-04T00:00:00Z", - "13. +: /Collection/LastUpdate" => "2015-08-04T00:00:00.000Z", - "14. -: /Collection/LongName" => "Anthropogenic Biomes of the World, Version 2: 1700", - "15. +: /Collection/LongName" => "Not provided", - "16. -: /Collection/CollectionState" => "Final", - "17. +: /Collection/CollectionState" => "NOT PROVIDED", - "18. -: /Collection/Price" => "0", - "19. +: /Collection/Price" => " 0.00", - "20. -: /Collection/SpatialKeywords/Keyword[0]" => "Africa", - "21. -: /Collection/SpatialKeywords/Keyword[1]" => "Asia", - "22. +: /Collection/SpatialKeywords/Keyword[0]" => "AFRICA", - "23. +: /Collection/SpatialKeywords/Keyword[1]" => "GAZA STRIP", - "24. -: /Collection/Contacts/Contact[0]" => { - "Role" => "Archive", "HoursOfService" => "9:00 A.M. to 5:00 P.M., Monday to Friday", "OrganizationName" => "Socioeconomic Data and Applications Center (SEDAC)", "OrganizationAddresses" => { - "Address" => { - "StreetAddress" => "CIESIN, Columbia University, 61 Route 9W, P.O. Box 1000", "City" => "Palisades", "StateProvince" => "NY", "PostalCode" => "10964", "Country" => "USA" - } - }, "OrganizationPhones" => { - "Phone" => [{ - "Number" => "+1 845-365-8920", - "Type" => "Telephone" - }, { - "Number" => "+1 845-365-8922", - "Type" => "Fax" - }] - }, "OrganizationEmails" => { - "Email" => "ciesin.info@ciesin.columbia.edu" - }, "ContactPersons" => { - "ContactPerson" => { - "FirstName" => "SEDAC", "MiddleName" => "User", "LastName" => "Services" - } - } - }, - "25. +: /Collection/Contacts/Contact[0]" => { - "Role" => "PROCESSOR", "OrganizationName" => "SEDAC" - }, - "26. +: /Collection/Contacts/Contact[1]" => { - "Role" => "ARCHIVER", "OrganizationName" => "SEDAC" - }, - "27. +: /Collection/Contacts/Contact[2]" => { - "Role" => "ARCHIVER", "HoursOfService" => "9:00 A.M. to 5:00 P.M., Monday to Friday", "OrganizationName" => "Socioeconomic Data and Applications Center (SEDAC)", "OrganizationAddresses" => { - "Address" => { - "StreetAddress" => "CIESIN, Columbia University, 61 Route 9W, P.O. Box 1000", "City" => "Palisades", "StateProvince" => "NY", "PostalCode" => "10964", "Country" => "USA" - } - }, "OrganizationPhones" => { - "Phone" => [{ - "Number" => "+1 845-365-8920", - "Type" => "Telephone" - }, { - "Number" => "+1 845-365-8922", - "Type" => "Fax" - }] - }, "OrganizationEmails" => { - "Email" => "ciesin.info@ciesin.columbia.edu" - }, "ContactPersons" => { - "ContactPerson" => { - "FirstName" => "SEDAC", "MiddleName" => "User", "LastName" => "Services", "JobPosition" => "TECHNICAL CONTACT" - } - } - }, - "28. -: /Collection/SpatialInfo/SpatialCoverageType" => "Horizontal", - "29. +: /Collection/SpatialInfo/SpatialCoverageType" => "HORIZONTAL", - "30. -: /Collection/OnlineResources/OnlineResource/Type" => "DOI URL", - "31. +: /Collection/OnlineResources/OnlineResource/Type" => "CollectionURL : DATA SET LANDING PAGE", - "32. -: /Collection/Spatial/SpatialCoverageType" => "Horizontal", - "33. +: /Collection/Spatial/SpatialCoverageType" => "HORIZONTAL", - "34. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate" => "-180.000000", - "35. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate" => "-180.0", - "36. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate" => "90.000000", - "37. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate" => "90.0", - "38. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate" => "180.000000", - "39. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate" => "180.0", - "40. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate" => "-90.000000", - "41. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate" => "-90.0" - } - end - - def iso_text_report - 'application/iso:smap+xml - - 1. -: /DS_Series/schemaLocation - 2. -: /DS_Series/seriesMetadata/MI_Metadata/fileIdentifier/FileName - 3. -: /DS_Series/seriesMetadata/MI_Metadata/characterSet/MD_CharacterSetCode - 4. -: /DS_Series/seriesMetadata/MI_Metadata/hierarchyLevel/MD_ScopeCode - 5. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/attributeDescription - 6. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/contentType - 7. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/code/CharacterString - 8. +: /DS_Series/seriesMetadata/MI_Metadata/contentInfo/MD_ImageDescription/processingLevelCode/MD_Identifier/codeSpace/CharacterString - 9. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardName/CharacterString - 10. +: /DS_Series/seriesMetadata/MI_Metadata/dataQualityInfo/DQ_DataQuality/scope/DQ_Scope/level/MD_ScopeCode - 11. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/id - 12. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/code/CharacterString - 13. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/identifier/MD_Identifier/codeSpace/CharacterString - 14. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/description/CharacterString - 15. +: /DS_Series/seriesMetadata/MI_Metadata/acquisitionInformation/MI_AcquisitionInformation/platform/EOS_Platform/instrument/nilReason - 16. -: /DS_Series/seriesMetadata/MI_Metadata/metadataStandardVersion/CharacterString - 17. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0] - 18. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1] - 19. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[2] - 20. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[3] - 21. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[4] - 22. -: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[5] - 23. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[0] - 24. +: /DS_Series/seriesMetadata/MI_Metadata/identificationInfo[1] - 25. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/organisationName/CharacterString - 26. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/address/CI_Address/electronicMailAddress/CharacterString - 27. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/contactInfo/CI_Contact/onlineResource/CI_OnlineResource/linkage/URL - 28. -: /DS_Series/seriesMetadata/MI_Metadata/contact/CI_ResponsibleParty/role/CI_RoleCode - 29. +: /DS_Series/seriesMetadata/MI_Metadata/contact/href - 30. -: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date - 31. +: /DS_Series/seriesMetadata/MI_Metadata/dateStamp/Date' - end - - def echo_text_report - 'application/echo10+xml - - 1. -: /Collection/Orderable - 2. -: /Collection/Visible - 3. -: /Collection/MaintenanceAndUpdateFrequency - 4. +: /Collection/Temporal/EndsAtPresentFlag - 5. +: /Collection/Temporal/RangeDateTime/BeginningDateTime - 6. +: /Collection/Platforms/Platform/ShortName - 7. +: /Collection/Platforms/Platform/LongName - 8. +: /Collection/Platforms/Platform/Type - 9. -: /Collection/AssociatedDIFs/DIF/EntryId - 10. -: /Collection/InsertTime - 11. +: /Collection/InsertTime - 12. -: /Collection/LastUpdate - 13. +: /Collection/LastUpdate - 14. -: /Collection/LongName - 15. +: /Collection/LongName - 16. -: /Collection/CollectionState - 17. +: /Collection/CollectionState - 18. -: /Collection/Price - 19. +: /Collection/Price - 20. -: /Collection/SpatialKeywords/Keyword[0] - 21. -: /Collection/SpatialKeywords/Keyword[1] - 22. +: /Collection/SpatialKeywords/Keyword[0] - 23. +: /Collection/SpatialKeywords/Keyword[1] - 24. -: /Collection/Contacts/Contact[0] - 25. +: /Collection/Contacts/Contact[0] - 26. +: /Collection/Contacts/Contact[1] - 27. +: /Collection/Contacts/Contact[2] - 28. -: /Collection/SpatialInfo/SpatialCoverageType - 29. +: /Collection/SpatialInfo/SpatialCoverageType - 30. -: /Collection/OnlineResources/OnlineResource/Type - 31. +: /Collection/OnlineResources/OnlineResource/Type - 32. -: /Collection/Spatial/SpatialCoverageType - 33. +: /Collection/Spatial/SpatialCoverageType - 34. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate - 35. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/WestBoundingCoordinate - 36. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate - 37. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/NorthBoundingCoordinate - 38. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate - 39. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/EastBoundingCoordinate - 40. -: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate - 41. +: /Collection/Spatial/HorizontalSpatialDomain/Geometry/BoundingRectangle/SouthBoundingCoordinate' - end - - def dif_text_report - 'application/dif10+xml - - 1. -: /DIF/Temporal_Coverage/Temporal_Range_Type - 2. -: /DIF/Related_URL[1] - 3. +: /DIF/Related_URL[1]' - end - end -end From 28c5f3425ffa1c5f2c118abe75ab6817f0e78e52 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Mon, 10 Aug 2020 11:14:34 -0400 Subject: [PATCH 29/38] MMT-2346: adjusted modal styling --- .../javascripts/change_current_provider.coffee | 3 +++ app/views/shared/_loss_report_modal.html.erb | 14 +++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/change_current_provider.coffee b/app/assets/javascripts/change_current_provider.coffee index 5c1b626c9..858592d15 100644 --- a/app/assets/javascripts/change_current_provider.coffee +++ b/app/assets/javascripts/change_current_provider.coffee @@ -4,6 +4,9 @@ $(document).ready -> overlay: 0.6 closeButton: '.modal-close' + $('a.modal-close-link').on 'click', (element) -> + $('#close-loss-report-modal').click() + $('a.loss-report').on 'click', (element) -> $('#loss-report-modal').hide() $('#loss-report-to-edit-collection').click() diff --git a/app/views/shared/_loss_report_modal.html.erb b/app/views/shared/_loss_report_modal.html.erb index 617abd15e..3537a88de 100644 --- a/app/views/shared/_loss_report_modal.html.erb +++ b/app/views/shared/_loss_report_modal.html.erb @@ -1,19 +1,19 @@
- +

Data Loss Warning

This collection possesses a non-UMM native format and could experience data loss while being translated to UMM. Would you like to view a loss report before you begin editing?

<% if current_provider?(options[:provider_id]) %> - <%= link_to 'Yes', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'modal-close eui-btn--blue', target: :_blank %> - <%= link_to 'No', edit_collection_path(revision_id: options[:revision_id]), class: 'modal-close eui-btn' %> + <%= link_to 'No', edit_collection_path(revision_id: options[:revision_id]), class: 'eui-btn modal-close-link' %> + <%= link_to 'Yes', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--blue modal-close-link', target: :_blank %> <% elsif available_provider?(options[:provider_id]) %> - <%= link_to 'Yes', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'loss-report eui-btn--blue', target: :_blank %> - <%= link_to 'No', '#', class: 'loss-report eui-btn', data: {}%> + <%= link_to 'No', '#', class: 'eui-btn loss-report', data: {}%> + <%= link_to 'Yes', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--blue loss-report', target: :_blank %> <% else %> - <%= link_to 'Yes', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'modal-close eui-btn--blue', target: :_blank %> - No + No + <%= link_to 'Yes', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--blue modal-close-link', target: :_blank %> <% end %> <%= link_to 'Invisible Link', '#not-current-provider-modal', class: 'display-modal not-current-provider eui-btn is-invisible', id: 'loss-report-to-edit-collection', data: { provider: @provider_id, record_action: 'edit-collection' } %>

From afe1331ad53f0b17b95acfa4bcd2a72940ae6889 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Wed, 12 Aug 2020 12:55:59 -0400 Subject: [PATCH 30/38] MMT-2346: fixed another conflict --- app/controllers/collections_controller.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/controllers/collections_controller.rb b/app/controllers/collections_controller.rb index 390a2a3f0..77ea18871 100644 --- a/app/controllers/collections_controller.rb +++ b/app/controllers/collections_controller.rb @@ -5,7 +5,6 @@ class CollectionsController < ManageCollectionsController include LossReportHelper before_action :set_collection - before_action :prepare_translated_collections, only: [:loss_report] before_action :ensure_correct_collection_provider, only: [:edit, :clone, :revert, :destroy] layout 'collection_preview', only: [:show] From d4c872e125f240ccc26df22fde6736bcab308eb3 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Thu, 13 Aug 2020 10:02:22 -0400 Subject: [PATCH 31/38] MMT-2346: adjusted some styling --- .../stylesheets/components/_buttons.scss | 29 +++++++++++++++++-- app/views/collections/show.html.erb | 2 +- app/views/shared/_loss_report_modal.html.erb | 14 ++++----- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/app/assets/stylesheets/components/_buttons.scss b/app/assets/stylesheets/components/_buttons.scss index aa7db57f0..ce57cae14 100644 --- a/app/assets/stylesheets/components/_buttons.scss +++ b/app/assets/stylesheets/components/_buttons.scss @@ -26,7 +26,32 @@ &:hover { background-color: darken($red, 6%); } -} +} + +.eui-btn--blue { + @extend .eui-btn; + + background-color: $ocean-blue; + color: $white !important; + padding: 0.5em 1em; + margin-right: 0.625em; + + &:hover { + background-color: darken($ocean-blue, 6%); + } +} + +.eui-btn--light-blue { + @extend .eui-btn; + + background-color: $sky-blue; + color: $white !important; + padding: 0.5em 1em; + + &:hover { + background-color: darken($sky-blue, 6%); + } +} button { &:disabled { @@ -36,4 +61,4 @@ button { background-color: lighten($dolphin-grey, 10%); } } -} \ No newline at end of file +} diff --git a/app/views/collections/show.html.erb b/app/views/collections/show.html.erb index bad1c05c2..fb042d0c2 100644 --- a/app/views/collections/show.html.erb +++ b/app/views/collections/show.html.erb @@ -57,7 +57,7 @@

- <% if !@revisions.first.fetch('meta', {})['format'].include?('umm') %> + <% if !@revisions.first.fetch('meta', {})['format'].include?('umm') && (current_provider?(@provider_id) || available_provider?(@provider_id)) %> <%= link_to 'Edit Collection Record', '#loss-report-modal', class: 'display-modal eui-btn--link bar-after' %> <% else %> <% if current_provider?(@provider_id) %> diff --git a/app/views/shared/_loss_report_modal.html.erb b/app/views/shared/_loss_report_modal.html.erb index 3537a88de..1edadd3b9 100644 --- a/app/views/shared/_loss_report_modal.html.erb +++ b/app/views/shared/_loss_report_modal.html.erb @@ -1,4 +1,3 @@ -

@@ -6,14 +5,13 @@

This collection possesses a non-UMM native format and could experience data loss while being translated to UMM. Would you like to view a loss report before you begin editing?

<% if current_provider?(options[:provider_id]) %> - <%= link_to 'No', edit_collection_path(revision_id: options[:revision_id]), class: 'eui-btn modal-close-link' %> - <%= link_to 'Yes', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--blue modal-close-link', target: :_blank %> + <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--light-blue', target: :_blank %> + <%= link_to 'Edit Collection', edit_collection_path(revision_id: options[:revision_id]), class: 'eui-btn--blue modal-close-link' %> + <%= link_to 'Cancel', 'javascript:void(0);', class: 'eui-btn modal-close-link' %> <% elsif available_provider?(options[:provider_id]) %> - <%= link_to 'No', '#', class: 'eui-btn loss-report', data: {}%> - <%= link_to 'Yes', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--blue loss-report', target: :_blank %> - <% else %> - No - <%= link_to 'Yes', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--blue modal-close-link', target: :_blank %> + <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--light-blue loss-report', target: :_blank %> + <%= link_to 'Edit Collection', '#', class: 'eui-btn--blue loss-report' %> + <%= link_to 'Cancel', 'javascript:void(0);', class: 'eui-btn modal-close-link' %> <% end %> <%= link_to 'Invisible Link', '#not-current-provider-modal', class: 'display-modal not-current-provider eui-btn is-invisible', id: 'loss-report-to-edit-collection', data: { provider: @provider_id, record_action: 'edit-collection' } %>

From 76fb3660362df549ff4ade1f3d0ce1ecadbb1dba Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Thu, 13 Aug 2020 13:08:29 -0400 Subject: [PATCH 32/38] MMT-2346: adjusted specs and styling --- .../stylesheets/components/_buttons.scss | 13 ---- app/views/collections/show.html.erb | 9 ++- app/views/shared/_loss_report_modal.html.erb | 16 ++--- .../collections/loss_report_modal_spec.rb | 65 ++++++++++++------- 4 files changed, 55 insertions(+), 48 deletions(-) diff --git a/app/assets/stylesheets/components/_buttons.scss b/app/assets/stylesheets/components/_buttons.scss index ce57cae14..c122d3219 100644 --- a/app/assets/stylesheets/components/_buttons.scss +++ b/app/assets/stylesheets/components/_buttons.scss @@ -28,19 +28,6 @@ } } -.eui-btn--blue { - @extend .eui-btn; - - background-color: $ocean-blue; - color: $white !important; - padding: 0.5em 1em; - margin-right: 0.625em; - - &:hover { - background-color: darken($ocean-blue, 6%); - } -} - .eui-btn--light-blue { @extend .eui-btn; diff --git a/app/views/collections/show.html.erb b/app/views/collections/show.html.erb index fb042d0c2..23b2956b5 100644 --- a/app/views/collections/show.html.erb +++ b/app/views/collections/show.html.erb @@ -57,7 +57,8 @@

- <% if !@revisions.first.fetch('meta', {})['format'].include?('umm') && (current_provider?(@provider_id) || available_provider?(@provider_id)) %> + <% native_format = @revisions.first.fetch('meta', {})['format'] %> + <% if !native_format.include?('umm') && (current_provider?(@provider_id) || available_provider?(@provider_id)) %> <%= link_to 'Edit Collection Record', '#loss-report-modal', class: 'display-modal eui-btn--link bar-after' %> <% else %> <% if current_provider?(@provider_id) %> @@ -119,11 +120,9 @@ <%= render partial: 'shared/loss_report_modal', locals: { options: { provider_id: @provider_id, - collection: @collection, + native_format: native_format, concept_id: @concept_id, - revision_id: @revision_id, - draft: @draft, - draft_id: @draft.try(:id) } + revision_id: @revision_id } } %> diff --git a/app/views/shared/_loss_report_modal.html.erb b/app/views/shared/_loss_report_modal.html.erb index 1edadd3b9..8f0b4a9b7 100644 --- a/app/views/shared/_loss_report_modal.html.erb +++ b/app/views/shared/_loss_report_modal.html.erb @@ -1,19 +1,19 @@

-

Data Loss Warning

-

This collection possesses a non-UMM native format and could experience data loss while being translated to UMM. Would you like to view a loss report before you begin editing?

-

+

Conversion to UMM Format Required

+

The native format of this collection is <%= options[:native_format].split('/').last.gsub('+xml','').upcase %>. Editing this record using the MMT will convert it to UMM-JSON, which may result in data loss.

+

<% if current_provider?(options[:provider_id]) %> - <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--light-blue', target: :_blank %> - <%= link_to 'Edit Collection', edit_collection_path(revision_id: options[:revision_id]), class: 'eui-btn--blue modal-close-link' %> + <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--light-blue', style: 'margin-right: 1em', target: :_blank %> + <%= link_to 'Edit Collection', edit_collection_path(revision_id: options[:revision_id]), class: 'eui-btn--blue modal-close-link', style: 'margin-right: 1em' %> <%= link_to 'Cancel', 'javascript:void(0);', class: 'eui-btn modal-close-link' %> <% elsif available_provider?(options[:provider_id]) %> - <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--light-blue loss-report', target: :_blank %> - <%= link_to 'Edit Collection', '#', class: 'eui-btn--blue loss-report' %> + <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--light-blue', style: 'margin-right: 1em', target: :_blank %> + <%= link_to 'Edit Collection', '#', class: 'eui-btn--blue loss-report', style: 'margin-right: 1em' %> <%= link_to 'Cancel', 'javascript:void(0);', class: 'eui-btn modal-close-link' %> <% end %> - <%= link_to 'Invisible Link', '#not-current-provider-modal', class: 'display-modal not-current-provider eui-btn is-invisible', id: 'loss-report-to-edit-collection', data: { provider: @provider_id, record_action: 'edit-collection' } %> + <%= link_to 'Invisible Link', '#not-current-provider-modal', class: 'display-modal not-current-provider eui-btn is-invisible', id: 'loss-report-to-edit-collection', data: { provider: options[:provider_id], record_action: 'edit-collection' } %>

diff --git a/spec/features/collections/loss_report_modal_spec.rb b/spec/features/collections/loss_report_modal_spec.rb index 678970494..c5b59160b 100644 --- a/spec/features/collections/loss_report_modal_spec.rb +++ b/spec/features/collections/loss_report_modal_spec.rb @@ -1,5 +1,4 @@ -# need to make sure the change current provider modal specs don't fail describe 'loss report modal', js: true do # this is an echo collection (SEDAC provider) let(:cmr_response) { cmr_client.get_collections({'EntryTitle': 'Anthropogenic Biomes of the World, Version 2: 1700'}) } @@ -14,30 +13,44 @@ click_on 'Edit Collection Record' end - it 'displays the loss-report-modal' do + it 'displays the loss-report-modal with correct links' do within '#loss-report-modal' do - expect(page).to have_link('Yes', href: loss_report_collections_path(concept_id, format: 'json')) - expect(page).to have_link('No', href: edit_collection_path(id: concept_id)) + expect(page).to have_content('The native format of this collection is ECHO10. Editing this record using the MMT will convert it to UMM-JSON, which may result in data loss.') + expect(page).to have_link('View Loss Report', href: loss_report_collections_path(concept_id, format: 'json')) + expect(page).to have_link('Edit Collection', href: edit_collection_path(id: concept_id)) + expect(page).to have_link('Cancel', href: 'javascript:void(0);') end end - context 'when the "No" button is clicked' do + context 'when the "Edit Collection" button is clicked' do it 'does not display the not-current-provider-modal' do within '#loss-report-modal' do - click_on 'No' + click_on 'Edit Collection' end expect(page).to have_no_css('#not-current-provider-modal') end end - context 'when the "Yes" button is clicked' do - it 'does not display display the not-current-provider-modal' do + context 'when the "View Loss Report" button is clicked' do + it 'does not display display the not-current-provider-modal and does not close the loss-report-modal' do within '#loss-report-modal' do - click_on 'Yes' + click_on 'View Loss Report' end expect(page).to have_no_css('#not-current-provider-modal') + expect(page).to have_css('#loss-report-modal') + end + end + + context 'when the "Cancel" button is clicked' do + it 'does not display display the not-current-provider-modal and closes loss-report-modal' do + within '#loss-report-modal' do + click_on 'Cancel' + end + + expect(page).to have_no_css('#not-current-provider-modal') + expect(page).to have_no_css('#loss-report-modal') end end end @@ -50,17 +63,19 @@ click_on 'Edit Collection Record' end - it 'displays the loss-report-modal' do + it 'displays the loss-report-modal with correct links' do within '#loss-report-modal' do - expect(page).to have_link('Yes', href: loss_report_collections_path(concept_id, format: 'json')) - expect(page).to have_link('No', href: '#') + expect(page).to have_content('The native format of this collection is ECHO10. Editing this record using the MMT will convert it to UMM-JSON, which may result in data loss.') + expect(page).to have_link('View Loss Report', href: loss_report_collections_path(concept_id, format: 'json')) + expect(page).to have_link('Edit Collection', href: '#') + expect(page).to have_link('Cancel', href: 'javascript:void(0);') end end - context 'when the "No" button is clicked' do + context 'when the "Edit Collection" button is clicked' do it 'displays the not-current-provider-modal' do within '#loss-report-modal' do - click_on 'No' + click_on 'Edit Collection' end expect(page).to have_css('#not-current-provider-modal') @@ -72,21 +87,27 @@ end end - context 'when the "Yes" button is clicked' do - it 'displays the not-current-provider-modal' do + context 'when the "View Loss Report" button is clicked' do + it 'does not display the not-current-provider-modal and does not close the loss-report-modal' do within '#loss-report-modal' do - click_on 'Yes' + click_on 'View Loss Report' end - expect(page).to have_css('#not-current-provider-modal') + expect(page).to have_css('#loss-report-modal') + expect(page).to have_no_css('#not-current-provider-modal') + end + end - within '#not-current-provider-modal' do - expect(page).to have_link('Yes', href: '#') - expect(page).to have_link('No', href: 'javascript:void(0)') + context 'when the "Cancel" button is clicked' do + it 'does not display display the not-current-provider-modal and closes loss-report-modal' do + within '#loss-report-modal' do + click_on 'Cancel' end + + expect(page).to have_no_css('#not-current-provider-modal') + expect(page).to have_no_css('#loss-report-modal') end end - end end end From 3e0c54ed870a37f3cf501b0662a713a4d0f1a643 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Thu, 13 Aug 2020 15:05:42 -0400 Subject: [PATCH 33/38] MMT-2346: Fixed affected specs --- app/assets/stylesheets/components/_buttons.scss | 6 +++--- app/views/shared/_loss_report_modal.html.erb | 6 +++--- .../create_collection_draft_from_dif10_collection_spec.rb | 4 ++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/components/_buttons.scss b/app/assets/stylesheets/components/_buttons.scss index c122d3219..e620d6fd0 100644 --- a/app/assets/stylesheets/components/_buttons.scss +++ b/app/assets/stylesheets/components/_buttons.scss @@ -28,15 +28,15 @@ } } -.eui-btn--light-blue { +.eui-btn--steel-blue { @extend .eui-btn; - background-color: $sky-blue; + background-color: $steel-blue; color: $white !important; padding: 0.5em 1em; &:hover { - background-color: darken($sky-blue, 6%); + background-color: darken($steel-blue, 6%); } } diff --git a/app/views/shared/_loss_report_modal.html.erb b/app/views/shared/_loss_report_modal.html.erb index 8f0b4a9b7..86c56a575 100644 --- a/app/views/shared/_loss_report_modal.html.erb +++ b/app/views/shared/_loss_report_modal.html.erb @@ -2,14 +2,14 @@

Conversion to UMM Format Required

-

The native format of this collection is <%= options[:native_format].split('/').last.gsub('+xml','').upcase %>. Editing this record using the MMT will convert it to UMM-JSON, which may result in data loss.

+

The native format of this collection is <%= options[:native_format].split('/').last.gsub('+xml','').upcase %>. Editing this record using the MMT will convert it to UMM-JSON, which may result in data loss. Select 'View Loss Report' to see how the conversion will affect this record.

<% if current_provider?(options[:provider_id]) %> - <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--light-blue', style: 'margin-right: 1em', target: :_blank %> + <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--steel-blue', style: 'margin-right: 1em', target: :_blank %> <%= link_to 'Edit Collection', edit_collection_path(revision_id: options[:revision_id]), class: 'eui-btn--blue modal-close-link', style: 'margin-right: 1em' %> <%= link_to 'Cancel', 'javascript:void(0);', class: 'eui-btn modal-close-link' %> <% elsif available_provider?(options[:provider_id]) %> - <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--light-blue', style: 'margin-right: 1em', target: :_blank %> + <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--steel-blue', style: 'margin-right: 1em', target: :_blank %> <%= link_to 'Edit Collection', '#', class: 'eui-btn--blue loss-report', style: 'margin-right: 1em' %> <%= link_to 'Cancel', 'javascript:void(0);', class: 'eui-btn modal-close-link' %> <% end %> diff --git a/spec/features/collection_drafts/create_collection_draft_from_dif10_collection_spec.rb b/spec/features/collection_drafts/create_collection_draft_from_dif10_collection_spec.rb index 5d7796289..6ee0cccf9 100644 --- a/spec/features/collection_drafts/create_collection_draft_from_dif10_collection_spec.rb +++ b/spec/features/collection_drafts/create_collection_draft_from_dif10_collection_spec.rb @@ -29,6 +29,10 @@ before do click_on 'Edit Collection Record' + within '#loss-report-modal' do + click_on 'Edit Collection' + end + within '.metadata' do click_on 'Spatial Information', match: :first end From bbd315a221ff380b01654a2f8f24bbab73953874 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Fri, 14 Aug 2020 16:38:05 -0400 Subject: [PATCH 34/38] MMT-2346: completed PR change requests --- .../stylesheets/components/_buttons.scss | 4 ++ app/views/shared/_loss_report_modal.html.erb | 8 ++-- .../collections/loss_report_modal_spec.rb | 38 ++++++++++++------- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/app/assets/stylesheets/components/_buttons.scss b/app/assets/stylesheets/components/_buttons.scss index e620d6fd0..a34e19ab3 100644 --- a/app/assets/stylesheets/components/_buttons.scss +++ b/app/assets/stylesheets/components/_buttons.scss @@ -40,6 +40,10 @@ } } +.button-spacing { + margin-right: 1em; +} + button { &:disabled { background-color: lighten($dolphin-grey, 10%); diff --git a/app/views/shared/_loss_report_modal.html.erb b/app/views/shared/_loss_report_modal.html.erb index 86c56a575..37c18566b 100644 --- a/app/views/shared/_loss_report_modal.html.erb +++ b/app/views/shared/_loss_report_modal.html.erb @@ -5,12 +5,12 @@

The native format of this collection is <%= options[:native_format].split('/').last.gsub('+xml','').upcase %>. Editing this record using the MMT will convert it to UMM-JSON, which may result in data loss. Select 'View Loss Report' to see how the conversion will affect this record.

<% if current_provider?(options[:provider_id]) %> - <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--steel-blue', style: 'margin-right: 1em', target: :_blank %> - <%= link_to 'Edit Collection', edit_collection_path(revision_id: options[:revision_id]), class: 'eui-btn--blue modal-close-link', style: 'margin-right: 1em' %> + <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'button-spacing eui-btn--steel-blue', target: :_blank %> + <%= link_to 'Edit Collection', edit_collection_path(revision_id: options[:revision_id]), class: 'eui-btn--blue modal-close-link button-spacing' %> <%= link_to 'Cancel', 'javascript:void(0);', class: 'eui-btn modal-close-link' %> <% elsif available_provider?(options[:provider_id]) %> - <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn--steel-blue', style: 'margin-right: 1em', target: :_blank %> - <%= link_to 'Edit Collection', '#', class: 'eui-btn--blue loss-report', style: 'margin-right: 1em' %> + <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'button-spacing eui-btn--steel-blue', target: :_blank %> + <%= link_to 'Edit Collection', '#', class: 'eui-btn--blue loss-report button-spacing' %> <%= link_to 'Cancel', 'javascript:void(0);', class: 'eui-btn modal-close-link' %> <% end %> <%= link_to 'Invisible Link', '#not-current-provider-modal', class: 'display-modal not-current-provider eui-btn is-invisible', id: 'loss-report-to-edit-collection', data: { provider: options[:provider_id], record_action: 'edit-collection' } %> diff --git a/spec/features/collections/loss_report_modal_spec.rb b/spec/features/collections/loss_report_modal_spec.rb index c5b59160b..4c8e44cca 100644 --- a/spec/features/collections/loss_report_modal_spec.rb +++ b/spec/features/collections/loss_report_modal_spec.rb @@ -23,32 +23,42 @@ end context 'when the "Edit Collection" button is clicked' do - it 'does not display the not-current-provider-modal' do + before do within '#loss-report-modal' do click_on 'Edit Collection' end - + end + it 'does not display the not-current-provider-modal' do + expect(page).to have_content('Metadata Fields') + expect(page).to have_content('Collection Information') + expect(page).to have_content('Acquisition Information') + expect(page).to have_content('Data Contacts') + expect(page).to have_content('Metadata Information') expect(page).to have_no_css('#not-current-provider-modal') end end context 'when the "View Loss Report" button is clicked' do - it 'does not display display the not-current-provider-modal and does not close the loss-report-modal' do + before do within '#loss-report-modal' do click_on 'View Loss Report' end - + end + it 'does not display the not-current-provider-modal and does not close the loss-report-modal' do expect(page).to have_no_css('#not-current-provider-modal') expect(page).to have_css('#loss-report-modal') end end context 'when the "Cancel" button is clicked' do - it 'does not display display the not-current-provider-modal and closes loss-report-modal' do + before do within '#loss-report-modal' do click_on 'Cancel' end - + end + it 'does not display the not-current-provider-modal and closes loss-report-modal' do + expect(page).to have_no_content('Metadata Preview') + expect(page).to have_link('Edit Collection Record', href: '#loss-report-modal') expect(page).to have_no_css('#not-current-provider-modal') expect(page).to have_no_css('#loss-report-modal') end @@ -73,13 +83,13 @@ end context 'when the "Edit Collection" button is clicked' do - it 'displays the not-current-provider-modal' do + before do within '#loss-report-modal' do click_on 'Edit Collection' end - + end + it 'displays the not-current-provider-modal' do expect(page).to have_css('#not-current-provider-modal') - within '#not-current-provider-modal' do expect(page).to have_link('Yes', href: '#') expect(page).to have_link('No', href: 'javascript:void(0)') @@ -88,22 +98,24 @@ end context 'when the "View Loss Report" button is clicked' do - it 'does not display the not-current-provider-modal and does not close the loss-report-modal' do + before do within '#loss-report-modal' do click_on 'View Loss Report' end - + end + it 'does not display the not-current-provider-modal and does not close the loss-report-modal' do expect(page).to have_css('#loss-report-modal') expect(page).to have_no_css('#not-current-provider-modal') end end context 'when the "Cancel" button is clicked' do - it 'does not display display the not-current-provider-modal and closes loss-report-modal' do + before do within '#loss-report-modal' do click_on 'Cancel' end - + end + it 'does not display the not-current-provider-modal and closes loss-report-modal' do expect(page).to have_no_css('#not-current-provider-modal') expect(page).to have_no_css('#loss-report-modal') end From 1faafc04f2551ab696d9e822da6d57ce92d62d50 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Mon, 17 Aug 2020 10:43:11 -0400 Subject: [PATCH 35/38] MMT-2346: remove LARC from available context --- spec/features/collections/loss_report_modal_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/collections/loss_report_modal_spec.rb b/spec/features/collections/loss_report_modal_spec.rb index 4c8e44cca..e9ef99ed3 100644 --- a/spec/features/collections/loss_report_modal_spec.rb +++ b/spec/features/collections/loss_report_modal_spec.rb @@ -8,7 +8,7 @@ context 'when provider context does not need to be changed' do before do - login(provider: 'SEDAC', providers: %w[SEDAC LARC]) + login(provider: 'SEDAC', providers: %w[SEDAC]) visit collection_path(concept_id) click_on 'Edit Collection Record' end From 365a9039b9e18024a1c301217316846a8db60644 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Wed, 19 Aug 2020 11:52:39 -0400 Subject: [PATCH 36/38] MMT-2346: made specs more readable, improved styling of loss-report-modal --- Gemfile | 2 -- .../change_current_provider.coffee | 3 --- .../stylesheets/components/_buttons.scss | 2 +- app/assets/stylesheets/components/_modal.scss | 1 - app/views/shared/_loss_report_modal.html.erb | 14 +++++------ .../collections/loss_report_modal_spec.rb | 24 ++++++++++--------- 6 files changed, 21 insertions(+), 25 deletions(-) diff --git a/Gemfile b/Gemfile index 9e57b7d02..ecbe52f2f 100644 --- a/Gemfile +++ b/Gemfile @@ -75,8 +75,6 @@ gem 'aasm' gem 'browser' -gem 'nokogiri-diff', '~> 0.2.0' # for comparing xml documents - # collections metadata preview # run this command to work from a local copy of the gem's repo # bundle config local.cmr_metadata_preview /path/to/local/git/repository diff --git a/app/assets/javascripts/change_current_provider.coffee b/app/assets/javascripts/change_current_provider.coffee index 04de68e75..6889ddd11 100644 --- a/app/assets/javascripts/change_current_provider.coffee +++ b/app/assets/javascripts/change_current_provider.coffee @@ -4,9 +4,6 @@ $(document).ready -> overlay: 0.6 closeButton: '.modal-close' - $('a.modal-close-link').on 'click', (element) -> - $('#close-loss-report-modal').click() - $('a.loss-report').on 'click', (element) -> $('#loss-report-modal').hide() $('#loss-report-to-edit-collection').click() diff --git a/app/assets/stylesheets/components/_buttons.scss b/app/assets/stylesheets/components/_buttons.scss index a34e19ab3..10708a3be 100644 --- a/app/assets/stylesheets/components/_buttons.scss +++ b/app/assets/stylesheets/components/_buttons.scss @@ -41,7 +41,7 @@ } .button-spacing { - margin-right: 1em; + margin-right: 0.625em; } button { diff --git a/app/assets/stylesheets/components/_modal.scss b/app/assets/stylesheets/components/_modal.scss index 682715c15..785e24d93 100644 --- a/app/assets/stylesheets/components/_modal.scss +++ b/app/assets/stylesheets/components/_modal.scss @@ -15,7 +15,6 @@ display: none; } -#loss-report-modal, #provider-context, #help-modal, #download-xml-modal, diff --git a/app/views/shared/_loss_report_modal.html.erb b/app/views/shared/_loss_report_modal.html.erb index 37c18566b..09ff8b735 100644 --- a/app/views/shared/_loss_report_modal.html.erb +++ b/app/views/shared/_loss_report_modal.html.erb @@ -3,17 +3,17 @@

Conversion to UMM Format Required

The native format of this collection is <%= options[:native_format].split('/').last.gsub('+xml','').upcase %>. Editing this record using the MMT will convert it to UMM-JSON, which may result in data loss. Select 'View Loss Report' to see how the conversion will affect this record.

-

+

+ <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'eui-btn eui-btn--steel-blue', target: :_blank %> + <% if current_provider?(options[:provider_id]) %> - <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'button-spacing eui-btn--steel-blue', target: :_blank %> - <%= link_to 'Edit Collection', edit_collection_path(revision_id: options[:revision_id]), class: 'eui-btn--blue modal-close-link button-spacing' %> - <%= link_to 'Cancel', 'javascript:void(0);', class: 'eui-btn modal-close-link' %> + <%= link_to 'Edit Collection', edit_collection_path(revision_id: options[:revision_id]), class: 'eui-btn--blue modal-close button-spacing' %> <% elsif available_provider?(options[:provider_id]) %> - <%= link_to 'View Loss Report', loss_report_collections_path(options[:concept_id], format: 'json'), class: 'button-spacing eui-btn--steel-blue', target: :_blank %> <%= link_to 'Edit Collection', '#', class: 'eui-btn--blue loss-report button-spacing' %> - <%= link_to 'Cancel', 'javascript:void(0);', class: 'eui-btn modal-close-link' %> <% end %> - <%= link_to 'Invisible Link', '#not-current-provider-modal', class: 'display-modal not-current-provider eui-btn is-invisible', id: 'loss-report-to-edit-collection', data: { provider: options[:provider_id], record_action: 'edit-collection' } %> + + <%= link_to 'Cancel', 'javascript:void(0);', class: 'eui-btn modal-close' %> + <%= link_to 'Invisible Link', '#not-current-provider-modal', class: 'display-modal not-current-provider is-invisible', id: 'loss-report-to-edit-collection', data: { provider: options[:provider_id], record_action: 'edit-collection' } %>

diff --git a/spec/features/collections/loss_report_modal_spec.rb b/spec/features/collections/loss_report_modal_spec.rb index e9ef99ed3..f8a8ddb44 100644 --- a/spec/features/collections/loss_report_modal_spec.rb +++ b/spec/features/collections/loss_report_modal_spec.rb @@ -15,7 +15,7 @@ it 'displays the loss-report-modal with correct links' do within '#loss-report-modal' do - expect(page).to have_content('The native format of this collection is ECHO10. Editing this record using the MMT will convert it to UMM-JSON, which may result in data loss.') + expect(page).to have_content("The native format of this collection is ECHO10. Editing this record using the MMT will convert it to UMM-JSON, which may result in data loss. Select 'View Loss Report' to see how the conversion will affect this record.") expect(page).to have_link('View Loss Report', href: loss_report_collections_path(concept_id, format: 'json')) expect(page).to have_link('Edit Collection', href: edit_collection_path(id: concept_id)) expect(page).to have_link('Cancel', href: 'javascript:void(0);') @@ -28,7 +28,7 @@ click_on 'Edit Collection' end end - it 'does not display the not-current-provider-modal' do + it 'opens collection draft without displaying the not-current-provider-modal' do expect(page).to have_content('Metadata Fields') expect(page).to have_content('Collection Information') expect(page).to have_content('Acquisition Information') @@ -44,9 +44,11 @@ click_on 'View Loss Report' end end - it 'does not display the not-current-provider-modal and does not close the loss-report-modal' do + it 'does not close the loss-report-modal and does not open the not-current-provider-modal' do expect(page).to have_no_css('#not-current-provider-modal') - expect(page).to have_css('#loss-report-modal') + within '#loss-report-modal' do + expect(page).to have_content("The native format of this collection is ECHO10. Editing this record using the MMT will convert it to UMM-JSON, which may result in data loss. Select 'View Loss Report' to see how the conversion will affect this record.") + end end end @@ -56,7 +58,7 @@ click_on 'Cancel' end end - it 'does not display the not-current-provider-modal and closes loss-report-modal' do + it 'closes loss-report-modal and does not display the not-current-provider-modal' do expect(page).to have_no_content('Metadata Preview') expect(page).to have_link('Edit Collection Record', href: '#loss-report-modal') expect(page).to have_no_css('#not-current-provider-modal') @@ -75,7 +77,7 @@ it 'displays the loss-report-modal with correct links' do within '#loss-report-modal' do - expect(page).to have_content('The native format of this collection is ECHO10. Editing this record using the MMT will convert it to UMM-JSON, which may result in data loss.') + expect(page).to have_content("The native format of this collection is ECHO10. Editing this record using the MMT will convert it to UMM-JSON, which may result in data loss. Select 'View Loss Report' to see how the conversion will affect this record.") expect(page).to have_link('View Loss Report', href: loss_report_collections_path(concept_id, format: 'json')) expect(page).to have_link('Edit Collection', href: '#') expect(page).to have_link('Cancel', href: 'javascript:void(0);') @@ -88,8 +90,8 @@ click_on 'Edit Collection' end end - it 'displays the not-current-provider-modal' do - expect(page).to have_css('#not-current-provider-modal') + it 'closes loss-report-modal and opens the not-current-provider-modal' do + expect(page).to have_no_css('#loss-report-modal') within '#not-current-provider-modal' do expect(page).to have_link('Yes', href: '#') expect(page).to have_link('No', href: 'javascript:void(0)') @@ -103,8 +105,8 @@ click_on 'View Loss Report' end end - it 'does not display the not-current-provider-modal and does not close the loss-report-modal' do - expect(page).to have_css('#loss-report-modal') + it 'does not close the loss-report-modal and does not open the not-current-provider-modal' do + expect(page).to have_content("The native format of this collection is ECHO10. Editing this record using the MMT will convert it to UMM-JSON, which may result in data loss. Select 'View Loss Report' to see how the conversion will affect this record.") expect(page).to have_no_css('#not-current-provider-modal') end end @@ -115,7 +117,7 @@ click_on 'Cancel' end end - it 'does not display the not-current-provider-modal and closes loss-report-modal' do + it 'closes loss-report-modal and does not open the not-current-provider-modal' do expect(page).to have_no_css('#not-current-provider-modal') expect(page).to have_no_css('#loss-report-modal') end From ce63e8c705e7da1c97e928a8642d928d92c7e76f Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Thu, 20 Aug 2020 11:08:37 -0400 Subject: [PATCH 37/38] MMT-2346: adjusted modal Edit button and coffee script --- app/assets/javascripts/change_current_provider.coffee | 1 - app/views/shared/_loss_report_modal.html.erb | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/assets/javascripts/change_current_provider.coffee b/app/assets/javascripts/change_current_provider.coffee index 6889ddd11..18caae746 100644 --- a/app/assets/javascripts/change_current_provider.coffee +++ b/app/assets/javascripts/change_current_provider.coffee @@ -6,7 +6,6 @@ $(document).ready -> $('a.loss-report').on 'click', (element) -> $('#loss-report-modal').hide() - $('#loss-report-to-edit-collection').click() # Handle not-current-provider-modal $('a.not-current-provider').on 'click', (element) -> diff --git a/app/views/shared/_loss_report_modal.html.erb b/app/views/shared/_loss_report_modal.html.erb index 09ff8b735..782090c89 100644 --- a/app/views/shared/_loss_report_modal.html.erb +++ b/app/views/shared/_loss_report_modal.html.erb @@ -9,11 +9,10 @@ <% if current_provider?(options[:provider_id]) %> <%= link_to 'Edit Collection', edit_collection_path(revision_id: options[:revision_id]), class: 'eui-btn--blue modal-close button-spacing' %> <% elsif available_provider?(options[:provider_id]) %> - <%= link_to 'Edit Collection', '#', class: 'eui-btn--blue loss-report button-spacing' %> + <%= link_to 'Edit Collection', '#not-current-provider-modal', class: 'eui-btn--blue loss-report button-spacing display-modal not-current-provider', data: { provider: options[:provider_id], record_action: 'edit-collection' } %> <% end %> <%= link_to 'Cancel', 'javascript:void(0);', class: 'eui-btn modal-close' %> - <%= link_to 'Invisible Link', '#not-current-provider-modal', class: 'display-modal not-current-provider is-invisible', id: 'loss-report-to-edit-collection', data: { provider: options[:provider_id], record_action: 'edit-collection' } %>

From c6268492078debf023d844ff6af830dd254df3b9 Mon Sep 17 00:00:00 2001 From: Christian Trummer Date: Fri, 21 Aug 2020 11:36:20 -0400 Subject: [PATCH 38/38] MMT-2346: updated spec to match change requests --- spec/features/collections/loss_report_modal_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/collections/loss_report_modal_spec.rb b/spec/features/collections/loss_report_modal_spec.rb index f8a8ddb44..777291160 100644 --- a/spec/features/collections/loss_report_modal_spec.rb +++ b/spec/features/collections/loss_report_modal_spec.rb @@ -58,7 +58,7 @@ click_on 'Cancel' end end - it 'closes loss-report-modal and does not display the not-current-provider-modal' do + it 'closes loss-report-modal and does not open the not-current-provider-modal' do expect(page).to have_no_content('Metadata Preview') expect(page).to have_link('Edit Collection Record', href: '#loss-report-modal') expect(page).to have_no_css('#not-current-provider-modal') @@ -79,7 +79,7 @@ within '#loss-report-modal' do expect(page).to have_content("The native format of this collection is ECHO10. Editing this record using the MMT will convert it to UMM-JSON, which may result in data loss. Select 'View Loss Report' to see how the conversion will affect this record.") expect(page).to have_link('View Loss Report', href: loss_report_collections_path(concept_id, format: 'json')) - expect(page).to have_link('Edit Collection', href: '#') + expect(page).to have_link('Edit Collection', href: '#not-current-provider-modal') expect(page).to have_link('Cancel', href: 'javascript:void(0);') end end