class DeterminationsComparison::Comparison

Attributes

baseline_filepath[RW]
filepath_baseline_origin[RW]
filepath_undertest_origin[RW]
hashCategory[RW]
hashStd[RW]
hashTgt[RW]
html_filepath[RW]
png_filepath[RW]
undertest_filepath[RW]

Public Class Methods

new(filepath_baseline,filepath_undertest,opts={}) click to toggle source
# File lib/determinations_comparison.rb, line 20
def initialize(filepath_baseline,filepath_undertest,opts={})

  @filepath_comparedeterminations = opts[:filepath_comparedeterminations]
  @filepath_cpplogreader =          opts[:filepath_cpplogreader]

  hashPropertyThresholds = opts[:hashPropertyThresholds] || {:area=>0,:apex_time=>0,:apex_intensity=>0}

  @folderpath_with_log = opts[:folderpath_with_log] || File.dirname(filepath_undertest)

  @filepath_baseline_origin = filepath_baseline
  @filepath_undertest_origin = filepath_undertest

  @filepath_keymapping = File.join( LIB_FOLDER,'..','config','key_mapping.yaml')

  @uuid = UUIDTools::UUID.random_create

  $log_detercomp = Logger.new File.join(LIB_FOLDER,'..',"log.txt")

  @hashDiffs = Hash.new
  differences 'Compound', hashCompoundUnderTest, hashCompoundBaseLine, @hashDiffs
  differences_extrapolated

  @hashCategory = categorize hashPropertyThresholds

end

Public Instance Methods

as_hash() click to toggle source
# File lib/determinations_comparison.rb, line 167
def as_hash
  @hashDiffs
end
to_html(folderpath_output) click to toggle source
# File lib/determinations_comparison.rb, line 46
def to_html(folderpath_output)

  FileUtils.mkdir_p folderpath_output

  html_filepath = File.join      folderpath_output, "#{@uuid}.html"
  png_filepath = File.join       folderpath_output, "#{@uuid}.png"
  baseline_filepath = File.join  folderpath_output, "#{@uuid}_baseline.json"
  undertest_filepath = File.join folderpath_output, "#{@uuid}_undertest.json"

  # these are necessary for cpplogreader
  @sample_index = hashCompoundBaseLine['Sample_Index']
  @analyte_name = hashCompoundBaseLine['Analyte']

  begin

    #coder = HTMLEntities.new

    FileUtils.copy_file @filepath_baseline_origin, baseline_filepath
    FileUtils.copy_file @filepath_undertest_origin, undertest_filepath

    # html for file links
      strFiles = "<p>baseline file: <a href=#{File.basename(baseline_filepath)}>#{File.basename(@filepath_baseline_origin)}</a></p>"
      strFiles += "<p>undertest file: <a href=#{File.basename(undertest_filepath)}>#{File.basename(@filepath_undertest_origin)}</a></p>"

    # html for plot
      strPlot = nil
      @plotter = Exe_CompareDeterminations.new @filepath_baseline_origin, @filepath_undertest_origin, @filepath_comparedeterminations
      retcodePNG,rundetailsPNG = @plotter.generate_png png_filepath
      if retcodePNG
        strPlot = "<img src='#{File.basename(png_filepath)}' alt='chromatogram' height='500' width='800' align='top'>"
      end

    # html for peak picking logs
      hashChromHTML = Hash.new

      @hashDiffs.each_pair do |k,v|

        chrom_type = k

        ht = HTML_Table_of_Peak_Picking_Log.new(@folderpath_with_log,@sample_index,@analyte_name,k)
        str_pp = ht.render
        unless str_pp.nil?

          peak_picked = "unknown"
          arrPP = v.select { |h| h['peak_picked'] }
          unless arrPP.empty?
            peak_picked = "#{arrPP.first[0]} #{arrPP.first[1]}"
          end

          strChrom = "<p><b>#{chrom_type}</b> - #{peak_picked}</p>"
          strChrom += str_pp
        end

        hashChromHTML[k] = strChrom

      end

    # html for property differences
      arr = Array.new

      @hashDiffs.each_pair do |k,v|

        chrom_type = k

        v.each_pair do |k,v|
          if k == 'peak'
            v.each_pair do |k,v|
              arr << property_diff_to_hash(chrom_type,k,v)
            end
          else
            unless k == 'peak_picked'
              arr << property_diff_to_hash(chrom_type,k,v)
            end
          end

        end

      end

    strProps = nil

    ht = HTML_Table_of_Property_Differences.new(arr)
    str_p = ht.render
    unless str_p.nil?
      strProps = str_p
    end

    strFull = strFiles
    strFull += strPlot unless strPlot.nil?

    hashGroups = Hash.new
    hashGroups['Properties'] = strProps
    hashChromHTML.each_pair do |k,v|
      hashGroups["Pick Log - #{k.to_s}"] = v
    end

    jt = Jquery_Tabs.new hashGroups
    strFull += jt.render


    File.open(html_filepath, "w") do |f|
      f.write(strFull)
    end

    retcode = true

  rescue Exception => e
    retcode = false
    $log_detercomp.error "unable to render html"
  end

  @html_filepath = html_filepath
  @png_filepath = png_filepath
  @baseline_filepath = baseline_filepath
  @undertest_filepath = undertest_filepath


  return retcode

end

Private Instance Methods

categorize(hashPropertyThresholds) click to toggle source
# File lib/determinations_comparison.rb, line 174
def categorize(hashPropertyThresholds)

  hashCategory = Hash.new

  hash = Hash.new

  # peak picked by baseline and not undertest?
  arr = Array.new
  @hashDiffs.each_pair do |chrom,hashChrom|

    hash = Hash.new

    if hashChrom['peak_picked'] == 'by baseline but not by undertest'
      hash[:desc] = "peak picked #{hashChrom['peak_picked']}"
      hash[:chrom] = chrom.to_s
      hash[:per_diff] = 100  # temporarily using per_diff for a severity level
      arr << hash
    end

    break unless arr.empty?

    if hashChrom['peak_picked'] == 'by baseline (simpsons rule) but not by undertest'
      hash[:desc] = "peak picked #{hashChrom['peak_picked']}"
      hash[:chrom] = chrom.to_s
      hash[:per_diff] = 50
      arr << hash
    end

  end

  unless arr.empty?
    arr.sort_by! { |h| h[:per_diff] }
    arr.first.delete(:per_diff)
    return arr.first
  end


  # area, apex_time, apex_intensity within threshold?
  [:area,:apex_time,:apex_intensity].each do |prop|

    arr = Array.new
    @hashDiffs.each_pair do |chrom,hashChrom|

      hash = Hash.new

      per_diff = hashChrom['peak'][prop.to_s][:percent_diff]
      threshold = hashPropertyThresholds[prop]

      if per_diff > threshold
        hash[:desc] = "difference in #{prop.to_s}"
        hash[:chrom] = chrom.to_s
        hash[:per_diff] = per_diff
        arr << hash
      end

    end

    unless arr.empty?
      arr.sort_by! { |h| h[:per_diff] }.reverse
      return arr.first
    end

  end

  hash[:desc] = 'match within thresholds'

  return hash

end
differences(item, hashUnderTest, hashBaseLine, hashDifferences) click to toggle source
# File lib/determinations_comparison.rb, line 245
def differences(item, hashUnderTest, hashBaseLine, hashDifferences)

  hashUnderTest.each_pair do |k_undertest,v_undertest|

    #if k_undertest == 'expected_retention_time'
    #  puts 'wait'
    #end

    v_baseline = get_baseline_and_undertest_values item, hashBaseLine, :k_undertest => k_undertest

    if v_baseline.nil?
      $log_detercomp.info "corresponding key for '#{k_undertest}' does not exist in baseline file (if different name than add to key_mapping.yaml"
    else

      if k_undertest == 'Compound' || k_undertest == 'chromatogram_list'

        v_undertest.each do |hashChromUnderTest|

          @hashChromUnderTest = hashChromUnderTest

          chromtype_key_undertest =
              filetype_undertest == 'json' ? 'classifier' : 'SampleType'

          chromtype_undertest = hashChromUnderTest[chromtype_key_undertest]

          chromtype_generic = generic_chromtype chromtype_undertest

          chromtype_baseline = generic_chromtype_to_filetype_specific chromtype_generic, filetype_baseline

          chromtype_key_baseline =
              filetype_baseline == 'json' ? 'classifier' : 'SampleType'

          @hashChromBaseLine = v_baseline.select { |h| h[chromtype_key_baseline] == chromtype_baseline }.first

          hashDiff_Chrom = Hash.new
          hashDifferences[generic_key(chromtype_generic)] = hashDiff_Chrom
          differences 'Chromatogram', hashChromUnderTest, @hashChromBaseLine, hashDiff_Chrom

        end

      elsif(k_undertest == 'RawTrace' || k_undertest == 'raw_trace' || k_undertest == 'SmoothTrace' || k_undertest == 'smooth_trace')
        $log_detercomp.info "'#{k_undertest}' - comparing traces not supported yet"
      elsif(k_undertest == 'Peaks' || k_undertest == 'peak')

        @hashPeakUnderTest = v_undertest
        @hashPeakBaseLine = v_baseline

        hashDiff_Peak = Hash.new
        hashDifferences['peak'] = hashDiff_Peak
        differences 'Peak', @hashPeakUnderTest, @hashPeakBaseLine, hashDiff_Peak

      elsif v_undertest.respond_to? :each
        $log_detercomp.info "'#{k_undertest}' - comparison not supported"
      else

        if v_undertest.to_s.numeric?

          #if k_undertest == 'start_intensity'
          #  puts 'wait'
          #end

          hashDiff = percentage_difference item , k_undertest , v_baseline , v_undertest

          hashDifferences[generic_key(k_undertest)] = hashDiff
        else
          $log_detercomp.info "'#{k_undertest}' - comparison not supported"
        end

      end

    end

  end


