Class: Station

Inherits:
ActiveRecord::Base
  • Object
show all
Extended by:
FriendlyId
Defined in:
app/models/station.rb

Overview

Note:

When getting a station use the Friendly ID method! Station.friendly.find(params[:id]) Since stations can use either the slug or id as param

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#balanceFloat

the balance on prepaid phone cards

Returns:

  • (Float)

    the current value of balance



21
22
23
# File 'app/models/station.rb', line 21

def balance
  @balance
end

#created_atDateTime

Returns the current value of created_at

Returns:

  • (DateTime)

    the current value of created_at



21
22
23
# File 'app/models/station.rb', line 21

def created_at
  @created_at
end

#hw_idString

Returns the current value of hw_id

Returns:

  • (String)

    the current value of hw_id



21
22
23
# File 'app/models/station.rb', line 21

def hw_id
  @hw_id
end

#idInteger

Returns the current value of id

Returns:

  • (Integer)

    the current value of id



21
22
23
# File 'app/models/station.rb', line 21

def id
  @id
end

#last_observation_received_atDateTime

Returns the current value of last_observation_received_at

Returns:

  • (DateTime)

    the current value of last_observation_received_at



21
22
23
# File 'app/models/station.rb', line 21

def last_observation_received_at
  @last_observation_received_at
end

#latest_observationObject

Returns the value of attribute latest_observation



54
55
56
# File 'app/models/station.rb', line 54

def latest_observation
  @latest_observation
end

#latitudeFloat

Returns the current value of latitude

Returns:

  • (Float)

    the current value of latitude



21
22
23
# File 'app/models/station.rb', line 21

def latitude
  @latitude
end

#longitudeFloat

Returns the current value of longitude

Returns:

  • (Float)

    the current value of longitude



21
22
23
# File 'app/models/station.rb', line 21

def longitude
  @longitude
end

#nameString

Returns the current value of name

Returns:

  • (String)

    the current value of name



21
22
23
# File 'app/models/station.rb', line 21

def name
  @name
end

#offlineBoolean

Returns the current value of offline

Returns:

  • (Boolean)

    the current value of offline



21
22
23
# File 'app/models/station.rb', line 21

def offline
  @offline
end

#sampling_rate(unit: :seconds) ⇒ ActiveSupport::Duration | nil

Custom getter to get the rate as a Duration instead of an integer

Returns:

  • (ActiveSupport::Duration | nil)

See Also:



21
22
23
# File 'app/models/station.rb', line 21

def sampling_rate
  @sampling_rate
end

#showBoolean

Returns the current value of show

Returns:

  • (Boolean)

    the current value of show



21
22
23
# File 'app/models/station.rb', line 21

def show
  @show
end

#slugString

a URL friendly version of the name. Can be used instead of ID.

Returns:

  • (String)

    the current value of slug



21
22
23
# File 'app/models/station.rb', line 21

def slug
  @slug
end

#speed_calibrationFloat

Returns the current value of speed_calibration

Returns:

  • (Float)

    the current value of speed_calibration



21
22
23
# File 'app/models/station.rb', line 21

def speed_calibration
  @speed_calibration
end

#timezoneString

Returns the current value of timezone

Returns:

  • (String)

    the current value of timezone



21
22
23
# File 'app/models/station.rb', line 21

def timezone
  @timezone
end

#updated_atDateTime

Returns the current value of updated_at

Returns:

  • (DateTime)

    the current value of updated_at



21
22
23
# File 'app/models/station.rb', line 21

def updated_at
  @updated_at
end

#user_idInteger

Returns the current value of user_id

Returns:

  • (Integer)

    the current value of user_id



21
22
23
# File 'app/models/station.rb', line 21

def user_id
  @user_id
end

#zoneObject

Returns the value of attribute zone



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

def zone
  @zone
end

Class Method Details

.check_all_stations(stations = Station.all) ⇒ Object

Rake task which periodically tests the status of each station.

Parameters:

  • stations (defaults to: Station.all)

    array



135
136
137
138
139
# File 'app/models/station.rb', line 135

def self.check_all_stations stations = Station.all
  stations.each do |s|
    s.check_status!
  end
end

.send_low_balance_alerts(stations = Station.all()) ⇒ Object



127
128
129
130
131
# File 'app/models/station.rb', line 127

def self.send_low_balance_alerts stations = Station.all()
  stations.each do |station|
    station.check_balance
  end
end

.with_observations(limit = 1) ⇒ ActiveRecord::Relation

Note:

requires Postgres 9.3+

Scope that eager loads the latest N number of observations.

Parameters:

  • limit (Integer) (defaults to: 1)
    • the number of observations to eager load

Returns:

  • (ActiveRecord::Relation)


63
64
65
66
67
# File 'app/models/station.rb', line 63

def self.with_observations(limit = 1)
  eager_load(:observations).where(
    observations: { id: Observation.pluck_from_each_station(limit) }
  )
