Class: Ekylibre::FirstRun::Booker

Inherits:
Object
  • Object
show all
Defined in:
lib/ekylibre/first_run/booker.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(procedure, started_at, duration) ⇒ Booker

Returns a new instance of Booker.


177
178
179
180
181
182
# File 'lib/ekylibre/first_run/booker.rb', line 177

def initialize(procedure, started_at, duration)
  @procedure = procedure
  @duration = duration
  @started_at = started_at
  @product_parameters = []
end

Instance Attribute Details

#durationObject (readonly)

Returns the value of attribute duration


175
176
177
# File 'lib/ekylibre/first_run/booker.rb', line 175

def duration
  @duration
end

#procedureObject (readonly)

Returns the value of attribute procedure


175
176
177
# File 'lib/ekylibre/first_run/booker.rb', line 175

def procedure
  @procedure
end

#product_parametersObject (readonly)

Returns the value of attribute product_parameters


175
176
177
# File 'lib/ekylibre/first_run/booker.rb', line 175

def product_parameters
  @product_parameters
end

#started_atObject (readonly)

Returns the value of attribute started_at


175
176
177
# File 'lib/ekylibre/first_run/booker.rb', line 175

def started_at
  @started_at
end

Class Method Details

.daytime_duration(on) ⇒ Object

Compute approximately day time duration in France at given date


55
56
57
# File 'lib/ekylibre/first_run/booker.rb', line 55

def daytime_duration(on)
  12.0 - 4.0 * Math.cos((on + 11.days).yday.to_f / (365.25 / Math::PI / 2))
end

.find(model, options = {}) ⇒ Object

Find a product with given options

- started_at
- work_number
- can
- variety
- derivative_of
- filter: WSQL expression

Options for product creation only:

- default_storage

Special options for worker creation only:

- first_name
- last_name
- born_at
- default_storage

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/ekylibre/first_run/booker.rb', line 23

def find(model, options = {})
  relation = model
  relation = relation.where('COALESCE(born_at, ?) <= ? ', options[:started_at], options[:started_at]) if options[:started_at]
  relation = relation.of_expression(options[:filter]) if options[:filter]
  relation = relation.of_work_numbers(options[:work_number]) if options[:work_number]
  relation = relation.can(options[:can]) if options[:can]
  relation = relation.of_variety(options[:variety]) if options[:variety]
  relation = relation.derivative_of(options[:derivative_of]) if options[:derivative_of]
  return relation.all.sample if relation.any?
  # Create product with given elements
  attributes = {}
  unless options[:default_storage].is_a?(FalseClass)
    attributes[:default_storage] = find(BuildingDivision, default_storage: find(Building, default_storage: false))
  end
  if model == Worker
    options[:first_name] ||= ::FFaker::Name.first_name
    options[:last_name] ||= ::FFaker::Name.last_name
    options[:born_at] ||= Date.new(1970 + rand(20), 1 + rand(12), 1 + rand(28))
    unless person = Contact.find_by(first_name: options[:first_name], last_name: options[:last_name])
      person = Contact.create!(first_name: options[:first_name], last_name: options[:last_name], born_at: options[:born_at])
    end
    attributes[:person] = person
  end
  variants = ProductNatureVariant.find_or_import!(options[:variety] || model.name.underscore, derivative_of: options[:derivative_of])
  variants.can(options[:can]) if options[:can]
  unless attributes[:variant] = variants.first
    raise StandardError, "Cannot find product variant with options #{options.inspect}"
  end
  model.create!(attributes)
end

.force(procedure_name, started_at, duration, options = {}) {|booker| ... } ⇒ Object

Used for importing intervention from others editors procedure_code symbol (from procedure) started_at datetime duration integer (hours)

Yields:

  • (booker)

Raises:

  • (ArgumentError)

137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/ekylibre/first_run/booker.rb', line 137

