.rett
Intentionality
IntentionalityRelTyposCR.rb
require 'Datavyu_API.rb'

begin

  intent_cells = getColumn("intentionalityRel").cells

  required_codes = %w{visatt headorient usetarg}
  optional_codes = %w{intentionality}

  allowed_values = %w{n y u}

  errors = 0
  good = 0
  incomplete = []

  intent_cells.map{|cell|
    cell_errors = []

    # check codes in cell
    required_codes.map{|code|

      value = cell.send code

      # must be in enumerated values
      unless allowed_values.member? value
        cell_errors.push code
        errors += 1
      else
        good += 1
      end
    }

    # log any errors for this cell
    unless cell_errors.empty?
      id = cell.ordinal
      puts ['cell',
            id,
            'invalid answer(s) in',
            cell_errors.join(', ')].join ' '
      incomplete.push id
    end
  }

  # summarize and request further action
  print "\n#{good} codes, "
  puts errors == 0 ? "all OK!" : "PLEASE CHECK cell #{incomplete.join ', '}"

end 
CheckIntentRelCR.rb
require 'Datavyu_API.rb'

begin

  cellsA = getColumn("intentionality").cells
  cellsB = getColumn("intentionalityRel").cells

  attrs = %w{visatt headorient usetarg}
  count = cellsA.size
  agrees = 0
  intentionals = 0

  (1..count).map{|ordinal|
    index = ordinal - 1
    intentional = false
    agree = false

    intentA = cellsA[index].intentionality == 'y'
    intentB = cellsB[index].intentionality == 'y'

#    intentA = attrs.map{|a| cellsA[index].send a }.member? 'y'
#    intentB = attrs.map{|a| cellsB[index].send a }.member? 'y'

#    if intentA || intentB
    if intentA && intentB
      intentional = true
      intentionals += 1
    end

    if intentA == intentB
      agree = true
      agrees += 1
    end

    puts "reach #{ordinal} has intent #{agree ? '+' : ''}" if intentional
  }

  agreement = agrees / count.to_f * 100

  unless stat = getColumn("stat")
    stat = createNewColumn("stat","type","count","agreement")
  end

  summarycell = stat.cells.find{|c|c.offset == 1000} || stat.create_cell
  summarycell.onset = summarycell.offset = 1000

  summarycell.change_code "type","Intentional"
  summarycell.change_code "count", intentionals
  summarycell.change_code "agreement","#{agreement}% agreement"
  setColumn(stat)

  puts ""
  puts "#{intentionals} of #{count} are intentional - #{intentionals / count.to_f * 100}%"
  puts "reviewers agree #{agreement}% of the time"

end
MakeIntentionalityRelCR.rb
require 'Datavyu_API.rb'

begin

makeReliability("intentionalityRel", "intentionality", 1, "onset", "offset")

end
IntentionalityTyposCR.rb
require 'Datavyu_API.rb'

begin

  intent_cells = getColumn("intentionality").cells

  required_codes = %w{visatt headorient usetarg}
  optional_codes = %w{intentionality}

  allowed_values = %w{n y u}

  errors = 0
  good = 0
  incomplete = []

  intent_cells.map{|cell|
    cell_errors = []

    # check codes in cell
    required_codes.map{|code|

      value = cell.send code

      # must be in enumerated values
      unless allowed_values.member? value
        cell_errors.push code
        errors += 1
      else
        good += 1
      end
    }

    # log any errors for this cell
    unless cell_errors.empty?
      id = cell.ordinal
      puts ['cell',
            id,
            'invalid answer(s) in',
            cell_errors.join(', ')].join ' '
      incomplete.push id
    end
  }

  # summarize and request further action
  print "\n#{good} codes, "
  puts errors == 0 ? "all OK!" : "PLEASE CHECK cell #{incomplete.join ', '}"

end 
MakeIntentionalityCR.rb
require 'Datavyu_API.rb'
begin

  intent = createNewColumn("intentionality","visatt","headorient","usetarg","intentionality")

  reaches = getColumn("reach").cells


  reaches.map{|reach|
    intentcell = intent.create_cell
    intentcell.onset = reach.onset
    intentcell.offset = reach.offset
  }

  setVariable("intentionality",intent)