end
differences_extrapolated() click to toggle source
# File lib/determinations_comparison.rb, line 322
def differences_extrapolated

  @hashDiffs.each_pair do |k,v|

    boolPeakPickedByBaseline =
        v['peak']['area'][:baseline] == 0 ? false:true

    boolPeakPickedByUnderTest =
        v['peak']['area'][:undertest] == 0 ? false:true

    boolSimpsonsRuleByBaseLine =
        v['peak']['nls_amp'][:baseline] == 0

    if boolPeakPickedByBaseline && !(boolPeakPickedByUnderTest)

      if boolSimpsonsRuleByBaseLine
        v['peak_picked'] = 'by baseline (simpsons rule) but not by undertest'
      else
        v['peak_picked'] = 'by baseline but not by undertest'
      end

    elsif !(boolPeakPickedByBaseline) && boolPeakPickedByUnderTest
      v['peak_picked'] = 'by undertest but not by baseline'
    elsif boolPeakPickedByBaseline && boolPeakPickedByUnderTest

      if boolSimpsonsRuleByBaseLine
        v['peak_picked'] = 'by baseline (simpsons rule) and by undertest'
      else
        v['peak_picked'] = 'by baseline and by undertest'
      end
    elsif !boolPeakPickedByBaseline && !boolPeakPickedByUnderTest
      v['peak_picked'] = 'by neither baseline nor by undertest'
    end

  end

end
file_to_hash(file) click to toggle source
# File lib/determinations_comparison.rb, line 593
def file_to_hash(file)

  begin
    json = File.read(file)
    hash = JSON.parse(json)
  rescue Exception => e
    puts "error retrieving - #{File.basename(file)} - #{e.message}"
  end

  hash

end
filetype_baseline() click to toggle source
# File lib/determinations_comparison.rb, line 573
def filetype_baseline
  @filetype_baseline ||= File.extname(@filepath_baseline_origin)[1..-1] # [1..-1] is to remove '.' from extension name
end
filetype_undertest() click to toggle source
# File lib/determinations_comparison.rb, line 577
def filetype_undertest
  @filetype_undertest ||= File.extname(@filepath_undertest_origin)[1..-1]
end
generic_chromtype(chromtype) click to toggle source
# File lib/determinations_comparison.rb, line 539
def generic_chromtype(chromtype)

  case chromtype
    when 'Q'
      'Qual'
    when 'A'
      'Quant'
    else
      chromtype
    end

end
generic_chromtype_to_filetype_specific(generic_chromtype, filetype) click to toggle source
# File lib/determinations_comparison.rb, line 552
def generic_chromtype_to_filetype_specific(generic_chromtype, filetype)

  case generic_chromtype
    when 'Quant'
      filetype == 'json' ? 'Quant' : 'A'
    when 'Qual'
      filetype == 'json' ? 'Qual' : 'Q'
    else
      'IS'
  end

