Commit e31a3af2 by John Doe

refactored code to use adapters instead of a direct backend HTTPclient

parent 5c9ff2b5
Showing with 563 additions and 440 deletions
......@@ -24,4 +24,5 @@ vendor/bundle
spec/cassettes
vagrant_boxes/*.log
vagrant_boxes/.vagrant
coverage
\ No newline at end of file
coverage
.idea
\ No newline at end of file
<component name="ProjectDictionaryState">
<dictionary name="jdonnal">
<words>
<w>dbinfo</w>
<w>nilm</w>
<w>nilmdb</w>
</words>
</dictionary>
</component>
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Rubocop" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>
\ No newline at end of file
module Joule
class Adapter
def node_type
'joule'
end
end
end
\ No newline at end of file
#frozen_string_literal: true
module Joule
# Wrapper around Joule HTTP service
class Backend
include HTTParty
default_timeout 5
open_timeout 5
read_timeout 5
# Wrapper around Joule HTTP service
class JouleAdapter
include HTTParty
default_timeout 5
open_timeout 5
read_timeout 5
attr_reader :url
attr_reader :url
def initialize(url)
@url = url
end
def initialize(url)
@url = url
end
def module_info
begin
resp = self.class.get("#{@url}/modules.json")
return nil unless resp.success?
items = resp.parsed_response
# if the site exists but is not a joule server...
required_keys = %w(name exec_cmd)
items.each do |item|
return nil unless item.respond_to?(:has_key?) &&
required_keys.all? { |s| item.key? s }
item.symbolize_keys!
def module_info
begin
resp = self.class.get("#{@url}/modules.json")
return nil unless resp.success?
items = resp.parsed_response
# if the site exists but is not a joule server...
required_keys = %w(name exec_cmd)
items.each do |item|
return nil unless item.respond_to?(:has_key?) &&
required_keys.all? { |s| item.key? s }
item.symbolize_keys!
end
rescue
return nil
end
rescue
return nil
return items
end
return items
end
def module_interface(joule_module, req)
self.class.get("#{@url}/interface/#{joule_module.joule_id}/#{req}")
end
def module_interface(joule_module, req)
self.class.get("#{@url}/interface/#{joule_module.joule_id}/#{req}")
end
end
end
module Nilmdb
class Adapter
def initialize(url)
@backend = Backend.new(url)
end
def refresh(db:)
db_service = UpdateDb.new(db: db)
db_service.run(@backend.dbinfo, @backend.schema)
end
def refresh_stream(db_stream)
entries = @backend.stream_info(db_stream)
service = UpdateStream.new(db_stream,
entries[:base_entry],
entries[:decimation_entries])
service.run
end
def save_stream(db_stream)
@backend.set_stream_metadata(db_stream)
end
def save_folder(db_folder)
@backend.set_folder_metadata(db_folder)
end
def load_data(db_stream, start_time, end_time, elements=[], resolution=nil)
data_service = LoadStreamData.new(@backend)
data_service.run(db_stream, start_time, end_time, elements, resolution)
unless data_service.success?
return nil
end
{data: data_service.data,
decimation_factor: data_service.decimation_factor}
end
def node_type
'nilmdb'
end
end
end
# frozen_string_literal: true
# Handles construction of database objects
class UpdateDb
include ServiceStatus
def initialize(db:)
@db = db
super()
end
def run(dbinfo, schema)
# check to make sure dbinfo and schema are set
# if either is nil, the database is not available
if(dbinfo.nil? || schema.nil?)
add_error("cannot contact database at #{@db.url}")
@db.update_attributes(available: false)
return self
else
@db.available = true
module Nilmdb
# Handles construction of database objects
class UpdateDb
include ServiceStatus
def initialize(db:)
@db = db
super()
end
# create the root folder if it doesn't exist
@db.root_folder ||= DbFolder.create(db: @db, name: 'root', path: '/')
@root_folder = @db.root_folder
# create the entry array from the schema
entries = __create_entries(schema)
updater = UpdateFolder.new(@root_folder, entries)
# update db attributes from dbinfo
@db.size_total = dbinfo[:size_total]
@db.size_db = dbinfo[:size_db]
@db.size_other = dbinfo[:size_other]
@db.version = dbinfo[:version]
#@root_folder.transaction do
absorb_status(updater.run)
#end
@db.save
set_notice("Database refreshed")
self
end
def run(dbinfo, schema)
# check to make sure dbinfo and schema are set
# if either is nil, the database is not available
if(dbinfo.nil? || schema.nil?)
add_error("cannot contact database at #{@db.url}")
@db.update_attributes(available: false)
return self
else
@db.available = true
end
# create the root folder if it doesn't exist
@db.root_folder ||= DbFolder.create(db: @db, name: 'root', path: '/')
@root_folder = @db.root_folder
# create the entry array from the schema
entries = __create_entries(schema)
updater = UpdateFolder.new(@root_folder, entries)
# update db attributes from dbinfo
@db.size_total = dbinfo[:size_total]
@db.size_db = dbinfo[:size_db]
@db.size_other = dbinfo[:size_other]
@db.version = dbinfo[:version]
#@root_folder.transaction do
absorb_status(updater.run)
#end
@db.save
set_notice("Database refreshed")
self
end
protected
# Adds :chunks to each schema element
# :chunks is an array of the entry's path elements
# this makes it easier to traverse the database structure.
# The array is reversed so the chunks can be popped off in order
# path: '/data/meter/prep-a'
# chunks: ['prep-a','meter','data']
#
def __create_entries(schema)
schema.map do |entry|
entry[:chunks] = entry[:path][1..-1].split('/').reverse
entry
protected
# Adds :chunks to each schema element
# :chunks is an array of the entry's path elements
# this makes it easier to traverse the database structure.
# The array is reversed so the chunks can be popped off in order
# path: '/data/meter/prep-a'
# chunks: ['prep-a','meter','data']
#
def __create_entries(schema)
schema.map do |entry|
entry[:chunks] = entry[:path][1..-1].split('/').reverse
entry
end
end
end
end
# frozen_string_literal: true
module Nilmdb
# Handles construction of DbFolder objects
class UpdateStream
include ServiceStatus
attr_reader :start_time, :end_time, :size_on_disk
# Handles construction of DbFolder objects
class UpdateStream
include ServiceStatus
attr_reader :start_time, :end_time, :size_on_disk
def initialize(stream, base_entry, decimation_entries)
@stream = stream
@base_entry = base_entry
@decimation_entries = decimation_entries
# initialize extents, these set during run
@start_time = nil
@end_time = nil
@size_on_disk = 0
super()
end
def initialize(stream, base_entry, decimation_entries)
@stream = stream
@base_entry = base_entry
@decimation_entries = decimation_entries
# initialize extents, these set during run
@start_time = nil
@end_time = nil
@size_on_disk = 0
super()
end
def run
__update_stream(@stream, @base_entry, @decimation_entries)
set_notice("Stream updated")
self
end
def run
__update_stream(@stream, @base_entry, @decimation_entries)
set_notice("Stream updated")
self
end
# regex matching the ~decimXX ending on a stream path
def self.decimation_tag
/~decim-([\d]+)$/
end
# regex matching the ~decimXX ending on a stream path
def self.decimation_tag
/~decim-([\d]+)$/
end
# create or update a DbStream object at the
# specified path.
def __update_stream(stream, base_entry, decimation_entries)
# use default attributes if metadata is corrupt
unless stream.update_attributes(base_entry[:attributes])
stream.use_default_attributes
Rails.logger.warn("corrupt metadata: #{stream.path}")
# create or update a DbStream object at the
# specified path.
def __update_stream(stream, base_entry, decimation_entries)
# use default attributes if metadata is corrupt
unless stream.update_attributes(base_entry[:attributes])
stream.use_default_attributes
Rails.logger.warn("corrupt metadata: #{stream.path}")
end
__compute_extents([base_entry] + decimation_entries)
stream.start_time = @start_time
stream.end_time = @end_time
stream.size_on_disk = @size_on_disk
stream.save!
__build_decimations(stream: stream,
entry_group: decimation_entries)
__build_elements(stream: stream, stream_data: base_entry[:elements])
end
__compute_extents([base_entry] + decimation_entries)
stream.start_time = @start_time
stream.end_time = @end_time
stream.size_on_disk = @size_on_disk
stream.save!
__build_decimations(stream: stream,
entry_group: decimation_entries)
__build_elements(stream: stream, stream_data: base_entry[:elements])
end
# create or update DbDecimations for the
# specified DbStream
def __build_decimations(stream:, entry_group:)
if !entry_group.empty?
Rails.logger.debug("deleting decimations for #{stream.path}")
stream.db_decimations.destroy_all #remove existing decimations
end
entry_group.each do |entry|
level = entry[:path].match(UpdateStream.decimation_tag)[1].to_i
decim = stream.db_decimations.find_by_level(level)
decim ||= DbDecimation.new(db_stream: stream, level: level)
decim.update_attributes(entry[:attributes])
# create or update DbDecimations for the
# specified DbStream
def __build_decimations(stream:, entry_group:)
if !entry_group.empty?
Rails.logger.debug("deleting decimations for #{stream.path}")
stream.db_decimations.destroy_all #remove existing decimations
end
entry_group.each do |entry|
level = entry[:path].match(UpdateStream.decimation_tag)[1].to_i
decim = stream.db_decimations.find_by_level(level)
decim ||= DbDecimation.new(db_stream: stream, level: level)
decim.update_attributes(entry[:attributes])
#decim.save!
#decim.save!
end
end
end
# create or update DbStreams for the
# specified DbStream
def __build_elements(stream:, stream_data:)
stream.column_count.times do |x|
element = stream.db_elements.find_by_column(x)
element ||= DbElement.new(db_stream: stream, column: x,
display_type: 'continuous')
# check if there is stream metadata for column x
entry = stream_data.select { |meta| meta[:column] == x }
# use the metadata if present
unless element.update_attributes(entry[0] || {})
element.use_default_attributes
element.save!
Rails.logger.warn(stream_data)
Rails.logger.warn("corrupt metadata: #{stream.path}:"\
"e#{element.column}")
# create or update DbStreams for the
# specified DbStream
def __build_elements(stream:, stream_data:)
stream.column_count.times do |x|
element = stream.db_elements.find_by_column(x)
element ||= DbElement.new(db_stream: stream, column: x,
display_type: 'continuous')
# check if there is stream metadata for column x
entry = stream_data.select { |meta| meta[:column] == x }
# use the metadata if present
unless element.update_attributes(entry[0] || {})
element.use_default_attributes
element.save!
Rails.logger.warn(stream_data)
Rails.logger.warn("corrupt metadata: #{stream.path}:"\
"e#{element.column}")
end
end
end
end
# compute the time range and total size of this stream
# accepts an array of entries (include base & decim)
def __compute_extents(entries)
entries.map { |x| x[:attributes] }.each do |attrs|
next if (attrs[:total_rows]).zero?
if @start_time.nil?
@start_time = attrs[:start_time]
@end_time = attrs[:end_time]
# compute the time range and total size of this stream
# accepts an array of entries (include base & decim)
def __compute_extents(entries)
entries.map { |x| x[:attributes] }.each do |attrs|
next if (attrs[:total_rows]).zero?
if @start_time.nil?
@start_time = attrs[:start_time]
@end_time = attrs[:end_time]
end
@start_time = [@start_time, attrs[:start_time]].min
@end_time = [@end_time, attrs[:end_time]].max
@size_on_disk += attrs[:total_rows] *
__bytes_per_row(attrs[:data_type])
end
@start_time = [@start_time, attrs[:start_time]].min
@end_time = [@end_time, attrs[:end_time]].max
@size_on_disk += attrs[:total_rows] *
__bytes_per_row(attrs[:data_type])
end
end
# compute how many bytes are required per row based
# on the datatype (float32_8 => 4*8+8)
def __bytes_per_row(data_type)
regex = /[a-z]*(\d*)_(\d*)/.match(data_type)
dtype_bytes = regex[1].to_i / 8
num_cols = regex[2].to_i
ts_bytes = 8
ts_bytes + num_cols * dtype_bytes
# compute how many bytes are required per row based
# on the datatype (float32_8 => 4*8+8)
def __bytes_per_row(data_type)
regex = /[a-z]*(\d*)_(\d*)/.match(data_type)
dtype_bytes = regex[1].to_i / 8
num_cols = regex[2].to_i
ts_bytes = 8
ts_bytes + num_cols * dtype_bytes
end
end
end
end
\ No newline at end of file
class NodeAdapterFactory
include HTTParty
def self.from_url(url)
begin
resp = self.class.get(url)
return nil unless resp.success?
info = resp.parsed_response
rescue
return nil
end
if info.include? 'NilmDB'
return Nilmdb::Adapter(url)
elsif info.include? 'Joule'
return Joule::Adapter(url)
else
return nil
end
end
def self.from_nilm(nilm)
if nilm.type=='nilmdb'
return Nilmdb::Adapter(nilm.url)
elsif nilm.type=='joule'
return Joule::Adapter(nilm.url)
else
# try to figure out what this nilm is
return self.from_url(nilm.url)
end
end
end
\ No newline at end of file
......@@ -6,6 +6,7 @@ class DbFoldersController < ApplicationController
before_action :set_folder, only: [:show, :update]
before_action :authorize_viewer, only: [:show]
before_action :authorize_owner, only: [:update]
before_action :create_adapter, only: [:update]
# GET /db_folders.json
def show; end
......@@ -13,8 +14,7 @@ class DbFoldersController < ApplicationController
# PATCH/PUT /db_folders/1.json
# TODO: create info stream on folders on edit
def update
adapter = DbAdapter.new(@db.url)
@service = EditFolder.new(adapter)
@service = EditFolder.new(@node_adapter)
@service.run(@db_folder, folder_params)
render status: @service.success? ? :ok : :unprocessable_entity
end
......@@ -39,4 +39,13 @@ class DbFoldersController < ApplicationController
def authorize_viewer
head :unauthorized unless current_user.views_nilm?(@nilm)
end
def create_adapter
@node_adapter = NodeAdapterFactory.from_nilm(@nilm)
if @node_adapter.nil?
@service = StubService.new
@service.add_error("Cannot contact installation")
render 'helpers/empty_response', status: :unprocessable_entity
end
end
end
......@@ -6,7 +6,7 @@ class DbStreamsController < ApplicationController
before_action :set_stream, only: [:update, :data]
before_action :authorize_viewer, only: [:data]
before_action :authorize_owner, only: [:update]
before_action :create_adapter, only: [:update, :data]
def index
if params[:streams].nil?
head :unprocessable_entity
......@@ -24,14 +24,13 @@ class DbStreamsController < ApplicationController
end
def update
adapter = DbAdapter.new(@db.url)
@service = EditStream.new(adapter)
@service = EditStream.new(@node_adapter)
@service.run(@db_stream, stream_params)
render status: @service.success? ? :ok : :unprocessable_entity
end
def data
@service = BuildDataset.new
@service = BuildDataset.new(@node_adapter)
@service.run(@db_stream,params[:start_time].to_i,params[:end_time].to_i)
unless @service.success?
head :unprocessable_entity
......@@ -67,4 +66,13 @@ class DbStreamsController < ApplicationController
def authorize_viewer
head :unauthorized unless current_user.views_nilm?(@nilm)
end
def create_adapter
@node_adapter = NodeAdapterFactory.from_nilm(@nilm)
if @node_adapter.nil?
@service = StubService.new
@service.add_error("Cannot contact installation")
render 'helpers/empty_response', status: :unprocessable_entity
end
end
end
......@@ -24,8 +24,8 @@ class InterfacesController < ActionController::Base
def get
path = params[:path] || ''
req = path +"?"+request.query_string
adapter = JouleAdapter.new(@joule_module.nilm.url)
render plain: adapter.module_interface(@joule_module,req)
backend = JouleAdapter.new(@joule_module.nilm.url)
render plain: backend.module_interface(@joule_module,req)
end
def put
......
......@@ -7,6 +7,7 @@ class NilmsController < ApplicationController
before_action :authorize_viewer, only: [:show]
before_action :authorize_owner, only: [:update, :refresh]
before_action :authorize_admin, only: [:destroy]
before_action :create_adapter, only: [:create]
# GET /nilms.json
def index
......@@ -18,8 +19,8 @@ class NilmsController < ApplicationController
#render the database and joule modules
@role = current_user.get_nilm_permission(@nilm)
#request new information from the NILM
if(params[:refresh])
@service = UpdateNilm.new()
if params[:refresh]
@service = UpdateNilm.new(@adapter)
@service.run(@nilm)
render status: @service.success? ? :ok : :unprocessable_entity
else
......@@ -29,7 +30,7 @@ class NilmsController < ApplicationController
# POST /nilms.json
def create
@service = CreateNilm.new
@service = CreateNilm.new(@node_adapter)
@service.run(name: nilm_params[:name],
url: nilm_params[:url],
description: nilm_params[:description],
......@@ -74,6 +75,7 @@ class NilmsController < ApplicationController
def set_nilm
@nilm = Nilm.find(params[:id])
@db = @nilm.db
@adapter = Nilmdb::Adapter.new(@nilm.url)
end
# Never trust parameters from the scary internet,
......@@ -95,4 +97,13 @@ class NilmsController < ApplicationController
def authorize_viewer
head :unauthorized unless current_user.views_nilm?(@nilm)
end
def create_adapter
@node_adapter = NodeAdapterFactory.from_url(nilm_params[:url])
if @node_adapter.nil?
@service = StubService.new
@service.add_error("Cannot contact installation")
render 'helpers/empty_response', status: :unprocessable_entity
end
end
end
......@@ -14,7 +14,8 @@ class Nilm < ApplicationRecord
#---Validations-----
validates :name, presence: true, uniqueness: true
validates :url, presence: true, uniqueness: true
validates :node_type, presence: true,
inclusion: { in: %w(nilmdb joule) }
#---Callbacks------
before_destroy do |record|
DataView.destroy(record.data_views.pluck(:id))
......
......@@ -7,8 +7,9 @@ class BuildDataset
include ServiceStatus
attr_reader :data, :legend
def initialize
def initialize(node_adapter)
super()
@node_adapter = node_adapter
@data = [] # [[ts, val1, val2, val3, ...],
# [ts, val1, val2, val3, ...]]
@legend = {
......@@ -24,24 +25,20 @@ class BuildDataset
# fill @data with values from db_stream
# and populate @legend
def run(db_stream, start_time, end_time)
adapter = DbAdapter.new(db_stream.db.url)
data_service = LoadStreamData.new(adapter)
absorb_status(
data_service.run(db_stream, start_time, end_time)
)
unless data_service.success?
result = @node_adapter.load_data(db_stream, start_time, end_time)
if result.nil?
add_error("unable to retrieve data for #{db_stream.path}")
return self
end
@data = _build_dataset(data_service.data)
@legend[:columns] = _build_legend_columns(data_service.data, db_stream)
@data = _build_dataset(result[:data])
@legend[:columns] = _build_legend_columns(result[:data], db_stream)
@legend[:start_time] = start_time
@legend[:end_time] = end_time
@legend[:decimation_factor] = data_service.decimation_factor
@legend[:decimation_factor] = result[:decimation_factor]
@legend[:num_rows] = @data.length
if(@data.empty?)
if @data.empty?
@legend[:notes] = 'there is no data available over this interval'
elsif(@data[0].length!=db_stream.db_elements.length+1)
elsif @data[0].length!=db_stream.db_elements.length+1
@legend[:notes] = 'some elements omitted due to insufficient decimation'
end
self
......
require "benchmark"
# frozen_string_literal: true
# Loads data for specified elements
class LoadElementData
include ServiceStatus
attr_reader :data, :start_time, :end_time
def initialize()
def initialize
super()
@data = []
@start_time = nil
......@@ -35,18 +34,10 @@ class LoadElementData
end
end
#2 compute bounds by updating stream info if start/end are missing
if(start_time==nil || end_time==nil)
if start_time==nil || end_time==nil
req_streams.map do |stream|
adapter = DbAdapter.new(stream.db.url)
entries = adapter.stream_info(stream)
service = UpdateStream.new(
stream,
entries[:base_entry],
entries[:decimation_entries]
)
unless service.run.success?
Rails.logger.warn("Error updating #{stream.name}: #{service.errors}")
end
adapter = Nilmdb::Adapter.new(stream.db.url)
adapter.refresh_stream(stream)
end
end
......@@ -54,7 +45,7 @@ class LoadElementData
streams_with_data = req_streams.select{|stream| stream.total_time > 0}
if (start_time == nil || end_time == nil) && streams_with_data.empty?
add_error("no time bounds for requested elements, refresh database?")
return
return self
end
@start_time = start_time
@end_time = end_time
......@@ -70,18 +61,17 @@ class LoadElementData
end
if @start_time > @end_time
add_error("invalid time bounds")
return
return self
end
#4 pull data from streams
combined_data = []
req_streams.each do |stream|
adapter = DbAdapter.new(stream.db.url)
data_service = LoadStreamData.new(adapter)
stream_elements = elements.select{|e| e.db_stream_id==stream.id}.to_a
data_service.run(stream, @start_time, @end_time,stream_elements,resolution)
adapter = Nilmdb::Adapter.new(stream.db.url)
result = adapter.load_data(stream, @start_time, @end_time,stream_elements,resolution)
if data_service.success?
combined_data.concat(data_service.data)
if not result.nil?
combined_data.concat(result[:data])
else
#create error entries
error_entries = stream_elements.map do |e|
......@@ -94,6 +84,6 @@ class LoadElementData
#5 extract requested elements from the stream datasets
req_element_ids = elements.pluck(:id)
@data = combined_data.select{|d| req_element_ids.include? d[:id] }
return self
end
self
end
end
......@@ -4,9 +4,9 @@
class EditFolder
include ServiceStatus
def initialize(db_adapter)
def initialize(node_adapter)
super()
@db_adapter = db_adapter
@node_adapter = node_adapter
end
def run(db_folder, attribs)
......@@ -20,7 +20,8 @@ class EditFolder
return self
end
# local model checks out, update the remote NilmDB
status = @db_adapter.set_folder_metadata(db_folder)
status = @node_adapter.save_folder(db_folder)
# if there was an error don't save the model
if status[:error]
add_error(status[:msg])
......
......@@ -4,9 +4,9 @@
class EditStream
include ServiceStatus
def initialize(db_adapter)
def initialize(node_adapter)
super()
@db_adapter = db_adapter
@node_adapter = node_adapter
end
def run(db_stream, attribs)
......@@ -23,7 +23,7 @@ class EditStream
return self
end
# local model checks out, update the remote NilmDB
status = @db_adapter.set_stream_metadata(db_stream)
status = @node_adapter.save_stream(db_stream)
# if there was an error don't save the model
if status[:error]
add_error(status[:msg])
......
......@@ -5,13 +5,17 @@ class CreateNilm
include ServiceStatus
attr_reader :nilm
def initialize(node_adapter)
super()
@node_adapter = node_adapter
end
def run(name:, url:, owner:, description:'')
# note: url should be NilmDB url
@nilm = Nilm.new(name: name,
description: description,
url: url)
url: url,
node_type: @node_adapter.node_type)
unless @nilm.valid?
add_errors(@nilm.errors.full_messages)
return self
......@@ -31,12 +35,10 @@ class CreateNilm
#give the owner 'admin' permissions on the nilm
Permission.create(user: owner, nilm: nilm, role: 'admin')
#update the database
service = UpdateDb.new(db: db)
adapter = DbAdapter.new(db.url)
service.run(adapter.dbinfo, adapter.schema)
msgs = @node_adapter.refresh(db: db)
#errors on the database update are warnings on this service
#because we can still add the NILM, it will just be offline
add_warnings(service.errors + service.warnings)
add_warnings(msgs.errors + msgs.warnings)
add_notice('Created installation')
self
end
......
......@@ -4,21 +4,16 @@
class UpdateNilm
include ServiceStatus
def initialize(node_adapter)
super()
@node_adapter = node_adapter
end
def run(nilm)
if nilm.db.nil?
add_error('no associated db object')
return self
end
db_adapter = DbAdapter.new(nilm.url)
db_service = UpdateDb.new(db: nilm.db)
absorb_status(
db_service.run(db_adapter.dbinfo, db_adapter.schema)
)
joule_adapter = JouleAdapter.new(nilm.url)
joule_module_service = UpdateJouleModules.new(nilm)
absorb_status(
joule_module_service.run(joule_adapter.module_info)
)
absorb_status(@node_adapter.refresh(db: nilm.db))
self
end
end
......@@ -38,6 +38,9 @@ module ControlPanel
%w(data nilm db db_folder db_stream permission user_group user data_view joule_modules).each do |service|
config.autoload_paths << Rails.root.join("app/services/#{service}")
end
config.autoload_paths << Rails.root.join("app/adapters")
config.autoload_paths << Rails.root.join("app/adapters/nilmdb")
config.autoload_paths << Rails.root.join("app/adapters/joule")
#config.autoload_paths << Rails.root.join("app/adapters/nilmdb")
end
end
class AddNodeTypeToNilm < ActiveRecord::Migration[5.2]
def change
add_column :nilms, :node_type, :string, default: false, null: false
end
end
......@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2018_06_20_021012) do
ActiveRecord::Schema.define(version: 2018_07_10_014435) do
create_table "data_views", force: :cascade do |t|
t.integer "user_id"
......@@ -155,6 +155,7 @@ ActiveRecord::Schema.define(version: 2018_06_20_021012) do
t.string "url"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "node_type", default: "f", null: false
end
create_table "permissions", force: :cascade do |t|
......
......@@ -2,12 +2,12 @@
require 'rails_helper'
describe JouleAdapter do
describe Joule::Backend do
# use the benchtop server joule API
let (:url) {'http://172.16.1.12/joule'}
it 'retrieves module infos', :vcr do
adapter = JouleAdapter.new(url)
adapter.module_info.each do |m|
backend = Joule::Backend.new(url)
backend.module_info.each do |m|
expect(m).to include(:name, :exec_cmd, :web_interface)
end
end
......
......@@ -2,12 +2,12 @@
require 'rails_helper'
describe DbAdapter do
describe Nilmdb::Backend do
# use the vagrant box loaded with example database
let (:url) {'http://localhost:8080/nilmdb'}
it 'retrieves basic schema', :vcr do
adapter = DbAdapter.new(url)
adapter.schema.each do |entry|
backend = Nilmdb::Backend.new(url)
backend.schema.each do |entry|
expect(entry).to include(:path, :attributes)
expect(entry[:attributes]).to(
include(:data_type, :start_time,
......@@ -17,8 +17,8 @@ describe DbAdapter do
end
it 'retrieves stream specific schema', :vcr do
adapter = DbAdapter.new(url)
entries = adapter.stream_info(create(:db_stream,path:"/tutorial/pump-prep"))
backend = Nilmdb::Backend.new(url)
entries = backend.stream_info(create(:db_stream,path:"/tutorial/pump-prep"))
expect(entries[:base_entry][:path]).to eq "/tutorial/pump-prep"
#TODO: support decimation lookup, need HTTP API to process wild cards
expect(entries[:decimation_entries].length).to eq 0
......@@ -26,17 +26,17 @@ describe DbAdapter do
describe 'set_stream_metadata' do
it 'updates config_key in metadata', :vcr do
adapter = DbAdapter.new(url)
backend = Nilmdb::Backend.new(url)
stream = DbStream.new(path: '/tutorial/pump-events',
name: 'test', description: 'new', db_elements_attributes: [
{column: 0, name: 'element1'},{column: 1, name: 'element2'}])
result = adapter.set_stream_metadata(stream)
result = backend.set_stream_metadata(stream)
expect(result[:error]).to be false
end
it 'returns error on server failure', :vcr do
adapter = DbAdapter.new(url)
backend = Nilmdb::Backend.new(url)
stream = DbStream.new(path: '/badpath')
result = adapter.set_stream_metadata(stream)
result = backend.set_stream_metadata(stream)
expect(result[:error]).to be true
expect(result[:msg]).to match(/badpath/)
end
......@@ -44,23 +44,23 @@ describe DbAdapter do
describe 'set_folder_metadata' do
it 'updates config_key in metadata', :vcr do
adapter = DbAdapter.new(url)
backend = Nilmdb::Backend.new(url)
folder = DbFolder.new(path: '/tutorial',
name: 'test', description: 'new')
result = adapter.set_folder_metadata(folder)
result = backend.set_folder_metadata(folder)
expect(result[:error]).to be false
end
it 'creates info stream if missing', :vcr do
adapter = DbAdapter.new(url)
backend = Nilmdb::Backend.new(url)
folder = DbFolder.new(path: '/v2_folder/another',
name: 'another', description: 'new')
result = adapter.set_folder_metadata(folder)
result = backend.set_folder_metadata(folder)
expect(result[:error]).to be false
end
it 'returns error on server failure', :vcr do
adapter = DbAdapter.new(url)
backend = Nilmdb::Backend.new(url)
folder = DbFolder.new(path: '/badpath')
result = adapter.set_folder_metadata(folder)
result = backend.set_folder_metadata(folder)
expect(result[:error]).to be true
expect(result[:msg]).to match(/badpath/)
end
......@@ -68,42 +68,42 @@ describe DbAdapter do
describe 'get_count' do
it 'returns number of elements in path over interval', :vcr do
adapter = DbAdapter.new(url)
backend = Nilmdb::Backend.new(url)
start_time = 1361546159000000
end_time = 1361577615684742
path = '/tutorial/pump-events'
raw_count = adapter.get_count(path,start_time, end_time)
lvl4_count = adapter.get_count(path+"~decim-4",start_time, end_time)
raw_count = backend.get_count(path,start_time, end_time)
lvl4_count = backend.get_count(path+"~decim-4",start_time, end_time)
expect(raw_count>0).to be true
expect(raw_count/4).to eq(lvl4_count)
end
it 'returns nil on server failure', :vcr do
adapter = DbAdapter.new(url)
backend = Nilmdb::Backend.new(url)
start_time = 1361546159000000
end_time = 1361577615684742
path = '/path/does/not/exist'
count = adapter.get_count(path,start_time, end_time)
count = backend.get_count(path,start_time, end_time)
expect(count).to be nil
end
end
describe 'get_data' do
it 'returns array of data over interval', :vcr do
adapter = DbAdapter.new(url)
backend = Nilmdb::Backend.new(url)
start_time = 1361546159000000
end_time = 1361577615684742
path = '/tutorial/pump-events'
raw_data = adapter.get_data(path,start_time, end_time)
lvl4_data = adapter.get_data(path+"~decim-4",start_time, end_time)
raw_data = backend.get_data(path,start_time, end_time)
lvl4_data = backend.get_data(path+"~decim-4",start_time, end_time)
expect(raw_data.length>0).to be true
expect(raw_data.length/4).to eq(lvl4_data.length)
end
it 'adds nil to indicate interval breaks', :vcr do
adapter = DbAdapter.new(url)
backend = Nilmdb::Backend.new(url)
start_time = 1361466001000000
end_time = 1361577615684742
path = '/tutorial/pump-events'
data = adapter.get_data(path,start_time, end_time)
data = backend.get_data(path,start_time, end_time)
expect(data.length>0).to be true
num_intervals = data.select{|elem| elem==nil}.length
expect(num_intervals).to eq 1
......@@ -112,11 +112,11 @@ describe DbAdapter do
describe 'get_intervals' do
it 'returns array of interval line segments', :vcr do
adapter = DbAdapter.new(url)
backend = Nilmdb::Backend.new(url)
start_time = 1360017784000000
end_time = 1361579612066315
path = '/tutorial/pump-events'
intervals = adapter.get_intervals(path,start_time, end_time)
intervals = backend.get_intervals(path,start_time, end_time)
expect(intervals.length).to eq(60) #20 intervals
end
end
......
......@@ -27,7 +27,7 @@ RSpec.describe 'LoadStreamData' do
raw_count: 1600, data: @data
)
@service = LoadStreamData.new(@mockAdapter)
@service = Nilmdb::LoadStreamData.new(@mockAdapter)
end
it 'sets @type to [decimated]' do
@service.run(@db_stream, 10, 90)
......@@ -105,7 +105,7 @@ RSpec.describe 'LoadStreamData' do
end_time: @db_stream.end_time,
raw_count: 1000, data: @data
)
@service = LoadStreamData.new(@mockAdapter)
@service = Nilmdb::LoadStreamData.new(@mockAdapter)
end
it 'sets @type to [interval] if all decimations have too much data' do
@service.run(@db_stream, 10, 90)
......@@ -140,7 +140,7 @@ RSpec.describe 'LoadStreamData' do
raw_count: 100, data: @data
)
@service = LoadStreamData.new(@mockAdapter)
@service = Nilmdb::LoadStreamData.new(@mockAdapter)
end
it 'sets @type to [raw]' do
@service.run(@db_stream, 10, 90)
......@@ -150,17 +150,17 @@ RSpec.describe 'LoadStreamData' do
end
it 'only if count <= nilm resolution over interval' do
#must have decimated data ready!
#use custom adapter and service objects
#use custom backend and service objects
data = [[40,0,1,2,3,4,5,6,7,8],nil,[50,0,1,2,3,4,5,6,7,8]]
adapter = MockDataDbAdapter.new(
backend = MockDataDbAdapter.new(
start_time: @db_stream.start_time,
end_time: @db_stream.end_time,
raw_count: 100, data: data
)
service = LoadStreamData.new(adapter)
service = Nilmdb::LoadStreamData.new(backend)
db.max_points_per_plot = 90; db.save
service.run(@db_stream, 10, 90)
expect(adapter.level_retrieved).to be > 1
expect(backend.level_retrieved).to be > 1
end
it 'populates @data structure with raw data' do
@service.run(@db_stream, 10, 90)
......@@ -191,7 +191,7 @@ RSpec.describe 'LoadStreamData' do
end_time: @db_stream.end_time,
raw_count: 400, data: @data
)
@service = LoadStreamData.new(@mockAdapter)
@service = Nilmdb::LoadStreamData.new(@mockAdapter)
end
it 'still succeeds' do
#requested interval is before actual data
......
......@@ -28,7 +28,7 @@ describe 'UpdateDb' do
describe '*run*' do
def update_with_schema(schema, db: nil)
@db = db || Db.new
@service = UpdateDb.new(db: @db)
@service = Nilmdb::UpdateDb.new(db: @db)
mock_info =
@service.run(dbinfo, schema) #ignore dbinfo
@root = @db.root_folder
......
......@@ -4,7 +4,7 @@ require 'rails_helper'
describe 'UpdateFolder service' do
let(:db) { Db.new }
let(:service) { UpdateDb.new(db: db) }
let(:service) { Nilmdb::UpdateDb.new(db: db) }
let(:helper) { DbSchemaHelper.new }
let(:mock_dbinfo) { {} }
......@@ -20,7 +20,7 @@ describe 'UpdateFolder service' do
folder = DbFolder.find_by_name('old_name')
expect(folder).to be_present
# run update again with new metadata
service = UpdateDb.new(db: db)
service = Nilmdb::UpdateDb.new(db: db)
service.run(mock_dbinfo, [helper.entry('/folder1/subfolder/info',
metadata: { name: 'new_name' })])
folder.reload
......
......@@ -4,7 +4,7 @@ require 'rails_helper'
describe 'UpdateStream service' do
let(:db) { Db.new }
let(:service) { UpdateDb.new(db: db) }
let(:service) { Nilmdb::UpdateDb.new(db: db) }
let(:helper) { DbSchemaHelper.new }
let(:mock_dbinfo) { {} }
......@@ -21,7 +21,7 @@ describe 'UpdateStream service' do
stream = DbStream.find_by_name('old_name')
expect(stream).to be_present
# run update again with new metadata
service = UpdateDb.new(db: db)
service = Nilmdb::UpdateDb.new(db: db)
service.run(mock_dbinfo, [helper.entry('/folder1/stream1',
metadata: { name: 'new_name' })])
stream.reload
......@@ -59,7 +59,7 @@ describe 'UpdateStream service' do
expect(element).to be_present
# run update again with new metadata
schema[0][:elements][0][:name] = 'new_name'
service = UpdateDb.new(db: db)
service = Nilmdb::UpdateDb.new(db: db)
service.run(mock_dbinfo, schema)
element.reload
expect(element.name).to eq('new_name')
......
......@@ -54,17 +54,17 @@ RSpec.describe DbFoldersController, type: :request do
describe 'PUT update' do
before do
@mock_adapter = double(DbAdapter) # MockDbAdapter.new #instance_double(DbAdapter)
@db_success = { error: false, msg: 'success' }
@db_failure = { error: true, msg: 'dberror' }
allow(DbAdapter).to receive(:new).and_return(@mock_adapter)
@mock_adapter = double(Nilmdb::Adapter) # MockDbAdapter.new #instance_double(DbAdapter)
@node_success = { error: false, msg: 'success' }
@node_failure = { error: true, msg: 'dberror' }
allow(NodeAdapterFactory).to receive(:from_nilm).and_return(@mock_adapter)
end
context 'with owner permissions' do
it 'updates nilmdb and local database' do
@auth_headers = john.create_new_auth_token
expect(@mock_adapter).to receive(:set_folder_metadata)
.and_return(@db_success)
expect(@mock_adapter).to receive(:save_folder)
.and_return(@node_success)
put "/db_folders/#{john_folder.id}.json",
params: { name: 'new name' },
headers: @auth_headers
......@@ -75,8 +75,8 @@ RSpec.describe DbFoldersController, type: :request do
it 'does not update if nilmdb update fails' do
@auth_headers = john.create_new_auth_token
expect(@mock_adapter).to receive(:set_folder_metadata)
.and_return(@db_failure)
expect(@mock_adapter).to receive(:save_folder)
.and_return(@node_failure)
name = john_folder.name
put "/db_folders/#{john_folder.id}.json",
params: { name: 'new name' },
......@@ -88,7 +88,7 @@ RSpec.describe DbFoldersController, type: :request do
it 'returns 422 on invalid parameters' do
# name cannot be blank
expect(@mock_adapter).to_not receive(:set_folder_metadata)
expect(@mock_adapter).to_not receive(:save_folder)
@auth_headers = john.create_new_auth_token
put "/db_folders/#{john_folder.id}.json",
params: { name: '' },
......@@ -99,8 +99,8 @@ RSpec.describe DbFoldersController, type: :request do
it 'only allows configurable parameters to be changed' do
# should ignore start_time and accept description
expect(@mock_adapter).to receive(:set_folder_metadata)
.and_return(@db_success)
expect(@mock_adapter).to receive(:save_folder)
.and_return(@node_success)
@auth_headers = john.create_new_auth_token
start_time = john_folder.start_time
put "/db_folders/#{john_folder.id}.json",
......@@ -110,6 +110,15 @@ RSpec.describe DbFoldersController, type: :request do
expect(john_folder.reload.start_time).to eq(start_time)
expect(john_folder.description).to eq('changed')
end
it 'fails if an adapter cannot be created' do
allow(NodeAdapterFactory).to receive(:from_nilm).and_return(nil)
put "/db_folders/#{john_folder.id}.json",
params: { name: 'new name' },
headers: john.create_new_auth_token
expect(response).to have_http_status(:unprocessable_entity)
expect(response).to have_error_message
end
end
......
......@@ -62,16 +62,16 @@ RSpec.describe DbStreamsController, type: :request do
describe 'PUT update' do
before do
@mock_adapter = double(DbAdapter) # MockDbAdapter.new #instance_double(DbAdapter)
@mock_adapter = double(Nilmdb::Adapter) # MockDbAdapter.new #instance_double(DbAdapter)
@db_success = { error: false, msg: 'success' }
@db_failure = { error: true, msg: 'dberror' }
allow(DbAdapter).to receive(:new).and_return(@mock_adapter)
allow(NodeAdapterFactory).to receive(:from_nilm).and_return(@mock_adapter)
end
context 'with owner permissions' do
it 'updates nilmdb and local database' do
@auth_headers = john.create_new_auth_token
expect(@mock_adapter).to receive(:set_stream_metadata)
expect(@mock_adapter).to receive(:save_stream)
.and_return(@db_success)
elem = @stream.db_elements.first
put "/db_streams/#{@stream.id}.json",
......@@ -91,7 +91,7 @@ RSpec.describe DbStreamsController, type: :request do
it 'does not update if nilmdb update fails' do
@auth_headers = john.create_new_auth_token
expect(@mock_adapter).to receive(:set_stream_metadata)
expect(@mock_adapter).to receive(:save_stream)
.and_return(@db_failure)
name = @stream.name
put "/db_streams/#{@stream.id}.json",
......@@ -104,7 +104,7 @@ RSpec.describe DbStreamsController, type: :request do
it 'returns 422 on invalid stream parameters' do
# name cannot be blank
expect(@mock_adapter).to_not receive(:set_stream_metadata)
expect(@mock_adapter).to_not receive(:save_stream)
@auth_headers = john.create_new_auth_token
put "/db_streams/#{@stream.id}.json",
params: { name: '' },
......@@ -115,7 +115,7 @@ RSpec.describe DbStreamsController, type: :request do
it 'returns 422 on invalid element parameters' do
# elements cannot have the same name
expect(@mock_adapter).to_not receive(:set_stream_metadata)
expect(@mock_adapter).to_not receive(:save_stream)
@auth_headers = john.create_new_auth_token
elem1 = @stream.db_elements.first
elemN = @stream.db_elements.last
......@@ -129,7 +129,7 @@ RSpec.describe DbStreamsController, type: :request do
it 'only allows configurable parameters to be changed' do
# should ignore start_time and accept name
expect(@mock_adapter).to receive(:set_stream_metadata)
expect(@mock_adapter).to receive(:save_stream)
.and_return(@db_success)
@auth_headers = john.create_new_auth_token
start_time = @stream.start_time
......@@ -140,6 +140,14 @@ RSpec.describe DbStreamsController, type: :request do
expect(@stream.reload.start_time).to eq(start_time)
expect(@stream.name).to eq('changed')
end
it 'fails if an adapter cannot be created' do
allow(NodeAdapterFactory).to receive(:from_nilm).and_return(nil)
put "/db_streams/#{@stream.id}.json",
params: { start_time: 10, name: 'changed' },
headers: john.create_new_auth_token
expect(response).to have_http_status(:unprocessable_entity)
expect(response).to have_error_message
end
end
context 'without owner permissions' do
......@@ -167,10 +175,10 @@ RSpec.describe DbStreamsController, type: :request do
describe 'POST data' do
before do
@mock_adapter = double(DbAdapter) # MockDbAdapter.new #instance_double(DbAdapter)
@mock_adapter = double(Nilmdb::Adapter) # MockDbAdapter.new #instance_double(DbAdapter)
@db_success = { error: false, msg: 'success' }
@db_failure = { error: true, msg: 'dberror' }
allow(DbAdapter).to receive(:new).and_return(@mock_adapter)
allow(NodeAdapterFactory).to receive(:from_nilm).and_return(@mock_adapter)
end
context 'with viewer permissions' do
......
......@@ -137,7 +137,8 @@ RSpec.describe NilmsController, type: :request do
it 'refreshes nilm data when requested' do
@auth_headers = john.create_new_auth_token
[john_nilm, lab_nilm].each do |nilm|
mock_service = UpdateNilm.new
mock_adapter = instance_double(Nilmdb::Adapter)
mock_service = UpdateNilm.new(mock_adapter)
expect(mock_service).to receive(:run).and_return StubService.new
allow(UpdateNilm).to receive(:new)
.and_return(mock_service)
......@@ -167,6 +168,13 @@ RSpec.describe NilmsController, type: :request do
describe 'POST create' do
context 'with authenticated user' do
it 'creates a NILM' do
result = StubService.new
result.add_error("cannot contact database")
@mock_adapter = instance_double(Nilmdb::Adapter,
node_type: 'nilmdb',
refresh: result)
allow(NodeAdapterFactory).to receive(:from_url).and_return(@mock_adapter)
@auth_headers = john.create_new_auth_token
post "/nilms.json",
params: {name: 'new', url: 'http://sampleurl/nilmdb'},
......@@ -177,7 +185,7 @@ RSpec.describe NilmsController, type: :request do
# make sure the NILM was built
nilm = Nilm.find_by_name('new')
expect(nilm).to_not be nil
expect(nilm.db.available).to be false
expect(@mock_adapter).to have_received(:refresh)
# user should be an admin
expect(john.admins_nilm?(nilm)).to be true
end
......
......@@ -10,7 +10,7 @@ class MockDataDbAdapter
@raw_count = raw_count
@data = data
@last_path = nil
@url = "http://mockadapter/nilmdb"
@url = "http://mockbackend/nilmdb"
end
def get_data(path, start_time, end_time)
......
......@@ -8,6 +8,7 @@ FactoryBot.define do
name {Faker::Lorem.unique.words(3).join(' ')}
description { Faker::Lorem.sentence }
url {Faker::Internet.unique.url}
node_type 'nilmdb'
transient do
admins []
owners []
......
......@@ -9,7 +9,7 @@ require 'rspec/rails'
require 'support/factory_bot'
require 'support/api_messages'
require 'support/mock_service'
require 'support/mock_adapter'
# Add additional requires below this line. Rails is not loaded until this point!
# Requires supporting ruby files with custom matchers and macros, etc, in
......
......@@ -13,13 +13,10 @@ RSpec.describe 'BuildDataset' do
data = [{id: elem0.id, type: 'raw', values: [[10,0],[11,1],nil,[12,2]]},
{id: elem1.id, type: 'raw', values: [[10,3],[11,4],nil,[12,5]]},
{id: elem2.id, type: 'raw', values: [[10,6],[11,7],nil,[12,8]]}]
@mock_stream_service = instance_double(LoadStreamData,
run: StubService.new,
success?: true,
data: data,
decimation_factor: 1)
allow(LoadStreamData).to receive(:new).and_return(@mock_stream_service)
@service = BuildDataset.new
@mock_adapter = instance_double(Nilmdb::Adapter,
load_data: { data: data, decimation_factor: 1})
allow(NodeAdapterFactory).to receive(:from_nilm).and_return(@mock_adapter)
@service = BuildDataset.new(@mock_adapter)
@service.run(db_stream,0,100)
end
it 'builds the dataset' do
......@@ -46,13 +43,10 @@ RSpec.describe 'BuildDataset' do
data = [{id: elem0.id, type: 'decimated', values: [[10,0,-1,1],[11,1,0,2],nil,[12,2,1,3]]},
{id: elem1.id, type: 'decimated', values: [[10,3,2,4],[11,4,3,5],nil,[12,5,6,7]]},
{id: elem2.id, type: 'interval', values: [[10,0],[11,0],nil,[12,0]]}]
@mock_stream_service = instance_double(LoadStreamData,
run: StubService.new,
success?: true,
data: data,
decimation_factor: 4)
allow(LoadStreamData).to receive(:new).and_return(@mock_stream_service)
@service = BuildDataset.new
@mock_adapter = instance_double(Nilmdb::Adapter,
load_data: { data: data, decimation_factor: 4})
allow(NodeAdapterFactory).to receive(:from_nilm).and_return(@mock_adapter)
@service = BuildDataset.new(@mock_adapter)
@service.run(db_stream,0,100)
end
it 'omits event elements' do
......@@ -75,13 +69,10 @@ RSpec.describe 'BuildDataset' do
data = [{id: elem0.id, type: 'interval', values: [[10,0],[11,0],nil,[12,0]]},
{id: elem1.id, type: 'interval', values: [[10,0],[11,0],nil,[12,0]]},
{id: elem2.id, type: 'interval', values: [[10,0],[11,0],nil,[12,0]]}]
@mock_stream_service = instance_double(LoadStreamData,
run: StubService.new,
success?: true,
data: data,
decimation_factor: 1)
allow(LoadStreamData).to receive(:new).and_return(@mock_stream_service)
@service = BuildDataset.new
@mock_adapter = instance_double(Nilmdb::Adapter,
load_data: { data: data, decimation_factor: 1})
#allow(LoadStreamData).to receive(:new).and_return(@mock_stream_service)
@service = BuildDataset.new(@mock_adapter)
@service.run(db_stream,0,100)
end
it 'returns no data' do
......@@ -96,13 +87,10 @@ RSpec.describe 'BuildDataset' do
data = [{id: elem0.id, type: 'raw', values: []},
{id: elem1.id, type: 'raw', values: []},
{id: elem2.id, type: 'raw', values: []}]
@mock_stream_service = instance_double(LoadStreamData,
run: StubService.new,
success?: true,
data: data,
decimation_factor: 1)
allow(LoadStreamData).to receive(:new).and_return(@mock_stream_service)
@service = BuildDataset.new
@mock_adapter = instance_double(Nilmdb::Adapter,
load_data:{data: data, decimation_factor: 1})
#allow(LoadStreamData).to receive(:new).and_return(@mock_stream_service)
@service = BuildDataset.new(@mock_adapter)
@service.run(db_stream,0,100)
end
it 'returns no data' do
......@@ -111,14 +99,9 @@ RSpec.describe 'BuildDataset' do
end
describe 'when stream service returns error' do
before do
@mock_stream_service = instance_double(LoadStreamData,
run: StubService.new,
success?: false,
errors: ['generic error'],
warnings: [],
notices: [])
allow(LoadStreamData).to receive(:new).and_return(@mock_stream_service)
@service = BuildDataset.new
@mock_adapter = instance_double(Nilmdb::Adapter, load_data: nil)
#allow(LoadStreamData).to receive(:new).and_return(@mock_stream_service)
@service = BuildDataset.new(@mock_adapter)
@service.run(db_stream,0,100)
end
it 'returns error' do
......
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'LoadElementData' do
let(:db) { create(:db, max_points_per_plot: 100) }
......@@ -14,19 +15,16 @@ RSpec.describe 'LoadElementData' do
@stream_data = [{id: @elem0.id, values: 'mock0'},
{id: @elem1.id, values: 'mock1'},
{id: @elem2.id, values: 'mock2'}]
@mock_stream_service = MockLoadStreamData.new(
[stream: @db_stream, data: @stream_data])
allow(LoadStreamData).to receive(:new).and_return(@mock_stream_service)
@mock_adapter = MockAdapter.new([stream: @db_stream,
data: @stream_data])
allow(Nilmdb::Adapter).to receive(:new).and_return(@mock_adapter)
end
it 'makes one request for the stream data' do
expect(@mock_stream_service).to receive(:data).and_return(@stream_data)
#expect(@mock_adapter).to receive(:load_data)
service = LoadElementData.new
service.run([@elem0,@elem2],0,100)
expect(service.success?).to be true
expect(service.data).to eq [
{id: @elem0.id, values: 'mock0'},
{id: @elem2.id, values: 'mock2'}
]
expect(service.data).to eq [@stream_data[0], @stream_data[2]]
end
end
......@@ -43,10 +41,10 @@ RSpec.describe 'LoadElementData' do
@elem3 = create(:db_element, column: 3, db_stream: @db_stream2)
@stream2_data = [{id: @elem2.id, values: 'mock2'},
{id: @elem3.id, values: 'mock3'}]
@mock_stream_service = MockLoadStreamData.new(
@mock_adapter = MockAdapter.new(
[{stream: @db_stream1, data: @stream1_data},
{stream: @db_stream2, data: @stream2_data}])
allow(LoadStreamData).to receive(:new).and_return(@mock_stream_service)
allow(Nilmdb::Adapter).to receive(:new).and_return(@mock_adapter)
end
it 'makes one request per stream' do
......@@ -57,7 +55,7 @@ RSpec.describe 'LoadElementData' do
{id: @elem0.id, values: 'mock0'},
{id: @elem3.id, values: 'mock3'}
]
expect(@mock_stream_service.run_count).to eq 2
expect(@mock_adapter.run_count).to eq 2
end
end
......@@ -76,10 +74,10 @@ RSpec.describe 'LoadElementData' do
@elem3 = create(:db_element, column: 3, db_stream: @db_stream2)
@stream2_data = [{id: @elem2.id, values: 'mock2'},
{id: @elem3.id, values: 'mock3'}]
@mock_stream_service = MockLoadStreamData.new(
@mock_adapter = MockAdapter.new(
[{stream: @db_stream1, data: @stream1_data},
{stream: @db_stream2, data: nil}])
allow(LoadStreamData).to receive(:new).and_return(@mock_stream_service)
allow(Nilmdb::Adapter).to receive(:new).and_return(@mock_adapter)
end
it 'fills in the data that is available' do
service = LoadElementData.new
......@@ -89,7 +87,7 @@ RSpec.describe 'LoadElementData' do
{id: @elem0.id, values: 'mock0'},
{id: @elem3.id, type: 'error', values: nil}
]
expect(@mock_stream_service.run_count).to eq 2
expect(@mock_adapter.run_count).to eq 2
end
end
......@@ -102,10 +100,10 @@ RSpec.describe 'LoadElementData' do
let(:user) {create(:user)}
it 'updates the streams', :vcr do
adapter = DbAdapter.new(url)
service = CreateNilm.new
@adapter = Nilmdb::Adapter.new(url)
service = CreateNilm.new(@adapter)
service.run(name: 'test', url: url, owner: user)
db = service.nilm.db
#db = service.nilm.db
#request data from ac-power (15 Jun 2015 - 27 Jun 2015)
# pump-events (04 Feb 2013 - 23 Feb 2013)
elem1 = DbStream.find_by_path("/tutorial/ac-power").db_elements.first
......
......@@ -3,11 +3,11 @@
require 'rails_helper'
describe 'EditStream service' do
let(:db_adapter) { instance_double(DbAdapter) }
let(:mock_adapter) { instance_double(Nilmdb::Adapter) }
let(:stream) { FactoryBot.create(:db_stream, path: '/stream/path', name: 'old') }
let(:element) { stream.db_elements.first}
let(:service) { EditStream.new(db_adapter) }
# db adapter return values
let(:service) { EditStream.new(mock_adapter) }
# db backend return values
let(:success) { { error: false, msg: '' } }
let(:error) { { error: true, msg: 'server error' } }
......@@ -15,10 +15,10 @@ describe 'EditStream service' do
attribs = { id: 0, invalid_attrib: 'ignore',
name: 'new name', name_abbrev: 'nn',
db_elements_attributes: [{id: element.id, name: 'new!'}] }
allow(db_adapter).to receive(:set_stream_metadata).and_return(success)
# run the service, it should call the adapter and save the folder
allow(mock_adapter).to receive(:save_stream).and_return(success)
# run the service, it should call the backend and save the folder
service.run(stream, attribs)
expect(db_adapter).to have_received(:set_stream_metadata).with(stream)
expect(mock_adapter).to have_received(:save_stream).with(stream)
expect(stream.name).to eq('new name')
expect(stream.name_abbrev).to eq('nn')
expect(DbElement.find(element.id).name).to eq('new!')
......@@ -26,17 +26,17 @@ describe 'EditStream service' do
it 'checks to make sure new attributes are valid' do
attribs = { name: '' } # cannot have empty name
allow(db_adapter).to receive(:set_stream_metadata).and_return(success)
# run the service, it shouldn't call the database adapter
allow(mock_adapter).to receive(:save_stream).and_return(success)
# run the service, it shouldn't call the database backend
service.run(stream, attribs)
expect(service.errors?).to be true
expect(db_adapter).to_not have_received(:set_stream_metadata)
expect(mock_adapter).to_not have_received(:save_stream)
end
it 'does not change stream or elements on a server error' do
attribs = { name: 'new',
db_elements_attributes: [{id: element.id, name: 'new'}]}
allow(db_adapter).to receive(:set_stream_metadata).and_return(error)
allow(mock_adapter).to receive(:save_stream).and_return(error)
allow(stream).to receive(:save!)
allow(element).to receive(:save!)
# run the service, it shouldn't save the folder object
......
......@@ -3,10 +3,10 @@
require 'rails_helper'
describe 'EditFolder service' do
let(:db_adapter) { instance_double(DbAdapter) }
let(:mock_adapter) { instance_double(Nilmdb::Adapter) }
let(:folder) { DbFolder.new(path: '/folder/path', name: 'old') }
let(:service) { EditFolder.new(db_adapter) }
# db adapter return values
let(:service) { EditFolder.new(mock_adapter) }
# mock_adapter return values
let(:success) { { error: false, msg: '' } }
let(:error) { { error: true, msg: 'server error' } }
......@@ -14,10 +14,10 @@ describe 'EditFolder service' do
attribs = { id: 0, invalid_attrib: 'ignore',
name: 'new', description: 'updated' }
allow(folder).to receive(:save!)
allow(db_adapter).to receive(:set_folder_metadata).and_return(success)
# run the service, it should call the adapter and save the folder
allow(mock_adapter).to receive(:save_folder).and_return(success)
# run the service, it should call the backend and save the folder
service.run(folder, attribs)
expect(db_adapter).to have_received(:set_folder_metadata).with(folder)
expect(mock_adapter).to have_received(:save_folder).with(folder)
expect(folder.name).to eq('new')
expect(folder.description).to eq('updated')
expect(folder).to have_received(:save!)
......@@ -25,16 +25,16 @@ describe 'EditFolder service' do
it 'checks to make sure new attributes are valid' do
attribs = { name: '' } # cannot have empty name
allow(db_adapter).to receive(:set_folder_metadata).and_return(success)
# run the service, it shouldn't call the database adapter
allow(mock_adapter).to receive(:save_folder).and_return(success)
# run the service, it shouldn't call the database backend
service.run(folder, attribs)
expect(service.errors?).to be true
expect(db_adapter).to_not have_received(:set_folder_metadata)
expect(mock_adapter).to_not have_received(:save_folder)
end
it 'does not change folder on a server error' do
attribs = { name: 'new' }
allow(db_adapter).to receive(:set_folder_metadata).and_return(error)
allow(mock_adapter).to receive(:save_folder).and_return(error)
allow(folder).to receive(:save!)
# run the service, it shouldn't save the folder object
service.run(folder, attribs)
......
......@@ -8,13 +8,13 @@ describe 'UpdateJouleModules' do
nilm = create(:nilm)
nilm.joule_modules << create(:joule_module, name: 'prev1')
nilm.joule_modules << create(:joule_module, name: 'prev2')
adapter = MockJouleAdapter.new
adapter.add_module("new1",inputs={i1: '/path/1'},
backend = MockJouleAdapter.new
backend.add_module("new1",inputs={i1: '/path/1'},
outputs={o1: '/path/2'})
adapter.add_module("new2",inputs={i1: '/path/3',i2: '/path/4'},
backend.add_module("new2",inputs={i1: '/path/3',i2: '/path/4'},
outputs={o1: '/path/5',o2: '/path/5'})
service = UpdateJouleModules.new(nilm)
service.run(adapter.module_info)
service.run(backend.module_info)
expect(service.success?).to be true
# new modules are in the database
expect(nilm.joule_modules.find_by_name('new1')).to be_present
......@@ -31,21 +31,21 @@ describe 'UpdateJouleModules' do
end
it 'produces a warning if a stream is not in the database' do
nilm = create(:nilm)
adapter = MockJouleAdapter.new
adapter.add_module("module",outputs={output: '/missing/path'})
backend = MockJouleAdapter.new
backend.add_module("module",outputs={output: '/missing/path'})
service = UpdateJouleModules.new(nilm)
service.run(adapter.module_info)
service.run(backend.module_info)
expect(service.warnings?).to be true
end
it 'links db_stream to the pipe if the stream is in the database' do
nilm = create(:nilm)
nilm.db.db_streams << create(:db_stream, path: '/matched/path1')
nilm.db.db_streams << create(:db_stream, path: '/matched/path2')
adapter = MockJouleAdapter.new
adapter.add_module("module",inputs={input: '/matched/path1'},
backend = MockJouleAdapter.new
backend.add_module("module",inputs={input: '/matched/path1'},
outputs={output: '/matched/path2'})
service = UpdateJouleModules.new(nilm)
service.run(adapter.module_info)
service.run(backend.module_info)
expect(service.warnings?).to be false
end
it 'returns error if Joule server is unavailable' do
......
......@@ -5,16 +5,16 @@ test_nilm_url = 'http://localhost:8080/nilmdb'
RSpec.describe 'CreateNilm' do
describe 'build' do
it 'creates and populates a Db object', :vcr do
it 'creates and populates a Db object' do
result = StubService.new
result.add_error("unable to contact database")
# mock the database updater
service = instance_double(UpdateDb,
run: StubService.new,
errors: ['cannot contact database'],
warnings: [])
allow(UpdateDb).to receive(:new).and_return(service)
@mock_adapter = instance_double(Nilmdb::Adapter,
refresh: result,
node_type: 'nilmdb')
user = create(:user, first_name: "John")
# run the NILM creation
nilm_creator = CreateNilm.new
nilm_creator = CreateNilm.new(@mock_adapter)
nilm_creator.run(
name: 'test',
description: 'test description',
......@@ -28,7 +28,7 @@ RSpec.describe 'CreateNilm' do
expect(nilm).to_not be nil
expect(nilm.db).to be_present
# ...and the database has been populated
expect(service).to have_received(:run)
expect(@mock_adapter).to have_received(:refresh)
expect(user.owns_nilm?(nilm)).to be true
end
end
......
......@@ -4,41 +4,27 @@ require 'rails_helper'
describe 'UpdateNilm' do
it 'updates the db when NilmDB is accessible' do
mock_service = instance_double(UpdateDb,
run: StubService.new,
errors: [],
warnings: [])
allow(UpdateDb).to receive(:new)
.and_return(mock_service)
mock_adapter = double(JouleAdapter)
allow(JouleAdapter).to receive(:new).and_return(mock_adapter)
expect(mock_adapter).to receive(:module_info).and_return([])
mock_adapter = instance_double(Nilmdb::Adapter,
refresh: StubService.new)
nilm = create(:nilm)
service = UpdateNilm.new()
service = UpdateNilm.new(mock_adapter)
service.run(nilm)
expect(service.success?).to be true
end
it 'returns error if db is nil' do
mock_adapter = instance_double(Nilmdb::Adapter)
nilm = Nilm.create(name: 'test', url: 'invalid')
mock_service = instance_double(UpdateDb,
run: StubService.new)
allow(UpdateDb).to receive(:new)
.and_return(mock_service)
service = UpdateNilm.new()
service = UpdateNilm.new(mock_adapter)
service.run(nilm)
expect(service.success?).to be false
expect(mock_service).to_not have_received(:run)
end
it 'returns error if db is offline' do
nilm = create(:nilm)
resp = StubService.new
resp.add_error('offline')
mock_service = instance_double(UpdateDb,
run: resp)
allow(UpdateDb).to receive(:new)
.and_return(mock_service)
mock_adapter = instance_double(Nilmdb::Adapter,
refresh: resp)
nilm = create(:nilm)
service = UpdateNilm.new()
service = UpdateNilm.new(mock_adapter)
service.run(nilm)
expect(service.success?).to be false
end
......
class MockLoadStreamData
include ServiceStatus
attr_reader :data, :run_count
class MockAdapter
attr_reader :run_count
def initialize(dataset)
super()
@dataset = dataset
@data = nil
@run_count = 0
end
def run(db_stream, start_time, end_time, elements=[], resolution=nil)
@data = @dataset.select{|d| d[:stream]==db_stream}.first[:data]
def load_data(db_stream, start_time, end_time, elements=[], resolution=nil)
data = @dataset.select{|d| d[:stream]==db_stream}.first[:data]
@run_count += 1
if(@data == nil)
self.add_error('could not retrieve stream data')
if data == nil
return nil
else
self.reset_messages
end
return self
{data: data, decimation_factor: 1}
end
end
end
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment