flex-station-data 0.3.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +33 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +106 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +23 -1
- data/Gemfile +2 -0
- data/Gemfile.lock +51 -28
- data/README.md +16 -29
- data/Rakefile +5 -1
- data/bin/flex-station +33 -11
- data/bin/flex-station-linear-regression +19 -37
- data/flex-station-data.gemspec +7 -4
- data/lib/flex_station_data.rb +2 -0
- data/lib/flex_station_data/concerns/callable.rb +33 -0
- data/lib/flex_station_data/concerns/presenter.rb +4 -18
- data/lib/flex_station_data/concerns/service.rb +4 -18
- data/lib/flex_station_data/default_sample_map.rb +33 -0
- data/lib/flex_station_data/linear_regression.rb +2 -0
- data/lib/flex_station_data/plate.rb +14 -3
- data/lib/flex_station_data/presenters/plate_hash.rb +26 -0
- data/lib/flex_station_data/presenters/plates_hash.rb +26 -0
- data/lib/flex_station_data/presenters/sample_hash.rb +47 -0
- data/lib/flex_station_data/presenters/sample_regression_hash.rb +44 -0
- data/lib/flex_station_data/sample.rb +20 -5
- data/lib/flex_station_data/services/compute_mean.rb +2 -0
- data/lib/flex_station_data/services/load_plates.rb +12 -1
- data/lib/flex_station_data/services/parse_plate.rb +6 -4
- data/lib/flex_station_data/services/parse_plate_readings.rb +43 -36
- data/lib/flex_station_data/services/parse_sample_map.rb +47 -0
- data/lib/flex_station_data/services/sample_quality.rb +6 -5
- data/lib/flex_station_data/services/value_quality.rb +5 -1
- data/lib/flex_station_data/version.rb +3 -1
- data/lib/flex_station_data/wells.rb +16 -15
- metadata +31 -19
- data/bin/flex-station-sample-data +0 -54
- data/lib/flex_station_data/presenters/linear_regression/plate_hash.rb +0 -27
- data/lib/flex_station_data/presenters/linear_regression/plates_hash.rb +0 -28
- data/lib/flex_station_data/presenters/linear_regression/sample_hash.rb +0 -47
- data/lib/flex_station_data/presenters/linear_regression/sample_regression_hash.rb +0 -43
- data/lib/flex_station_data/presenters/linear_regression/verbose_sample_csv.rb +0 -28
- data/lib/flex_station_data/presenters/plate_csv.rb +0 -29
- data/lib/flex_station_data/presenters/plates_csv.rb +0 -28
- data/lib/flex_station_data/presenters/sample_csv.rb +0 -56
- data/lib/flex_station_data/readings.rb +0 -16
- data/lib/flex_station_data/services/parse_plate_samples.rb +0 -49
| @@ -1,15 +1,13 @@ | |
| 1 1 | 
             
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 2 3 |  | 
| 4 | 
            +
            require "flex_station_data/concerns/callable"
         | 
| 3 5 | 
             
            require "flex_station_data/services/load_plates"
         | 
| 4 | 
            -
            require "flex_station_data/presenters/ | 
| 5 | 
            -
            require "flex_station_data/presenters/linear_regression/plates_hash"
         | 
| 6 | 
            -
            require "flex_station_data/presenters/linear_regression/verbose_sample_csv"
         | 
| 7 | 
            -
             | 
| 8 | 
            -
            require "pry"
         | 
| 6 | 
            +
            require "flex_station_data/presenters/plates_hash"
         | 
| 9 7 |  | 
| 10 8 | 
             
            module FlexStationData
         | 
| 11 9 | 
             
              class LinearRegressionApp
         | 
| 12 | 
            -
                include Concerns:: | 
| 10 | 
            +
                include Concerns::Callable[:run]
         | 
| 13 11 |  | 
| 14 12 | 
             
                OPTION_RE = /\A--(\w+(?:-\w+)*)(?:=(.*))?\z/.freeze
         | 
| 15 13 |  | 
| @@ -30,15 +28,15 @@ module FlexStationData | |
| 30 28 | 
             
                end
         | 
| 31 29 |  | 
| 32 30 | 
             
                def threshold
         | 
| 33 | 
            -
                  Float(option(:threshold)) | 
| 34 | 
            -
                 | 
| 35 | 
            -
             | 