end 
Kappa.rb
require 'Datavyu_API.rb'
require 'pathname'
require 'csv'
begin
  def chooseDir
    fc = Java::javax::swing::JFileChooser.new("JRuby panel")
    fc.set_dialog_title("Choose folder")
    fc.set_file_selection_mode(Java::javax::swing::JFileChooser::DIRECTORIES_ONLY)
    fc.setCurrentDirectory(java.io.File.new(File.expand_path("~/Desktop")))
    success = fc.show_open_dialog(javax.swing.JPanel.new)
    if success == Java::javax::swing::JFileChooser::APPROVE_OPTION
      return Pathname.new(fc.get_selected_file.get_absolute_path)
    else
      nil
    end
  end
  if dir = chooseDir
    puts "#{dir} selected for Kappa stats"
    glob = dir.to_s + '/*.opf'    # file-match pattern
    sheets =  Pathname.glob(glob) # find sheets
    reachRows = []
    graspRows = []

    ## Tabulate
    sheets.sort.map{|sheet| # visit spreadsheets
      $db, $pj = loadDB(sheet.to_s)
      name = sheet.basename.sub(/\.opf$/,'')
      # ensure data exists
      columns = getColumnList
      hasReach = columns.include?('reach')
      hasReachRel = columns.include?('reachRel')
      hasBlocks = columns.include?('reachRelBlocks')
      hasQ = columns.include?('qualityofreach')
      hasQRel = columns.include?('qualityRel')

      # collect reach codings
      if hasReach && hasReachRel && hasBlocks
        blocks = getColumn("reachRelBlocks").cells  # blocks assigned for analysis
        reachEventsA = getColumn("reach").cells          # reviewer A Reach data
        reachEventsB = getColumn("reachRel").cells       # reviewer B Reach data
        itemN = 0 # item count
        # visit blocks
        blocks.map{|review|
          blkID = "#{name}.Reach.#{itemN += 1}"
          # inspect reach events
          reachA, reachB = [reachEventsA, reachEventsB].map{|events|
            # find if each reviewer found there to be a reach
            events.find{|event|
              event.onset >= review.onset && event.onset <= review.offset
            } ? 1 : 0}

          # build row and append to output report
          reachRows << [blkID, reachA, reachB]
        }
      else
        puts "#{sheet.basename} missing required columns: #{hasReach ? '' : 'reach'} #{hasReachRel ? '' : 'reachRel'} #{hasBlocks ? '' : 'reachRelBlocks'}"
      end

      # collect grasp codings
      if hasQ && hasQRel
        graspEventsA = getColumn("qualityofreach").cells # reviewer A Grasp data
        graspEventsB = getColumn("qualityRel").cells     # reviewer B Grasp data
        graspCount = 0
        graspEventsB.map{|graspB| # reviewer B's coding
          graspEventsA.map{|graspA| # reviewer A's coding
            if graspA.onset == graspB.onset # match events
              rowID = "#{name}.Grasp.#{graspCount += 1}" # event identifier
              graspRows << [rowID, (graspA.send('grasp') == 'y' ? 1 : 0), (graspB.send('grasp') == 'y' ? 1 : 0)] # event coding
            end
          }
        }
      else
        puts "#{sheet.basename} missing required columns: #{hasQ ? '' : 'qualityofreach'} #{hasQRel ? '' : 'qualityRel'}"
      end
    }

    ## Export

    CSV.open(dir.to_s + '/KappaAllItems.csv',"wb") do |csv|
      # Header
      csv << %w{
ReviewedEvent
ReviewerA
ReviewerB
}
      # Body
      allRows = []

      reachRows.map{|row| allRows << row}
      graspRows.map{|row| allRows << row}

      allRows.sort.map{|row| csv << row}
    end

    CSV.open(dir.to_s + '/KappaReach.csv',"wb") do |csv|
      # Header
      csv << %w{
ReviewedEvent
ReviewerA
ReviewerB
}
      # Body
      reachRows.map{|row| csv << row}
    end

    CSV.open(dir.to_s + '/KappaGrasp.csv',"wb") do |csv|
      # Header
      csv << %w{
ReviewedEvent
ReviewerA
ReviewerB
}
      # Body
      graspRows.map{|row| csv << row}
    end
  end
end
Quality
CheckQualityRel.rb
require 'Datavyu_API.rb'

begin

  mainCells = getColumn("qualityofreach").cells
  relCells = getColumn("qualityRel").cells

  attrs = ["hand","preshaping","grasp","mouth"]
 
  count = relCells.size
  total = count * 4

  totalAgree = 0
  agree = {
    'hand' => 0,
    'preshaping' => 0,
    'grasp' => 0,
    'mouth' => 0,
  }


  relCells.map{|rel| # find matching primary-cell
    mainCells.map{|main|
      if rel.onset == main.onset
        attrs.map{|attr|
          if rel.send(attr) == main.send(attr)
            agree[attr] += 1
          end
        }
      end
    }
  }

  puts "agreement:"
  agree.map{|p,n|
    agreement = n / count.to_f * 100
    puts "#{p} #{agreement}%"
    totalAgree += n
  }

  unless stat = getColumn("stat")
    stat = createNewColumn("stat","type","count","agreement")
  end

  summarycell = stat.cells.find{|c|c.offset == 2000} || stat.create_cell
  summarycell.onset = summarycell.offset = 2000

  agreement = totalAgree / total.to_f * 100
  summarycell.change_code "type","Quality"
  summarycell.change_code "count", totalAgree
  summarycell.change_code "agreement","#{agreement}% agreement"
  setColumn(stat)

