aemo 0.5.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/aemo/dispatchable.rb +2 -2
- data/lib/aemo/exceptions/invalid_nmi_allocation_type.rb +1 -1
- data/lib/aemo/exceptions/time_error.rb +20 -0
- data/lib/aemo/market/interval.rb +4 -4
- data/lib/aemo/market/node.rb +9 -3
- data/lib/aemo/market.rb +2 -4
- data/lib/aemo/msats.rb +70 -54
- data/lib/aemo/nem12/data_stream_suffix.rb +1 -1
- data/lib/aemo/nem12/quality_method.rb +25 -13
- data/lib/aemo/nem12/unit_of_measurement.rb +42 -42
- data/lib/aemo/nem12.rb +300 -94
- data/lib/aemo/nem13.rb +1 -2
- data/lib/aemo/nmi/allocation.rb +6 -7
- data/lib/aemo/nmi.rb +48 -38
- data/lib/aemo/region.rb +4 -3
- data/lib/aemo/register.rb +7 -7
- data/lib/aemo/struct.rb +3 -0
- data/lib/aemo/time.rb +112 -0
- data/lib/aemo/version.rb +1 -2
- data/lib/aemo.rb +13 -11
- data/lib/data/xml_to_json.rb +2 -4
- data/spec/aemo_spec.rb +0 -7
- data/spec/lib/aemo/market/interval_spec.rb +30 -12
- data/spec/lib/aemo/market/node_spec.rb +11 -9
- data/spec/lib/aemo/market_spec.rb +5 -4
- data/spec/lib/aemo/meter_spec.rb +2 -2
- data/spec/lib/aemo/msats_spec.rb +68 -56
- data/spec/lib/aemo/nem12_spec.rb +154 -43
- data/spec/lib/aemo/nmi/allocation_spec.rb +23 -18
- data/spec/lib/aemo/nmi_spec.rb +98 -72
- data/spec/lib/aemo/region_spec.rb +23 -18
- data/spec/spec_helper.rb +13 -13
- metadata +13 -435
data/lib/aemo/nmi.rb
CHANGED
@@ -5,7 +5,7 @@ require 'json'
|
|
5
5
|
require 'time'
|
6
6
|
require 'ostruct'
|
7
7
|
|
8
|
-
require 'aemo/nmi/allocation
|
8
|
+
require 'aemo/nmi/allocation'
|
9
9
|
|
10
10
|
module AEMO
|
11
11
|
# [AEMO::NMI]
|
@@ -22,11 +22,11 @@ module AEMO
|
|
22
22
|
'ACT' => 'Australian Capital Territory',
|
23
23
|
'NSW' => 'New South Wales',
|
24
24
|
'QLD' => 'Queensland',
|
25
|
-
'SA'
|
25
|
+
'SA' => 'South Australia',
|
26
26
|
'TAS' => 'Tasmania',
|
27
27
|
'VIC' => 'Victoria',
|
28
|
-
'WA'
|
29
|
-
'NT'
|
28
|
+
'WA' => 'Western Australia',
|
29
|
+
'NT' => 'Northern Territory'
|
30
30
|
}.freeze
|
31
31
|
|
32
32
|
# Transmission Node Identifier Codes are loaded from a json file
|
@@ -79,7 +79,7 @@ module AEMO
|
|
79
79
|
# @param [String] nmi the nmi to be checked
|
80
80
|
# @return [Boolean] whether or not the nmi is valid
|
81
81
|
def valid_nmi?(nmi)
|
82
|
-
(
|
82
|
+
(nmi.length == 10) && !nmi.match(/^([A-HJ-NP-Z\d]{10})/).nil?
|
83
83
|
end
|
84
84
|
|
85
85
|
# A function to calculate the checksum value for a given National Meter
|
@@ -159,14 +159,13 @@ module AEMO
|
|
159
159
|
# Identifier
|
160
160
|
def checksum
|
161
161
|
summation = 0
|
162
|
-
@nmi.reverse.
|
162
|
+
@nmi.reverse.chars.each_index do |i|
|
163
163
|
value = nmi[nmi.length - i - 1].ord
|
164
164
|
value *= 2 if i.even?
|
165
|
-
value = value.to_s.
|
165
|
+
value = value.to_s.chars.map(&:to_i).reduce(:+)
|
166
166
|
summation += value
|
167
167
|
end
|
168
|
-
|
169
|
-
checksum
|
168
|
+
(10 - (summation % 10)) % 10
|
170
169
|
end
|
171
170
|
|
172
171
|
# Provided MSATS is configured, gets the MSATS data for the NMI
|
@@ -174,6 +173,7 @@ module AEMO
|
|
174
173
|
# @return [Hash] MSATS NMI Detail data
|
175
174
|
def raw_msats_nmi_detail(options = {})
|
176
175
|
raise ArgumentError, 'MSATS has no authentication credentials' unless AEMO::MSATS.can_authenticate?
|
176
|
+
|
177
177
|
AEMO::MSATS.nmi_detail(@nmi, options)
|
178
178
|
end
|
179
179
|
|
@@ -233,7 +233,7 @@ module AEMO
|
|
233
233
|
data_streams = @msats_detail['DataStreams']['DataStream']
|
234
234
|
data_streams = [data_streams] if data_streams.is_a?(Hash) # Deal with issue of only one existing
|
235
235
|
data_streams.each do |stream|
|
236
|
-
@data_streams <<
|
236
|
+
@data_streams << Struct::DataStream.new(
|
237
237
|
suffix: stream['Suffix'],
|
238
238
|
profile_name: stream['ProfileName'],
|
239
239
|
averaged_daily_load: stream['AveragedDailyLoad'],
|
@@ -270,10 +270,10 @@ module AEMO
|
|
270
270
|
@meters.select { |x| x.status == status.to_s }
|
271
271
|
end
|
272
272
|
|
273
|
-
# Returns the data_stream
|
273
|
+
# Returns the data_stream Structs for the requested status (A/I)
|
274
274
|
#
|
275
275
|
# @param [String] status the stateus [A|I]
|
276
|
-
# @return [Array<
|
276
|
+
# @return [Array<Struct>] Returns an array of Structs for the
|
277
277
|
# current Meters
|
278
278
|
def data_streams_by_status(status = 'A')
|
279
279
|
@data_streams.select { |x| x.status == status.to_s }
|
@@ -297,19 +297,20 @@ module AEMO
|
|
297
297
|
|
298
298
|
# A function to return the distribution loss factor value for a given date
|
299
299
|
#
|
300
|
-
# @param [DateTime, Time] datetime the date for the distribution loss factor
|
300
|
+
# @param [DateTime, ::Time] datetime the date for the distribution loss factor
|
301
301
|
# value
|
302
302
|
# @return [nil, float] the distribution loss factor value
|
303
|
-
def dlfc_value(datetime = Time.now)
|
303
|
+
def dlfc_value(datetime = ::Time.now)
|
304
304
|
if @dlf.nil?
|
305
305
|
raise 'No DLF set, ensure that you have set the value either via the' \
|
306
306
|
'update_from_msats! function or manually'
|
307
307
|
end
|
308
308
|
raise 'DLF is invalid' unless DLF_CODES.keys.include?(@dlf)
|
309
|
-
raise 'Invalid date' unless [DateTime, Time].include?(datetime.class)
|
309
|
+
raise 'Invalid date' unless [DateTime, ::Time].include?(datetime.class)
|
310
|
+
|
310
311
|
possible_values = DLF_CODES[@dlf].select do |x|
|
311
|
-
Time.parse(x['FromDate']) <= datetime &&
|
312
|
-
Time.parse(x['ToDate']) >= datetime
|
312
|
+
::Time.parse(x['FromDate']) <= datetime &&
|
313
|
+
::Time.parse(x['ToDate']) >= datetime
|
313
314
|
end
|
314
315
|
if possible_values.empty?
|
315
316
|
nil
|
@@ -320,61 +321,70 @@ module AEMO
|
|
320
321
|
|
321
322
|
# A function to return the distribution loss factor value for a given date
|
322
323
|
#
|
323
|
-
# @param [DateTime, Time] start the date for the distribution loss factor value
|
324
|
-
# @param [DateTime, Time] finish the date for the distribution loss factor value
|
324
|
+
# @param [DateTime, ::Time] start the date for the distribution loss factor value
|
325
|
+
# @param [DateTime, ::Time] finish the date for the distribution loss factor value
|
325
326
|
# @return [Array(Hash)] array of hashes of start, finish and value
|
326
|
-
def dlfc_values(start = Time.now, finish = Time.now)
|
327
|
+
def dlfc_values(start = ::Time.now, finish = ::Time.now)
|
327
328
|
if @dlf.nil?
|
328
|
-
raise 'No DLF set, ensure that you have set the value either via the '\
|
329
|
+
raise 'No DLF set, ensure that you have set the value either via the ' \
|
329
330
|
'update_from_msats! function or manually'
|
330
331
|
end
|
331
332
|
raise 'DLF is invalid' unless DLF_CODES.keys.include?(@dlf)
|
332
|
-
raise 'Invalid start' unless [DateTime, Time].include?(start.class)
|
333
|
-
raise 'Invalid finish' unless [DateTime, Time].include?(finish.class)
|
333
|
+
raise 'Invalid start' unless [DateTime, ::Time].include?(start.class)
|
334
|
+
raise 'Invalid finish' unless [DateTime, ::Time].include?(finish.class)
|
334
335
|
raise 'start cannot be after finish' if start > finish
|
335
|
-
|
336
|
+
|
337
|
+
DLF_CODES[@dlf].reject { |x| start > ::Time.parse(x['ToDate']) || finish < ::Time.parse(x['FromDate']) }
|
336
338
|
.map { |x| { 'start' => x['FromDate'], 'finish' => x['ToDate'], 'value' => x['Value'].to_f } }
|
337
339
|
end
|
338
340
|
|
339
341
|
# A function to return the transmission node identifier loss factor value for a given date
|
340
342
|
#
|
341
|
-
# @param [DateTime, Time] datetime the date for the distribution loss factor value
|
343
|
+
# @param [DateTime, ::Time] datetime the date for the distribution loss factor value
|
342
344
|
# @return [nil, float] the transmission node identifier loss factor value
|
343
|
-
def tni_value(datetime = Time.now)
|
345
|
+
def tni_value(datetime = ::Time.now)
|
344
346
|
if @tni.nil?
|
345
|
-
raise 'No TNI set, ensure that you have set the value either via the '\
|
347
|
+
raise 'No TNI set, ensure that you have set the value either via the ' \
|
346
348
|
'update_from_msats! function or manually'
|
347
349
|
end
|
348
350
|
raise 'TNI is invalid' unless TNI_CODES.keys.include?(@tni)
|
349
|
-
raise 'Invalid date' unless [DateTime, Time].include?(datetime.class)
|
350
|
-
|
351
|
+
raise 'Invalid date' unless [DateTime, ::Time].include?(datetime.class)
|
352
|
+
|
353
|
+
possible_values = TNI_CODES[@tni].select do |x|
|
354
|
+
::Time.parse(x['FromDate']) <= datetime && datetime <= ::Time.parse(x['ToDate'])
|
355
|
+
end
|
351
356
|
return nil if possible_values.empty?
|
352
|
-
|
357
|
+
|
358
|
+
possible_values = possible_values.first['mlf_data']['loss_factors'].select do |x|
|
359
|
+
::Time.parse(x['start']) <= datetime && datetime <= ::Time.parse(x['finish'])
|
360
|
+
end
|
353
361
|
return nil if possible_values.empty?
|
362
|
+
|
354
363
|
possible_values.first['value'].to_f
|
355
364
|
end
|
356
365
|
|
357
366
|
# A function to return the transmission node identifier loss factor value for a given date
|
358
367
|
#
|
359
|
-
# @param [DateTime, Time] start the date for the distribution loss factor value
|
360
|
-
# @param [DateTime, Time] finish the date for the distribution loss factor value
|
368
|
+
# @param [DateTime, ::Time] start the date for the distribution loss factor value
|
369
|
+
# @param [DateTime, ::Time] finish the date for the distribution loss factor value
|
361
370
|
# @return [Array(Hash)] array of hashes of start, finish and value
|
362
|
-
def tni_values(start = Time.now, finish = Time.now)
|
371
|
+
def tni_values(start = ::Time.now, finish = ::Time.now)
|
363
372
|
if @tni.nil?
|
364
|
-
raise 'No TNI set, ensure that you have set the value either via the '\
|
373
|
+
raise 'No TNI set, ensure that you have set the value either via the ' \
|
365
374
|
'update_from_msats! function or manually'
|
366
375
|
end
|
367
376
|
raise 'TNI is invalid' unless TNI_CODES.keys.include?(@tni)
|
368
|
-
raise 'Invalid start' unless [DateTime, Time].include?(start.class)
|
369
|
-
raise 'Invalid finish' unless [DateTime, Time].include?(finish.class)
|
377
|
+
raise 'Invalid start' unless [DateTime, ::Time].include?(start.class)
|
378
|
+
raise 'Invalid finish' unless [DateTime, ::Time].include?(finish.class)
|
370
379
|
raise 'start cannot be after finish' if start > finish
|
371
380
|
|
372
381
|
possible_values = TNI_CODES[@tni].reject do |tni_code|
|
373
|
-
start > Time.parse(tni_code['ToDate']) ||
|
374
|
-
finish < Time.parse(tni_code['FromDate'])
|
382
|
+
start > ::Time.parse(tni_code['ToDate']) ||
|
383
|
+
finish < ::Time.parse(tni_code['FromDate'])
|
375
384
|
end
|
376
385
|
|
377
386
|
return nil if possible_values.empty?
|
387
|
+
|
378
388
|
possible_values.map { |x| x['mlf_data']['loss_factors'] }
|
379
389
|
end
|
380
390
|
end
|
data/lib/aemo/region.rb
CHANGED
@@ -12,11 +12,11 @@ module AEMO
|
|
12
12
|
'ACT' => 'Australian Capital Territory',
|
13
13
|
'NSW' => 'New South Wales',
|
14
14
|
'QLD' => 'Queensland',
|
15
|
-
'SA'
|
15
|
+
'SA' => 'South Australia',
|
16
16
|
'TAS' => 'Tasmania',
|
17
17
|
'VIC' => 'Victoria',
|
18
|
-
'NT'
|
19
|
-
'WA'
|
18
|
+
'NT' => 'Northern Territory',
|
19
|
+
'WA' => 'Western Australia'
|
20
20
|
}.freeze
|
21
21
|
|
22
22
|
attr_accessor :region
|
@@ -34,6 +34,7 @@ module AEMO
|
|
34
34
|
# @return [self]
|
35
35
|
def initialize(region)
|
36
36
|
raise ArgumentError, "Region '#{region}' is not valid." unless valid_region?(region)
|
37
|
+
|
37
38
|
@region = region.upcase
|
38
39
|
@full_name = REGIONS[@region]
|
39
40
|
@current_trading = []
|
data/lib/aemo/register.rb
CHANGED
@@ -28,14 +28,14 @@ module AEMO
|
|
28
28
|
# @return [AEMO::Register] description of returned object
|
29
29
|
def self.from_hash(register)
|
30
30
|
AEMO::Register.new(
|
31
|
-
controlled_load:
|
32
|
-
dial_format:
|
33
|
-
multiplier:
|
31
|
+
controlled_load: register['ControlledLoad'] == 'Y',
|
32
|
+
dial_format: register['DialFormat'],
|
33
|
+
multiplier: register['Multiplier'],
|
34
34
|
network_tariff_code: register['NetworkTariffCode'],
|
35
|
-
register_id:
|
36
|
-
status:
|
37
|
-
time_of_day:
|
38
|
-
unit_of_measure:
|
35
|
+
register_id: register['RegisterID'],
|
36
|
+
status: register['Status'],
|
37
|
+
time_of_day: register['TimeOfDay'],
|
38
|
+
unit_of_measure: register['UnitOfMeasure']
|
39
39
|
)
|
40
40
|
end
|
41
41
|
end
|
data/lib/aemo/struct.rb
ADDED
data/lib/aemo/time.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'time'
|
4
|
+
require 'active_support/all'
|
5
|
+
|
6
|
+
require_relative 'exceptions/time_error'
|
7
|
+
|
8
|
+
module AEMO
|
9
|
+
# [AEMO::Time] provides time helpers for AEMO services.
|
10
|
+
module Time
|
11
|
+
NEMTIMEZONE = 'Australia/Brisbane'
|
12
|
+
TIMESTAMP14 = '%Y%m%d%H%M%S'
|
13
|
+
TIMESTAMP14_PATTERN = /^\d{4}\d{2}\d{2}\d{2}\d{2}\d{2}$/
|
14
|
+
TIMESTAMP12 = '%Y%m%d%H%M'
|
15
|
+
TIMESTAMP12_PATTERN = /^\d{4}\d{2}\d{2}\d{2}\d{2}$/
|
16
|
+
TIMESTAMP8 = '%Y%m%d'
|
17
|
+
TIMESTAMP8_PATTERN = /^\d{4}\d{2}\d{2}$/
|
18
|
+
|
19
|
+
class << self
|
20
|
+
# Format a time to a timestamp 14.
|
21
|
+
#
|
22
|
+
# @param [Time] time
|
23
|
+
# @return [String]
|
24
|
+
def format_timestamp14(time)
|
25
|
+
time.in_time_zone(NEMTIMEZONE).strftime(TIMESTAMP14)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Format a time to a timestamp 12.
|
29
|
+
#
|
30
|
+
# @param [Time] time
|
31
|
+
# @return [String]
|
32
|
+
def format_timestamp12(time)
|
33
|
+
time.in_time_zone(NEMTIMEZONE).strftime(TIMESTAMP12)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Format a time to a timestamp 8.
|
37
|
+
#
|
38
|
+
# @param [Time] time
|
39
|
+
# @return [String]
|
40
|
+
def format_timestamp8(time)
|
41
|
+
time.in_time_zone(NEMTIMEZONE).strftime(TIMESTAMP8)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Parse a 14 character timestamp.
|
45
|
+
#
|
46
|
+
# @param [String] string
|
47
|
+
# @raise [AEMO::TimeError]
|
48
|
+
# @return [Time]
|
49
|
+
def parse_timestamp14(string)
|
50
|
+
raise AEMO::TimeError unless string.match(TIMESTAMP14_PATTERN)
|
51
|
+
|
52
|
+
::Time.find_zone(NEMTIMEZONE).strptime(string, TIMESTAMP14)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Parse a 12 character timestamp.
|
56
|
+
#
|
57
|
+
# @param [String] string
|
58
|
+
# @return [Time]
|
59
|
+
def parse_timestamp12(string)
|
60
|
+
raise AEMO::TimeError unless string.match(TIMESTAMP12_PATTERN)
|
61
|
+
|
62
|
+
::Time.find_zone(NEMTIMEZONE).strptime(string, TIMESTAMP12)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Parse an 8 character date.
|
66
|
+
#
|
67
|
+
# @param [String] string
|
68
|
+
# @return [Time]
|
69
|
+
def parse_timestamp8(string)
|
70
|
+
raise AEMO::TimeError unless string.match(TIMESTAMP8_PATTERN)
|
71
|
+
|
72
|
+
::Time.find_zone(NEMTIMEZONE).strptime(string, TIMESTAMP8)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Check if a string is a valid timestamp 14.
|
76
|
+
#
|
77
|
+
# @param [String] string
|
78
|
+
# @return [Boolean]
|
79
|
+
def valid_timestamp14?(string)
|
80
|
+
parse_timestamp14(string)
|
81
|
+
|
82
|
+
true
|
83
|
+
rescue AEMO::TimeError
|
84
|
+
false
|
85
|
+
end
|
86
|
+
|
87
|
+
# Check if a string is a valid timestamp 12.
|
88
|
+
#
|
89
|
+
# @param [String] string
|
90
|
+
# @return [Boolean]
|
91
|
+
def valid_timestamp12?(string)
|
92
|
+
parse_timestamp12(string)
|
93
|
+
|
94
|
+
true
|
95
|
+
rescue AEMO::TimeError
|
96
|
+
false
|
97
|
+
end
|
98
|
+
|
99
|
+
# Check if a string is a valid timestamp 8.
|
100
|
+
#
|
101
|
+
# @param [String] string
|
102
|
+
# @return [Boolean]
|
103
|
+
def valid_timestamp8?(string)
|
104
|
+
parse_timestamp8(string)
|
105
|
+
|
106
|
+
true
|
107
|
+
rescue AEMO::TimeError
|
108
|
+
false
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/lib/aemo/version.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# -*- coding: UTF-8 -*-
|
4
3
|
#
|
5
4
|
# Copyright 2014 Joel Courtney
|
6
5
|
#
|
@@ -24,7 +23,7 @@
|
|
24
23
|
# @author Joel Courtney <euphemize@gmail.com>
|
25
24
|
module AEMO
|
26
25
|
# aemo version
|
27
|
-
VERSION = '0.
|
26
|
+
VERSION = '0.7.0'
|
28
27
|
|
29
28
|
# aemo version split amongst different revisions
|
30
29
|
MAJOR_VERSION, MINOR_VERSION, REVISION = VERSION.split('.').map(&:to_i)
|
data/lib/aemo.rb
CHANGED
@@ -4,17 +4,19 @@ require 'active_support/all'
|
|
4
4
|
require 'httparty'
|
5
5
|
require 'csv'
|
6
6
|
|
7
|
-
require 'aemo/
|
8
|
-
require 'aemo/
|
9
|
-
require 'aemo/
|
10
|
-
require 'aemo/market
|
11
|
-
require 'aemo/
|
12
|
-
require 'aemo/
|
13
|
-
require 'aemo/
|
14
|
-
require 'aemo/
|
15
|
-
require 'aemo/
|
16
|
-
require 'aemo/
|
17
|
-
require 'aemo/
|
7
|
+
require 'aemo/struct'
|
8
|
+
require 'aemo/time'
|
9
|
+
require 'aemo/region'
|
10
|
+
require 'aemo/market'
|
11
|
+
require 'aemo/market/interval'
|
12
|
+
require 'aemo/market/node'
|
13
|
+
require 'aemo/meter'
|
14
|
+
require 'aemo/nem12'
|
15
|
+
require 'aemo/nmi'
|
16
|
+
require 'aemo/msats'
|
17
|
+
require 'aemo/register'
|
18
|
+
require 'aemo/version'
|
19
|
+
require 'aemo/exceptions/invalid_nmi_allocation_type'
|
18
20
|
|
19
21
|
# AEMO Module to encapsulate all AEMO classes
|
20
22
|
module AEMO
|
data/lib/data/xml_to_json.rb
CHANGED
@@ -21,7 +21,7 @@ CSV.parse(file_contents, headers: true, converters: :numeric).each do |row|
|
|
21
21
|
@mlf_data[row['TNI']] ||= { location: row['Location'],
|
22
22
|
voltage: row['Voltage'],
|
23
23
|
loss_factors: [] }
|
24
|
-
row.headers.
|
24
|
+
row.headers.grep(/^FY\d{2}$/).sort.reverse.each do |fin_year|
|
25
25
|
year = "20#{fin_year.match(/FY(\d{2})/)[1]}".to_i
|
26
26
|
@mlf_data[row['TNI']][:loss_factors] << {
|
27
27
|
start: Time.parse("#{year - 1}-07-01T00:00:00+1000"),
|
@@ -74,7 +74,5 @@ end
|
|
74
74
|
output_data[code] << output_data_instance
|
75
75
|
end
|
76
76
|
|
77
|
-
File.
|
78
|
-
write_file.write(output_data.to_json)
|
79
|
-
end
|
77
|
+
File.write(File.join(@path, output_file), output_data.to_json)
|
80
78
|
end
|
data/spec/aemo_spec.rb
CHANGED
@@ -8,42 +8,60 @@ describe AEMO::Market::Interval do
|
|
8
8
|
expect(AEMO::Market::Interval::INTERVALS).to eq(trading: 'Trading', dispatch: 'Dispatch')
|
9
9
|
end
|
10
10
|
end
|
11
|
+
|
11
12
|
describe 'AEMO::Market::Interval instance methods' do
|
12
|
-
before
|
13
|
-
@interval =
|
13
|
+
before do
|
14
|
+
@interval = described_class.new('2016-03-01T00:30:00', 'REGION' => 'NSW', 'TOTALDEMAND' => 1000.23,
|
15
|
+
'RRP' => 76.54, 'PERIODTYPE' => 'TRADING')
|
14
16
|
end
|
17
|
+
|
15
18
|
it 'creates a valid interval' do
|
16
|
-
expect
|
19
|
+
expect do
|
20
|
+
described_class.new('2016-03-01T00:30:00', 'REGION' => 'NSW', 'TOTALDEMAND' => 1000.23, 'RRP' => 76.54,
|
21
|
+
'PERIODTYPE' => 'TRADING')
|
22
|
+
end.not_to raise_error
|
17
23
|
end
|
24
|
+
|
18
25
|
it 'has a trailing datetime' do
|
19
26
|
expect(@interval.datetime).to eq(Time.parse('2016-03-01T00:30:00+1000'))
|
20
27
|
end
|
28
|
+
|
21
29
|
it 'has a leading datetime' do
|
22
|
-
expect(@interval.datetime(false)).to eq(Time.parse('2016-03-01T00:00:00+1000'))
|
30
|
+
expect(@interval.datetime(trailing_edge: false)).to eq(Time.parse('2016-03-01T00:00:00+1000'))
|
23
31
|
end
|
32
|
+
|
24
33
|
it 'has a leading datetime for dispatch' do
|
25
|
-
@interval =
|
26
|
-
|
34
|
+
@interval = described_class.new('2016-03-01T00:30:00', 'REGION' => 'NSW', 'TOTALDEMAND' => 1000.23,
|
35
|
+
'RRP' => 76.54, 'PERIODTYPE' => '')
|
36
|
+
expect(@interval.datetime(trailing_edge: false)).to eq(Time.parse('2016-03-01T00:25:00+1000'))
|
27
37
|
end
|
38
|
+
|
28
39
|
it 'has an interval length' do
|
29
40
|
expect(@interval.interval_length).to eq(Time.at(300))
|
30
41
|
end
|
42
|
+
|
31
43
|
it 'is a trading interval' do
|
32
44
|
expect(@interval.interval_type).to eq(:trading)
|
33
45
|
end
|
46
|
+
|
34
47
|
it 'is a trading interval' do
|
35
|
-
expect(@interval.trading?).to
|
36
|
-
expect(@interval.dispatch?).to
|
48
|
+
expect(@interval.trading?).to be(true)
|
49
|
+
expect(@interval.dispatch?).to be(false)
|
37
50
|
end
|
51
|
+
|
38
52
|
it 'is a dispatch interval' do
|
39
|
-
@interval =
|
53
|
+
@interval = described_class.new('2016-03-01T00:30:00', 'REGION' => 'NSW', 'TOTALDEMAND' => 1000.23,
|
54
|
+
'RRP' => 76.54, 'PERIODTYPE' => '')
|
40
55
|
expect(@interval.interval_type).to eq(:dispatch)
|
41
56
|
end
|
57
|
+
|
42
58
|
it 'is a dispatch interval' do
|
43
|
-
@interval =
|
44
|
-
|
45
|
-
expect(@interval.
|
59
|
+
@interval = described_class.new('2016-03-01T00:30:00', 'REGION' => 'NSW', 'TOTALDEMAND' => 1000.23,
|
60
|
+
'RRP' => 76.54, 'PERIODTYPE' => '')
|
61
|
+
expect(@interval.trading?).to be(false)
|
62
|
+
expect(@interval.dispatch?).to be(true)
|
46
63
|
end
|
64
|
+
|
47
65
|
it 'has a valid value' do
|
48
66
|
expect(@interval.value).to eq((@interval.total_demand * @interval.rrp).round(2))
|
49
67
|
end
|
@@ -4,30 +4,32 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe AEMO::Market::Node do
|
6
6
|
describe '.IDENTIFIERS' do
|
7
|
-
it '
|
7
|
+
it 'is an array' do
|
8
8
|
expect(AEMO::Market::Node::IDENTIFIERS).to be_instance_of(Array)
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
12
|
describe 'creating a node' do
|
13
|
-
it '
|
14
|
-
expect {
|
13
|
+
it 'raises an error if invalid region' do
|
14
|
+
expect { described_class.new('BOTTOMS') }.to raise_error(ArgumentError)
|
15
15
|
end
|
16
|
-
|
17
|
-
|
16
|
+
|
17
|
+
it 'creates if node valid' do
|
18
|
+
expect { described_class.new('NSW') }.not_to raise_error
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
21
22
|
describe 'AEMO::Region instance methods' do
|
22
|
-
before
|
23
|
-
@nsw =
|
23
|
+
before do
|
24
|
+
@nsw = described_class.new('NSW')
|
24
25
|
end
|
25
26
|
|
26
27
|
describe 'AEMO::Region dispatch information' do
|
27
|
-
it '
|
28
|
+
it 'returns current dispatch data' do
|
28
29
|
expect(@nsw.current_dispatch.count).to eq(AEMO::Market.current_dispatch(@nsw.identifier).count)
|
29
30
|
end
|
30
|
-
|
31
|
+
|
32
|
+
it 'returns current trading data' do
|
31
33
|
expect(@nsw.current_trading.count).to eq(AEMO::Market.current_trading(@nsw.identifier).count)
|
32
34
|
end
|
33
35
|
end
|
@@ -14,25 +14,26 @@ describe AEMO::Market do
|
|
14
14
|
|
15
15
|
describe '.current_dispatch' do
|
16
16
|
it 'has an array of data' do
|
17
|
-
expect(
|
17
|
+
expect(described_class.current_dispatch('NSW').class).to eq(Array)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
21
|
describe '.current_trading' do
|
22
22
|
it 'has an array of data' do
|
23
|
-
expect(
|
23
|
+
expect(described_class.current_trading('NSW').class).to eq(Array)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
27
|
describe '.historic_trading_by_range' do
|
28
28
|
it 'has an array of data' do
|
29
|
-
expect(
|
29
|
+
expect(described_class.historic_trading_by_range('NSW', Date.parse('2015-01-01'),
|
30
|
+
Date.parse('2015-02-28')).class).to eq(Array)
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
33
34
|
describe '.historic_trading' do
|
34
35
|
it 'has an array of data' do
|
35
|
-
expect(
|
36
|
+
expect(described_class.historic_trading('NSW', 2015, 1).class).to eq(Array)
|
36
37
|
end
|
37
38
|
end
|
38
39
|
end
|
data/spec/lib/aemo/meter_spec.rb
CHANGED
@@ -5,14 +5,14 @@ require 'spec_helper'
|
|
5
5
|
describe AEMO::Meter do
|
6
6
|
describe 'instance methods' do
|
7
7
|
it 'creates a new instance' do
|
8
|
-
expect(
|
8
|
+
expect(described_class.new).to be_a described_class
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'can be initialized from MSATS mumbo jumbo' do
|
12
12
|
AEMO::MSATS.authorize('ER', 'ER', 'ER')
|
13
13
|
nmi_detail_query = AEMO::MSATS.nmi_detail('4001234567')
|
14
14
|
meter = nmi_detail_query['MeterRegister']['Meter'].first
|
15
|
-
expect(
|
15
|
+
expect(described_class.from_hash(meter)).to be_a described_class
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|