| 36 | 
            -
                def verbose?
         | 
| 37 | 
            -
                  option(:verbose)
         | 
| 31 | 
            +
                  Float(option(:threshold))
         | 
| 32 | 
            +
                rescue ArgumentError, TypeError
         | 
| 33 | 
            +
                  nil
         | 
| 38 34 | 
             
                end
         | 
| 39 35 |  | 
| 40 36 | 
             
                def min_r_squared
         | 
| 41 | 
            -
                  Float(option("min-r-squared")) | 
| 37 | 
            +
                  Float(option("min-r-squared"))
         | 
| 38 | 
            +
                rescue ArgumentError, TypeError
         | 
| 39 | 
            +
                  0.75
         | 
| 42 40 | 
             
                end
         | 
| 43 41 |  | 
| 44 42 | 
             
                def files
         | 
| @@ -51,21 +49,9 @@ module FlexStationData | |
| 51 49 | 
             
                  end
         | 
| 52 50 | 
             
                end
         | 
| 53 51 |  | 
| 54 | 
            -
                def  | 
| 55 | 
            -
                  plates.flat_map do |file, file_plates|
         | 
| 56 | 
            -
                    Presenters::PlatesCsv.present(
         | 
| 57 | 
            -
                      file,
         | 
| 58 | 
            -
                      file_plates,
         | 
| 59 | 
            -
                      sample_presenter: Presenters::LinearRegression::VerboseSampleCsv,
         | 
| 60 | 
            -
                      threshold:        threshold,
         | 
| 61 | 
            -
                      min_r_squared:    min_r_squared
         | 
| 62 | 
            -
                    )
         | 
| 63 | 
            -
                  end
         | 
| 64 | 
            -
                end
         | 
| 65 | 
            -
             | 
| 66 | 
            -
                def summary_hashes
         | 
| 52 | 
            +
                def plate_hashes
         | 
| 67 53 | 
             
                  plates.flat_map do |file, file_plates|
         | 
| 68 | 
            -
                    Presenters:: | 
| 54 | 
            +
                    Presenters::PlatesHash.present(
         | 
| 69 55 | 
             
                      file,
         | 
| 70 56 | 
             
                      file_plates,
         | 
| 71 57 | 
             
                      threshold:     threshold,
         | 
| @@ -76,13 +62,13 @@ module FlexStationData | |
| 76 62 |  | 
| 77 63 | 
             
                def clean_values(values)
         | 
| 78 64 | 
             
                  values.each_with_object([]) do |value, memo|
         | 
| 79 | 
            -
                    memo << ( | 
| 65 | 
            +
                    memo << (value != memo.compact.last ? value : nil)
         | 
| 80 66 | 
             
                  end
         | 
| 81 67 | 
             
                end
         | 
| 82 68 |  | 
| 83 | 
            -
                def  | 
| 84 | 
            -
                  @ | 
| 85 | 
            -
                    result =  | 
| 69 | 
            +
                def hash
         | 
| 70 | 
            +
                  @hash ||= begin
         | 
| 71 | 
            +
                    result = plate_hashes.each_with_object({}) do |hash, memo|
         | 
| 86 72 | 
             
                      hash.each do |header, value|
         | 
| 87 73 | 
             
                        memo[header] ||= []
         | 
| 88 74 | 
             
                        memo[header] << value
         | 
| @@ -94,15 +80,11 @@ module FlexStationData | |
| 94 80 | 
             
                  end
         | 
| 95 81 | 
             
                end
         | 
| 96 82 |  | 
| 97 | 
            -
                def summary_csv
         | 
| 98 | 
            -
                  [ summary_hash.keys, *summary_hash.values.transpose ]
         | 
| 99 | 
            -
                end
         | 
| 100 | 
            -
             | 
| 101 83 | 
             
                def csv
         | 
| 102 | 
            -
                   | 
| 84 | 
            +
                  [ hash.keys, *hash.values.transpose ]
         | 
| 103 85 | 
             
                end
         | 
| 104 86 |  | 
| 105 | 
            -
                def  | 
| 87 | 
            +
                def run
         | 
| 106 88 | 
             
                  CSV do |out|
         | 
| 107 89 | 
             
                    csv.each do |row|
         | 
| 108 90 | 
             
                      out << row
         | 
| @@ -112,4 +94,4 @@ module FlexStationData | |
| 112 94 | 
             
              end
         | 
| 113 95 | 
             
            end
         | 
| 114 96 |  | 
| 115 | 
            -
            FlexStationData::LinearRegressionApp. | 
| 97 | 
            +
            FlexStationData::LinearRegressionApp.run(*ARGV)
         | 
    
        data/flex-station-data.gemspec
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            lib = File.expand_path("lib", __dir__)
         | 
| 2 4 | 
             
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         | 
| 3 5 | 
             
            require "flex_station_data/version"
         | 
| @@ -21,19 +23,20 @@ Gem::Specification.new do |spec| | |
| 21 23 |  | 
| 22 24 | 
             
              # Specify which files should be added to the gem when it is released.
         | 
| 23 25 | 
             
              # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
         | 
| 24 | 
            -
              spec.files = Dir.chdir(File.expand_path( | 
| 26 | 
            +
              spec.files = Dir.chdir(File.expand_path(__dir__)) do
         | 
| 25 27 | 
             
                `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
         | 
| 26 28 | 
             
              end
         | 
| 27 29 | 
             
              spec.bindir        = "bin"
         | 
| 28 30 | 
             
              spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
         | 
| 29 31 | 
             
              spec.require_paths = ["lib"]
         | 
| 30 32 |  | 
| 31 | 
            -
              spec.required_ruby_version = ">= 2. | 
| 33 | 
            +
              spec.required_ruby_version = ">= 2.7.1"
         | 
| 32 34 |  | 
| 33 35 | 
             
              spec.add_development_dependency "bundler", "~> 2.0"
         | 
| 34 | 
            -
              spec.add_development_dependency "rake", "~> 10.0"
         | 
| 35 | 
            -
              spec.add_development_dependency "rspec", "~> 3.8"
         | 
| 36 36 | 
             
              spec.add_development_dependency "pry"
         | 
| 37 | 
            +
              spec.add_development_dependency "rake", "~> 13.0"
         | 
| 38 | 
            +
              spec.add_development_dependency "rspec", "~> 3.8"
         | 
| 39 | 
            +
              spec.add_development_dependency "rubocop-rspec"
         | 
| 37 40 |  | 
| 38 41 | 
             
              spec.add_runtime_dependency "activesupport"
         | 
| 39 42 | 
             
              spec.add_runtime_dependency "linefit"
         | 
    
        data/lib/flex_station_data.rb
    CHANGED
    
    
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module FlexStationData
         | 
| 4 | 
            +
              module Concerns
         | 
| 5 | 
            +
                module Callable
         | 
| 6 | 
            +
                  class << self
         | 
| 7 | 
            +
                    def included(base)
         | 
| 8 | 
            +
                      base.include with(:call)
         | 
| 9 | 
            +
                    end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    def with(verb)
         | 
| 12 | 
            +
                      callable_modules[verb.to_sym] ||= Module.new do
         | 
| 13 | 
            +
                        define_singleton_method(:included) do |base|
         | 
| 14 | 
            +
                          base.define_singleton_method(verb) do |*args|
         | 
| 15 | 
            +
                            new(*args).public_send(verb)
         | 
| 16 | 
            +
                          end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                          base.define_singleton_method(:to_proc) do
         | 
| 19 | 
            +
                            method(verb).to_proc
         | 
| 20 | 
            +
                          end
         | 
| 21 | 
            +
                        end
         | 
| 22 | 
            +
                      end
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    alias [] with
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    def callable_modules
         | 
| 28 | 
            +
                      @callable_modules ||= {}
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -1,23 +1,9 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative "callable"
         | 
| 2 4 |  | 
| 3 5 | 
             
            module FlexStationData
         | 
| 4 6 | 
             
              module Concerns
         | 
| 5 | 
            -
                 | 
| 6 | 
            -
                  extend ActiveSupport::Concern
         | 
| 7 | 
            -
             | 
| 8 | 
            -
                  def to_proc
         | 
| 9 | 
            -
                    Proc.new(&method(:present))
         | 
| 10 | 
            -
                  end
         | 
| 11 | 
            -
             | 
| 12 | 
            -
                  class_methods do
         | 
| 13 | 
            -
                    def present(*args, &block)
         | 
| 14 | 
            -
                      new(*args).present(&block)
         | 
| 15 | 
            -
                    end
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                    def to_proc
         | 
| 18 | 
            -
                      Proc.new(&method(:present))
         | 
| 19 | 
            -
                    end
         | 
| 20 | 
            -
                  end
         | 
| 21 | 
            -
                end
         | 
| 7 | 
            +
                Presenter = Callable[:present]
         | 
| 22 8 | 
             
              end
         | 
| 23 9 | 
             
            end
         | 
| @@ -1,23 +1,9 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative "callable"
         | 
| 2 4 |  | 
| 3 5 | 
             
            module FlexStationData
         | 
| 4 6 | 
             
              module Concerns
         | 
| 5 | 
            -
                 | 
| 6 | 
            -
                  extend ActiveSupport::Concern
         | 
| 7 | 
            -
             | 
| 8 | 
            -
                  def to_proc
         | 
| 9 | 
            -
                    Proc.new(&method(:call))
         | 
| 10 | 
            -
                  end
         | 
| 11 | 
            -
             | 
| 12 | 
            -
                  class_methods do
         | 
| 13 | 
            -
                    def call(*args, &block)
         | 
| 14 | 
            -
                      new(*args).call(&block)
         | 
| 15 | 
            -
                    end
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                    def to_proc
         | 
| 18 | 
            -
                      Proc.new(&method(:call))
         | 
| 19 | 
            -
                    end
         | 
| 20 | 
            -
                  end
         | 
| 21 | 
            -
                end
         | 
| 7 | 
            +
                Service = Callable
         | 
| 22 8 | 
             
              end
         | 
| 23 9 | 
             
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module FlexStationData
         | 
| 4 | 
            +
              class DefaultSampleMap
         | 
| 5 | 
            +
                attr_reader :rows, :columns, :wells_per_sample
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(rows, columns, wells_per_sample)
         | 
| 8 | 
            +
                  @rows             = rows
         | 
| 9 | 
            +
                  @columns          = columns
         | 
| 10 | 
            +
                  @wells_per_sample = wells_per_sample
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def [](sample_label)
         | 
| 14 | 
            +
                  sample_label = Integer(sample_label)
         | 
| 15 | 
            +
                  map[sample_label] ||= map_sample(sample_label)
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                private
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def map_sample(sample_label)
         | 
| 21 | 
            +
                  column, row = (sample_label - 1).divmod(rows)
         | 
| 22 | 
            +
                  row_label = ("A".ord + row).chr
         | 
| 23 | 
            +
                  base_column = (column * wells_per_sample) + 1
         | 
| 24 | 
            +
                  (0...wells_per_sample).map do |column_offset|
         | 
| 25 | 
            +
                    [ row_label, base_column + column_offset ].join("")
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def map
         | 
| 30 | 
            +
                  @map ||= {}
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -1,12 +1,23 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "flex_station_data/sample"
         | 
| 4 | 
            +
             | 
| 1 5 | 
             
            module FlexStationData
         | 
| 2 6 | 
             
              class Plate
         | 
| 3 | 
            -
                attr_reader :label, :times, :temperatures, : | 
| 7 | 
            +
                attr_reader :label, :times, :temperatures, :wells, :sample_map
         | 
| 4 8 |  | 
| 5 | 
            -
                def initialize(label, times, temperatures,  | 
| 9 | 
            +
                def initialize(label, times, temperatures, wells, sample_map)
         | 
| 6 10 | 
             
                  @label = label
         | 
| 7 11 | 
             
                  @times = times
         | 
| 8 12 | 
             
                  @temperatures = temperatures
         | 
| 9 | 
            -
                  @ | 
| 13 | 
            +
                  @wells = wells
         | 
| 14 | 
            +
                  @sample_map = sample_map
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def samples
         | 
| 18 | 
            +
                  @samples ||= sample_map.map do |label, well_labels|
         | 
| 19 | 
            +
                    Sample.new(label, well_labels, self)
         | 
| 20 | 
            +
                  end
         | 
| 10 21 | 
             
                end
         | 
| 11 22 | 
             
              end
         | 
| 12 23 | 
             
            end
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "flex_station_data/presenters/sample_hash"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module FlexStationData
         | 
| 6 | 
            +
              module Presenters
         | 
| 7 | 
            +
                class PlateHash
         | 
| 8 | 
            +
                  include Concerns::Presenter
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  attr_reader :plate, :options
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  delegate :times, :samples, to: :plate
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def initialize(plate, **options)
         | 
| 15 | 
            +
                    @plate = plate
         | 
| 16 | 
            +
                    @options = options
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def present
         | 
| 20 | 
            +
                    samples.map do |sample|
         | 
| 21 | 
            +
                      { "plate" => plate.label }.merge(SampleHash.present(times, sample, **options))
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
            end
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "flex_station_data/presenters/plate_hash"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module FlexStationData
         | 
| 6 | 
            +
              module Presenters
         | 
| 7 | 
            +
                class PlatesHash
         | 
| 8 | 
            +
                  include Concerns::Presenter
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  attr_reader :file, :plates, :options
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def initialize(file, plates, **options)
         | 
| 13 | 
            +
                    @file = file
         | 
| 14 | 
            +
                    @plates = plates
         | 
| 15 | 
            +
                    @options = options
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def present
         | 
| 19 | 
            +
                    base = { "file" => file.basename.to_s }
         | 
| 20 | 
            +
                    plates.flat_map do |plate|
         | 
| 21 | 
            +
                      PlateHash.present(plate, **options).map(&base.method(:merge))
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
            end
         | 
| @@ -0,0 +1,47 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "flex_station_data/concerns/presenter"
         | 
| 4 | 
            +
            require "flex_station_data/services/sample_quality"
         | 
| 5 | 
            +
            require "flex_station_data/presenters/sample_regression_hash"
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module FlexStationData
         | 
| 8 | 
            +
              module Presenters
         | 
| 9 | 
            +
                class SampleHash
         | 
| 10 | 
            +
                  include Concerns::Presenter
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  attr_reader :times, :sample, :quality_control, :options
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def initialize(times, sample, **options)
         | 
| 15 | 
            +
                    @times = times
         | 
| 16 | 
            +
                    @sample = sample
         | 
| 17 | 
            +
                    @options = options
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def errors
         | 
| 21 | 
            +
                    @errors ||= SampleQuality.call(sample, **options).reject(&:good?)
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def errors?
         | 
| 25 | 
            +
                    errors.present?
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def errors_hash
         | 
| 29 | 
            +
                    { "error" => errors.first&.to_s }
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def wells_hash
         | 
| 33 | 
            +
                    { "wells" => sample.wells.join(", ") }
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def regression_hash
         | 
| 37 | 
            +
                    return SampleRegressionHash.headers.zip([]).to_h if errors?
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    SampleRegressionHash.present(times, sample.mean, **options).transform_values(&:first)
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  def present
         | 
| 43 | 
            +
                    { "sample" => sample.label }.merge(wells_hash).merge(errors_hash).merge(regression_hash)
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
            end
         | 
| @@ -0,0 +1,44 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "flex_station_data/concerns/presenter"
         | 
| 4 | 
            +
            require "flex_station_data/linear_regression"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module FlexStationData
         | 
| 7 | 
            +
              module Presenters
         | 
| 8 | 
            +
                class SampleRegressionHash
         | 
| 9 | 
            +
                  include Concerns::Presenter
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  PRODUCTS = {
         | 
| 12 | 
            +
                    slope:     "slope",
         | 
| 13 | 
            +
                    intercept: "intercept",
         | 
| 14 | 
            +
                    r_squared: "R²",
         | 
| 15 | 
            +
                    quality:   "quality"
         | 
| 16 | 
            +
                  }.freeze
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  attr_reader :times, :sample_values, :min_r_squared, :options
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def initialize(times, *sample_values, min_r_squared: nil, **options)
         | 
| 21 | 
            +
                    @times = times
         | 
| 22 | 
            +
                    @sample_values = sample_values
         | 
| 23 | 
            +
                    @min_r_squared = min_r_squared
         | 
| 24 | 
            +
                    @options = options
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  def sample_regressions
         | 
| 28 | 
            +
                    @sample_regressions ||= sample_values.map do |values|
         | 
| 29 | 
            +
                      FlexStationData::LinearRegression.new(times, values, min_r_squared: min_r_squared)
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  def present
         | 
| 34 | 
            +
                    PRODUCTS.each_with_object({}) do |(method, label), memo|
         | 
| 35 | 
            +
                      memo[label] = sample_regressions.map(&method)
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  def self.headers
         | 
| 40 | 
            +
                    PRODUCTS.values
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
            end
         |