end
MakeQuality.rb
require 'Datavyu_API.rb'
begin

  quality = createNewColumn("qualityofreach","hand","preshaping","grasp","mouth")

  cellsA = getColumn("intentionality").cells
  cellsB = getColumn("intentionalityRel").cells

  attrs = %w{visatt headorient usetarg}

  (1..cellsA.size).map{|ordinal| # find intentional reaches
    index = ordinal - 1

    intentA = cellsA[index].intentionality == 'y'
    intentB = cellsB[index].intentionality == 'y'
#    intentA = attrs.map{|a| cellsA[index].send a }.member? 'y'
#    intentB = attrs.map{|a| cellsB[index].send a }.member? 'y'

    if intentA && intentB # add quality cell
      qCell = quality.create_cell
      qCell.onset = cellsA[index].onset
      qCell.offset = cellsA[index].offset
    end
  }

  setColumn(quality)

end 
MakeQualityRel.rb
require 'Datavyu_API.rb'
begin

  # existing Quality column
  quality = getColumn("qualityofreach")

  # statistics
  count = quality.cells.size
  hasQuality = !count.zero?
  countQ = count.to_f
  countQR = 0.0

  # create Quality-Reliability column
  qualityRel = createNewColumn("qualityRel","hand","preshaping","grasp","mouth")

  # create Quality-Reliability cells
  quality.cells.shuffle.map{|cell|

    if hasQuality && (countQR / countQ) < 0.25 # minimum 25% of primary-cells

      rcell = qualityRel.create_cell
      rcell.onset = cell.onset
      rcell.offset = cell.offset
      countQR += 1

    end}

  # update
  setColumn(qualityRel)

end 
QualityRelTypos.rb
require 'Datavyu_API.rb'

begin

  quality_cells = getColumn("qualityRel").cells

  required_codes = ["hand","preshaping","grasp","mouth"]

  allowed_values = {
    "hand" => %w{r l b},
    "preshaping" => %w{y n u},
    "grasp" => %w{y n u},
    "mouth" => %w{y n u},
  }

  errors = 0
  good = 0
  incomplete = []

  quality_cells.map{|cell|
    cell_errors = []

    # check codes in cell
    required_codes.map{|code|

      value = cell.send code

      # must be in enumerated values
      unless allowed_values[code].member? value
        cell_errors.push code
        errors += 1
      else
        good += 1
      end
    }

    # log any errors for this cell
    unless cell_errors.empty?
      id = cell.ordinal
      puts ['cell',
            id,
            'invalid answer(s) in',
            cell_errors.join(', ')].join ' '
      incomplete.push id
    end
  }

  # summarize and request further action
  print "\n#{good} codes, "
  puts errors == 0 ? "all OK!" : "PLEASE CHECK cell #{incomplete.join ', '}"

end 
QualityTypos.rb
require 'Datavyu_API.rb'

begin

  quality_cells = getColumn("qualityofreach").cells

  required_codes = ["hand","preshaping","grasp","mouth"]

  allowed_values = {
    "hand" => %w{r l b},
    "preshaping" => %w{y n u},
    "grasp" => %w{y n u},
    "mouth" => %w{y n u},
  }

  errors = 0
  good = 0
  incomplete = []

  quality_cells.map{|cell|
    cell_errors = []

    # check codes in cell
    required_codes.map{|code|

      value = cell.send code

      # must be in enumerated values
      unless allowed_values[code].member? value
        cell_errors.push code
        errors += 1
      else
        good += 1
      end
    }

    # log any errors for this cell
    unless cell_errors.empty?
      id = cell.ordinal
      puts ['cell',
            id,
            'invalid answer(s) in',
            cell_errors.join(', ')].join ' '
      incomplete.push id
    end
  }

  # summarize and request further action
  print "\n#{good} codes, "
  puts errors == 0 ? "all OK!" : "PLEASE CHECK cell #{incomplete.join ', '}"

end 
Reach
ReachRelTypos.rb
require 'Datavyu_API.rb'
begin

  errors = 0

  # format time into something readable
  humanTime = lambda {|ms|
      totalSeconds = ms / 1000.0
      minutes = totalSeconds.to_i / 60
      remainingSeconds = (totalSeconds % 60).to_i
      "#{minutes}:#{remainingSeconds}"}

  # existing data
reachCells = getColumn("reach").cells
  relCells = getColumn("reachRel").cells
    blocks = getColumn("reachRelBlocks").cells

    puts ""

  # check for forgotten offset
  relCells.map{|cell|
    if cell.offset==0
      puts "Reach #{cell.ordinal} at #{humanTime[cell.onset]} missing an offset!"
      errors += 1
    end}

  puts ""

  # find un-checked blocks
  blocks.map{|block|
    hasReach = false
    hasRel = false

    reachCells.map{|cell|
      if cell.is_within block
#        puts "reachCell #{cell.ordinal} in block #{block.ordinal}"
        hasReach = true
      end}

    relCells.map{|cell|
      if cell.is_within block
#        puts "relCell #{cell.ordinal} in block #{block.ordinal}"
        hasRel = true
      end}
    if hasReach && !hasRel
      puts "did you check block #{block.ordinal} starting at #{humanTime[block.onset]}?"
      errors += 1
    end
  }

  puts "OK, looks Good." if errors == 0