end

Instance Method Details

#calibrate_observations!Object

Updates the "cached" speed_calibration value on the observations table



77
78
79
# File 'app/models/station.rb', line 77

def calibrate_observations!
  self.observations.update_all(speed_calibration: self.speed_calibration)
end

#check_balanceObject

Send notifications if station balance is low

Returns:

  • boolean true for ok balance, false if balance is low



168
169
170
171
172
173
# File 'app/models/station.rb', line 168

def check_balance
  if low_balance?
    Services::Notifiers::LowBalance.call(self)
  end
  !low_balance?
end

#check_status!Object



152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'app/models/station.rb', line 152

def check_status!
  if should_be_offline?
    unless offline?
      update_attribute('offline', true)
      Services::Notifiers::StationOffline.call(self)
    end
  else
    if offline?
      update_attribute('offline', false)
      Services::Notifiers::StationOnline.call(self)
    end
  end
end

#created_at_localObject



183
184
185
# File 'app/models/station.rb', line 183

def created_at_local
  time_to_local created_at if created_at.present?
end

#current_observationObject



115
116
117
# File 'app/models/station.rb', line 115

def current_observation
  latest_observation.presence || observations.last
end

#last_observation_received_at_localObject



191
192
193
# File 'app/models/station.rb', line 191

def last_observation_received_at_local
  time_to_local updated_at if last_observation_received_at.present?
end

#load_observations!(limit = 1, query: Observation.desc) ⇒ ActiveRecord::Associations::CollectionProxy

Does a select query to fetch observations and manually sets up active record association to avoid n+1 query and memory issues when Rails tries to eager load the association without a limit.

Parameters:

  • limit (Integer) (defaults to: 1)
  • query (ActiveRecord::Relation)

Returns:

  • (ActiveRecord::Associations::CollectionProxy)

See Also:



209
210
211
212
213
214
215
216
# File 'app/models/station.rb', line 209

def load_observations!(limit = 1, query: Observation.desc)
  observations = query.merge(Observation.where(station: self).limit(limit))
  association = self.association(:observations)
  association.loaded!
  association.target.concat(observations)
  observations.each { |observation| association.set_inverse_instance(observation) }
  self.observations
end

#lookup_timezoneObject

Lookup timezone via lat/lng



82
83
84
85
# File 'app/models/station.rb', line 82

def lookup_timezone
  self.zone = Timezone::Zone.new(latlon: [self.lat, self.lon])
  self.zone.zone
end

#low_balance?Boolean

Returns:

  • (Boolean)


175
176
177
# File 'app/models/station.rb', line 175

def low_balance?
  balance < 15
end

#next_observation_expected_inObject



195
196
197
198
199
200
201
# File 'app/models/station.rb', line 195

def next_observation_expected_in
  if last_observation_received_at
    eta = (last_observation_received_at - sampling_rate.ago).round
  else
    sampling_rate
  end
end

#observations?Boolean

Returns:

  • (Boolean)


119
120
121
122
123
124
125
# File 'app/models/station.rb', line 119

def observations?
  if self.observations.loaded?
    self.observations.length > 0
  else
    self.observations.present?
  end
end

#observations_per_dayObject



229
230
231
# File 'app/models/station.rb', line 229

def observations_per_day
  1.day / sampling_rate
end

#set_timezone!Object

Lookup and set timezone Also catches any errors caused by Timezone and logs them



89
90
91
92
93
94
95
96
97
98
99
100
# File 'app/models/station.rb', line 89

def set_timezone!
  if self.timezone.nil? and !self.latitude.nil? and !self.longitude.nil?
    # Lookup timezone and catch errors due to geonames not answering
    begin
      self.timezone = self.lookup_timezone
    rescue Timezone::Error::Base => e
      logger.warn e.message
    end
  elsif self.timezone and self.zone.nil?
    self.zone = Timezone::Zone.new( zone: self.timezone )
  end
end

#should_be_offline?Boolean

do heuristics if station is down

Returns:

  • (Boolean)


146
147
148
149
150
# File 'app/models/station.rb', line 146

def should_be_offline?
    observations.desc
               .since( (sampling_rate * 4.8).seconds.ago  )
               .count < 3
end

#should_generate_new_friendly_id?Boolean

Generate a slug from name if none is given when creating station

Returns:

  • (Boolean)


107
108
109
110
111
112
113
# File 'app/models/station.rb', line 107

def should_generate_new_friendly_id?
  if !slug?
    name_changed?
  else
    false
  end
end

#time_to_local(time) ⇒ Object



141
142
143
# File 'app/models/station.rb', line 141

def time_to_local time
  self.zone.nil? ? time : zone.time(time)
end

#updated_at_localObject



187
188
189
# File 'app/models/station.rb', line 187

def updated_at_local
  time_to_local updated_at if updated_at.present?
end