Class: SplitCat::Experiment

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/split_cat/experiment.rb

Constant Summary collapse

@@cache =
HashWithIndifferentAccess.new

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.factory(name) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'app/models/split_cat/experiment.rb', line 145

def self.factory( name )
  if experiment = @@cache[ name ]
    return experiment
  end

  template = SplitCat.config.template( name )
  if experiment = Experiment.includes( :goals, :hypotheses ).find_by_name( name )
    return nil if template && !experiment.same_structure?( template )
  else
    experiment = template if template && template.save
  end

  @@cache[ name.to_sym ] = experiment

  return experiment
end

Instance Method Details

#choose_hypothesisObject

Returns a random hypothesis with weighted probability



83
84
85
86
87
88
# File 'app/models/split_cat/experiment.rb', line 83

def choose_hypothesis
  total = 0
  roll = rand( total_weight ) + 1
  hypotheses.each { |h| return h if roll <= ( total += h.weight ) }
  return hypotheses.first
end

#get_hypothesis(token) ⇒ Object

Return the winner if one has been chosen. Return nil if the token can’t be found. Return the previously assigned hypothesis (or nil on error). Choose a hypothesis randomly. Record the hypothesis assignment and return it.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'app/models/split_cat/experiment.rb', line 66

def get_hypothesis( token )
  return winner if winner.present?
  return nil unless subject = Subject.find_by_token( token )

  if join = HypothesisSubject.find_by_experiment_id_and_subject_id( id, subject.id )
    hypotheses.each { |h| return h if h.id == join.hypothesis_id }
    return nil
  end

  hypothesis = choose_hypothesis
  HypothesisSubject.create( :hypothesis_id => hypothesis.id, :subject_id => subject.id, :experiment_id => id )

  return hypothesis
end

#goal_countsObject

Returns a memoized array of goal name => hypothesis_name => subject counts



17
18
19
# File 'app/models/split_cat/experiment.rb', line 17

def goal_counts
  @goal_counts ||= GoalSubject.subject_counts( self )
end

#goal_hashObject

Return a memoized hash of goal name => goals



23
24
25
26
27
28
29
# File 'app/models/split_cat/experiment.rb', line 23

def goal_hash
 unless @goal_hash
   @goal_hash = HashWithIndifferentAccess.new
   goals.map { |goal| @goal_hash[ goal.name ] = goal }
 end
 return @goal_hash
end

#hypothesis_countsObject

Returns a memoized array of hypothesis name => subject counts



33
34
35
# File 'app/models/split_cat/experiment.rb', line 33

def hypothesis_counts
  @hypothesis_counts ||= HypothesisSubject.subject_counts( self )
end

#hypothesis_hashObject

Return a memoized hash of hypothesis name => hypotheses



39
40
41
42
43
44
45
# File 'app/models/split_cat/experiment.rb', line 39

def hypothesis_hash
  unless @hypothesis_hash
    @hypothesis_hash = HashWithIndifferentAccess.new
    hypotheses.map { |hypothesis| @hypothesis_hash[ hypothesis.name ] = hypothesis }
  end
  return @hypothesis_hash
end

#record_goal(goal, token) ⇒ Object

Return true immediately if a winner has already been chosen. Return false if the goal or token can’t be found. Return true if the user isn’t in the experiment or has already recorded this goal. Record the goal and return true.



98
99
100
101
102
103
104
105
106
107
108
109
# File 'app/models/split_cat/experiment.rb', line 98

def record_goal( goal, token )
  return true if winner_id

  return false unless goal = goal_hash[ goal ]
  return false unless subject = Subject.find_by_token( token )

  return true unless join = HypothesisSubject.find_by_experiment_id_and_subject_id( id, subject.id )
  return true if GoalSubject.find_by_goal_id_and_subject_id( goal.id, subject.id )

  GoalSubject.create( :goal_id => goal.id, :subject_id => subject.id, :experiment_id => id, :hypothesis_id => join.hypothesis_id)
  return true
end

#same_structure?(experiment) ⇒ Boolean

Returns true if the experiment has the same name, goals, and hypotheses as this one

Returns:

  • (Boolean)


113
114
115
116
117
118
# File 'app/models/split_cat/experiment.rb', line 113

def same_structure?( experiment )
  return nil if name.to_sym != experiment.name.to_sym
  return nil if goal_hash.keys != experiment.goal_hash.keys
  return nil if hypothesis_hash.keys != experiment.hypothesis_hash.keys
  return experiment
end

#to_csvObject

Generates a CSV representing the experiment results

* header row of hypothesis names
* row of total subject count per hypothesis
* goal rows of subject count per hypothesis


128
129
130
131
132
133
134
135
136
137
# File 'app/models/split_cat/experiment.rb', line 128

def to_csv
  CSV.generate do |csv|
    csv << [ nil ] + hypotheses.map { |h| h.name }
    csv << [ 'total' ] + hypotheses.map { |h| hypothesis_counts[ h.name ] || 0 }

    goals.each do |g|
      csv << [ g.name ] + hypotheses.map { |h| goal_counts[ g.name ][ h.name ] || 0 }
    end
  end
end

#total_subjectsObject



47
48
49
# File 'app/models/split_cat/experiment.rb', line 47

def total_subjects
  @total_subjects ||= hypothesis_counts.values.inject( 0 ) { |sum,count| sum + ( count || 0 ) }
end

#total_weightObject

Returns a memoized sum of hypothesis weights



53
54
55
# File 'app/models/split_cat/experiment.rb', line 53

def total_weight
  @total_weight ||= hypotheses.inject( 0 ) { |sum,h| sum + h.weight }
end