end
CSV.rb
require 'Datavyu_API.rb'
require 'pathname'
require 'csv'
begin

  def chooseDir
    fc = Java::javax::swing::JFileChooser.new("JRuby panel")
    fc.set_dialog_title("Choose folder of spreadsheets")
    fc.set_file_selection_mode(Java::javax::swing::JFileChooser::DIRECTORIES_ONLY)
    fc.setCurrentDirectory(java.io.File.new(File.expand_path("~/Desktop")))
    success = fc.show_open_dialog(javax.swing.JPanel.new)
    if success == Java::javax::swing::JFileChooser::APPROVE_OPTION
      return Pathname.new(fc.get_selected_file.get_absolute_path)
    else
      nil
    end
  end

  if dir = chooseDir               # dir containing docs
    puts "#{dir} selected for export"
    glob = dir.to_s + '/*.opf'    # file-match pattern
    sheets =  Pathname.glob(glob) # find sheets
    
    CSV.open(dir.to_s + '/' + dir.basename.to_s + '.csv',"wb") do |csv|

      # Header
      csv << %w{
Video_No
Phase
LessonType
Participant
Birthdate
TestDate
Classroom
Teacher
IndGroup
Sex
Cdkl5
Mecp2
Mecp2_related
ResponseTime
Intentionals
ContactTime
RightHand
LeftHand
BothHands
Preshaping
Grasp
Mouth
}

      # visit each sheet
      sheets.map{|sheet|

        # load to active sheet
        $db, $pj = loadDB(sheet.to_s)
        puts "found #{sheet}"

        # find id cell
        id = getColumn("id").cells[0]

        # row identifier
        name = sheet.basename.sub(/\.opf$/,'')
        row = [name,nil]

        # fields to preserve
        fields = %w{lesson subj bdate tdate classroom teacher indgroup sex cdkl5 mecp211 mecp2rel responsetime}
        fields.map{|f|
          val = id.get_code(f)||''
          row.push(val)}

        # find intentional reaches and their duration
        intents = 0
        duration = 0

        begin

          # Intentionality

          cellsA = getColumn("intentionality").cells
          cellsB = getColumn("intentionalityRel").cells
          count = cellsA.size

          (1..count).map{|nth|
            index = nth - 1
            intentA = cellsA[index].intentionality == 'y' # both reviewers must agree it is intentional
            intentB = cellsB[index].intentionality == 'y'
            if intentA && intentB
              intents += 1        # count intentional
              r = cellsA[index]   # cell data
              if r.offset <= 0
                puts "ERROR offset missing in sheet #{sheet} cell #{index}"
              else
                duration += (r.offset - r.onset) # add duration to total
              end
            end
          }

          durationSec = duration / 1000.0 # convert ms to sec          
          row.push intents
          row.push durationSec

          # Quality 

          q = {
            'LeftHand' => 0,
            'RightHand' => 0,
            'BothHands' => 0,
            'preshaping' => 0,
            'grasp' => 0,
            'mouth' => 0}

          hProps = %w{RightHand LeftHand BothHands}
          qProps = %w{preshaping grasp mouth}

          cells = getColumn("qualityofreach").cells
          cells.map{|c|
            hProp = case c.hand
                    when 'l'
                      'LeftHand'
                    when 'r'
                      'RightHand'
                    when 'b'
                      'BothHands'
                    end
            q[hProp] += 1 unless !hProp
                      
            qProps.map{|p|
              v = c.send p
              if v == 'y'
                q[p] += 1
              end
            }
          }

          hProps.concat(qProps).map{|p|
            row.push q[p]
          }
          csv << row

        rescue Exception => e
          puts ["#{name}:", e.class, e.message, ", skipping"].join(' ')
        end

      }
    end
  end
end 
MakeReachReliabilityCR.rb
require 'Datavyu_API.rb'

class RVariable

  def spaces # free-space regions unoccupied by cells
    ss = []; onset = 0; offset = 0               # space/location vars
    length = 1.8e6.to_i                          # total length
    cells.sort_by(&:onset).map{|cell|            # visit cells
      offset = cell.onset                        # mark leading edge
      ss << {:onset => onset, :offset => offset} # complete a space
      onset = cell.offset}                       # mark trailing edge
    ss << {:onset => onset, :offset => length}   # space after last cell
    ss.map{|s|s[:size] = s[:offset] - s[:onset]} # measure sizes

    #puts "spaces\nstart\tend\tsize";puts ss.map{|s|[s[:onset],s[:offset],s[:size]].join "\t"} # inspect spaces
    ss
  end

  def captures cCells # count cells temporally contained in this column's cells
    c = 0
    cells.map{|cell|
      cCells.map{|cCell|
        if cCell.is_within cell
          c += 1
#          puts "captured reach #{cCell.ordinal} in region #{cell.ordinal}"
        end
      }
    }
    c
  end

end

class RCell
  def duration
    offset - onset
  end
end

begin

  # constants
  min_assigned_percent = 25 # amount to assign for quality-verification
  minute = 60000            # one minute in milliseconds
  total_time = 1.8e6        # total time of thirty minutes
  region_size = minute      # default review-region size
  reaches = getColumn("reach").cells # Reach cells
  reachCount = reaches.size
  reachRel = createNewColumn("reachRel","reach") # column for second-review entry
  setColumn(reachRel)

  def fitOverlap r, cells
    dirtyL = dirtyR = true
    iterations = 0
    while dirtyL == true || dirtyR == true
      if dirtyL
        dirtyL = false
        cells.map{|cell|
          if cell.onset < r[:onset] && cell.offset > r[:onset]
#            puts "overlapLeft d#{iterations} #{r[:onset]} by #{cell.onset}->#{cell.offset}"
            r[:onset] = cell.onset - 1000 # subsume and pre-roll pad
            dirtyL = true
          end}
      end
      if dirtyR
        dirtyR = false
        cells.map{|cell|
          if cell.onset < r[:offset] && cell.offset > r[:offset]
#            puts "overlapRight d#{iterations} #{r[:offset]} by #{cell.onset}->#{cell.offset}"
            r[:offset] = cell.offset + 1000 # post-roll
            dirtyR = true
          end}
      end
      iterations += 1
    end
  end

  solutions = 0
  assigned_time = 0
  captures = 0
  percent_assigned = lambda { assigned_time / total_time * 100 }

  while solutions < 1 || (reachCount > 0 && captures == 0) # discard empty-solution and repeat
    assigned_time = 0
    review = createNewColumn("reachRelBlocks","review") # review-region column

    # allocate review-regions
    while percent_assigned[] < min_assigned_percent
      # find space for region
      space = review.spaces.sort_by{|s|s[:size]}[-1]  # take a chunk of free-space
      size = space[:size]                             # size
      width = size < region_size ? size : region_size # smaller of available-space or default size
      loc = rand(size - width) + width/2              # select random point

      # new region
      r = {}                                     # review-region
      r[:onset]  = space[:onset] + loc - width/2 #  start
      r[:offset] = space[:onset] + loc + width/2 #  end
      fitOverlap r,reaches                       # grow to fit captured-cells

      # write region to cell
      region = review.create_cell
      region.onset = r[:onset] < space[:onset] ? space[:onset] : r[:onset]    # trim excess padding
      region.offset = r[:offset] > space[:offset] ? space[:offset] : r[:offset]

#      puts "review-region at #{region.onset / 1000.0} of duration #{region.duration / 1000.0}s"
      assigned_time += region.duration           # accumulate reviewed-time
    end
    captures = review.captures reaches
    solutions += 1
    puts "solution #{solutions} reaches #{reachCount} captures #{captures}"
  end
  puts "\nreviewing #{assigned_time / 1000.0} seconds, #{percent_assigned[]}% of total"
  setColumn(review)

end
CheckReachRelCR.rb
require 'Datavyu_API.rb'
begin

  reviews = getColumn("reachRelBlocks") # review-regions
  reachA = getColumn("reach")           # reachA
  reachB = getColumn("reachRel")        # reachB
  eventsA = reachA.cells                # coded-events, reviewer A
  eventsB = reachB.cells                # coded-events, reviewer B
  deviation = []
  total = 0
  good = 0
  errors = 0

  hTime = lambda {|ms|
      totalSeconds = ms / 1000.0
      minutes = totalSeconds.to_i / 60
      remainingSeconds = (totalSeconds % 60).to_i
      "#{minutes}:#{remainingSeconds}"}

  reviews.cells.map{|review| # review-regions
    puts "review segment #{review.ordinal}: #{hTime[review.onset]} -> #{hTime[review.offset]}"

    # event tables
    aT = {} # reviewer A
    bT = {} # reviewer B
    [[eventsA,aT],
     [eventsB,bT]].map{|events,table| # populate event-tables
      events.select{|e|e.onset > review.onset && e.offset < review.offset}.map{|t| # coded events
        table[t.ordinal] = t }} # index in table on ordinal-key
    countA = aT.keys.size
    countB = bT.keys.size
    puts " a #{countA} b #{countB}"

    # match events
    aT.map{|indexA,eventA|
      bT.map{|indexB,eventB|
        onDistance = (eventA.onset - eventB.onset).abs
        offDistance = (eventA.offset - eventB.offset).abs
        if onDistance < 500 && offDistance < 500 # within threshold-distance
          good += 1
          total += 1
          puts "matched reach #{indexA} rel #{indexB}"
          aT.delete indexA
          bT.delete indexB
          deviation.concat [onDistance,offDistance]
        end}}

    # log errors
    [[aT,:Reach],
     [bT,:ReachReliability]
    ].map{|table,pass|
      unless table.empty?
        c = table.keys.size
        errors += c
        total += c
        puts "\nNOTE #{pass} cell#{c==1 ? '' : 's'} #{table.keys.join ' '} not in #{pass == :Reach ? :ReachReliability : :Reach}\n "
      end}}

  agreement = if total == 0
                100
              else
                good.to_f / total.to_f * 100.0
              end

  unless stat = getColumn("stat")
    stat = createNewColumn("stat","type","count","agreement")
  end

  summarycell = stat.cells.find{|c|c.offset == 0} || stat.create_cell
  summarycell.onset = summarycell.offset = 0

  summarycell.change_code "type", "Reach"
  summarycell.change_code "count", eventsA.size
  summarycell.change_code "agreement","#{agreement}% agreement"
  setColumn(stat)

  puts ""
  puts [:total,:matches,:unmatched].join "\t"
  puts [total,good,errors].join "\t"
  puts "\nagreement: #{agreement}%"
  #    puts "reviewers avg #{deviation.inject(&:+) / deviation.size.to_f / 1000.0}s apart"

