aemo 0.1.27 → 0.1.28
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 +4 -4
- data/lib/aemo/market.rb +30 -27
- data/lib/aemo/market/interval.rb +27 -23
- data/lib/aemo/msats.rb +218 -210
- data/lib/aemo/nem12.rb +165 -160
- data/lib/aemo/nem13.rb +1 -2
- data/lib/aemo/nmi.rb +91 -85
- data/lib/aemo/region.rb +12 -11
- data/lib/aemo/version.rb +3 -3
- data/lib/data/xml_to_json.rb +62 -0
- data/spec/aemo_spec.rb +1 -2
- data/spec/lib/aemo/market/interval_spec.rb +13 -13
- data/spec/lib/aemo/market_spec.rb +8 -6
- data/spec/lib/aemo/msats_spec.rb +14 -15
- data/spec/lib/aemo/nem12_spec.rb +8 -10
- data/spec/lib/aemo/nmi_spec.rb +79 -71
- data/spec/lib/aemo/region_spec.rb +5 -6
- data/spec/spec_helper.rb +38 -35
- metadata +16 -2
- data/lib/data/xml-to-json.rb +0 -61
data/lib/aemo/nem13.rb
CHANGED
data/lib/aemo/nmi.rb
CHANGED
@@ -15,9 +15,10 @@ module AEMO
|
|
15
15
|
'VIC' => 'Victoria',
|
16
16
|
'WA' => 'Western Australia',
|
17
17
|
'NT' => 'Northern Territory'
|
18
|
-
}
|
19
|
-
|
20
|
-
#
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
# NMI_ALLOCATIONS as per AEMO Documentation at http://aemo.com.au/Electricity/Policies-and-Procedures/Retail-and-Metering/~/media/Files/Other/Retail% 20and% 20Metering/NMI_Allocation_List_v7_June_2012.ashx
|
21
|
+
# Last accessed 2016-05-15
|
21
22
|
NMI_ALLOCATIONS = {
|
22
23
|
'ACTEWP' => {
|
23
24
|
title: 'Actew Distribution Ltd and Jemena Networks (ACT) Pty Ltd',
|
@@ -192,7 +193,7 @@ module AEMO
|
|
192
193
|
state: 'TAS',
|
193
194
|
type: 'electricity',
|
194
195
|
includes: [
|
195
|
-
/^(T[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})
|
196
|
+
/^(T[A-HJ-NP-Z\d]{3}W[A-HJ-NP-Z\d]{5})$/
|
196
197
|
],
|
197
198
|
excludes: [
|
198
199
|
]
|
@@ -395,17 +396,17 @@ module AEMO
|
|
395
396
|
excludes: [
|
396
397
|
]
|
397
398
|
}
|
398
|
-
}
|
399
|
+
}.freeze
|
399
400
|
# Transmission Node Identifier Codes are loaded from a json file
|
400
401
|
# Obtained from http://www.nemweb.com.au/
|
401
402
|
#
|
402
403
|
# See /lib/data for further data manipulation required
|
403
|
-
TNI_CODES = JSON.parse(File.read(File.join(File.dirname(__FILE__),'..','data','aemo-tni.json')))
|
404
|
+
TNI_CODES = JSON.parse(File.read(File.join(File.dirname(__FILE__), '..', 'data', 'aemo-tni.json'))).freeze
|
404
405
|
# Distribution Loss Factor Codes are loaded from a json file
|
405
|
-
# Obtained from MSATS, matching to DNSP from file http://www.aemo.com.au/Electricity/Market-Operations/Loss-Factors-and-Regional-Boundaries/~/media/Files/Other/loss%20factors/DLF_FINAL_V2_2014_2015.ashx
|
406
|
+
# Obtained from MSATS, matching to DNSP from file http://www.aemo.com.au/Electricity/Market-Operations/Loss-Factors-and-Regional-Boundaries/~/media/Files/Other/loss% 20factors/DLF_FINAL_V2_2014_2015.ashx
|
406
407
|
# Last accessed 2015-02-06
|
407
408
|
# See /lib/data for further data manipulation required
|
408
|
-
DLF_CODES = JSON.parse(File.read(File.join(File.dirname(__FILE__),'..','data','aemo-dlf.json')))
|
409
|
+
DLF_CODES = JSON.parse(File.read(File.join(File.dirname(__FILE__), '..', 'data', 'aemo-dlf.json'))).freeze
|
409
410
|
|
410
411
|
# [String] National Meter Identifier
|
411
412
|
@nmi = nil
|
@@ -424,20 +425,24 @@ module AEMO
|
|
424
425
|
|
425
426
|
attr_accessor :nmi, :msats_detail, :tni, :dlf, :customer_classification_code, :customer_threshold_code, :jurisdiction_code, :classification_code, :status, :address, :meters, :roles, :data_streams
|
426
427
|
|
427
|
-
# Initialize a
|
428
|
+
# Initialize a NMI file
|
428
429
|
#
|
429
430
|
# @param nmi [String] the National Meter Identifier (NMI)
|
430
431
|
# @param options [Hash] a hash of options
|
432
|
+
# @option options [Hash] :msats_detail MSATS details as per #parse_msats_detail requirements
|
431
433
|
# @return [AEMO::NMI] an instance of AEMO::NMI is returned
|
432
|
-
def initialize(nmi,options={})
|
433
|
-
raise ArgumentError
|
434
|
-
raise ArgumentError
|
435
|
-
raise ArgumentError
|
434
|
+
def initialize(nmi, options = {})
|
435
|
+
raise ArgumentError, 'NMI is not a string' unless nmi.is_a?(String)
|
436
|
+
raise ArgumentError, 'NMI is not 10 characters' unless nmi.length == 10
|
437
|
+
raise ArgumentError, 'NMI is not constructed with valid characters' unless AEMO::NMI.valid_nmi?(nmi)
|
438
|
+
|
439
|
+
@nmi = nmi
|
440
|
+
@meters = []
|
441
|
+
@roles = {}
|
442
|
+
@data_streams = []
|
443
|
+
@msats_detail = options[:msats_detail]
|
436
444
|
|
437
|
-
@
|
438
|
-
@meters = []
|
439
|
-
@roles = {}
|
440
|
-
@data_streams = []
|
445
|
+
parse_msats_detail unless @msats_detail.nil?
|
441
446
|
end
|
442
447
|
|
443
448
|
# A function to validate the instance's nmi value
|
@@ -459,7 +464,7 @@ module AEMO
|
|
459
464
|
# @param checksum_value [Integer] the checksum value to check against the current National Meter Identifier's checksum value
|
460
465
|
# @return [Boolean] whether or not the checksum is valid
|
461
466
|
def valid_checksum?(checksum_value)
|
462
|
-
checksum_value ==
|
467
|
+
checksum_value == checksum
|
463
468
|
end
|
464
469
|
|
465
470
|
# Checksum is a function to calculate the checksum value for a given National Meter Identifier
|
@@ -469,10 +474,8 @@ module AEMO
|
|
469
474
|
summation = 0
|
470
475
|
@nmi.reverse.split(//).each_index do |i|
|
471
476
|
value = nmi[nmi.length - i - 1].ord
|
472
|
-
|
473
|
-
|
474
|
-
end
|
475
|
-
value = value.to_s.split(//).map{|i| i.to_i}.reduce(:+)
|
477
|
+
value *= 2 if i.even?
|
478
|
+
value = value.to_s.split(//).map(&:to_i).reduce(:+)
|
476
479
|
summation += value
|
477
480
|
end
|
478
481
|
checksum = (10 - (summation % 10)) % 10
|
@@ -482,18 +485,26 @@ module AEMO
|
|
482
485
|
# Provided MSATS is configured, gets the MSATS data for the NMI
|
483
486
|
#
|
484
487
|
# @return [Hash] MSATS NMI Detail data
|
485
|
-
def raw_msats_nmi_detail(options={})
|
486
|
-
raise ArgumentError,
|
487
|
-
|
488
|
-
AEMO::MSATS.nmi_detail(@nmi,options)
|
488
|
+
def raw_msats_nmi_detail(options = {})
|
489
|
+
raise ArgumentError,
|
490
|
+
'MSATS has no authentication credentials' unless AEMO::MSATS.can_authenticate?
|
491
|
+
AEMO::MSATS.nmi_detail(@nmi, options)
|
489
492
|
end
|
490
493
|
|
491
494
|
# Provided MSATS is configured, uses the raw MSATS data to augment NMI information
|
492
495
|
#
|
493
496
|
# @return [self] returns self
|
494
|
-
def update_from_msats!(options={})
|
497
|
+
def update_from_msats!(options = {})
|
495
498
|
# Update local cache
|
496
499
|
@msats_detail = raw_msats_nmi_detail(options)
|
500
|
+
parse_msats_detail
|
501
|
+
self
|
502
|
+
end
|
503
|
+
|
504
|
+
# Turns raw MSATS junk into useful things
|
505
|
+
#
|
506
|
+
# @return [self] returns self
|
507
|
+
def parse_msats_detail
|
497
508
|
# Set the details if there are any
|
498
509
|
unless @msats_detail['MasterData'].nil?
|
499
510
|
@tni = @msats_detail['MasterData']['TransmissionNodeIdentifier']
|
@@ -512,7 +523,7 @@ module AEMO
|
|
512
523
|
unless @msats_detail['MeterRegister'].nil?
|
513
524
|
meters = @msats_detail['MeterRegister']['Meter']
|
514
525
|
meters = [meters] if meters.is_a?(Hash)
|
515
|
-
meters.select{|x| !x['Status'].nil? }.each do |meter|
|
526
|
+
meters.select { |x| !x['Status'].nil? }.each do |meter|
|
516
527
|
@meters << OpenStruct.new(
|
517
528
|
status: meter['Status'],
|
518
529
|
installation_type_code: meter['InstallationTypeCode'],
|
@@ -522,9 +533,9 @@ module AEMO
|
|
522
533
|
serial_number: meter['SerialNumber']
|
523
534
|
)
|
524
535
|
end
|
525
|
-
meters.select{|x| x['Status'].nil? }.each do |registers|
|
526
|
-
m = @meters.find{|x| x.serial_number == registers['SerialNumber']}
|
527
|
-
m.registers <<
|
536
|
+
meters.select { |x| x['Status'].nil? }.each do |registers|
|
537
|
+
m = @meters.find { |x| x.serial_number == registers['SerialNumber'] }
|
538
|
+
m.registers << OpenStruct.new(
|
528
539
|
controlled_load: (registers['RegisterConfiguration']['Register']['ControlledLoad'] == 'Y'),
|
529
540
|
dial_format: registers['RegisterConfiguration']['Register']['DialFormat'],
|
530
541
|
multiplier: registers['RegisterConfiguration']['Register']['Multiplier'],
|
@@ -549,7 +560,13 @@ module AEMO
|
|
549
560
|
data_streams = @msats_detail['DataStreams']['DataStream']
|
550
561
|
data_streams = [data_streams] if data_streams.is_a?(Hash) # Deal with issue of only one existing
|
551
562
|
data_streams.each do |stream|
|
552
|
-
@data_streams << OpenStruct.new(
|
563
|
+
@data_streams << OpenStruct.new(
|
564
|
+
suffix: stream['Suffix'],
|
565
|
+
profile_name: stream['ProfileName'],
|
566
|
+
averaged_daily_load: stream['AveragedDailyLoad'],
|
567
|
+
data_stream_type: stream['DataStreamType'],
|
568
|
+
status: stream['Status']
|
569
|
+
)
|
553
570
|
end
|
554
571
|
end
|
555
572
|
self
|
@@ -561,7 +578,12 @@ module AEMO
|
|
561
578
|
def friendly_address
|
562
579
|
friendly_address = ''
|
563
580
|
if @address.is_a?(Hash)
|
564
|
-
friendly_address = @address.values.map
|
581
|
+
friendly_address = @address.values.map do |x|
|
582
|
+
if x.is_a?(Hash)
|
583
|
+
x = x.values.map { |y| y.is_a?(Hash) ? y.values.join(' ') : y }.join(' ')
|
584
|
+
end
|
585
|
+
x
|
586
|
+
end.join(', ')
|
565
587
|
end
|
566
588
|
friendly_address
|
567
589
|
end
|
@@ -571,7 +593,7 @@ module AEMO
|
|
571
593
|
# @param status [String] the stateus [C|R]
|
572
594
|
# @return [Array<OpenStruct>] Returns an array of OpenStructs for Meters with the status provided
|
573
595
|
def meters_by_status(status = 'C')
|
574
|
-
@meters.select{|x| x.status ==
|
596
|
+
@meters.select { |x| x.status == status.to_s }
|
575
597
|
end
|
576
598
|
|
577
599
|
# Returns the data_stream OpenStructs for the requested status (A/I)
|
@@ -579,14 +601,14 @@ module AEMO
|
|
579
601
|
# @param status [String] the stateus [A|I]
|
580
602
|
# @return [Array<OpenStruct>] Returns an array of OpenStructs for the current Meters
|
581
603
|
def data_streams_by_status(status = 'A')
|
582
|
-
@data_streams.select{|x| x.status ==
|
604
|
+
@data_streams.select { |x| x.status == status.to_s }
|
583
605
|
end
|
584
606
|
|
585
607
|
# The current daily load
|
586
608
|
#
|
587
609
|
# @return [Integer] the current daily load for the meter
|
588
610
|
def current_daily_load
|
589
|
-
data_streams_by_status
|
611
|
+
data_streams_by_status.map { |x| x.averaged_daily_load.to_i }.inject(0, :+)
|
590
612
|
end
|
591
613
|
|
592
614
|
# A function to validate the NMI provided
|
@@ -602,7 +624,7 @@ module AEMO
|
|
602
624
|
# @param nmi [String] the NMI to check the checksum against
|
603
625
|
# @param checksum_value [Integer] the checksum value to check against the current National Meter Identifier's checksum value
|
604
626
|
# @return [Boolean] whether or not the checksum is valid
|
605
|
-
def self.valid_checksum?(nmi,checksum_value)
|
627
|
+
def self.valid_checksum?(nmi, checksum_value)
|
606
628
|
nmi = AEMO::NMI.new(nmi)
|
607
629
|
nmi.valid_checksum?(checksum_value)
|
608
630
|
end
|
@@ -626,14 +648,14 @@ module AEMO
|
|
626
648
|
|
627
649
|
# A function to return the distribution loss factor value for a given date
|
628
650
|
#
|
629
|
-
# @param [DateTime,Time] datetime the date for the distribution loss factor value
|
630
|
-
# @return [nil,float] the distribution loss factor value
|
651
|
+
# @param [DateTime, Time] datetime the date for the distribution loss factor value
|
652
|
+
# @return [nil, float] the distribution loss factor value
|
631
653
|
def dlfc_value(datetime = DateTime.now)
|
632
654
|
raise 'No DLF set, ensure that you have set the value either via the update_from_msats! function or manually' if @dlf.nil?
|
633
655
|
raise 'DLF is invalid' unless DLF_CODES.keys.include?(@dlf)
|
634
|
-
raise 'Invalid date' unless [DateTime,Time].include?(datetime.class)
|
635
|
-
possible_values = DLF_CODES[@dlf].select{|x| DateTime.parse(x['FromDate']) <= datetime && datetime <= DateTime.parse(x['ToDate']) }
|
636
|
-
if possible_values.
|
656
|
+
raise 'Invalid date' unless [DateTime, Time].include?(datetime.class)
|
657
|
+
possible_values = DLF_CODES[@dlf].select { |x| DateTime.parse(x['FromDate']) <= datetime && datetime <= DateTime.parse(x['ToDate']) }
|
658
|
+
if possible_values.empty?
|
637
659
|
nil
|
638
660
|
else
|
639
661
|
possible_values.first['Value'].to_f
|
@@ -642,66 +664,50 @@ module AEMO
|
|
642
664
|
|
643
665
|
# A function to return the distribution loss factor value for a given date
|
644
666
|
#
|
645
|
-
# @param [DateTime,Time] start the date for the distribution loss factor value
|
646
|
-
# @param [DateTime,Time] finish the date for the distribution loss factor value
|
647
|
-
# @return [Array(Hash)] array of hashes of start,finish and value
|
648
|
-
def dlfc_values(start = DateTime.now, finish=DateTime.now)
|
667
|
+
# @param [DateTime, Time] start the date for the distribution loss factor value
|
668
|
+
# @param [DateTime, Time] finish the date for the distribution loss factor value
|
669
|
+
# @return [Array(Hash)] array of hashes of start, finish and value
|
670
|
+
def dlfc_values(start = DateTime.now, finish = DateTime.now)
|
649
671
|
raise 'No DLF set, ensure that you have set the value either via the update_from_msats! function or manually' if @dlf.nil?
|
650
672
|
raise 'DLF is invalid' unless DLF_CODES.keys.include?(@dlf)
|
651
|
-
raise 'Invalid start' unless [DateTime,Time].include?(start.class)
|
652
|
-
raise 'Invalid finish' unless [DateTime,Time].include?(finish.class)
|
673
|
+
raise 'Invalid start' unless [DateTime, Time].include?(start.class)
|
674
|
+
raise 'Invalid finish' unless [DateTime, Time].include?(finish.class)
|
653
675
|
raise 'start cannot be after finish' if start > finish
|
654
|
-
DLF_CODES[@dlf].reject{|x| start > DateTime.parse(x['ToDate']) || finish < DateTime.parse(x['FromDate']) }
|
676
|
+
DLF_CODES[@dlf].reject { |x| start > DateTime.parse(x['ToDate']) || finish < DateTime.parse(x['FromDate']) }
|
677
|
+
.map { |x| { 'start' => x['FromDate'], 'finish' => x['ToDate'], 'value' => x['Value'].to_f } }
|
655
678
|
end
|
656
679
|
|
657
680
|
# A function to return the transmission node identifier loss factor value for a given date
|
658
681
|
#
|
659
|
-
# @param [DateTime,Time] datetime the date for the distribution loss factor value
|
660
|
-
# @return [nil,float] the transmission node identifier loss factor value
|
682
|
+
# @param [DateTime, Time] datetime the date for the distribution loss factor value
|
683
|
+
# @return [nil, float] the transmission node identifier loss factor value
|
661
684
|
def tni_value(datetime = DateTime.now)
|
662
685
|
raise 'No TNI set, ensure that you have set the value either via the update_from_msats! function or manually' if @tni.nil?
|
663
686
|
raise 'TNI is invalid' unless TNI_CODES.keys.include?(@tni)
|
664
|
-
raise 'Invalid date' unless [DateTime,Time].include?(datetime.class)
|
665
|
-
possible_values = TNI_CODES[@tni].select{|x| DateTime.parse(x['FromDate']) <= datetime && datetime <= DateTime.parse(x['ToDate']) }
|
666
|
-
if possible_values.
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
if possible_values.length == 0
|
671
|
-
nil
|
672
|
-
else
|
673
|
-
possible_values.first['value'].to_f
|
674
|
-
end
|
675
|
-
end
|
687
|
+
raise 'Invalid date' unless [DateTime, Time].include?(datetime.class)
|
688
|
+
possible_values = TNI_CODES[@tni].select { |x| DateTime.parse(x['FromDate']) <= datetime && datetime <= DateTime.parse(x['ToDate']) }
|
689
|
+
return nil if possible_values.empty?
|
690
|
+
possible_values = possible_values.first['mlf_data']['loss_factors'].select { |x| DateTime.parse(x['start']) <= datetime && datetime <= DateTime.parse(x['finish']) }
|
691
|
+
return nil if possible_values.empty?
|
692
|
+
possible_values.first['value'].to_f
|
676
693
|
end
|
677
694
|
|
678
695
|
# A function to return the transmission node identifier loss factor value for a given date
|
679
696
|
#
|
680
|
-
# @param [DateTime,Time] start the date for the distribution loss factor value
|
681
|
-
# @param [DateTime,Time] finish the date for the distribution loss factor value
|
682
|
-
# @return [Array(Hash)] array of hashes of start,finish and value
|
683
|
-
def tni_values(start=DateTime.now,finish=DateTime.now)
|
697
|
+
# @param [DateTime, Time] start the date for the distribution loss factor value
|
698
|
+
# @param [DateTime, Time] finish the date for the distribution loss factor value
|
699
|
+
# @return [Array(Hash)] array of hashes of start, finish and value
|
700
|
+
def tni_values(start = DateTime.now, finish = DateTime.now)
|
684
701
|
raise 'No TNI set, ensure that you have set the value either via the update_from_msats! function or manually' if @tni.nil?
|
685
702
|
raise 'TNI is invalid' unless TNI_CODES.keys.include?(@tni)
|
686
|
-
raise 'Invalid start' unless [DateTime,Time].include?(start.class)
|
687
|
-
raise 'Invalid finish' unless [DateTime,Time].include?(finish.class)
|
703
|
+
raise 'Invalid start' unless [DateTime, Time].include?(start.class)
|
704
|
+
raise 'Invalid finish' unless [DateTime, Time].include?(finish.class)
|
688
705
|
raise 'start cannot be after finish' if start > finish
|
689
|
-
possible_values = TNI_CODES[@tni]
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
possible_values.each do |possible_value|
|
695
|
-
tni_values += possible_value['mlf_data']['loss_factors'].reject{|x| start > DateTime.parse(x['finish']) || finish < DateTime.parse(x['start']) }
|
696
|
-
end
|
697
|
-
end
|
698
|
-
tni_values
|
706
|
+
possible_values = TNI_CODES[@tni]
|
707
|
+
.reject { |x| start > DateTime.parse(x['ToDate']) || finish < DateTime.parse(x['FromDate']) }
|
708
|
+
.reject { |x| start > DateTime.parse(x['finish']) || finish < DateTime.parse(x['start']) }
|
709
|
+
return nil if possible_values.empty?
|
710
|
+
possible_values.map { |x| x['mlf_data']['loss_factors'] }
|
699
711
|
end
|
700
|
-
|
701
|
-
# ######### #
|
702
|
-
protected
|
703
|
-
# ######### #
|
704
|
-
|
705
712
|
end
|
706
|
-
|
707
713
|
end
|
data/lib/aemo/region.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
module AEMO
|
2
|
+
# AEMO::Region
|
3
|
+
#
|
4
|
+
# @author Joel Courtney
|
5
|
+
# @abstract
|
6
|
+
# @since 0.1.0
|
2
7
|
class Region
|
3
|
-
|
4
8
|
# Regions under juristiction
|
5
9
|
REGIONS = {
|
6
10
|
'ACT' => 'Australian Capital Territory',
|
@@ -11,12 +15,12 @@ module AEMO
|
|
11
15
|
'VIC' => 'Victoria',
|
12
16
|
'NT' => 'Northern Territory',
|
13
17
|
'WA' => 'Western Australia'
|
14
|
-
}
|
18
|
+
}.freeze
|
15
19
|
|
16
20
|
attr_accessor :region
|
17
21
|
|
18
22
|
def initialize(region)
|
19
|
-
raise ArgumentError
|
23
|
+
raise ArgumentError, "Region '#{region}' is not valid." unless valid_region?(region)
|
20
24
|
@region = region
|
21
25
|
@current_trading = []
|
22
26
|
@current_dispatch = []
|
@@ -35,30 +39,27 @@ module AEMO
|
|
35
39
|
end
|
36
40
|
|
37
41
|
def current_dispatch
|
38
|
-
if @current_dispatch.
|
42
|
+
if @current_dispatch.empty? || @current_dispatch.last.datetime != (Time.now - Time.now.to_i % 300)
|
39
43
|
@current_dispatch = AEMO::Market.current_dispatch(@region)
|
40
44
|
end
|
41
45
|
@current_dispatch
|
42
46
|
end
|
43
47
|
|
44
48
|
def current_trading
|
45
|
-
if @current_trading.
|
49
|
+
if @current_trading.empty? || @current_trading.reject { |i| i.period_type != 'TRADE' }.last.datetime != (Time.now - Time.now.to_i % 300)
|
46
50
|
@current_trading = AEMO::Market.current_trading(@region)
|
47
51
|
end
|
48
52
|
@current_trading
|
49
53
|
end
|
50
54
|
|
51
55
|
def self.all
|
52
|
-
REGIONS.keys.map{|k| AEMO::Region.new(k)}
|
56
|
+
REGIONS.keys.map { |k| AEMO::Region.new(k) }
|
53
57
|
end
|
54
58
|
|
55
|
-
|
56
|
-
protected
|
57
|
-
# ######### #
|
59
|
+
protected
|
58
60
|
|
59
|
-
def
|
61
|
+
def valid_region?(region)
|
60
62
|
REGIONS.keys.include?(region)
|
61
63
|
end
|
62
|
-
|
63
64
|
end
|
64
65
|
end
|
data/lib/aemo/version.rb
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
#
|
3
3
|
# Copyright 2014 Joel Courtney
|
4
4
|
#
|
5
|
-
# Licensed under the Apache License, Version 2.0 (the
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the 'License');
|
6
6
|
# you may not use this file except in compliance with the License.
|
7
7
|
# You may obtain a copy of the License at
|
8
8
|
#
|
9
9
|
# http://www.apache.org/licenses/LICENSE-2.0
|
10
10
|
#
|
11
11
|
# Unless required by applicable law or agreed to in writing, software
|
12
|
-
# distributed under the License is distributed on an
|
12
|
+
# distributed under the License is distributed on an 'AS IS' BASIS,
|
13
13
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
14
|
# See the License for the specific language governing permissions and
|
15
15
|
# limitations under the License.
|
@@ -22,7 +22,7 @@
|
|
22
22
|
# @author Joel Courtney <euphemize@gmail.com>
|
23
23
|
module AEMO
|
24
24
|
# aemo version
|
25
|
-
VERSION = '0.1.
|
25
|
+
VERSION = '0.1.28'.freeze
|
26
26
|
|
27
27
|
# aemo version split amongst different revisions
|
28
28
|
MAJOR_VERSION, MINOR_VERSION, REVISION = VERSION.split('.').map(&:to_i)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'json'
|
3
|
+
require 'csv'
|
4
|
+
require 'active_support/all'
|
5
|
+
|
6
|
+
@path = Dir.pwd
|
7
|
+
@files = Dir.entries(@path).reject { |f| %w(. ..).include?(f) }
|
8
|
+
|
9
|
+
@mlf_data = {}
|
10
|
+
@dlf_data = {}
|
11
|
+
|
12
|
+
# Let's get the CSV Data first
|
13
|
+
|
14
|
+
# TNI to MLF
|
15
|
+
CSV.open(File.join(@path, 'tni-mlf-codes.csv'), headers: true, converters: :numeric).each do |row|
|
16
|
+
@mlf_data[row['TNI']] ||= { location: row['Location'], voltage: row['Voltage'], loss_factors: [] }
|
17
|
+
@mlf_data[row['TNI']][:loss_factors] << { start: DateTime.parse('2015-07-01T00:00:00+1000'), finish: DateTime.parse('2016-07-01T00:00:00+1000'), value: row['FY16'] }
|
18
|
+
@mlf_data[row['TNI']][:loss_factors] << { start: DateTime.parse('2014-07-01T00:00:00+1000'), finish: DateTime.parse('2015-07-01T00:00:00+1000'), value: row['FY15'] }
|
19
|
+
@mlf_data[row['TNI']][:loss_factors] << { start: DateTime.parse('2013-07-01T00:00:00+1000'), finish: DateTime.parse('2014-07-01T00:00:00+1000'), value: row['FY14'] }
|
20
|
+
end
|
21
|
+
|
22
|
+
# TNI to MLF
|
23
|
+
CSV.open(File.join(@path, 'aemo-dlf-dnsp.csv'), headers: true, converters: :numeric).each do |row|
|
24
|
+
@dlf_data[row['dlf_code']] ||= row['nsp_code']
|
25
|
+
end
|
26
|
+
|
27
|
+
# Now to create the DLF and TNI output JSON files for use
|
28
|
+
@files.select { |x| ['aemo-tni.xml', 'aemo-dlf.xml'].include?(x) }.each do |file|
|
29
|
+
output_file = file.gsub('.xml', '.json')
|
30
|
+
output_data = {}
|
31
|
+
open_file = File.open(File.join(@path, file))
|
32
|
+
xml = Nokogiri::XML(open_file) { |c| c.options = Nokogiri::XML::ParseOptions::NOBLANKS }
|
33
|
+
open_file.close
|
34
|
+
|
35
|
+
xml.xpath('//Row').each do |row|
|
36
|
+
row_children = row.children
|
37
|
+
code = row_children.find { |x| x.name == 'Code' }.children.first.text
|
38
|
+
output_data[code] ||= []
|
39
|
+
output_data_instance = {}
|
40
|
+
row_children.each do |row_child|
|
41
|
+
output_data_instance[row_child.name] = row_child.children.first.text
|
42
|
+
end
|
43
|
+
if file =~ /tni/
|
44
|
+
puts "output_data_instance: #{output_data_instance.inspect}"
|
45
|
+
output_data_instance[:mlf_data] = {}
|
46
|
+
unless @mlf_data[code].nil?
|
47
|
+
output_data_instance[:mlf_data] = @mlf_data[code].deep_dup
|
48
|
+
output_data_instance[:mlf_data][:loss_factors] = output_data_instance[:mlf_data][:loss_factors].reject do |x|
|
49
|
+
DateTime.parse(output_data_instance['ToDate']) < x[:start] || DateTime.parse(output_data_instance['FromDate']) >= x[:finish]
|
50
|
+
end
|
51
|
+
puts "output_data_instance[:mlf_data][:loss_factors]: #{output_data_instance[:mlf_data][:loss_factors].inspect}"
|
52
|
+
end
|
53
|
+
elsif file =~ /dlf/
|
54
|
+
output_data_instance[:nsp_code] = @dlf_data[code]
|
55
|
+
end
|
56
|
+
output_data[code] << output_data_instance
|
57
|
+
end
|
58
|
+
|
59
|
+
File.open(File.join(@path, output_file), 'w') do |write_file|
|
60
|
+
write_file.write(output_data.to_json)
|
61
|
+
end
|
62
|
+
end
|