From 6efcb38de5e01cb2112eba0891646f686ade2ed7 Mon Sep 17 00:00:00 2001 From: Adrian Herrmann Date: Wed, 25 Sep 2024 11:05:38 +0000 Subject: [PATCH] feat: added wells to versioning --- app/models/well.rb | 2 ++ .../versioning/fetchers/wellplate_fetcher.rb | 20 ++++++++++++- app/services/versioning/reverter.rb | 2 ++ .../versioning/reverters/well_reverter.rb | 7 +++++ .../versioning/serializers/base_serializer.rb | 4 +++ .../versioning/serializers/well_serializer.rb | 29 +++++++++++++++++++ .../serializers/wellplate_serializer.rb | 5 ++++ ...0240920154515_add_logidze_to_wellplates.rb | 13 ++++++++- .../20240920155000_add_logidze_to_wells.rb | 28 ++++++++++++++++++ db/schema.rb | 6 +++- db/triggers/logidze_on_wells_v01.sql | 6 ++++ 11 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 app/services/versioning/reverters/well_reverter.rb create mode 100644 app/services/versioning/serializers/well_serializer.rb create mode 100644 db/migrate/20240920155000_add_logidze_to_wells.rb create mode 100644 db/triggers/logidze_on_wells_v01.sql diff --git a/app/models/well.rb b/app/models/well.rb index ea38803fae..83a85edc6b 100644 --- a/app/models/well.rb +++ b/app/models/well.rb @@ -25,6 +25,7 @@ # class Well < ApplicationRecord + has_logidze acts_as_paranoid belongs_to :wellplate belongs_to :sample, optional: true @@ -42,6 +43,7 @@ def readouts # translates well position within wellplate: X=2 Y=3 -> C2 def alphanumeric_position return 'n/a' if position_x.nil? || position_y.nil? + row = ('A'..'ZZ').to_a[position_y - 1] "#{row}#{position_x}" end diff --git a/app/services/versioning/fetchers/wellplate_fetcher.rb b/app/services/versioning/fetchers/wellplate_fetcher.rb index 14e5103dcc..00422f7b85 100644 --- a/app/services/versioning/fetchers/wellplate_fetcher.rb +++ b/app/services/versioning/fetchers/wellplate_fetcher.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +# rubocop:disable Metrics/AbcSize + module Versioning module Fetchers class WellplateFetcher @@ -14,6 +16,19 @@ def self.call(**args) def call versions = Versioning::Serializers::WellplateSerializer.call(wellplate) + wellplate.wells.with_log_data.each do |well| + versions += if well.position_y < 27 + Versioning::Serializers::WellSerializer.call( + well, ["Well: (#{(well.position_y + 64).chr}#{well.position_x})"] + ) + else + Versioning::Serializers::WellSerializer.call( + well, ["Well: (#{((well.position_y / 26) + 64).chr}" \ + "#{((well.position_y % 26) + 64).chr}#{well.position_x})"] + ) + end + end + analyses_container = wellplate.container.children.where(container_type: :analyses).first analyses_container.children.where(container_type: :analysis).with_deleted.with_log_data.each do |analysis| versions += Versioning::Serializers::ContainerSerializer.call(analysis, ["Analysis: #{analysis.name}"]) @@ -25,7 +40,8 @@ def call versions += dataset.attachments.with_log_data.flat_map do |attachment| Versioning::Serializers::AttachmentSerializer.call(attachment, - ["Analysis: #{analysis.name}", "Dataset: #{dataset.name}", + ["Analysis: #{analysis.name}", + "Dataset: #{dataset.name}", "Attachment: #{attachment.filename}"]) end end @@ -36,3 +52,5 @@ def call end end end + +# rubocop:enable Metrics/AbcSize diff --git a/app/services/versioning/reverter.rb b/app/services/versioning/reverter.rb index e53a0d24d5..131840af3a 100644 --- a/app/services/versioning/reverter.rb +++ b/app/services/versioning/reverter.rb @@ -37,6 +37,8 @@ def call Versioning::Reverters::ScreenReverter.call(change) when 'Wellplate' Versioning::Reverters::WellplateReverter.call(change) + when 'Well' + Versioning::Reverters::WellReverter.call(change) end end end diff --git a/app/services/versioning/reverters/well_reverter.rb b/app/services/versioning/reverters/well_reverter.rb new file mode 100644 index 0000000000..0e95ef0105 --- /dev/null +++ b/app/services/versioning/reverters/well_reverter.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class Versioning::Reverters::WellReverter < Versioning::Reverters::BaseReverter + def self.scope + Well.with_deleted + end +end diff --git a/app/services/versioning/serializers/base_serializer.rb b/app/services/versioning/serializers/base_serializer.rb index ac573c3e6f..a137b41ee8 100644 --- a/app/services/versioning/serializers/base_serializer.rb +++ b/app/services/versioning/serializers/base_serializer.rb @@ -109,6 +109,10 @@ def jsonb_formatter(*attributes) ->(key, value) { default_formatter.call(key, value)&.dig(*attributes) } end + def array_formatter + ->(key, value) { default_formatter.call(key, value)&.join("\n") } + end + def non_formatter ->(_key, value) { value } end diff --git a/app/services/versioning/serializers/well_serializer.rb b/app/services/versioning/serializers/well_serializer.rb new file mode 100644 index 0000000000..817a9c9b80 --- /dev/null +++ b/app/services/versioning/serializers/well_serializer.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Versioning + module Serializers + class WellSerializer < Versioning::Serializers::BaseSerializer + def self.call(record, name) + new(record: record, name: name).call + end + + def field_definitions + { + color_code: { + label: 'Color Code', + revert: %i[color_code], + }, + sample_id: { + label: 'Sample ID', + revert: %i[sample_id], + }, + readouts: { + label: 'Readouts', + revert: %i[readouts], + formatter: array_formatter, + }, + }.with_indifferent_access + end + end + end +end diff --git a/app/services/versioning/serializers/wellplate_serializer.rb b/app/services/versioning/serializers/wellplate_serializer.rb index 9bdbe1b0bf..ca2bf1a8f9 100644 --- a/app/services/versioning/serializers/wellplate_serializer.rb +++ b/app/services/versioning/serializers/wellplate_serializer.rb @@ -30,6 +30,11 @@ def field_definitions label: 'Height', revert: %i[heigth], }, + readout_titles: { + label: 'Readout Title', + revert: %i[readout_titles], + formatter: array_formatter, + }, }.with_indifferent_access end end diff --git a/db/migrate/20240920154515_add_logidze_to_wellplates.rb b/db/migrate/20240920154515_add_logidze_to_wellplates.rb index 108e900c72..6dc6ba4c9e 100644 --- a/db/migrate/20240920154515_add_logidze_to_wellplates.rb +++ b/db/migrate/20240920154515_add_logidze_to_wellplates.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AddLogidzeToWellplates < ActiveRecord::Migration[6.1] def change add_column :wellplates, :log_data, :jsonb @@ -8,10 +10,19 @@ def change end dir.down do - execute <<~SQL + execute <<~SQL.squish DROP TRIGGER IF EXISTS "logidze_on_wellplates" on "wellplates"; SQL end end + + reversible do |dir| + dir.up do + execute <<~SQL.squish + UPDATE wellplates as t + SET log_data = logidze_snapshot(to_jsonb(t), 'updated_at'); + SQL + end + end end end diff --git a/db/migrate/20240920155000_add_logidze_to_wells.rb b/db/migrate/20240920155000_add_logidze_to_wells.rb new file mode 100644 index 0000000000..18a1b9135b --- /dev/null +++ b/db/migrate/20240920155000_add_logidze_to_wells.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class AddLogidzeToWells < ActiveRecord::Migration[6.1] + def change + add_column :wells, :log_data, :jsonb + + reversible do |dir| + dir.up do + create_trigger :logidze_on_wells, on: :wells + end + + dir.down do + execute <<~SQL.squish + DROP TRIGGER IF EXISTS "logidze_on_wells" on "wells"; + SQL + end + end + + reversible do |dir| + dir.up do + execute <<~SQL.squish + UPDATE wells as t + SET log_data = logidze_snapshot(to_jsonb(t), 'updated_at'); + SQL + end + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 4257a8d17d..4c7c341e31 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2024_09_20_154515) do +ActiveRecord::Schema.define(version: 2024_09_20_155000) do # These are extensions that must be enabled in order to support this database enable_extension "hstore" @@ -1494,6 +1494,7 @@ t.jsonb "readouts", default: [{"unit"=>"", "value"=>""}] t.string "label", default: "Molecular structure", null: false t.string "color_code" + t.jsonb "log_data" t.index ["deleted_at"], name: "index_wells_on_deleted_at" t.index ["sample_id"], name: "index_wells_on_sample_id" t.index ["wellplate_id"], name: "index_wells_on_wellplate_id" @@ -2136,6 +2137,9 @@ create_trigger :logidze_on_wellplates, sql_definition: <<-SQL CREATE TRIGGER logidze_on_wellplates BEFORE INSERT OR UPDATE ON public.wellplates FOR EACH ROW WHEN ((COALESCE(current_setting('logidze.disabled'::text, true), ''::text) <> 'on'::text)) EXECUTE FUNCTION logidze_logger('null', 'updated_at') SQL + create_trigger :logidze_on_wells, sql_definition: <<-SQL + CREATE TRIGGER logidze_on_wells BEFORE INSERT OR UPDATE ON public.wells FOR EACH ROW WHEN ((COALESCE(current_setting('logidze.disabled'::text, true), ''::text) <> 'on'::text)) EXECUTE FUNCTION logidze_logger('null', 'updated_at') + SQL create_view "v_samples_collections", sql_definition: <<-SQL SELECT cols.id AS cols_id, diff --git a/db/triggers/logidze_on_wells_v01.sql b/db/triggers/logidze_on_wells_v01.sql new file mode 100644 index 0000000000..fa3476b6cc --- /dev/null +++ b/db/triggers/logidze_on_wells_v01.sql @@ -0,0 +1,6 @@ +CREATE TRIGGER "logidze_on_wells" +BEFORE UPDATE OR INSERT ON "wells" FOR EACH ROW +WHEN (coalesce(current_setting('logidze.disabled', true), '') <> 'on') +-- Parameters: history_size_limit (integer), timestamp_column (text), filtered_columns (text[]), +-- include_columns (boolean), debounce_time_ms (integer) +EXECUTE PROCEDURE logidze_logger(null, 'updated_at');