end
Stereotypy
MakeDowntime.rb
require 'Datavyu_API.rb'
begin

  downtime = createNewColumn("Downtime","reason")
  setColumn(downtime)

end 
CSV.rb
require 'Datavyu_API.rb'
require 'pathname'
require 'csv'
class RCell
  def duration
    offset - onset
  end
end

begin

  format_time = lambda {|ms|
      totalSeconds = ms / 1000.0
      minutes = totalSeconds.to_i / 60
      seconds = (totalSeconds % 60).to_i
      "#{'%02d' % minutes}:#{'%02d' % seconds}"}
  
  def chooseDir
    fc = Java::javax::swing::JFileChooser.new("JRuby panel")
    fc.set_dialog_title("Choose folder for Stereotypy export")
    fc.set_file_selection_mode(Java::javax::swing::JFileChooser::DIRECTORIES_ONLY)
    fc.setCurrentDirectory(java.io.File.new(File.expand_path("~/Desktop")))
    success = fc.show_open_dialog(javax.swing.JPanel.new)
    if success == Java::javax::swing::JFileChooser::APPROVE_OPTION
      return Pathname.new(fc.get_selected_file.get_absolute_path)
    else
      nil
    end
  end

  if dir = chooseDir               # dir containing docs
    puts "#{dir} selected for export"
    glob = dir.to_s + '/*.opf'    # file-match pattern
    sheets =  Pathname.glob(glob) # find sheets
    
    CSV.open(dir.to_s + '/' + dir.basename.to_s + '.csv',"wb") do |csv|

      # header
      csv << %w{
Video_No
LessonType
Participant
Birthdate
TestDate
Classroom
Teacher
IndGroup
Sex
Cdkl5
Mecp2
Mecp2_related
ResponseTime
skippedTime
activeTime
activePercent
stereoTime
stereoPercent
leftTime
leftPercent
rightTime
rightPercent
mixedTime
mixedPercent
joinedTime
joinedPercent
}
      
      # visit each sheet
      sheets.map{|sheet|

        # load to active sheet
        $db, $pj = loadDB(sheet.to_s)
#        puts "found #{sheet}"

        # find id cell
        id = getColumn("id").cells[0]

        # sheet identifier
        name = sheet.basename.sub(/\.opf$/,'')
        row = [name]

        # session info
        fields = %w{lesson subj bdate tdate classroom teacher indgroup sex cdkl5 mecp211 mecp2rel responsetime}
        fields.map{|f|
          val = id.get_code(f)||''
          row.push(val)}

        begin

          # get cells
          stereoCells = getColumn("stereotypy").cells
          handCells = getColumn("hand").cells

          totalTime  = (30 * 60 * 1000).to_f # constant
          activeTime = (30 * 60 * 1000).to_f # total excluding inactive time-regions

          stereoTime = 0
          missedTime = 0
          asleepTime = 0
          leftTime = 0
          rightTime = 0
          mixedTime = 0
          joinedTime = 0
          
          stereoCells.map{|sc|
            v = sc.get_code 'stereotypy'
            duration = sc.duration
            if duration < 0
              puts ["sheet",name,'cell',sc.ordinal,'at',format_time[sc.onset],"mins missing offset-time?"].join(" ")
            else
              case v
              when 's' # stereo cell
                stereoTime += duration
                handCell = handCells.find{|cell|cell.onset == sc.onset}
                if handCell
                  hand = handCell.get_code 'hand'
                  case hand
                  when 'r'
                    rightTime += duration
                  when 'l'
                    leftTime += duration
                  when 'j'
                    joinedTime += duration
                  when 'm'
                    mixedTime += duration
                  else
                    puts ["missing/invalid hand label in sheet",name,'cell',sc.ordinal,'at',format_time[handCell.onset]].join(" ")
                  end
                end
              when 'm' # missing/downtime
                missedTime += duration
              when 'a' # asleep
                asleepTime += duration
              else
                puts ["sheet",name,'stereotypy-cell',sc.ordinal,'at',format_time[sc.onset],"missing label"].join(" ")
              end
            end
          }

          inactiveTime = missedTime + asleepTime
          activeTime -= inactiveTime
          activePercent = (activeTime / totalTime * 100.0)
          stereoPercent = (stereoTime / activeTime.to_f * 100.0)
          leftPercent = (leftTime / stereoTime.to_f * 100.0)
          rightPercent = (rightTime / stereoTime.to_f * 100.0)
          mixedPercent = (mixedTime / stereoTime.to_f * 100.0)
          joinedPercent = (joinedTime / stereoTime.to_f * 100.0)
          
          row.push format_time[inactiveTime], format_time[activeTime], activePercent, format_time[stereoTime], stereoPercent, format_time[leftTime], leftPercent, format_time[rightTime], rightPercent, format_time[mixedTime], mixedPercent, format_time[joinedTime], joinedPercent

          csv << row

        rescue Exception => e
          puts ["#{name}:", e.class, e.message, ", skipping"].join(' ')
        end

      }
    end
  end