end
generic_key(key) click to toggle source
# File lib/determinations_comparison.rb, line 511
def generic_key(key)

  unless key.downcase == key
    key = key.to_snakecase
  end

  return key

end
get_baseline_and_undertest_values(item, hashParent, opts) click to toggle source
# File lib/determinations_comparison.rb, line 360
def get_baseline_and_undertest_values(item, hashParent, opts)

  k_baseline = opts[:k_baseline]
  k_undertest = opts[:k_undertest]
  k_generic = opts[:k_generic]

  unless k_generic.nil?
    val = get_corresponding_key_value_from_other_file 'baseline', item, hashParent, k_generic
  end

  unless k_undertest.nil?
    #v_undertest = hashParent_undertest[k_undertest]
    val = get_corresponding_key_value_from_other_file 'baseline', item, hashParent, k_undertest
  end

  unless k_baseline.nil?
    #v_baseline = hashParent_baseline[k_baseline]
    val = get_corresponding_key_value_from_other_file 'undertest', item, hashParent, k_baseline
  end

  return val


end
get_corresponding_key_value_from_other_file(file_type_to_get_key_value_for, item, unknown_hashParent, k_known) click to toggle source
# File lib/determinations_comparison.rb, line 385
def get_corresponding_key_value_from_other_file(file_type_to_get_key_value_for, item, unknown_hashParent, k_known)

  # guess at name of corresponding baseline key based on convention (i.e. convert snakecase to camelcase or vice-versa)
  if file_type_to_get_key_value_for == 'baseline'
    naming_convention_of_unknown = naming_convention_baseline
    naming_convention_of_known = naming_convention_undertest
    #naming_convention = naming_convention_baseline
    keytype_unknown = "#{filetype_baseline}_key"
    keytype_known = "#{filetype_undertest}_key"

  else
    naming_convention_of_unknown = naming_convention_undertest
    naming_convention_of_known = naming_convention_baseline

    #naming_convention = naming_convention_undertest
    keytype_unknown = "#{filetype_undertest}_key"
    keytype_known = "#{filetype_baseline}_key"

  end

  #convert known key to naming convention used by unknown file
  k_unknown =
      if naming_convention_of_unknown == 'snakecase'
        k_known.to_snakecase
      else
        k_known.to_camelcase_lower
      end

  if unknown_hashParent.has_key? k_unknown
    v_unknown = unknown_hashParent[k_unknown]
  else
    # look in mapping for alternatives to convention
    arr = mapping[item].select { |h|  h[keytype_known] == k_known }

    unless arr.empty?
      k_unknown = arr.first[keytype_unknown]
      v_unknown = unknown_hashParent[k_unknown]
    end

  end

end
hashCompoundBaseLine() click to toggle source
# File lib/determinations_comparison.rb, line 585
def hashCompoundBaseLine
  @hashCompoundBaseLine ||= file_to_hash @filepath_baseline_origin
end
hashCompoundUnderTest() click to toggle source
# File lib/determinations_comparison.rb, line 589
def hashCompoundUnderTest
  @hashCompoundUnderTest ||= file_to_hash @filepath_undertest_origin
end
mapping() click to toggle source
# File lib/determinations_comparison.rb, line 581
def mapping
  @mapping ||= YAML.load_file(@filepath_keymapping)
end
naming_convention_baseline() click to toggle source
# File lib/determinations_comparison.rb, line 565
def naming_convention_baseline
  @naming_convention_baseline ||= filetype_baseline == 'po' ? 'camelcase' : 'snakecase'
end
naming_convention_undertest() click to toggle source
# File lib/determinations_comparison.rb, line 569
def naming_convention_undertest
  @naming_convention_undertest ||= filetype_undertest == 'po' ? 'camelcase' : 'snakecase'
end
normalize_precision(val1,val2) click to toggle source
# File lib/determinations_comparison.rb, line 521
def normalize_precision(val1,val2)
  val1_precision = precision(val1)
  val2_precision = precision(val2)

  if val1_precision == val2_precision
    return val1,val2
  elsif(val1_precision > val2_precision)
    return val1.round(val2_precision), val2
  elsif(val1_precision < val2_precision)
    return val1, val2.round(val1_precision)
  end