def force(procedure_name, started_at, duration, options = {}, &_block)
  # Find procedure
  procedure = Procedo[procedure_name]
  unless procedure
    raise ArgumentError, "Unknown procedure: #{procedure_name.inspect}"
  end

  # Adds fixed durations to given time
  fixed_duration = procedure.fixed_duration / 3600
  duration += fixed_duration

  # Find actors
  booker = new(procedure, started_at, duration)
  yield booker
  actors = booker.product_parameters.collect { |c| c[:actor] }.compact
  raise ArgumentError, "What's the fuck ? No actors ? " if actors.empty?

  # Find a slot for all actors for given day and given duration
  at = nil
  9.times do |p|
    at = started_at + p
    break unless InterventionProductParameter.joins(:intervention).where(actor_id: actors.map(&:id)).where('? BETWEEN started_at AND stopped_at OR ? BETWEEN started_at AND stopped_at', at, at + duration.hours).any?
  end

  # Run interventions
  intervention = nil
  stopped_at = at + duration.hours
  if stopped_at < Time.zone.now
    intervention = Intervention.create!(procedure_name: procedure_name, production: Booker.production, production_support: options[:support], started_at: at, stopped_at: stopped_at, description: options[:description])
    booker.product_parameters.each do |cast|
      intervention.add_cast!(cast)
    end
    intervention.run!({ started_at: at, duration: duration.hours }, options[:parameters])
  end
  intervention
end

.intervene(procedure_name, year, month, day, duration, options = {}) {|booker| ... } ⇒ Object

Duration is expected to be in hours

Yields:

  • (booker)

Raises:

  • (ArgumentError)

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/ekylibre/first_run/booker.rb', line 70

def intervene(procedure_name, year, month, day, duration, options = {})
  day_range = options[:range] || 30

  duration += 1.5 - rand(0.5)

  # Find procedure
  procedure = Procedo[procedure_name]
  unless procedure
    raise ArgumentError, "Unknown procedure: #{procedure_name.inspect}"
  end

  # Find actors
  booker = new(procedure, Time.new(year, month, day), duration)
  yield booker
  actors = booker.product_parameters.collect { |c| c[:actor] }.compact
  raise ArgumentError, "What's the fuck ? No actors ? " if actors.empty?

  # Adds fixed durations to given time
  fixed_duration = procedure.fixed_duration / 3600
  duration += fixed_duration

  # Estimate number of days to work
  duration_days = (duration / 8.0).ceil

  # Find a slot for all actors for given number of day
  on = nil
  begin
    on = Date.civil(year, month, day) + rand(day_range - duration_days).days
  end while InterventionProductParameter.joins(:intervention).where(actor_id: actors.map(&:id)).where('? BETWEEN started_at AND stopped_at OR ? BETWEEN started_at AND stopped_at', on, on + duration_days).any?

  # Compute real number of day
  # 11 days shifting is here respect solstice shifting with 1st day of year
  daytime_duration = self.daytime_duration(on) - 2.0
  if duration > daytime_duration
    duration_days = (duration.to_f / daytime_duration).ceil
  end

  # Split into many interventions
  periods = []
  total = duration * 1.0 - fixed_duration
  duration_days.times do
    started_at = on.to_time + sunrise(on).hours + 1.hour
    d = self.daytime_duration(on) - 2.0 - fixed_duration
    d = total if d > total
    periods << { started_at: started_at, duration: (d + fixed_duration) * 3600 } if d > 0
    total -= d
    on += 1
  end

  # Run interventions
  intervention = nil
  for period in periods
    stopped_at = period[:started_at] + period[:duration]
    next unless stopped_at < Time.zone.now
    intervention = Intervention.create!(procedure_name: procedure_name, production: Booker.production, production_support: options[:support], started_at: period[:started_at], stopped_at: stopped_at)
    for cast in booker.product_parameters
      intervention.add_cast!(cast)
    end
    intervention.run!(period, options[:parameters])
  end
  intervention
end

.sunrise(on, shift = 1.5) ⇒ Object

Compute approximately sunrise hour in France at given date


60
61
62
# File 'lib/ekylibre/first_run/booker.rb', line 60

def sunrise(on, shift = 1.5)
  shift + (24.0 - daytime_duration(on)) / 2.0
end

.sunset(on, shift = 1.5) ⇒ Object

Compute approximately sunset hour in France at given date


65
66
67
# File 'lib/ekylibre/first_run/booker.rb', line 65

def sunset(on, shift = 1.5)
  daytime_duration(on) + sunrise(on, shift)
end

Instance Method Details

#add_cast(options = {}) ⇒ Object


184
185
186
187
188
189
# File 'lib/ekylibre/first_run/booker.rb', line 184

def add_cast(options = {})
  unless procedure.parameter[options[:parameter_name]]
    raise "Invalid parameter: #{options[:parameter_name]} in procedure #{procedure.name}"
  end
  @product_parameters << options
end

#find(model, options = {}) ⇒ Object

Find a valid actor in the given period


192
193
194
195
196
# File 'lib/ekylibre/first_run/booker.rb', line 192

def find(model, options = {})
  options[:started_at] = @started_at
  options[:stopped_at] = @started_at
  self.class.find(model, options)
end