end 
CheckStereoTime.rb
require 'Datavyu_API.rb'

# check Stereotypy data for inter-reviewer agreement. temporal-overlap method

class RCell
  def overlapTime b
    [0, [offset,b.offset].min - [onset,b.onset].max].max
  end
  def duration
    offset - onset
  end
end

class RVariable

  def totalCellDuration
    duration = 0
    cells.map{|cell|
      duration += cell.duration }
    duration
  end

  def intersect colB, colX
    x = createNewColumn(colX,"hand")
    cellsB = getColumn(colB).cells
    cellsH = getColumn('hand').cells
    cellsHR = getColumn('handRel').cells
    cells.map{|a|
      cellsB.map{|b|
        if a.overlapTime(b) > 0
          newcell = x.create_cell          
          newcell.onset = [a.onset,b.onset].max
          newcell.offset = [a.offset,b.offset].min
          hand = cellsH.find{|c|c.onset == a.onset}
          handRel = cellsHR.find{|c|c.onset == b.onset}
          if hand && handRel && hand.hand == handRel.hand
            newcell.change_code "hand", hand.hand
          end
        end
      }}
    setColumn x
  end

  def overlapTime colB
    duration = 0
    cellsB = getColumn(colB).cells
    cells.map{|a|
      cellsB.map{|b|
        duration += (a.overlapTime b)}}
    duration
  end

end

begin
  reviewCol = "ReviewBlock"
  agreeCol = 'stereotypyAgree'

  # 30 minutes
  totalTime = 30 * 60 * 1000

  # human-readable time 
  hTime = lambda {|ms|
      seconds = ms / 1000.0
      minutes = seconds.to_i / 60
      extraSeconds = (seconds % 60).to_i
      "#{'%02d' % minutes}m#{'%02d' % extraSeconds}s"}

  
  # configure review-blocks

  # if review-blocks don't exist (of MakeStereoRel25 or MakeStereoRel100 origin)
  unless $db.getVariable reviewCol
    review = createNewColumn(reviewCol,"review")
    region = review.create_cell
    region.onset = 0           # assign entire session
    region.offset = totalTime
    setColumn review
  end

  # feedback on amount assigned
  time = getColumn(reviewCol).totalCellDuration.to_f
  puts "#{hTime[time]} assigned for reliability-coding, #{'%.2f' % (time / totalTime.to_f * 100.0)}% of total"

  # match data from both coders
  getColumn('stereotypy').intersect('stereotypyRel', agreeCol)

  # calculate durations
  timeA = getColumn('stereotypy').overlapTime(reviewCol)
  timeB = getColumn('stereotypyRel').overlapTime(reviewCol)
  timeX = getColumn(agreeCol).overlapTime(reviewCol)
  timeH = 0
  getColumn(agreeCol).cells.map{|c|timeH += c.duration unless c.hand.empty?}

  # calculate agreements
  agree = timeX * 2 / (timeA + timeB).to_f * 100.0
  agreeHand = timeH / timeX.to_f * 100

  # write results to output window
  puts ""
  puts "coder A duration #{hTime[timeA]}"
  puts "coder B duration #{hTime[timeB]}"
  puts "agreeAB duration #{hTime[timeX]}"
  puts ""
  puts "coders are in #{'%.02f' % agree}% agreement on stereotypy time"
  puts "hand agrees #{'%.02f' % agreeHand}% of the time"

  # write results to document

  stat = if $db.getVariable 'Stat'
           getColumn("Stat")
         else
           createNewColumn("Stat","name","value")
         end

  stereocell = stat.cells.find{|c|c.offset == 0} || stat.create_cell
  stereocell.onset = stereocell.offset = 0
  stereocell.change_code "name","Stereotypy"
  stereocell.change_code "value","#{'%.3f' % agree}% agreement"
  
  handcell = stat.cells.find{|c|c.offset == 1} || stat.create_cell
  handcell.onset = handcell.offset = 1
  handcell.change_code "name","Hand"
  handcell.change_code "value","#{'%.3f' % agreeHand}% agreement"

  setColumn(stat)