end
percentage_difference(item,key_undertest,v_baseline,v_undertest) click to toggle source
# File lib/determinations_comparison.rb, line 428
def percentage_difference(item,key_undertest,v_baseline,v_undertest)

  hashDiff = Hash.new
  hashDiff[:baseline] = v_baseline
  hashDiff[:undertest] = v_undertest

  #normalize based on least precision betweeen baseline and undertest values
  v_baseline,v_undertest = normalize_precision v_baseline , v_undertest

  hashDiff[:baseline_rounded] = v_baseline
  hashDiff[:undertest_rounded] = v_undertest

  #check if property has a scale for proportionate comparison
  arr = mapping[item].select { |h|  h["#{filetype_undertest}_key"]== key_undertest }

  unless arr.empty?
    hash = arr.first
    boolHasScale = hash.has_key?('scale') ? true:false
  end

  if boolHasScale
    dif,hashScale = percentage_difference_relative_to_scale v_baseline,v_undertest,hash['scale']
    hashDiff.merge! hashScale
  else
    dif,calculation = percentage_difference_without_scale v_baseline,v_undertest
    hashDiff[:calculation] = calculation
  end

  hashDiff[:percent_diff] = dif

  return hashDiff

end
percentage_difference_relative_to_scale(val1,val2,scale_description) click to toggle source
# File lib/determinations_comparison.rb, line 483
def percentage_difference_relative_to_scale(val1,val2,scale_description)

  # use json keys
  case scale_description
    when 'relative_to_apex_intensity'
      min_scale = 0  # TODO - should this be baseline?
      max_scale = get_baseline_and_undertest_values 'Peak', @hashPeakBaseLine,:k_generic => 'apex_intensity'

    when 'relative_to_peak_start_and_end_time'
      min_scale = get_baseline_and_undertest_values 'Chromatogram', @hashChromBaseLine, :k_generic => 'start_time'
      max_scale = get_baseline_and_undertest_values 'Chromatogram', @hashChromBaseLine, :k_generic => 'end_time'
  end

  scale = max_scale - min_scale

  if scale == 0
    dif,calc = percentage_difference_without_scale val1,val2
  else
    dif = (((val1 - val2).abs / scale ) * 100).round(2)
    calc = "(((#{val1} - #{val2}.abs / scale ) * 100).round(2)"
  end

  dif = dif.abs # use absolute value

  return dif,{:scale=>scale_description,:min=>min_scale,:max=>max_scale,:calculation=>calc}

end
percentage_difference_without_scale(a,b) click to toggle source
# File lib/determinations_comparison.rb, line 462
def percentage_difference_without_scale(a,b)
  if (b.to_f == a.to_f) && (a.to_f == 0)
    chg = 0
    calc = '0'
  elsif b.to_f == 0
    diff = b.to_f - a.to_f
    chg = diff / a.to_f
    calc = "#{b} - #{a} / #{a}"
  else a.to_f == 0
  diff = a.to_f - b.to_f
  chg = diff / b.to_f
  calc = "#{a} - #{b} / #{b}"
  end

  diff = (chg * 100.0).round(2)
  diff = diff.abs

  return diff, calc

end
precision(number) click to toggle source
# File lib/determinations_comparison.rb, line 535
def precision(number)
  number.to_s.split('.').last.size
end
property_diff_to_hash(chromatogram_type, property_description, hashDif) click to toggle source
# File lib/determinations_comparison.rb, line 606
def property_diff_to_hash(chromatogram_type, property_description, hashDif)

  hash = Hash.new

  begin
    hash[:chromatogram] = chromatogram_type
    hash[:property] = property_description

    if property_description == 'peak_picked'
      hash[:diff] = 0
      hash[:baseline] = 0
      hash[:undertest] = 0
      hash[:scale] = hashDif
      hash[:calculation] = ''
    else

      hash[:diff] = hashDif[:percent_diff]
      hash[:baseline] = hashDif[:baseline_rounded] || hashDif[:baseline]
      hash[:undertest] = hashDif[:undertest_rounded] || hashDif[:undertest]
      hash[:calculation] = ''

      if hashDif[:scale].nil?
        hash[:scale] = ''
      else
        hash[:scale] = "#{hashDif[:scale]} (#{hashDif[:min]}-#{hashDif[:max]})"
      end

      if hashDif[:calculation].nil?
        hash[:calculation] = ''
      else
        hash[:calculation] = hashDif[:calculation]
      end

    end
  rescue Exception => e
    $log_detercomp.err "error creating html table for #{property_description}"
  end

  return hash

end