diff --git a/Gemfile b/Gemfile index 845d443f..d53e80f7 100644 --- a/Gemfile +++ b/Gemfile @@ -87,6 +87,7 @@ end group :development, :test do # gem 'rspec_api_blueprint', require: false + gem "pry" gem 'brakeman', github: 'presidentbeef/brakeman', require: false gem 'byebug' gem 'cane' diff --git a/Gemfile.lock b/Gemfile.lock index feae9900..a770b0e0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -572,6 +572,7 @@ DEPENDENCIES pg pg_search premailer-rails + pry pry-rails puma pundit diff --git a/app/controllers/v0/components_controller.rb b/app/controllers/v0/components_controller.rb index 9b525c4a..12906d8f 100644 --- a/app/controllers/v0/components_controller.rb +++ b/app/controllers/v0/components_controller.rb @@ -7,7 +7,7 @@ def index end def show - @component = Component.includes(:board, :sensor).find(params[:id]) + @component = Component.includes(:device, :sensor).find(params[:id]) authorize @component end @@ -15,10 +15,8 @@ def show def component_params params.permit( - :board_id, - :board_type, - :sensor_id, - :equation + :device_id, + :sensor_id ) end diff --git a/app/controllers/v0/devices_controller.rb b/app/controllers/v0/devices_controller.rb index 07affcc4..9ae77afa 100644 --- a/app/controllers/v0/devices_controller.rb +++ b/app/controllers/v0/devices_controller.rb @@ -7,7 +7,7 @@ class DevicesController < ApplicationController def show @device = Device.includes( - :kit, :owner, :sensors,:tags).find(params[:id]) + :owner, :sensors,:tags).find(params[:id]) authorize @device @device end @@ -15,8 +15,9 @@ def show def index raise_ransack_errors_as_bad_request do @q = policy_scope(Device) - .includes(:owner, :tags, kit: [:components, :sensors]) + .includes(:owner, :tags, :components, :sensors) .ransack(params[:q], auth_object: (current_user&.is_admin? ? :admin : nil)) + # We are here customly adding multiple tags into the Ransack query. # Ransack supports this, but how do we add multiple tag names in URL string? Which separator to use? # See Issue #186 https://github.com/fablabbcn/smartcitizen-api/issues/186 @@ -87,11 +88,9 @@ def fresh_world_map city: device.city, country_code: device.country_code, is_private: device.is_private, - kit_id: device.kit_id, state: device.state, system_tags: device.system_tags, user_tags: device.user_tags, - added_at: device.added_at, updated_at: device.updated_at, last_reading_at: (device.last_reading_at.present? ? device.last_reading_at : nil) } @@ -122,7 +121,6 @@ def device_params :notify_stopped_publishing, :exposure, :meta, - :kit_id, :user_tags, postprocessing_attributes: [:blueprint_url, :hardware_url, :latest_postprocessing, :meta, :forwarding_params], ] diff --git a/app/controllers/v0/kits_controller.rb b/app/controllers/v0/kits_controller.rb deleted file mode 100644 index 0de3cdbd..00000000 --- a/app/controllers/v0/kits_controller.rb +++ /dev/null @@ -1,48 +0,0 @@ -module V0 - class KitsController < ApplicationController - - def show - @kit = Kit.includes(:sensors).friendly.find(params[:id]) - authorize @kit - @kit - end - - def index - @kits = Kit.includes(:sensors, sensors: [:measurement, :tag_sensors]) - @kits = paginate(@kits) - end - - def create - @kit = Kit.new(kit_params) - authorize @kit - if @kit.save - render :show, status: :created - else - raise Smartcitizen::UnprocessableEntity.new @kit.errors - end - end - - def update - @kit = Kit.find(params[:id]) - authorize @kit - if @kit.update(kit_params) - render :show, status: :ok - else - raise Smartcitizen::UnprocessableEntity.new @kit.errors - end - end - -private - - def kit_params - params.permit( - :name, - :description, - :slug, - :sensor_map, - :sensor_ids => [] - ) - end - - end -end diff --git a/app/controllers/v0/onboarding/orphan_devices_controller.rb b/app/controllers/v0/onboarding/orphan_devices_controller.rb index 742dcdcd..7f46f8a0 100644 --- a/app/controllers/v0/onboarding/orphan_devices_controller.rb +++ b/app/controllers/v0/onboarding/orphan_devices_controller.rb @@ -43,7 +43,7 @@ def update private def orphan_device_params - params.permit(:name, :description, :kit_id, :exposure, :latitude, :longitude, :user_tags) + params.permit(:name, :description, :exposure, :latitude, :longitude, :user_tags) end def set_orphan_device diff --git a/app/controllers/v0/readings_controller.rb b/app/controllers/v0/readings_controller.rb index 397a4ceb..71ae774f 100644 --- a/app/controllers/v0/readings_controller.rb +++ b/app/controllers/v0/readings_controller.rb @@ -57,11 +57,6 @@ def csv_archive @device = Device.find(params[:id]) authorize @device, :update? - if @device.kit.nil? - render json: { id: "error", message: "Device does not have a kit", url: "", errors: "" }, status: 420 - return - end - if !@device.csv_export_requested_at or (@device.csv_export_requested_at < 15.minutes.ago) @device.update_column(:csv_export_requested_at, Time.now.utc) if Rails.env.test? diff --git a/app/controllers/v0/static_controller.rb b/app/controllers/v0/static_controller.rb index 55b3e170..bece7061 100644 --- a/app/controllers/v0/static_controller.rb +++ b/app/controllers/v0/static_controller.rb @@ -15,7 +15,6 @@ def home current_user_url: [request.base_url, v0_me_index_path].join, components_url: [request.base_url, v0_components_path].join, devices_url: [request.base_url, v0_devices_path].join, - kits_url: [request.base_url, v0_kits_path].join, measurements_url: [request.base_url, v0_measurements_path].join, sensors_url: [request.base_url, v0_sensors_path].join, users_url: [request.base_url, v0_users_path].join, @@ -32,12 +31,12 @@ def metrics private: Device.where(is_private: true).count, test: Device.where(is_test: true).count, online: { - now: Device.where('last_recorded_at > ?', 10.minutes.ago).count, - last_hour: Device.where('last_recorded_at > ?', 1.hour.ago).count, - today: Device.where('last_recorded_at > ?', Time.now.beginning_of_day).count, - this_month: Device.where('last_recorded_at > ?', Time.now.beginning_of_month).count, - this_year: Device.where('last_recorded_at > ?', Time.now.beginning_of_year).count, - all_time: Device.where.not(last_recorded_at: nil).count + now: Device.where('last_reading_at > ?', 10.minutes.ago).count, + last_hour: Device.where('last_reading_at > ?', 1.hour.ago).count, + today: Device.where('last_reading_at > ?', Time.now.beginning_of_day).count, + this_month: Device.where('last_reading_at > ?', Time.now.beginning_of_month).count, + this_year: Device.where('last_reading_at > ?', Time.now.beginning_of_year).count, + all_time: Device.where.not(last_reading_at: nil).count }, readings: { good: { diff --git a/app/jobs/check_device_stopped_publishing_job.rb b/app/jobs/check_device_stopped_publishing_job.rb index 8b65c65b..bbeab457 100644 --- a/app/jobs/check_device_stopped_publishing_job.rb +++ b/app/jobs/check_device_stopped_publishing_job.rb @@ -4,7 +4,7 @@ class CheckDeviceStoppedPublishingJob < ApplicationJob def perform(*args) # Do something later - devices = Device.where(notify_stopped_publishing: true).where("last_recorded_at < ?", 60.minutes.ago) + devices = Device.where(notify_stopped_publishing: true).where("last_reading_at < ?", 60.minutes.ago) CheckupNotifyJob.perform_now("#{devices.count} devices with notification on: stopped_publishing at least an hour ago. Ids: #{devices.pluck(:id)}") devices.each do |device| diff --git a/app/lib/mqtt_messages_handler.rb b/app/lib/mqtt_messages_handler.rb index cda67471..2eed1fbf 100644 --- a/app/lib/mqtt_messages_handler.rb +++ b/app/lib/mqtt_messages_handler.rb @@ -53,6 +53,7 @@ def self.handle_readings(device, message) end rescue Exception => e Sentry.capture_exception(e) + raise e if Rails.env.test? #puts e.inspect #puts message end diff --git a/app/models/component.rb b/app/models/component.rb index 55350bd4..13f2ed13 100644 --- a/app/models/component.rb +++ b/app/models/component.rb @@ -1,11 +1,13 @@ # This joins a device with its sensors. class Component < ActiveRecord::Base - belongs_to :board, polymorphic: true + belongs_to :device belongs_to :sensor - validates_presence_of :board, :sensor - validates :sensor_id, :uniqueness => { :scope => [:board_id, :board_type] } + validates_presence_of :device, :sensor + validates :sensor_id, :uniqueness => { :scope => [:device_id] } + + delegate :equation, :reverse_equation, to: :sensor # Accepts a raw sensor reading and uses its equation to process and return # a calibrated version diff --git a/app/models/concerns/data_parser/storer.rb b/app/models/concerns/data_parser/storer.rb index 9d09dd0e..329e88fd 100644 --- a/app/models/concerns/data_parser/storer.rb +++ b/app/models/concerns/data_parser/storer.rb @@ -61,11 +61,8 @@ def sensor_reading(device, sensor) key = sensor['id'] id = device.find_sensor_id_by_key(key) end - component = device.components.detect{ |c| c["sensor_id"] == id } - - #raise "This component does not have sensor_id: #{id}" if component.nil? + component = device.find_or_create_component_by_sensor_id(id) return nil if component.nil? - value = component.normalized_value( (Float(sensor['value']) rescue sensor['value']) ) { id: id, diff --git a/app/models/device.rb b/app/models/device.rb index 48a484d6..fad3b753 100644 --- a/app/models/device.rb +++ b/app/models/device.rb @@ -17,12 +17,11 @@ class Device < ActiveRecord::Base multisearchable :against => [:name, :description, :city, :country_name], if: :active? - belongs_to :kit, optional: :true belongs_to :owner, class_name: 'User' has_many :devices_tags, dependent: :destroy has_many :tags, through: :devices_tags - has_many :components, as: :board + has_many :components has_many :sensors, through: :components has_one :postprocessing, dependent: :destroy @@ -56,9 +55,6 @@ class Device < ActiveRecord::Base :debug_push, :enclosure_type - alias_attribute :added_at, :created_at - alias_attribute :last_reading_at, :last_recorded_at - before_save :set_state reverse_geocoded_by :latitude, :longitude do |obj, results| @@ -78,13 +74,13 @@ def self.ransackable_attributes(auth_object = nil) # admin can ransack on every attribute self.authorizable_ransackable_attributes else - ["id", "name", "description", "created_at", "updated_at", "last_recorded_at", "state","geohash", "uuid", "kit_id"] + ["id", "name", "description", "created_at", "updated_at", "last_reading_at", "state","geohash", "uuid", "kit_id"] end end def self.ransackable_associations(auth_object = nil) [ - "components", "devices_tags", "kit", "owner", + "components", "devices_tags", "owner", "pg_search_document" , "postprocessing", "sensors", "tags" ] @@ -95,16 +91,21 @@ def sensor_keys %w(temp bat co hum light nets no2 noise panel) end - def find_component_by_sensor_id sensor_id - components.where(sensor_id: sensor_id).first + def sensor_map + components.map { |c| [c.key || c.sensor.key, c.sensor.id]}.to_h + end + + def find_or_create_component_by_sensor_id sensor_id + return nil if sensor_id.nil? || !Sensor.exists?(id: sensor_id) + components.find_or_create_by(sensor_id: sensor_id) end def find_sensor_id_by_key sensor_key - kit.sensor_map[sensor_key.to_s] rescue nil + sensor_map[sensor_key.to_s] rescue nil end def find_sensor_key_by_id sensor_id - kit.sensor_map.invert[sensor_id] rescue nil + sensor_map.invert[sensor_id] rescue nil end def user_tags @@ -117,25 +118,6 @@ def user_tags=(tag_names) end end - # temporary kit getter/setter - def kit_version - if self.kit_id - if self.kit_id == 2 - "1.0" - elsif self.kit_id == 3 - "1.1" - end - end - end - - def kit_version=(kv) - if kv == "1.0" - self.kit_id = 2 - elsif kv == "1.1" - self.kit_id = 3 - end - end - def owner_username owner.username if owner end @@ -145,7 +127,7 @@ def system_tags exposure, # indoor / outdoor ('new' if created_at > 1.week.ago), # new ('test_device' if is_test?), - ((last_recorded_at.present? and last_recorded_at > 60.minutes.ago) ? 'online' : 'offline') # state + ((last_reading_at.present? and last_reading_at > 60.minutes.ago) ? 'online' : 'offline') # state ].reject(&:blank?).sort end @@ -171,14 +153,6 @@ def firmware end end - def components - kit ? kit.components : super - end - - def sensors - kit ? kit.sensors : super - end - def status data.present? ? state : 'new' end @@ -195,9 +169,6 @@ def soft_state def formatted_data s = { - recorded_at: last_recorded_at, - added_at: last_recorded_at, - # calibrated_at: updated_at, location: { ip: nil, exposure: exposure, @@ -212,13 +183,13 @@ def formatted_data sensors: [] } - sensors.sort_by(&:name).each do |sensor| - sa = sensor.attributes + components.sort_by {|c| c.sensor.name }.each do |component| + sensor = component.sensor + sa = sensor.attributes.except(*%w{key equation reverse_equation}) sa = sa.merge( value: (data ? data["#{sensor.id}"] : nil), - raw_value: (data ? data["#{sensor.id}_raw"] : nil), prev_value: (old_data ? old_data["#{sensor.id}"] : nil), - prev_raw_value: (old_data ? old_data["#{sensor.id}_raw"] : nil) + last_reading_at: component.last_reading_at ) s[:sensors] << sa end @@ -234,15 +205,6 @@ def self.geocode_all_without_location end end - def set_version_if_required! identifier - if identifier and (identifier == "1.1" or identifier == "1.0") # and !device.kit_id - if self.kit_version.blank? or self.kit_version != identifier - self.kit_version = identifier - self.save validate: false - end - end - end - def self.for_world_map Rails.cache.fetch("world_map", expires_in: 10.seconds) do where @@ -261,11 +223,9 @@ def self.for_world_map longitude: device.longitude, city: device.city, country_code: device.country_code, - kit_id: device.kit_id, state: device.state, system_tags: device.system_tags, user_tags: device.user_tags, - added_at: device.added_at, updated_at: device.updated_at, last_reading_at: (device.last_reading_at.present? ? device.last_reading_at : nil) } @@ -277,6 +237,12 @@ def remove_mac_address_for_newly_registered_device! update(old_mac_address: mac_address, mac_address: nil) end + def update_component_timestamps(timestamp, sensor_ids) + components.select {|c| sensor_ids.include?(c.sensor_id) }.each do |component| + component.update(last_reading_at: timestamp) + end + end + private def set_state diff --git a/app/models/kit.rb b/app/models/kit.rb deleted file mode 100644 index 5f39e932..00000000 --- a/app/models/kit.rb +++ /dev/null @@ -1,19 +0,0 @@ -# There is a naming conflict between frontend and backend. Here, a Kit is -# a template for Device, that includes all of its sensors. Instead of every -# Device having 9 sensors, a Device has_one Kit, and that Kit has 9 sensors. -# Doing this means 1000 SCKs == 1000 Devices, 1 Kit, 9 components and 9 sensors. -# Without Kit it would require 1000 Devices, 9000 components and 9 sensors. - - # See device.rb for more information regarding the name conflict. - -class Kit < ActiveRecord::Base - - extend FriendlyId - friendly_id :slug - - has_many :devices - has_many :components, as: :board - has_many :sensors, through: :components - validates_presence_of :name, :description - -end diff --git a/app/models/orphan_device.rb b/app/models/orphan_device.rb index 2fdcf353..a254f9ae 100644 --- a/app/models/orphan_device.rb +++ b/app/models/orphan_device.rb @@ -15,7 +15,6 @@ def device_attributes { name: name, description: description, - kit_id: kit_id, user_tags: user_tags, exposure: exposure, latitude: latitude, diff --git a/app/models/raw_storer.rb b/app/models/raw_storer.rb index 59436ed3..361775ee 100644 --- a/app/models/raw_storer.rb +++ b/app/models/raw_storer.rb @@ -4,7 +4,7 @@ class RawStorer - def initialize data, mac, version, ip + def initialize data, mac, version, ip, raise_errors=false success = true @@ -17,8 +17,6 @@ def initialize data, mac, version, ip identifier = version.split('-').first - device.set_version_if_required!(identifier) - ts = data['timestamp'] || data[:timestamp] parsed_ts = Time.parse(ts) raise "timestamp error (raw)" if parsed_ts > 1.day.from_now or parsed_ts < 3.years.ago @@ -31,7 +29,7 @@ def initialize data, mac, version, ip metric = sensor metric_id = device.find_sensor_id_by_key(metric) - component = device.components.detect{|c|c["sensor_id"] == metric_id} #find_component_by_sensor_id(metric_id) + component = device.find_or_create_component_by_sensor_id(metric_id) next if component.nil? value = component.normalized_value( (Float(value) rescue value) ) @@ -56,15 +54,18 @@ def initialize data, mac, version, ip #Kairos.http_post_to("/datapoints", _data) Redis.current.publish('telnet_queue', _data.to_json) - if parsed_ts > (device.last_recorded_at || Time.at(0)) + sensor_ids = sql_data.select {|k, v| k.is_a?(Integer) }.keys.compact.uniq + device.update_component_timestamps(parsed_ts, sensor_ids) + + if parsed_ts > (device.last_reading_at || Time.at(0)) # update without touching updated_at - device.update_columns(last_recorded_at: parsed_ts, data: sql_data, state: 'has_published') + device.update_columns(last_reading_at: parsed_ts, data: sql_data, state: 'has_published') end rescue Exception => e success = false - + raise e if raise_errors end if !Rails.env.test? and device diff --git a/app/models/sensor.rb b/app/models/sensor.rb index 10192832..4bb14587 100644 --- a/app/models/sensor.rb +++ b/app/models/sensor.rb @@ -1,12 +1,9 @@ -# Every Device has one or more sensors. A Kit is a blueprint/group of sensors. -# A Kit is not an SCK. There is a naming conflict with the frontend, please see -# app/models/kit.rb for more information. +# Every Device has one or more sensors. class Sensor < ActiveRecord::Base has_many :components - has_many :boards, through: :components - has_many :kits, through: :components + has_many :devices, through: :components has_many :sensor_tags has_many :tag_sensors, through: :sensor_tags diff --git a/app/models/storer.rb b/app/models/storer.rb index c8f499df..efa5762a 100644 --- a/app/models/storer.rb +++ b/app/models/storer.rb @@ -12,6 +12,7 @@ def initialize device, reading, do_update = true rescue Exception => e Sentry.capture_exception(e) + raise e if Rails.env.test? end raise e unless e.nil? @@ -20,15 +21,17 @@ def initialize device, reading, do_update = true def update_device(parsed_ts, sql_data) return if parsed_ts <= Time.at(0) - if @device.last_recorded_at.present? - # Comparison errors if @device.last_recorded_at is nil (new devices). + if @device.last_reading_at.present? + # Comparison errors if @device.last_reading_at is nil (new devices). # Devices can post multiple readings, in a non-sorted order. # Do not update data with an older timestamp. - return if parsed_ts < @device.last_recorded_at + return if parsed_ts < @device.last_reading_at end sql_data = @device.data.present? ? @device.data.merge(sql_data) : sql_data - @device.update_columns(last_recorded_at: parsed_ts, data: sql_data, state: 'has_published') + @device.update_columns(last_reading_at: parsed_ts, data: sql_data, state: 'has_published') + sensor_ids = sql_data.select { |k, v| k.is_a?(Integer) }.keys.compact.uniq + @device.update_component_timestamps(parsed_ts, sensor_ids) ws_publish() end diff --git a/app/models/user.rb b/app/models/user.rb index 1954ad79..f44295d3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -39,8 +39,6 @@ class User < ActiveRecord::Base before_create :generate_legacy_api_key - alias_attribute :joined_at, :created_at - def self.ransackable_attributes(auth_object = nil) [ "city", "country_code", "id", "username", "uuid", "created_at", "updated_at"] end diff --git a/app/policies/kit_policy.rb b/app/policies/kit_policy.rb deleted file mode 100644 index 29e37ae1..00000000 --- a/app/policies/kit_policy.rb +++ /dev/null @@ -1,2 +0,0 @@ -class KitPolicy < AdminPolicy -end diff --git a/app/services/device_archive.rb b/app/services/device_archive.rb index 06c8c498..cdb72fef 100644 --- a/app/services/device_archive.rb +++ b/app/services/device_archive.rb @@ -45,18 +45,18 @@ def self.csv_file device_id def self.data_array device data = {} - keys = Array.new(device.kit.sensor_map.keys.length) - device.kit.sensor_map.keys.each_with_index do |key, index| + keys = Array.new(device.sensor_map.keys.length) + device.sensor_map.keys.each_with_index do |key, index| metric_id = device.find_sensor_id_by_key(key) return unless component = device.components.detect{ |c| c["sensor_id"] == metric_id } values = self.sensor_data(device, key) values.each do |v| time = Time.at(v[0]/1000).utc - data[time] ||= Array.new(device.kit.sensor_map.keys.length) + data[time] ||= Array.new(device.sensor_map.keys.length) data[time][index] = component.calibrated_value(v[1]) end - sensor = Sensor.find(device.kit.sensor_map[key]) + sensor = Sensor.find(device.sensor_map[key]) keys[index] = "#{sensor.measurement.name} in #{sensor.unit} (#{sensor.name})" end return [["timestamp"] + keys] + data.sort_by { |k, _| k.to_i } diff --git a/app/views/v0/components/_component.jbuilder b/app/views/v0/components/_component.jbuilder index 57c93afd..354a33fc 100644 --- a/app/views/v0/components/_component.jbuilder +++ b/app/views/v0/components/_component.jbuilder @@ -1 +1 @@ -json.(component, :id, :uuid, :board_id, :board_type, :sensor_id, :created_at, :updated_at) +json.(component, :id, :uuid, :device_id, :sensor_id, :created_at, :updated_at) diff --git a/app/views/v0/devices/_device.jbuilder b/app/views/v0/devices/_device.jbuilder index 224ff593..1bf5d1b2 100644 --- a/app/views/v0/devices/_device.jbuilder +++ b/app/views/v0/devices/_device.jbuilder @@ -13,7 +13,7 @@ json.( :notify_low_battery, :notify_stopped_publishing, :last_reading_at, - :added_at, + :created_at, :updated_at ) @@ -33,7 +33,6 @@ if device.owner json.profile_picture profile_picture_url(device.owner) json.url device.owner.url - json.joined_at device.owner.joined_at json.location device.owner.location json.device_ids device.owner.cached_device_ids end @@ -43,8 +42,4 @@ end json.data device.formatted_data -if device.kit - json.kit device.kit, :id, :uuid, :slug, :name, :description, :created_at, :updated_at -else - json.merge! kit: nil -end + diff --git a/app/views/v0/kits/_kit.jbuilder b/app/views/v0/kits/_kit.jbuilder deleted file mode 100644 index 11d8d222..00000000 --- a/app/views/v0/kits/_kit.jbuilder +++ /dev/null @@ -1,14 +0,0 @@ -json.(kit, - :id, :uuid, :slug, :name, :description, :created_at, :updated_at -) - -# json . kit do -# json.id kit.id -# json.slug kit.slug -# json.name kit.name -# json.description kit.description -# json.created_at kit.created_at.utc.iso8601 -# json.updated_at kit.updated_at.utc.iso8601 -# end - -json.sensors kit.sensors, partial: 'sensors/sensor', as: :sensor diff --git a/app/views/v0/kits/index.jbuilder b/app/views/v0/kits/index.jbuilder deleted file mode 100644 index 92e7c722..00000000 --- a/app/views/v0/kits/index.jbuilder +++ /dev/null @@ -1 +0,0 @@ -json.array! @kits, partial: 'kit', as: :kit diff --git a/app/views/v0/kits/show.jbuilder b/app/views/v0/kits/show.jbuilder deleted file mode 100644 index b96b0470..00000000 --- a/app/views/v0/kits/show.jbuilder +++ /dev/null @@ -1 +0,0 @@ -json.partial! "kit", kit: @kit diff --git a/app/views/v0/users/_user.jbuilder b/app/views/v0/users/_user.jbuilder index 621144e1..03a534b7 100644 --- a/app/views/v0/users/_user.jbuilder +++ b/app/views/v0/users/_user.jbuilder @@ -7,7 +7,6 @@ json.(user, :profile_picture, :url, :location, - :joined_at, :updated_at ) @@ -40,10 +39,8 @@ json.devices user.devices do |device| json.location device.location json.latitude device.latitude json.longitude device.longitude - json.kit_id device.kit_id json.state device.state json.system_tags device.system_tags json.last_reading_at device.last_reading_at - json.added_at device.added_at.utc.iso8601 json.updated_at device.updated_at.utc.iso8601 end diff --git a/config/routes.rb b/config/routes.rb index a23621fe..67148ffe 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -33,7 +33,6 @@ resources :tag_sensors resources :tags resources :measurements - resources :kits, except: [:destroy] resources :users resources :password_resets, only: [:show, :create, :update] resources :oauth_applications, path: 'applications' diff --git a/db/migrate/20230704150532_refactor_kits.rb b/db/migrate/20230704150532_refactor_kits.rb new file mode 100644 index 00000000..516c36ab --- /dev/null +++ b/db/migrate/20230704150532_refactor_kits.rb @@ -0,0 +1,167 @@ +class RefactorKits < ActiveRecord::Migration[6.0] + + def execute(query, args=[]) + args = [args] unless args.is_a?(Array) + sanitized = ActiveRecord::Base.send(:sanitize_sql_array, [query] + args) + ActiveRecord::Base.connection.execute(sanitized) + end + + def change + + # To start, create a hash of all devices to their sensors, to use as a check later on: + device_sensors_pre = Hash.new {|h, k| h[k] = []; } + execute(""" + SELECT devices.id AS device_id, components.sensor_id AS sensor_id + FROM devices + INNER JOIN components on components.board_id = devices.kit_id + AND components.board_type = 'Kit' + ORDER BY device_id, sensor_id ASC + """).each do |row| + device_sensors_pre[row["device_id"]] << row["sensor_id"] + end + + + # Add columns needed in new schema: + change_table :components do |t| + t.column :device_id, :integer + t.column :key, :string, null: true + end + change_column_null :components, :board_id, true + change_column_null :components, :board_type, true + + change_table :sensors do |t| + t.column :key, :string, null: true + t.column :equation, :string, null: true + t.column :reverse_equation, :string, null: true + end + + # Add default key to sensors: + puts "-- setting default keys for sensors" + + key_counter = Hash.new { |h, k| + h[k] = Hash.new {|h2, k2| + h2[k2] = 0 + } + } + + execute("SELECT sensor_map FROM kits").each { |row| + m = JSON.parse(row["sensor_map"]) + m.each { |k, v| key_counter[v][k] += 1 } + } + + key_counter.each do |id, keys| + key = keys.max_by {|k, v| v }[0] + execute("UPDATE sensors SET key=? WHERE id=?", [key, id]) + end + + + # Add equations to sensors: + puts "-- setting equations for sensors" + + execute(""" + SELECT sensor_id, + COUNT(DISTINCT equation) AS count_equation, + COUNT(DISTINCT reverse_equation) AS count_reverse_equation + FROM components + GROUP BY sensor_id + """).each do |row| + sensor_id = row["sensor_id"] + if row["count_equation"] == 1 && row["count_reverse_equation"] == 1 + equations = execute(""" + SELECT equation, reverse_equation + FROM components + WHERE sensor_id = ? + LIMIT 1 + """, sensor_id)[0] + execute(""" + UPDATE sensors + SET equation = ?, + reverse_equation = ? + WHERE id = ? + """, [equations["equation"], equations["reverse_equation"], sensor_id]) + end + end + + # For each existing device. Look up its kit, and create a component for each of that kit's components, with reference to the device itself. + # Override the key if it doesn't match that on sensor. + + puts "-- creating device components" + + execute("SELECT * FROM devices").each do |device_row| + execute("SELECT * FROM kits WHERE id = ? LIMIT 1", device_row["kit_id"]).each do |kit_row| + kit_component_rows = execute(""" + SELECT * FROM components + WHERE board_type = 'Kit' + AND board_id = ? + """, kit_row["id"]) + sensor_map = JSON.parse(kit_row["sensor_map"]) + kit_component_rows.each do |kit_component_row| + sensor_row = execute("SELECT * FROM sensors WHERE id = ?", kit_component_row["sensor_id"])[0] + device_id = device_row["id"] + sensor_id = sensor_row["id"] + created_at = kit_component_row["created_at"] + updated_at = kit_component_row["updated_at"] + #p [sensor_row["key"], sensor_id, sensor_map.invert[sensor_id]] + if sensor_row["key"] != sensor_map.invert[sensor_id] + key = sensor_map.invert[sensor_id] + end + begin + execute(""" + INSERT INTO components + (device_id, sensor_id, key, created_at, updated_at) + VALUES (?, ?, ?, ?, ?) + """, [device_id, sensor_id, key, created_at, updated_at]) + rescue Exception => e + raise e + end + end + end + end + + # Check the devices / sensors mapping we created before against the current + # state: + + puts "-- checking device sensors state matches previous" + + device_sensors_post = Hash.new {|h, k| h[k] = [] } + execute(""" + SELECT devices.id AS device_id, components.sensor_id AS sensor_id + FROM devices + INNER JOIN components on components.device_id = devices.id + ORDER BY device_id, sensor_id ASC + """).each do |row| + device_sensors_post[row["device_id"]] << row["sensor_id"] + end + + unless device_sensors_pre == device_sensors_post + raise "Check failed - device sensors not the same before and after migration!" + end + + # Delete deprecated columns and tables: + change_table :orphan_devices do |t| + t.remove :kit_id + end + + change_table :components do |t| + t.remove :board_id + t.remove :board_type + t.remove :equation + t.remove :reverse_equation + end + + change_table :devices do |t| + t.remove :kit_id + end + + drop_table :kits + + # Remove the old component records: + execute "DELETE FROM components WHERE device_id IS NULL" + + # Set constraints on components and sensors for the new device relationshop: + change_column_null :components, :device_id, false + add_foreign_key :components, :devices + + change_column_null :sensors, :key, null: false + end +end diff --git a/db/migrate/20230929114837_further_kits_refactor_changes.rb b/db/migrate/20230929114837_further_kits_refactor_changes.rb new file mode 100644 index 00000000..504abea3 --- /dev/null +++ b/db/migrate/20230929114837_further_kits_refactor_changes.rb @@ -0,0 +1,12 @@ +class FurtherKitsRefactorChanges < ActiveRecord::Migration[6.1] + def change + rename_column :devices, :last_recorded_at, :last_reading_at + add_column :components, :location, :integer, default: 1 + connection.execute(%{ + UPDATE components + SET location=1 + WHERE location IS NULL + }) + change_column_null :components, :location, false + end +end diff --git a/db/migrate/20231005153412_rename_component_location_to_bus.rb b/db/migrate/20231005153412_rename_component_location_to_bus.rb new file mode 100644 index 00000000..7c0df694 --- /dev/null +++ b/db/migrate/20231005153412_rename_component_location_to_bus.rb @@ -0,0 +1,5 @@ +class RenameComponentLocationToBus < ActiveRecord::Migration[6.1] + def change + rename_column :components, :location, :bus + end +end diff --git a/db/migrate/20231006064514_add_last_reading_at_to_components.rb b/db/migrate/20231006064514_add_last_reading_at_to_components.rb new file mode 100644 index 00000000..fdf7aba2 --- /dev/null +++ b/db/migrate/20231006064514_add_last_reading_at_to_components.rb @@ -0,0 +1,11 @@ +class AddLastReadingAtToComponents < ActiveRecord::Migration[6.1] + def change + add_column :components, :last_reading_at, :datetime + execute %{ + UPDATE components + SET last_reading_at = devices.last_reading_at + FROM devices + WHERE components.device_id = devices.id + } + end +end diff --git a/db/schema.rb b/db/schema.rb index 02382447..30259d7b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_07_05_095430) do +ActiveRecord::Schema.define(version: 2023_10_06_064514) do # These are extensions that must be enabled in order to support this database enable_extension "adminpack" @@ -58,15 +58,14 @@ end create_table "components", id: :serial, force: :cascade do |t| - t.integer "board_id" - t.string "board_type" t.integer "sensor_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.uuid "uuid", default: -> { "uuid_generate_v4()" } - t.text "equation" - t.text "reverse_equation" - t.index ["board_type", "board_id"], name: "index_components_on_board_type_and_board_id" + t.integer "device_id", null: false + t.string "key" + t.integer "bus", default: 1, null: false + t.datetime "last_reading_at" t.index ["sensor_id"], name: "index_components_on_sensor_id" end @@ -79,10 +78,9 @@ t.float "longitude" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "kit_id" t.hstore "latest_data" t.string "geohash" - t.datetime "last_recorded_at" + t.datetime "last_reading_at" t.jsonb "meta" t.jsonb "location" t.jsonb "data" @@ -105,8 +103,7 @@ t.datetime "archived_at" t.index ["device_token"], name: "index_devices_on_device_token", unique: true t.index ["geohash"], name: "index_devices_on_geohash" - t.index ["kit_id"], name: "index_devices_on_kit_id" - t.index ["last_recorded_at"], name: "index_devices_on_last_recorded_at" + t.index ["last_reading_at"], name: "index_devices_on_last_reading_at" t.index ["owner_id"], name: "index_devices_on_owner_id" t.index ["state"], name: "index_devices_on_state" t.index ["workflow_state"], name: "index_devices_on_workflow_state" @@ -137,17 +134,6 @@ t.index ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type" end - create_table "kits", id: :serial, force: :cascade do |t| - t.string "name" - t.text "description" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "slug" - t.uuid "uuid", default: -> { "uuid_generate_v4()" } - t.jsonb "sensor_map" - t.index ["slug"], name: "index_kits_on_slug" - end - create_table "measurements", id: :serial, force: :cascade do |t| t.string "name" t.text "description" @@ -201,7 +187,6 @@ create_table "orphan_devices", id: :serial, force: :cascade do |t| t.string "name" t.text "description" - t.integer "kit_id" t.string "exposure" t.float "latitude" t.float "longitude" @@ -253,6 +238,9 @@ t.datetime "updated_at", null: false t.integer "measurement_id" t.uuid "uuid", default: -> { "uuid_generate_v4()" } + t.string "key" + t.string "equation" + t.string "reverse_equation" t.index ["ancestry"], name: "index_sensors_on_ancestry" t.index ["measurement_id"], name: "index_sensors_on_measurement_id" end @@ -310,8 +298,8 @@ add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "api_tokens", "users", column: "owner_id" + add_foreign_key "components", "devices" add_foreign_key "components", "sensors" - add_foreign_key "devices", "kits" add_foreign_key "devices_tags", "devices" add_foreign_key "devices_tags", "tags" add_foreign_key "postprocessings", "devices" diff --git a/db/seeds.rb b/db/seeds.rb index 02b16daa..bb3ac5a4 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -11,32 +11,21 @@ exit end -p '---- Seeding for development environment ----' +p "---- Seeding for development environment ----" User.create( - username: 'user1', - email: 'email@example.com', - password: 'password', + username: "user1", + email: "email@example.com", + password: "password", country_code: Faker::Address.country_code, - city: Faker::Address.city + city: Faker::Address.city, ) -#Kit.create(name: 'Making Sense WAAG #1', description: 'AQM sensor by WAAG', slug:'makingSenseSlug') -# Kits need to have a sensor_map -3.times do - Kit.create( - name: "Kit #{Faker::Educator.campus}", - description: Faker::Lorem.sentence, - slug: 'sck:1,1', - sensor_map: {"temp": 12, "hum": 13, "light": 14} - ) -end - Measurement.create( [ - { name: 'air temperature', description: 'How hot is the air', unit: 'C' }, - { name: 'air humidity', description: 'How humit is the air', unit: '% Rel' }, - { name: 'light', description: 'Lux is a measure', unit: 'lux' } + { name: "air temperature", description: "How hot is the air", unit: "C" }, + { name: "air humidity", description: "How humit is the air", unit: "% Rel" }, + { name: "light", description: "Lux is a measure", unit: "lux" }, ] ) @@ -45,25 +34,25 @@ [ { id: 12, - name: 'My temp sensor', - unit: 'temperature unit', + name: "My temp sensor", + unit: "temperature unit", measurement: Measurement.first, - description: 'temp sens descript' + description: "temp sens descript", }, { id: 13, - name: 'My hum sensor', - unit: 'hum unit', + name: "My hum sensor", + unit: "hum unit", measurement: Measurement.second, - description: 'light sens descript' + description: "light sens descript", }, { id: 14, - name: 'My light sensor', - unit: 'light unit', + name: "My light sensor", + unit: "light unit", measurement: Measurement.third, - description: 'light sens descript' - } + description: "light sens descript", + }, ] ) end @@ -72,26 +61,23 @@ Sensor.find(14).tag_sensors.create( [ { - name: 'environmental seed 1', - description: 'environmental sensor tag' + name: "environmental seed 1", + description: "environmental sensor tag", }, { - name: 'light seed', - description: 'Light sensor tag' + name: "light seed", + description: "Light sensor tag", }, { - name: 'digital seed', - description: 'Digital sensor tag' - } + name: "digital seed", + description: "Digital sensor tag", + }, ] ) end -#device has many sensors through components -#has_many :components, as: :board - 10.times do - Device.create( + device = Device.create( { owner: User.all.sample, name: Faker::Address.city, @@ -102,69 +88,62 @@ # reverse_geocode will FAIL if it receives a location at sea latitude: 42.385, longitude: 2.173, - device_token: Faker::Crypto.sha1[0,6], + device_token: Faker::Crypto.sha1[0, 6], is_private: [true, false].sample, notify_low_battery: [true, false].sample, notify_low_battery_timestamp: Time.now, notify_stopped_publishing: [true, false].sample, notify_stopped_publishing_timestamp: Time.now, data: { - 7 => 50, + 7 => 50, 10 => rand(20), #battery level below 15 get emails 12 => -0.629348144531249, 13 => 131.992370605469, 14 => 37.8, 15 => 27.384, 16 => 275.303, - 17 => 100 + 17 => 100, }, - kit: Kit.all.sample } ) + + Component.create( + device: device, sensor: Sensor.find(12), + ) + Component.create( + device: device, sensor: Sensor.find(13), + ) + Component.create( + device: device, sensor: Sensor.find(14), + ) + Component.create( + device: device, sensor: Sensor.find(12), + ) + Component.create( + device: device, sensor: Sensor.find(13), + ) + Component.create( + device: device, sensor: Sensor.find(14), + ) + Component.create( + device: device, sensor: Sensor.find(12), + ) + Component.create( + device: device, sensor: Sensor.find(13), + ) + Component.create( + device: device, sensor: Sensor.find(14), + ) end # Make the last Device an archived one? Device.last.archive! -# belongs_to :board, polymorphic: true -# belongs_to :sensor -# Kit and Device have many Components, as: :board - - -Component.create( - board: Kit.first, sensor: Sensor.find(12) -) -Component.create( - board: Kit.first, sensor: Sensor.find(13) -) -Component.create( - board: Kit.first, sensor: Sensor.find(14) -) -Component.create( - board: Kit.second, sensor: Sensor.find(12) -) -Component.create( - board: Kit.second, sensor: Sensor.find(13) -) -Component.create( - board: Kit.second, sensor: Sensor.find(14) -) -Component.create( - board: Kit.third, sensor: Sensor.find(12) -) -Component.create( - board: Kit.third, sensor: Sensor.find(13) -) -Component.create( - board: Kit.third, sensor: Sensor.find(14) -) - - Tag.create( [ - { name: 'Amsterdam', description: 'SCK in Adam' }, - { name: 'Barcelona', description: 'SCK in Barcelona' }, - { name: 'Manchester', description: 'SCK in Manchester' } + { name: "Amsterdam", description: "SCK in Adam" }, + { name: "Barcelona", description: "SCK in Barcelona" }, + { name: "Manchester", description: "SCK in Manchester" }, ] ) @@ -173,15 +152,15 @@ [ { device: Device.first, tag: Tag.first }, { device: Device.first, tag: Tag.second }, - { device: Device.second, tag: Tag.second } + { device: Device.second, tag: Tag.second }, ] ) rescue - p 'DevicesTags already created' + p "DevicesTags already created" end DeviceInventory.create( - report: {"random_property":"random_result"}, + report: { "random_property": "random_result" }, ) d = Device.first @@ -193,18 +172,18 @@ "description": "iSCAPE Station Lab test unit", "state": "has_published", "info": { - "time":"2018-07-17T06:55:06Z", - "hw_ver":"2.0", - "id":"6C4C1AF4504E4B4B372E314AFF031619", - "sam_ver":"0.3.0-ce87e64", - "sam_bd":"2018-07-17T06:55:06Z", - "esp_ver":"0.3.0-ce87e64", - "esp_bd":"2018-07-17T06:55:06Z" - } - } + "time": "2018-07-17T06:55:06Z", + "hw_ver": "2.0", + "id": "6C4C1AF4504E4B4B372E314AFF031619", + "sam_ver": "0.3.0-ce87e64", + "sam_bd": "2018-07-17T06:55:06Z", + "esp_ver": "0.3.0-ce87e64", + "esp_bd": "2018-07-17T06:55:06Z", + }, + }, ) #ApiToken.create( # owner_id: User.first, # token: 'random token' #) -p '---- Seeding complete! ----' +p "---- Seeding complete! ----" diff --git a/docs/onboarding.md b/docs/onboarding.md index 2886a2c2..a3ea3cc5 100644 --- a/docs/onboarding.md +++ b/docs/onboarding.md @@ -5,7 +5,7 @@ ### POST /v0/onboarding/device Method creates orphan_device and returns unique 'onboarding_session' and 'device_token'. -Requires no params at all, however, it can take any of the following: 'name', 'description', 'kit_id', 'exposure', 'latitude', 'longitude', 'user_tags'. Any passed params will be used on the creation of the 'orphan_device'. +Requires no params at all, however, it can take any of the following: 'name', 'description', 'exposure', 'latitude', 'longitude', 'user_tags'. Any passed params will be used on the creation of the 'orphan_device'. Note that both 'device_token' and 'onboarding_session' are unique for each 'orphan_device'. ``` @@ -14,9 +14,7 @@ request example: Content-Type: application/json Accept: application/json ------------------------------ -{ - "kit_id": 1 -} +{} ``` ``` @@ -46,7 +44,6 @@ OnboardingSession: a71095a2-e99c-4664-82d8-b4c1c9bbc531 { "name": "Owner", "description": "device description", - "kit_id": 1, "exposure": "indoor", "latitude": 41.3966908, "longitude": 2.1921909, @@ -61,7 +58,6 @@ response example: "id": 7, "name": "Owner", "description": "device description", - "kit_id": 1, "exposure": "indoor", "latitude": 41.3966908, "longitude": 2.1921909, @@ -114,7 +110,7 @@ If 'Onboarding-Session' is not valid, (404) "Invalid onboarding_session". Requires user authentication, otherwise (401) "Authorization required" is returned. -Requires all the `/onboarding/device` parameters to be provided (`name`, `description`, `kit_id`, `exposure`, `latitude`, `longitude`, `user_tags`), otherwise results in a 422, "Missing Params". +Requires all the `/onboarding/device` parameters to be provided (`name`, `description`, `exposure`, `latitude`, `longitude`, `user_tags`), otherwise results in a 422, "Missing Params". ``` POST v0/onboarding/register request example: @@ -141,10 +137,9 @@ response example: "longitude": 2.1921909, "created_at": "2016-10-29T12:31:25+02:00", "updated_at": "2016-10-29T12:31:25+02:00", - "kit_id": 1, "latest_data": nil, "geohash": "sp3e9bh31y", - "last_recorded_at": nil, + "last_reading_at": nil, "meta": { "exposure": "indoor" }, @@ -171,7 +166,7 @@ This is the end of the onboarding process. ## Token notification -This is tiggered when the platform receives the first **"Hello World"** from the Kit after the *light setup* process. +This is tiggered when the platform receives the first **"Hello World"** from the Kit after the *light setup* process. `io.connect(‘wss://smartcitizen.xyz’).on('token-received’, doSomething);`` diff --git a/lib/tasks/i.rake b/lib/tasks/i.rake index 730fd02f..468d957c 100644 --- a/lib/tasks/i.rake +++ b/lib/tasks/i.rake @@ -54,7 +54,7 @@ namespace :i do else next end - if device.last_recorded_at < 8.hours_ago + if device.last_reading_at < 8.hours_ago device.update_column(:data, a[0]) end end diff --git a/spec/controllers/v0/devices_controller_spec.rb b/spec/controllers/v0/devices_controller_spec.rb index f2613fdd..ac3dc5da 100644 --- a/spec/controllers/v0/devices_controller_spec.rb +++ b/spec/controllers/v0/devices_controller_spec.rb @@ -1,7 +1,5 @@ require 'rails_helper' RSpec.describe V0::DevicesController do - skip { is_expected.to permit(:name,:description,:mac_address,:latitude,:longitude,:elevation,:exposure,:meta,:kit_id,:user_tags).for(:create) } - - + skip { is_expected.to permit(:name,:description,:mac_address,:latitude,:longitude,:elevation,:exposure,:meta,:user_tags).for(:create) } end diff --git a/spec/controllers/v0/kits_controller_spec.rb b/spec/controllers/v0/kits_controller_spec.rb deleted file mode 100644 index 0f682e8d..00000000 --- a/spec/controllers/v0/kits_controller_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'rails_helper' - -RSpec.describe V0::KitsController do - skip { is_expected.to permit(:name,:description,:slug, :sensor_map).for(:update) } -end diff --git a/spec/controllers/v0/onboarding/orphan_devices_controller_spec.rb b/spec/controllers/v0/onboarding/orphan_devices_controller_spec.rb index de031aa2..eff31b44 100644 --- a/spec/controllers/v0/onboarding/orphan_devices_controller_spec.rb +++ b/spec/controllers/v0/onboarding/orphan_devices_controller_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' RSpec.describe V0::Onboarding::OrphanDevicesController, type: :controller do - it { is_expected.to permit(:name, :description, :kit_id, :exposure, :latitude, :longitude, + it { is_expected.to permit(:name, :description, :exposure, :latitude, :longitude, :user_tags).for(:create) } describe "save_orphan_device" do diff --git a/spec/factories/components.rb b/spec/factories/components.rb index cb8d8845..fe29b672 100644 --- a/spec/factories/components.rb +++ b/spec/factories/components.rb @@ -1,7 +1,7 @@ FactoryBot.define do factory :component do uuid { SecureRandom.uuid } - association :board, factory: :kit + association :device association :sensor end diff --git a/spec/factories/kits.rb b/spec/factories/kits.rb deleted file mode 100644 index 0156f967..00000000 --- a/spec/factories/kits.rb +++ /dev/null @@ -1,7 +0,0 @@ -FactoryBot.define do - factory :kit do - name { "Testing kit" } - description { "A kit that was made for the test environment" } - end - -end diff --git a/spec/factories/orphan_devices.rb b/spec/factories/orphan_devices.rb index b76a65ad..d423151f 100644 --- a/spec/factories/orphan_devices.rb +++ b/spec/factories/orphan_devices.rb @@ -2,7 +2,6 @@ factory :orphan_device do name { "OrphanDeviceName" } description { "OrphanDeviceDescription" } - kit_id { 1 } exposure { "indoor" } # same coordinates used for testing Device latitude { 41.3966908 } diff --git a/spec/lib/mqtt_messages_handler_spec.rb b/spec/lib/mqtt_messages_handler_spec.rb index c39bb414..8ca69d77 100644 --- a/spec/lib/mqtt_messages_handler_spec.rb +++ b/spec/lib/mqtt_messages_handler_spec.rb @@ -3,11 +3,14 @@ RSpec.describe MqttMessagesHandler do let(:device) { create(:device, device_token: 'aA1234') } let(:orphan_device) { create(:orphan_device, device_token: 'xX9876') } - let(:component) { build(:component, board: build(:kit), sensor: build(:sensor, id: 1)) } + let(:component) { build(:component, device: device, sensor: build(:sensor, id: 1)) } + let(:device_inventory) { create(:device_inventory, report: '{"random_property": "random_result"}') } before do device.components << component + create(:sensor, id: 13, key: "key13") + @data = [{ "recorded_at"=>"2016-06-08 10:30:00Z", @@ -129,6 +132,14 @@ device_id: device.id, method: 'REST' } + },{ + name: nil, + timestamp: 1490362514000, + value: 66.0, + tags: { + device_id: device.id, + method: 'REST' + } }].to_json ) diff --git a/spec/models/component_spec.rb b/spec/models/component_spec.rb index 07232d47..39073470 100644 --- a/spec/models/component_spec.rb +++ b/spec/models/component_spec.rb @@ -1,14 +1,14 @@ require 'rails_helper' RSpec.describe Component, :type => :model do - it { is_expected.to belong_to(:board) } + it { is_expected.to belong_to(:device) } it { is_expected.to belong_to(:sensor) } - it { is_expected.to validate_presence_of(:board) } + it { is_expected.to validate_presence_of(:device) } it { is_expected.to validate_presence_of(:sensor) } it "validates uniqueness of board to sensor" do - component = create(:component, board: create(:kit), sensor: create(:sensor)) - expect{ create(:component, board: component.board, sensor: component.sensor) }.to raise_error(ActiveRecord::RecordInvalid) + component = create(:component, device: create(:device), sensor: create(:sensor)) + expect{ create(:component, device: component.device, sensor: component.sensor) }.to raise_error(ActiveRecord::RecordInvalid) end end diff --git a/spec/models/device_spec.rb b/spec/models/device_spec.rb index d0988b13..1cf21a78 100644 --- a/spec/models/device_spec.rb +++ b/spec/models/device_spec.rb @@ -5,7 +5,6 @@ let(:mac_address) { "10:9a:dd:63:c0:10" } let(:device) { create(:device, mac_address: mac_address) } - it { is_expected.to belong_to(:kit).without_validating_presence } it { is_expected.to belong_to(:owner) } it { is_expected.to have_many(:devices_tags) } it { is_expected.to have_many(:tags).through(:devices_tags) } @@ -19,23 +18,39 @@ it "has last_reading_at" do Timecop.freeze do - device = create(:device, last_recorded_at: 1.minute.ago) + device = create(:device, last_reading_at: 1.minute.ago) expect(device.last_reading_at).to eq(1.minute.ago) end end - it "has added_at"do - Timecop.freeze do - expect(create(:device).added_at).to eq(Time.current.utc) - end - end - it "validates format of mac address, but allows nil" do expect{ create(:device, mac_address: '10:9A:DD:63:C0:10') }.to_not raise_error expect{ create(:device, mac_address: nil) }.to_not raise_error expect{ create(:device, mac_address: 123) }.to raise_error(ActiveRecord::RecordInvalid) end + describe "#sensor_map" do + context "when it has a sensor with its own key" do + it "maps the sensor key to the sensor id" do + device = create(:device) + sensor = create(:sensor, key: "sensor_key") + component = create(:component, device: device, sensor: sensor) + expect(device.sensor_map).to eq({"sensor_key" => sensor.id}) + end + end + + context "when it has a sensor with an overriden key" do + it "maps the component key to the sensor id" do + device = create(:device) + sensor = create(:sensor, key: "sensor_key") + component = create(:component, device: device, sensor: sensor, key: "component_key") + expect(device.sensor_map).to eq({"component_key" => sensor.id}) + end + end + + + end + describe "mac_address" do it "takes mac_address from existing device on update" do @@ -202,24 +217,6 @@ end - describe "kit_version" do - it "has kit_version setter" do - device = build(:device, kit_version: "1.1") - expect(device.kit_id).to eq(3) - - device = build(:device, kit_version: "1.0") - expect(device.kit_id).to eq(2) - end - - it "has kit_version getter" do - device = build(:device, kit_id: 3) - expect(device.kit_version).to eq("1.1") - - device = build(:device, kit_id: 2) - expect(device.kit_version).to eq("1.0") - end - end - it "has to_s" do device = create(:device, name: 'cool device') expect(device.to_s).to eq('cool device') @@ -233,31 +230,15 @@ skip "has status" do expect(Device.new.status).to eq('new') - expect(create(:device, last_recorded_at: 1.minute.ago).status).to eq('online') - expect(create(:device, last_recorded_at: 10.minutes.ago).status).to eq('offline') + expect(create(:device, last_reading_at: 1.minute.ago).status).to eq('online') + expect(create(:device, last_reading_at: 10.minutes.ago).status).to eq('offline') end it "has firmware" do expect(create(:device, firmware_version: 'xyz').firmware).to eq('sck:xyz') end - context "with kit" do - - let(:kit) { build(:kit) } - let(:sensor) { build(:sensor) } - let(:device) { build(:device, kit: kit) } - - it "has the kit's sensors" do - expect(device.sensors).to eq(kit.sensors) - end - - it "has the kit's components" do - expect(device.components).to eq(kit.components) - end - - end - - context "without kit" do + context "creation" do let(:sensor) { create(:sensor) } let(:device) { create(:device, sensors: [sensor]) } @@ -267,7 +248,7 @@ end it "has its own components" do - expect(device.components).to eq([Component.find_by(board: device, sensor: sensor)]) + expect(device.components).to eq([Component.find_by(device: device, sensor: sensor)]) end end @@ -314,6 +295,26 @@ end end + describe "update_column_timestamps" do + + before do + @component_1 = create(:component, sensor: create(:sensor, id: 1)) + @component_2 = create(:component, sensor: create(:sensor, id: 2)) + @device = create(:device, components: [@component_1, @component_2]) + @timestamp = Time.parse("2023-10-06 06:00:00") + end + + it "updates the timestamp for components with the given sensor ids" do + @device.update_component_timestamps(@timestamp, [1]) + expect(@component_1.reload.last_reading_at).to eq(@timestamp) + end + + it "does not update the timesatamp for components without the given sensor ids" do + expect(@component_2).not_to receive(:update).with(last_reading_at: @timestamp) + @device.update_component_timestamps(@timestamp, [1]) + end + end + context "notifications for low battery" do describe "do not get sent" do it 'when they are disabled' do @@ -378,7 +379,7 @@ context "notifications for stopped publishing" do describe "do not get sent" do it 'when they are disabled' do - device = create(:device, notify_stopped_publishing: false, last_recorded_at: 24.hours.ago) + device = create(:device, notify_stopped_publishing: false, last_reading_at: 24.hours.ago) expect(device).to have_attributes(notify_stopped_publishing: false) before_date = device.notify_stopped_publishing_timestamp CheckDeviceStoppedPublishingJob.perform_now @@ -389,7 +390,7 @@ it 'when they are enabled, but timestamp is too recent' do device = create(:device, notify_stopped_publishing: true, - last_recorded_at: 2.hours.ago, + last_reading_at: 2.hours.ago, notify_stopped_publishing_timestamp: 2.hours.ago) device.reload expect(device).to have_attributes(notify_stopped_publishing: true) @@ -403,9 +404,9 @@ end describe "do get sent" do - it 'when enabled, timestamp is more than 24 hours old AND last_recorded older than 10 minutes' do + it 'when enabled, timestamp is more than 24 hours old AND last_reading older than 10 minutes' do device = create(:device, notify_stopped_publishing: true, - last_recorded_at: 2.hours.ago, + last_reading_at: 2.hours.ago, notify_stopped_publishing_timestamp: 25.hours.ago) device.reload expect(device).to have_attributes(notify_stopped_publishing: true) @@ -431,4 +432,41 @@ end end + describe "#find_or_create_component_by_sensor_id" do + context "when the sensor exists and a component already exists for this device" do + it "returns the existing component" do + sensor = create(:sensor) + component = create(:component, sensor: sensor, device: device) + expect(device.find_or_create_component_by_sensor_id(sensor.id)).to eq(component) + end + end + + context "when the sensor exists and a component does not already exist for this device" do + it "returns a new valid component with the correct sensor and device" do + sensor = create(:sensor) + component = device.find_or_create_component_by_sensor_id(sensor.id) + expect(component).not_to be_blank + expect(component).to be_a Component + expect(component.valid?).to be(true) + expect(component.persisted?).to be(true) + expect(component.device).to eq(device) + expect(component.sensor).to eq(sensor) + end + end + + context "when no sensor exists with this id" do + it "returns nil" do + create(:sensor, id: 12345) + expect(device.find_or_create_component_by_sensor_id(54321)).to be_blank + end + end + + context "when the id is nil" do + it "returns nil" do + expect(device.find_or_create_component_by_sensor_id(nil)).to be_blank + end + end + + end + end diff --git a/spec/models/kit_spec.rb b/spec/models/kit_spec.rb deleted file mode 100644 index e3d484b2..00000000 --- a/spec/models/kit_spec.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'rails_helper' - -RSpec.describe Kit, :type => :model do - it { is_expected.to have_many(:devices) } - it { is_expected.to have_many(:components) } - it { is_expected.to have_many(:sensors) } - - it { is_expected.to validate_presence_of(:name) } - it { is_expected.to validate_presence_of(:description) } -end diff --git a/spec/models/orphan_device_spec.rb b/spec/models/orphan_device_spec.rb index 60fb717d..cb01a7c3 100644 --- a/spec/models/orphan_device_spec.rb +++ b/spec/models/orphan_device_spec.rb @@ -57,9 +57,8 @@ it 'attributes hash without device_token and onboarding_session' do device_attributes_hash = orphan_device.device_attributes - expect(device_attributes_hash.length).to eq(8) + expect(device_attributes_hash.length).to eq(7) expect(device_attributes_hash.key?(:name)).to eq(true) - expect(device_attributes_hash.key?(:kit_id)).to eq(true) expect(device_attributes_hash.key?(:description)).to eq(true) expect(device_attributes_hash.key?(:user_tags)).to eq(true) expect(device_attributes_hash.key?(:longitude)).to eq(true) diff --git a/spec/models/raw_storer_spec.rb b/spec/models/raw_storer_spec.rb index b1450934..a8711dc0 100644 --- a/spec/models/raw_storer_spec.rb +++ b/spec/models/raw_storer_spec.rb @@ -1,77 +1,65 @@ -require 'rails_helper' +require "rails_helper" def to_ts(time) time.strftime("%Y-%m-%d %H:%M:%S") end RSpec.describe RawStorer, :type => :model do - before(:each) do - Kit.create!(id: 2, name: 'SCK 1.0 - Ambient Board Goteo Board', description: "Goteo Board", slug: 'sck:1,0', sensor_map: {"co": 9, "bat": 10, "hum": 5, "no2": 8, "nets": 21, "temp": 4, "light": 6, "noise": 7, "panel": 11}) - Kit.create!(id: 3, name: 'SCK 1.1 - Ambient Board Kickstarter Board', description: "Kickstarter Board", slug: 'sck:1,1', sensor_map: {"co": 16, "bat": 17, "hum": 13, "no2": 15, "nets": 21, "temp": 12, "light": 14, "noise": 7, "panel": 18}) - - Sensor.create!(id:3, name:'DHT22', description: 'test') - Sensor.create!(id:4, name:'DHT22 - Temperature', description: 'test') - Sensor.create!(id:5, name:'DHT22 - Humidity', description: 'test') - Sensor.create!(id:6, name:'PVD-P8001', description: 'test') - Sensor.create!(id:7, name:'POM-3044P-R', description: 'test') - Sensor.create!(id:8, name:'MICS-2710', description: 'test') - Sensor.create!(id:9, name:'MICS-5525', description: 'test') - Sensor.create!(id:10, name:'Battery', description: 'test') - Sensor.create!(id:11, name:'Solar Panel', description: 'test') - Sensor.create!(id:12, name:'HPP828E031', description: 'test') - Sensor.create!(id:13, name:'HPP828E031', description: 'test') - Sensor.create!(id:14, name:'BH1730FVC', description: 'test') - Sensor.create!(id:15, name:'MiCS-4514', description: 'test') - Sensor.create!(id:16, name:'MiCS-4514', description: 'test') - Sensor.create!(id:17, name:'Battery', description: 'test') - Sensor.create!(id:18, name:'Solar Panel', description: 'test') - Sensor.create!(id:19, name:'HPP828E031 (SHT21)', description: 'test') - Sensor.create!(id:20, name:'MiCS4514', description: 'test') - Sensor.create!(id:21, name:'Microchip RN-131', description: 'test') + Sensor.create!(id: 7, name: "POM-3044P-R", description: "test", key: "noise", equation: "Mathematician.table_calibration({0=>50,2=>55,3=>57,6=>58,20=>59,40=>60,60=>61,75=>62,115=>63,150=>64,180=>65,220=>66,260=>67,300=>68,375=>69,430=>70,500=>71,575=>72,660=>73,720=>74,820=>75,900=>76,975=>77,1050=>78,1125=>79,1200=>80,1275=>81,1320=>82,1375=>83,1400=>84,1430=>85,1450=>86,1480=>87,1500=>88,1525=>89,1540=>90,1560=>91,1580=>92,1600=>93,1620=>94,1640=>95,1660=>96,1680=>97,1690=>98,1700=>99,1710=>100,1720=>101,1745=>102,1770=>103,1785=>104,1800=>105,1815=>106,1830=>107,1845=>108,1860=>109,1875=>110},x)", reverse_equation: "x") + Sensor.create!(id: 12, name: "HPP828E031", description: "test", key: "temp", equation: "(175.72 / 65536.0 * x) - 53", reverse_equation: "x") + Sensor.create!(id: 13, name: "HPP828E031", description: "test", key: "hum", equation: "(125.0 / 65536.0 * x) + 7", reverse_equation: "x") + Sensor.create!(id: 14, name: "BH1730FVC", description: "test", key: "light", equation: "x", reverse_equation: "x/10.0") + Sensor.create!(id: 15, name: "MiCS-4514", description: "test", key: "no2", equation: "x", reverse_equation: "x/1000.0") + Sensor.create!(id: 16, name: "MiCS-4514", description: "test", key: "co", equation: "x", reverse_equation: "x/1000.0") + Sensor.create!(id: 17, name: "Battery", description: "test", key: "bat", equation: "x", reverse_equation: "x/10.0") + Sensor.create!(id: 18, name: "Solar Panel", description: "test", key: "panel", equation: "x", reverse_equation: "x/1000.0") + Sensor.create!(id: 21, name: "Microchip RN-131", description: "test", key: "nets", equation: "x", reverse_equation: "x") - Component.create!(id: 4, board: Kit.find(2), sensor: Sensor.find(4), equation: 'x', reverse_equation: 'x/10.0') - Component.create!(id: 5, board: Kit.find(2), sensor: Sensor.find(5), equation: 'x', reverse_equation: 'x/10.0') - Component.create!(id: 6, board: Kit.find(2), sensor: Sensor.find(6), equation: 'x', reverse_equation: 'x/10.0') - Component.create!(id: 7, board: Kit.find(2), sensor: Sensor.find(7), equation: 'Mathematician.table_calibration({0=>0,5=>45,10=>55,15=>63,20=>65,30=>67,40=>69,50=>70,60=>71,80=>72,90=>73,100=>74,130=>75,160=>76,190=>77,220=>78,260=>79,300=>80,350=>81,410=>82,450=>83,550=>84,600=>85,650=>86,750=>87,850=>88,950=>89,1100=>90,1250=>91,1375=>92,1500=>93,1650=>94,1800=>95,1900=>96,2000=>97,2125=>98,2250=>99,2300=>100,2400=>101,2525=>102,2650=>103},x)', reverse_equation: 'x') - Component.create!(id: 8, board: Kit.find(2), sensor: Sensor.find(8), equation: 'x', reverse_equation: 'x/1000.0') - Component.create!(id: 9, board: Kit.find(2), sensor: Sensor.find(9), equation: 'x', reverse_equation: 'x/1000.0') - Component.create!(id: 10, board: Kit.find(2), sensor: Sensor.find(10), equation: 'x', reverse_equation: 'x/10.0') - Component.create!(id: 11, board: Kit.find(2), sensor: Sensor.find(11), equation: 'x', reverse_equation: 'x/1000.0') - # - Component.create!(id: 12, board: Kit.find(3), sensor: Sensor.find(12), equation: '(175.72 / 65536.0 * x) - 53', reverse_equation: 'x') - Component.create!(id: 13, board: Kit.find(3), sensor: Sensor.find(13), equation: '(125.0 / 65536.0 * x) + 7', reverse_equation: 'x') - Component.create!(id: 14, board: Kit.find(3), sensor: Sensor.find(14), equation: 'x', reverse_equation: 'x/10.0') - Component.create!(id: 15, board: Kit.find(3), sensor: Sensor.find(7), equation: 'Mathematician.table_calibration({0=>50,2=>55,3=>57,6=>58,20=>59,40=>60,60=>61,75=>62,115=>63,150=>64,180=>65,220=>66,260=>67,300=>68,375=>69,430=>70,500=>71,575=>72,660=>73,720=>74,820=>75,900=>76,975=>77,1050=>78,1125=>79,1200=>80,1275=>81,1320=>82,1375=>83,1400=>84,1430=>85,1450=>86,1480=>87,1500=>88,1525=>89,1540=>90,1560=>91,1580=>92,1600=>93,1620=>94,1640=>95,1660=>96,1680=>97,1690=>98,1700=>99,1710=>100,1720=>101,1745=>102,1770=>103,1785=>104,1800=>105,1815=>106,1830=>107,1845=>108,1860=>109,1875=>110},x)', reverse_equation: 'x') - Component.create!(id: 16, board: Kit.find(3), sensor: Sensor.find(15), equation: 'x', reverse_equation: 'x/1000.0') - Component.create!(id: 17, board: Kit.find(3), sensor: Sensor.find(16), equation: 'x', reverse_equation: 'x/1000.0') - Component.create!(id: 18, board: Kit.find(3), sensor: Sensor.find(17), equation: 'x', reverse_equation: 'x/10.0') - Component.create!(id: 19, board: Kit.find(3), sensor: Sensor.find(18), equation: 'x', reverse_equation: 'x/1000.0') - Component.create!(id: 20, board: Kit.find(2), sensor: Sensor.find(21), equation: 'x', reverse_equation: 'x') - Component.create!(id: 21, board: Kit.find(3), sensor: Sensor.find(21), equation: 'x', reverse_equation: 'x') + Component.create!(id: 12, device: device, sensor: Sensor.find(12)) + Component.create!(id: 13, device: device, sensor: Sensor.find(13)) + Component.create!(id: 14, device: device, sensor: Sensor.find(14)) + Component.create!(id: 15, device: device, sensor: Sensor.find(7)) + Component.create!(id: 16, device: device, sensor: Sensor.find(15)) + Component.create!(id: 17, device: device, sensor: Sensor.find(16)) + Component.create!(id: 18, device: device, sensor: Sensor.find(17)) + Component.create!(id: 19, device: device, sensor: Sensor.find(18)) + Component.create!(id: 21, device: device, sensor: Sensor.find(21)) end let(:json) { - {"co": "118439", "bat": "1000", "hum": "21592", "no2": "260941", "nets": "17", "temp": "25768", "light": "509", "noise": "0", "panel": "0", "timestamp": to_ts(1.day.ago) } + { "co": "118439", "bat": "1000", "hum": "21592", "no2": "260941", "nets": "17", "temp": "25768", "light": "509", "noise": "0", "panel": "0", "timestamp": to_ts(1.day.ago) } } - let(:device) { create(:device, kit: Kit.last) } + let(:device) { create(:device) } # RawStorer.new data, mac, version, ip it "will not be created with invalid past timestamp" do ts = { timestamp: to_ts(5.years.ago) } - raw_storer = RawStorer.new( json.merge(ts), device.mac_address, "1.1-0.9.0-A", "127.0.0.1" ) + raw_storer = RawStorer.new(json.merge(ts), device.mac_address, "1.1-0.9.0-A", "127.0.0.1") + end + + it "updates component last_reading_at" do + includes_proxy = double({ where: double({last: device})}) + allow(Device).to receive(:includes).and_return(includes_proxy) + + expect(device).to receive(:update_component_timestamps).with( + Time.parse(json[:timestamp]), + [16, 17, 13, 15, 21, 12, 14, 7, 18] + ) + + raw_storer = RawStorer.new(json, device.mac_address, "1.1-0.9.0-A", "127.0.0.1", true) end it "will not be created with invalid future timestamp" do ts = { timestamp: to_ts(2.days.from_now) } - raw_storer = RawStorer.new( json.merge(ts), device.mac_address, "1.1-0.9.0-A", "127.0.0.1" ) + raw_storer = RawStorer.new(json.merge(ts), device.mac_address, "1.1-0.9.0-A", "127.0.0.1") end it "will not be created with invalid data" do - expect(Kairos).to_not receive(:http_post_to) - raw_storer = RawStorer.new( {}, device.mac_address, "1.1-0.9.0-A", "127.0.0.1" ) + expect(Redis.current).not_to receive(:publish) + raw_storer = RawStorer.new({}, device.mac_address, "1.1-0.9.0-A", "127.0.0.1") end it "should return a correct sensor id number" do @@ -79,9 +67,8 @@ def to_ts(time) expect(device.find_sensor_id_by_key(:bat)).to eq(17) end - skip "will be created with valid data", :vcr do - expect(Kairos).to receive(:http_post_to) - raw_storer = RawStorer.new( json, device.mac_address, "1.1-0.9.0-A", "127.0.0.1" ) + it "will be created with valid data" do + expect(Redis.current).to receive(:publish) + raw_storer = RawStorer.new(json, device.mac_address, "1.1-0.9.0-A", "127.0.0.1", true) end - end diff --git a/spec/models/sensor_spec.rb b/spec/models/sensor_spec.rb index 6ea4783e..658d1b8b 100644 --- a/spec/models/sensor_spec.rb +++ b/spec/models/sensor_spec.rb @@ -8,8 +8,6 @@ # it { is_expected.to validate_presence_of(:unit) } it { is_expected.to have_many(:components) } - # it { is_expected.to have_many(:boards).through(:components) } - # it { is_expected.to have_many(:kits).through(:components) } it "has ancestry" diff --git a/spec/models/storer_spec.rb b/spec/models/storer_spec.rb index bbb84150..db3a784d 100644 --- a/spec/models/storer_spec.rb +++ b/spec/models/storer_spec.rb @@ -1,10 +1,10 @@ require 'rails_helper' RSpec.describe Storer, type: :model do - let(:kit){ build(:kit, id: 3, name: 'SCK', description: "Board", slug: 'sck', sensor_map: '{"temp": 12}')} - let(:sensor){ build(:sensor, id:12, name:'HPP828E031', description: 'test')} - let(:component){ create(:component, id: 12, board: kit, sensor: sensor, equation: '(175.72 / 65536.0 * x) - 53', reverse_equation: 'x')} - let(:device) { create(:device, device_token: 'aA1234', kit: kit) } + let(:sensor){ build(:sensor, id:12, name:'HPP828E031', description: 'test', equation: '(175.72 / 65536.0 * x) - 53', reverse_equation: 'x')} + let(:device) { create(:device, device_token: 'aA1234') } + let(:component){ create(:component, id: 12, device: device, sensor: sensor) } + context 'when receiving good data' do before do @@ -41,7 +41,16 @@ # model/storer.rb is not using Kairos, but Redis -> Telnet # expect(Kairos).to receive(:http_post_to).with("/datapoints", @karios_data) # expect_any_instance_of(Storer).to receive(:ws_publish) + expect do + Storer.new(device, @data) + end.not_to raise_error + end + it "updates the component last_reading_at timestamp for each of the provided sensors" do + expect(device).to receive(:update_component_timestamps).with( + Time.parse(@data['recorded_at']), + [sensor.id] + ) Storer.new(device, @data) end @@ -53,7 +62,7 @@ expect(device.reload.updated_at).to eq(updated_at) expect(device.reload.data).not_to eq(nil) - expect(device.reload.last_recorded_at).not_to eq(nil) + expect(device.reload.last_reading_at).not_to eq(nil) expect(device.reload.state).to eq('has_published') expect(Storer).to receive(:ws_publish) @@ -78,7 +87,7 @@ it 'does not update device' do expect{ Storer.new(device, @bad_data) }.to raise_error(ArgumentError) - expect(device.reload.last_recorded_at).to eq(nil) + expect(device.reload.last_reading_at).to eq(nil) expect(device.reload.data).to eq(nil) expect(device.reload.state).to eq('never_published') end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 74048c33..07fa099d 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -34,12 +34,6 @@ expect(build(:user, avatar: 'http://i.imgur.com/SZD8ADL.JPEG')).to be_valid end - it "has joined_at"do - Timecop.freeze do - expect(create(:user).joined_at).to eq(Time.current.utc) - end - end - it "has a location" do user = create(:user, country_code: 'es', city: 'Barcelona') expect(user.country.to_s).to eq("Spain") diff --git a/spec/policies/kit_policy_spec.rb b/spec/policies/kit_policy_spec.rb deleted file mode 100644 index 3cab0144..00000000 --- a/spec/policies/kit_policy_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'rails_helper' - -describe KitPolicy do - subject { KitPolicy.new(user, kit) } - - let(:kit) { FactoryBot.build(:kit) } - - context "for a visitor" do - let(:user) { nil } - it { is_expected.to permitz(:show) } - it { is_expected.to_not permitz(:update) } - it { is_expected.to_not permitz(:create) } - end - - context "for a user" do - let(:user) { FactoryBot.create(:user) } - it { is_expected.to permitz(:show) } - it { is_expected.to_not permitz(:update) } - it { is_expected.to_not permitz(:create) } - end - - context "for an admin" do - let(:user) { FactoryBot.create(:admin) } - it { is_expected.to permitz(:show) } - it { is_expected.to permitz(:update) } - it { is_expected.to permitz(:create) } - end - -end diff --git a/spec/requests/v0/application_spec.rb b/spec/requests/v0/application_spec.rb index 44d20137..10482227 100644 --- a/spec/requests/v0/application_spec.rb +++ b/spec/requests/v0/application_spec.rb @@ -4,20 +4,20 @@ describe "format" do it "(JSON) returns ugly JSON, with JSON Mimetype" do - json = api_get '/kits' + json = api_get '/devices' #expect( response.body.to_s ).to_not eq( JSON.pretty_generate(json) ) expect(response.header['Content-Type']).to include('application/json') end skip "(JSON) returns pretty JSON, with JSON Mimetype if ?pretty=true" do - json = api_get '/v0/kits?pretty=true' + json = api_get '/v0/devices?pretty=true' expect( response.body.to_s ).to eq( JSON.pretty_generate(json) ) expect(response.header['Content-Type']).to include('application/json') end skip "(JSON-P) returns JS Mimetype if callback param present" do # rails now handles this - api_get '/v0/kits?callback=something' + api_get '/v0/devices?callback=something' expect(response.header['Content-Type']).to include('text/javascript') end end diff --git a/spec/requests/v0/components_spec.rb b/spec/requests/v0/components_spec.rb index c82925aa..b0550460 100644 --- a/spec/requests/v0/components_spec.rb +++ b/spec/requests/v0/components_spec.rb @@ -21,7 +21,7 @@ expect(json.length).to eq(2) expect(json[0]['uuid']).to eq(component1.uuid) expect(json[0].keys).to eq( - %w(id uuid board_id board_type sensor_id created_at updated_at) + %w(id uuid device_id sensor_id created_at updated_at) ) end end diff --git a/spec/requests/v0/devices_spec.rb b/spec/requests/v0/devices_spec.rb index aca25eba..382a5a53 100644 --- a/spec/requests/v0/devices_spec.rb +++ b/spec/requests/v0/devices_spec.rb @@ -25,7 +25,7 @@ # expect(json[0]['name']).to eq(first.name) # expect(json[1]['name']).to eq(second.name) expect(json[0].keys).to eq(%w(id uuid name description state postprocessing - hardware_info system_tags user_tags is_private notify_low_battery notify_stopped_publishing last_reading_at added_at updated_at mac_address owner data kit)) + hardware_info system_tags user_tags is_private notify_low_battery notify_stopped_publishing last_reading_at created_at updated_at mac_address owner data)) end describe "when not logged in" do @@ -139,8 +139,8 @@ expect(response.status).to eq(200) end - it "allows searching by last_recorded_at" do - json = api_get "devices?q[last_recorded_at_lt]=2023-09-26" + it "allows searching by last_reading_at" do + json = api_get "devices?q[last_reading_at_lt]=2023-09-26" expect(response.status).to eq(200) end diff --git a/spec/requests/v0/kits_spec.rb b/spec/requests/v0/kits_spec.rb deleted file mode 100644 index f9e620f0..00000000 --- a/spec/requests/v0/kits_spec.rb +++ /dev/null @@ -1,90 +0,0 @@ -require 'rails_helper' - -describe V0::KitsController do - - let(:application) { build :application } - let(:user) { build :user } - let(:token) { build :access_token, application: application, resource_owner_id: user.id } - - let(:admin) { create :admin } - let(:admin_token) { create :access_token, application: application, resource_owner_id: admin.id } - - let(:kit) { build :kit } - - describe "GET /kits" do - it "returns all the kits" do - first = create(:kit) - second = create(:kit) - json = api_get 'kits' - - expect(response.status).to eq(200) - expect(json.length).to eq(2) - expect(json[0]['id']).to eq(first.id) - expect(json[0].keys).to eq(%w(id uuid slug name description created_at updated_at sensors)) - end - end - - describe "GET /kits/:id" do - it "returns a kit" do - kit = create(:kit) - j = api_get "kits/#{kit.id}" - expect(j['id']).to eq(kit.id) - expect(response.status).to eq(200) - end - - it "returns 404 if kit not found" do - j = api_get 'kits/100' - expect(j['id']).to eq('record_not_found') - expect(response.status).to eq(404) - end - end - - describe "POST /kits" do - - it "creates a kit" do - j = api_post 'kits', { - name: 'new kit', - description: 'blah blah blah', - access_token: admin_token.token - } - expect(j['name']).to eq('new kit') - expect(response.status).to eq(201) - end - - it "does not create a kit with missing parameters" do - j = api_post 'kits', { - access_token: admin_token.token - } - expect(j['id']).to eq('unprocessable_entity') - expect(response.status).to eq(422) - end - - end - - describe "PUT /kits/:id" do - - let!(:kit) { create :kit } - - it "updates a kit" do - api_put "kits/#{kit.id}", { name: 'new name', access_token: admin_token.token } - expect(response.status).to eq(200) - end - - it "does not update a kit with invalid access_token" do - api_put "kits/#{kit.id}", { name: 'new name', access_token: '123' } - expect(response.status).to eq(403) - end - - it "does not update a kit with missing access_token" do - api_put "kits/#{kit.id}", { name: 'new name', access_token: nil } - expect(response.status).to eq(403) - end - - it "does not update a kit with empty parameters access_token" do - api_put "kits/#{kit.id}", { name: nil, access_token: admin_token.token } - expect(response.status).to eq(422) - end - - end - -end diff --git a/spec/requests/v0/onboarding/device_registrations_spec.rb b/spec/requests/v0/onboarding/device_registrations_spec.rb index a0d9cca4..9e664750 100644 --- a/spec/requests/v0/onboarding/device_registrations_spec.rb +++ b/spec/requests/v0/onboarding/device_registrations_spec.rb @@ -33,9 +33,8 @@ end before do - create(:kit, id: 1) if Kit.where(id: 1).empty? - create(:tag, name: 'tag1') if Kit.where(name: 'tag1').empty? - create(:tag, name: 'tag2') if Kit.where(name: 'tag2').empty? + create(:tag, name: 'tag1') + create(:tag, name: 'tag2') end describe 'POST /onboarding/register' do @@ -63,7 +62,6 @@ expect(@device.device_token).to eq(orphan_device.device_token) expect(@device.exposure).to eq(orphan_device.exposure) expect(@device.description).to eq(orphan_device.description) - expect(@device.kit).to eq(Kit.first) expect(@device.tags.count).to eq(2) expect(@device.location['city']).to eq('Barcelona') end diff --git a/spec/requests/v0/onboarding/orphan_devices_spec.rb b/spec/requests/v0/onboarding/orphan_devices_spec.rb index 5848d815..435daa09 100644 --- a/spec/requests/v0/onboarding/orphan_devices_spec.rb +++ b/spec/requests/v0/onboarding/orphan_devices_spec.rb @@ -27,10 +27,10 @@ it 'creates an orphan_device with passed attributes' do j = api_post '/onboarding/device', { - kit_id: 3 + } - expect(OrphanDevice.where(kit_id: 3).count).to eq(1) + expect(OrphanDevice.count).to eq(1) end end diff --git a/spec/requests/v0/readings_spec.rb b/spec/requests/v0/readings_spec.rb index 0af8351d..48ae05e8 100644 --- a/spec/requests/v0/readings_spec.rb +++ b/spec/requests/v0/readings_spec.rb @@ -2,11 +2,10 @@ describe V0::ReadingsController do let(:user) { build(:user) } - let(:kit) { build(:kit, sensor_map: '{"noise": 7, "temp": 12, "light": 14, "no2": 15}' ) } - let(:device) { create(:device, owner: user, kit: kit) } + let(:device) { create(:device, owner: user) } let(:measurement) { build(:measurement) } let(:sensor) { build(:sensor, measurement: measurement) } - let(:component) { build(:component, board: kit, sensor: sensor) } + let(:component) { build(:component, device: device, sensor: sensor) } let(:application) { build :application } let(:token) { build :access_token, application: application, resource_owner_id: user.id } diff --git a/spec/requests/v0/static_spec.rb b/spec/requests/v0/static_spec.rb index cf75f917..45a435aa 100644 --- a/spec/requests/v0/static_spec.rb +++ b/spec/requests/v0/static_spec.rb @@ -13,7 +13,6 @@ current_user_url components_url devices_url - kits_url measurements_url sensors_url users_url diff --git a/spec/services/device_archive_spec.rb b/spec/services/device_archive_spec.rb index 183e63df..4cee29f1 100644 --- a/spec/services/device_archive_spec.rb +++ b/spec/services/device_archive_spec.rb @@ -6,30 +6,34 @@ def kairos_query(key) end describe DeviceArchive do + + let(:device) { create(:device) } + before(:each) do create(:measurement, id: 1, name: 'temp') create(:measurement, id: 2, name: 'light') create(:measurement, id: 3, name: 'noise') create(:measurement, id: 4, name: 'NO2') - create(:kit, id: 3, name: 'SCK', description: "Board", slug: 'sck', sensor_map: {"noise": 7, "temp": 12, "light": 14, "no2": 15}) - create(:sensor, id:12, name:'HPP828E031', description: 'test', measurement_id: 1, unit: 'ºC') - create(:sensor, id:7, name:'POM-3044P-R', description: 'test', measurement_id: 2, unit: 'dB') - create(:sensor, id:14, name:'BH1730FVC', description: 'test', measurement_id: 2, unit: 'KΩ') - create(:sensor, id:15, name:'MiCS-4514', description: 'test', measurement_id: 4, unit: 'kOhm') - create(:component, id: 12, board: Kit.find(3), sensor: Sensor.find(12), equation: '(175.72 / 65536.0 * x) - 53', reverse_equation: 'x') - create(:component, id: 14, board: Kit.find(3), sensor: Sensor.find(14), equation: 'x', reverse_equation: 'x/10.0') - create(:component, id: 15, board: Kit.find(3), sensor: Sensor.find(7), equation: 'Mathematician.table_calibration({0=>50,2=>55,3=>57,6=>58,20=>59,40=>60,60=>61,75=>62,115=>63,150=>64,180=>65,220=>66,260=>67,300=>68,375=>69,430=>70,500=>71,575=>72,660=>73,720=>74,820=>75,900=>76,975=>77,1050=>78,1125=>79,1200=>80,1275=>81,1320=>82,1375=>83,1400=>84,1430=>85,1450=>86,1480=>87,1500=>88,1525=>89,1540=>90,1560=>91,1580=>92,1600=>93,1620=>94,1640=>95,1660=>96,1680=>97,1690=>98,1700=>99,1710=>100,1720=>101,1745=>102,1770=>103,1785=>104,1800=>105,1815=>106,1830=>107,1845=>108,1860=>109,1875=>110},x)', reverse_equation: 'x') - create(:component, id: 16, board: Kit.find(3), sensor: Sensor.find(15), equation: 'x', reverse_equation: 'x/1000.0') + + create(:sensor, id:12, name:'HPP828E031', description: 'test', measurement_id: 1, unit: 'ºC', key: "temp", equation: '(175.72 / 65536.0 * x) - 53', reverse_equation: 'x') + create(:sensor, id:14, name:'BH1730FVC', description: 'test', measurement_id: 2, unit: 'KΩ', key: "light", equation: 'x', reverse_equation: 'x/10.0') + create(:sensor, id:7, name:'POM-3044P-R', description: 'test', measurement_id: 2, unit: 'dB', key: "noise", equation: 'Mathematician.table_calibration({0=>50,2=>55,3=>57,6=>58,20=>59,40=>60,60=>61,75=>62,115=>63,150=>64,180=>65,220=>66,260=>67,300=>68,375=>69,430=>70,500=>71,575=>72,660=>73,720=>74,820=>75,900=>76,975=>77,1050=>78,1125=>79,1200=>80,1275=>81,1320=>82,1375=>83,1400=>84,1430=>85,1450=>86,1480=>87,1500=>88,1525=>89,1540=>90,1560=>91,1580=>92,1600=>93,1620=>94,1640=>95,1660=>96,1680=>97,1690=>98,1700=>99,1710=>100,1720=>101,1745=>102,1770=>103,1785=>104,1800=>105,1815=>106,1830=>107,1845=>108,1860=>109,1875=>110},x)', reverse_equation: 'x') + create(:sensor, id:15, name:'MiCS-4514', description: 'test', measurement_id: 4, unit: 'kOhm', key: "no2", equation: 'x', reverse_equation: 'x/1000.0') + + create(:component, id: 12, device: device, sensor: Sensor.find(12)) + create(:component, id: 14, device: device, sensor: Sensor.find(14)) + create(:component, id: 15, device: device, sensor: Sensor.find(7)) + create(:component, id: 16, device: device, sensor: Sensor.find(15)) end - let(:device) { create(:device, kit: Kit.find(3)) } + let(:csv) { - "timestamp,NO2 in kOhm (MiCS-4514),temp in ºC (HPP828E031),light in KΩ (BH1730FVC),light in dB (POM-3044P-R)\n"\ - "2013-04-03 06:00:00 UTC,1.0,-52.997318725585934,1.0,52.5\n"\ - "2013-04-19 06:00:00 UTC,2.0,-52.994637451171876,2.0,55.0\n"\ - "2013-04-23 06:00:00 UTC,3.0,-52.99195617675781,3.0,57.0\n"\ - "2013-04-30 06:00:00 UTC,4.0,-52.98927490234375,4.0,57.333333333333336\n" + "timestamp,temp in ºC (HPP828E031),light in KΩ (BH1730FVC),light in dB (POM-3044P-R),NO2 in kOhm (MiCS-4514)\n"\ + "2013-04-03 06:00:00 UTC,-52.997318725585934,1.0,52.5,1.0\n"\ + "2013-04-19 06:00:00 UTC,-52.994637451171876,2.0,55.0,2.0\n"\ + "2013-04-23 06:00:00 UTC,-52.99195617675781,3.0,57.0,3.0\n"\ + "2013-04-30 06:00:00 UTC,-52.98927490234375,4.0,57.333333333333336,4.0\n" } let(:http_response) { diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 629d244a..2d5c8cb4 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -28,6 +28,7 @@ # ...rather than: # # => "be bigger than 2" expectations.include_chain_clauses_in_custom_matcher_descriptions = true + expectations.max_formatted_output_length = nil end # rspec-mocks config goes here. You can use an alternate test double