end
MakeSpreadsheets.rb
require 'Datavyu_API.rb'
require 'pathname'
begin

  def chooseDir
    fc = Java::javax::swing::JFileChooser.new("JRuby panel")
    fc.set_dialog_title("Choose folder containing 2015 spreadsheets")
    fc.set_file_selection_mode(Java::javax::swing::JFileChooser::DIRECTORIES_ONLY)
    fc.setCurrentDirectory(java.io.File.new(File.expand_path("~/Desktop")))
    success = fc.show_open_dialog(javax.swing.JPanel.new)
    if success == Java::javax::swing::JFileChooser::APPROVE_OPTION
      return Pathname.new(fc.get_selected_file.get_absolute_path)
    else
      nil
    end
  end
  
  year = chooseDir                               # this year
  parent = year.parent                           # container
  nextyear = Pathname.new(parent.to_s + '/Stereotypy') # next year
  nextyear.mkdir unless nextyear.exist?          # make next-year directory

  glob = year.to_s + '/*.opf'   # this-year sheets
  sheets =  Pathname.glob(glob) # find sheets

  sheets.map{|sheet|            # each sheet
    # names
    name = sheet.basename
    nextname = name.sub(/\./,"s.")
    puts "#{name} -> #{nextname}"
    nextsheet = nextyear.to_s + '/' + nextname # next-year sheet

    begin # sometimes loadDB likes to fail
      $db, $pj = loadDB(sheet.to_s)          # load this-year sheet

      fields = getVariableList.-(['id'])     # retain ID field
      fields.map{|field|                     # scrub prior fields
        deleteVariable field }

      stereo = createNewColumn("stereotypy","stereotypy") # add new fields
      hand = createNewColumn("hand","hand")
      setColumn stereo
      setColumn hand

      saveDB nextsheet # save next-year sheet
    rescue Exception => e
      puts ["#{name}:", e.class, e.message, ", skipping"].join(' ')
    end
  }

end 
MakeStereoRel100.rb
require 'Datavyu_API.rb'
begin

  # 100% recoding, create the columns for new coding

  # review-blocks arent assigned for 100% review
  reviewCol = 'ReviewBlock'
  delete_column reviewCol if $db.getVariable reviewCol

  stereoRel = createNewColumn("stereotypyRel","stereotypy")
  handRel = createNewColumn("handRel","hand")

  # update spreadsheet
  setColumn stereoRel
  setColumn handRel


end 
MakeStereoRel25.rb
require 'Datavyu_API.rb'

class RVariable

  def spaces # free-space regions unoccupied by cells
    ss = []; onset = 0; offset = 0               # space/location vars
    length = 1.8e6.to_i                          # total length
    cells.sort_by(&:onset).map{|cell|            # visit cells
      offset = cell.onset                        # mark leading edge
      ss << {:onset => onset, :offset => offset} # complete a space
      onset = cell.offset}                       # mark trailing edge
    ss << {:onset => onset, :offset => length}   # space after last cell
    ss.map{|s|s[:size] = s[:offset] - s[:onset]} # measure sizes
    ss
  end

  def captures cCells # count cells temporally contained in this column's cells
    c = 0
    cells.map{|cell|
      cCells.map{|cCell|
        if cCell.is_within cell
          c += 1
        end
      }
    }
    c
  end

end

class RCell
  def duration
    offset - onset
  end
end

begin

  # constants
  min_assigned_percent = 25 # amount to assign for quality-verification
  minute = 60000            # one minute in milliseconds
  total_time = 1.8e6        # total time thirty minutes
  region_size = minute      # default review-region size
  events = getColumn("stereotypy").cells
  eventCount = events.size

  def fitOverlap r, cells
    dirtyL = dirtyR = true
    iterations = 0
    while dirtyL == true || dirtyR == true
      if dirtyL
        dirtyL = false
        cells.map{|cell|
          if cell.onset < r[:onset] && cell.offset > r[:onset]
            r[:onset] = cell.onset - 1000 # subsume and pre-roll pad
            dirtyL = true
          end}
      end
      if dirtyR
        dirtyR = false
        cells.map{|cell|
          if cell.onset < r[:offset] && cell.offset > r[:offset]
            r[:offset] = cell.offset + 1000 # post-roll
            dirtyR = true
          end}
      end
      iterations += 1
    end
  end

  solutions = 0
  assigned_time = 0
  captures = 0
  percent_assigned = lambda { assigned_time / total_time * 100 }

  while solutions < 1 || (eventCount > 0 && captures == 0) # discard empty-solution and repeat
    assigned_time = 0
    review = createNewColumn("ReviewBlock","review") # review-region column

    # allocate review-regions
    while percent_assigned[] < min_assigned_percent
      # find space for region
      space = review.spaces.sort_by{|s|s[:size]}[-1]  # take a chunk of free-space
      size = space[:size]                             # size
      width = size < region_size ? size : region_size # smaller of available-space or default size
      loc = rand(size - width) + width/2              # select random point
      # new region
      r = {}                                     # review-region
      r[:onset]  = space[:onset] + loc - width/2 #  start
      r[:offset] = space[:onset] + loc + width/2 #  end
      fitOverlap r, events                       # grow to fit captured-cells

      # write region to cell
      region = review.create_cell
      region.onset = r[:onset] < space[:onset] ? space[:onset] : r[:onset]    # trim excess padding
      region.offset = r[:offset] > space[:offset] ? space[:offset] : r[:offset]

      assigned_time += region.duration           # accumulate reviewed-time
    end
    captures = review.captures events
    solutions += 1
    puts "solution-#{solutions} events: #{eventCount} captures: #{captures}"
  end
  puts "\nreviewing #{assigned_time / 1000.0} seconds, #{percent_assigned[]}% of total"
  setColumn(review)
  stereoRel = createNewColumn("stereotypyRel","stereotypy")
  handRel = createNewColumn("handRel","hand")
  setColumn stereoRel
  setColumn handRel


end