rail_feeds 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -1
- data/.travis.yml +17 -4
- data/CHANGELOG.md +12 -0
- data/README.md +11 -7
- data/doc/guides/National Rail/Knowledge Base/National Service Indicator.md +21 -0
- data/doc/guides/Network Rail/CORPUS.md +7 -3
- data/doc/guides/Network Rail/SMART.md +6 -2
- data/doc/guides/Network Rail/Schedule.md +4 -4
- data/lib/rail_feeds/credentials.rb +30 -0
- data/lib/rail_feeds/http_client.rb +56 -0
- data/lib/rail_feeds/logging.rb +0 -2
- data/lib/rail_feeds/national_rail/credentials.rb +11 -0
- data/lib/rail_feeds/national_rail/http_client.rb +45 -0
- data/lib/rail_feeds/national_rail/knowledge_base/national_service_indicator.rb +100 -0
- data/lib/rail_feeds/national_rail/knowledge_base.rb +8 -0
- data/lib/rail_feeds/national_rail.rb +9 -0
- data/lib/rail_feeds/network_rail/corpus.rb +5 -6
- data/lib/rail_feeds/network_rail/credentials.rb +0 -11
- data/lib/rail_feeds/network_rail/http_client.rb +8 -44
- data/lib/rail_feeds/network_rail/schedule/association.rb +2 -2
- data/lib/rail_feeds/network_rail/schedule/data.rb +3 -2
- data/lib/rail_feeds/network_rail/schedule/days.rb +1 -0
- data/lib/rail_feeds/network_rail/schedule/fetcher.rb +1 -2
- data/lib/rail_feeds/network_rail/schedule/header/json.rb +2 -2
- data/lib/rail_feeds/network_rail/schedule/header.rb +0 -3
- data/lib/rail_feeds/network_rail/schedule/parser/json.rb +0 -2
- data/lib/rail_feeds/network_rail/schedule/parser.rb +1 -4
- data/lib/rail_feeds/network_rail/schedule/tiploc.rb +2 -2
- data/lib/rail_feeds/network_rail/schedule/train_schedule/location.rb +1 -4
- data/lib/rail_feeds/network_rail/schedule/train_schedule.rb +8 -12
- data/lib/rail_feeds/network_rail/schedule.rb +3 -10
- data/lib/rail_feeds/network_rail/smart.rb +17 -17
- data/lib/rail_feeds/network_rail/stomp_client.rb +2 -3
- data/lib/rail_feeds/network_rail.rb +0 -7
- data/lib/rail_feeds/version.rb +1 -1
- data/lib/rail_feeds.rb +40 -4
- data/rail_feeds.gemspec +28 -23
- data/spec/rail_feeds/credentials_spec.rb +28 -1
- data/spec/rail_feeds/http_client_spec.rb +75 -0
- data/spec/rail_feeds/national_rail/credentials_spec.rb +13 -0
- data/spec/rail_feeds/national_rail/http_client_spec.rb +57 -0
- data/spec/rail_feeds/national_rail/knowledge_base/national_service_indicator_spec.rb +122 -0
- data/spec/rail_feeds/national_rail/knowledge_base_spec.rb +4 -0
- data/spec/rail_feeds/national_rail_spec.rb +7 -0
- data/spec/rail_feeds/network_rail/corpus_spec.rb +2 -2
- data/spec/rail_feeds/network_rail/credentials_spec.rb +3 -12
- data/spec/rail_feeds/network_rail/http_client_spec.rb +7 -75
- data/spec/rail_feeds/network_rail/schedule/data_spec.rb +1 -1
- data/spec/rail_feeds/network_rail/smart_spec.rb +2 -2
- data/spec/rail_feeds/network_rail/stomp_client_spec.rb +1 -1
- metadata +48 -9
- data/file +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48c8d73c18dddf2cbe0cdc89764f686112157e85b910ac07140a0718871aa20b
|
4
|
+
data.tar.gz: e27ab1045735dc9e1c69419821af37610c6234ca9ceda8fb7664614e3abdbafd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 58fb1b17bd665d1d1881d143bccbc5f1ee1a743710b28ecd8f88867b52ee4e238bcc438072425bf3098e3142769a9e671aea74796629c5015b82a0bb35c95770
|
7
|
+
data.tar.gz: 0ea4810aa3c8d91de9e7e0a6fad6adf26a7c4c7fbdb35c6a41e9bd47ad365b91be3e9de3ac7ac50baa416edadfa9231703bf25ba66a9085f5c359274fd7d3b7c
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-performance
|
1
3
|
AllCops:
|
2
|
-
TargetRubyVersion: 2.
|
4
|
+
TargetRubyVersion: 2.4
|
5
|
+
Exclude:
|
6
|
+
- '*.gemspec'
|
3
7
|
Style/SignalException:
|
4
8
|
Enabled: false
|
5
9
|
Metrics/MethodLength:
|
@@ -14,6 +18,9 @@ Metrics/LineLength:
|
|
14
18
|
Layout/TrailingBlankLines:
|
15
19
|
Exclude:
|
16
20
|
- rail_feeds.gemspec
|
21
|
+
Style/BracesAroundHashParameters:
|
22
|
+
Exclude:
|
23
|
+
- spec/**/*
|
17
24
|
Style/WordArray:
|
18
25
|
Exclude:
|
19
26
|
- spec/**/*
|
data/.travis.yml
CHANGED
@@ -1,11 +1,24 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 2.4.
|
4
|
-
- 2.4.
|
5
|
-
- 2.4.
|
6
|
-
- 2.4.3
|
3
|
+
- 2.4.4
|
4
|
+
- 2.4.5
|
5
|
+
- 2.4.6
|
7
6
|
- 2.5.0
|
8
7
|
- 2.5.1
|
8
|
+
- 2.5.2
|
9
|
+
- 2.5.3
|
10
|
+
- 2.5.4
|
11
|
+
- 2.5.5
|
12
|
+
- 2.6.0
|
13
|
+
- 2.6.1
|
14
|
+
- 2.6.2
|
15
|
+
- jruby-9.2.0.0
|
16
|
+
- jruby-9.2.1.0
|
17
|
+
- jruby-9.2.3.0
|
18
|
+
- jruby-9.2.4.0
|
19
|
+
- jruby-9.2.4.1
|
20
|
+
- jruby-9.2.5.0
|
21
|
+
- jruby-9.2.6.0
|
9
22
|
gemfile:
|
10
23
|
- Gemfile
|
11
24
|
branches:
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
## Version 0.0.2
|
2
|
+
|
3
|
+
* Add support for:
|
4
|
+
* rubies 2.4.4 - 2.6.2
|
5
|
+
* jrubyies 9.2.0.0 - 9.2.6.0
|
6
|
+
* Sort API consistency - when passing credentials use positional not keywork arguments, except:
|
7
|
+
* Initializing a new HTTP or Stomp client
|
8
|
+
* Initializing a new NetworkRail Schedule Fetcher
|
9
|
+
* Add National rail - Knowledge base - National service indicator via HTTP.
|
10
|
+
* Fix issue 1: OpenURI returns a StringIO not TempFile when size is under 10KB.
|
11
|
+
* Fix "undefined method `parse_cif_file'" when loading Network Rail schedule data.
|
12
|
+
|
1
13
|
## Version 0.0.1
|
2
14
|
|
3
15
|
* Initial release.
|
data/README.md
CHANGED
@@ -19,8 +19,12 @@ and Staging (which is what is currently being developed ready for moving to mast
|
|
19
19
|
## Ruby Versions
|
20
20
|
This gem supports the following versions of ruby, it may work on other versions but is not tested against them so don't rely on it.
|
21
21
|
|
22
|
-
*
|
23
|
-
|
22
|
+
* ruby:
|
23
|
+
* 2.4.4 - 2.4.6
|
24
|
+
* 2.5.0 - 2.5.5
|
25
|
+
* 2.6.0 - 2.6.2
|
26
|
+
* jruby:
|
27
|
+
* 9.2.0.0 - 9.2.6.0
|
24
28
|
|
25
29
|
|
26
30
|
## Rail Feeds
|
@@ -50,10 +54,10 @@ We follow the [Semantic Versioning](http://semver.org/) concept.
|
|
50
54
|
|
51
55
|
### Sources
|
52
56
|
|
53
|
-
| Source | Module | Module Alias | Support
|
54
|
-
| ------------- | ----------------------- | ------------ |
|
55
|
-
| Network Rail | RailFeeds::NetworkRail | NetRailFeeds | Being developed
|
56
|
-
| National Rail | RailFeeds::NationalRail | NatRailFeeds |
|
57
|
+
| Source | Module | Module Alias | Support |
|
58
|
+
| ------------- | ----------------------- | ------------ | --------------- |
|
59
|
+
| Network Rail | RailFeeds::NetworkRail | NetRailFeeds | Being developed |
|
60
|
+
| National Rail | RailFeeds::NationalRail | NatRailFeeds | |
|
57
61
|
|
58
62
|
### Feeds
|
59
63
|
|
@@ -72,6 +76,6 @@ We follow the [Semantic Versioning](http://semver.org/) concept.
|
|
72
76
|
| National Rail | stomp | Darwin Push Port | |
|
73
77
|
| National Rail | stomp | Darwin Timetable Feed | |
|
74
78
|
| National Rail | stomp | Knowledgebase | |
|
75
|
-
| National Rail | http | Knowledgebase |
|
79
|
+
| National Rail | http | Knowledgebase | Can download, fetch and parse NSI. |
|
76
80
|
| National Rail | soap | Darwin Webservice | |
|
77
81
|
| National Rail | rest | Historical Service Performance | |
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# National Rail - National Service Indicator
|
2
|
+
|
3
|
+
The national service indicator provides information on the current service
|
4
|
+
provided by each TOC.
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
# Download the file and then get the data from it
|
8
|
+
RailFeeds::NationalRail::Credentials.configure(
|
9
|
+
username: 'YOUR USERNAME HERE',
|
10
|
+
password: 'YOUR PASSWORD HERE'
|
11
|
+
)
|
12
|
+
RailFeeds::NationalRail::KnowledgeBase::NationalServiceIndicator.download('file name')
|
13
|
+
data = RailFeeds::NationalRail::KnowledgeBase::NationalServiceIndicator.load_file('file name')
|
14
|
+
|
15
|
+
# Get data by fetching it from the web
|
16
|
+
RailFeeds::NationalRail::Credentials.configure(
|
17
|
+
username: 'YOUR USERNAME HERE',
|
18
|
+
password: 'YOUR PASSWORD HERE'
|
19
|
+
)
|
20
|
+
data = RailFeeds::NationalRail::KnowledgeBase::NationalServiceIndicator.fetch_data
|
21
|
+
```
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Network Rail CORPUS (Codes for Operations, Retail & Planning – a Unified Solution)
|
1
|
+
# Network Rail - CORPUS (Codes for Operations, Retail & Planning – a Unified Solution)
|
2
2
|
|
3
3
|
The CORPUS data is a list of indentification information for locations around the network.
|
4
4
|
See <https://wiki.openraildata.com/index.php/Reference_Data#CORPUS:_Location_Reference_Data>
|
@@ -16,13 +16,17 @@ You'll get an array of a data struct with the following attributes:
|
|
16
16
|
|
17
17
|
```ruby
|
18
18
|
# Download the CORPUS data and get the data from it
|
19
|
+
RailFeeds::NetworkRail::Credentials.configure(
|
20
|
+
username: 'YOUR USERNAME HERE',
|
21
|
+
password: 'YOUR PASSWORD HERE'
|
22
|
+
)
|
19
23
|
temp_file = RailFeeds::NetworkRail::CORPUS.fetch
|
20
|
-
data = RailFeeds::NetworkRail::CORPUS.load_file(temp_file
|
24
|
+
data = RailFeeds::NetworkRail::CORPUS.load_file(temp_file)
|
21
25
|
|
22
26
|
# Get data from a previously saved file
|
23
27
|
data = RailFeeds::NetworkRail::CORPUS.load_file('PATH TO FILE.json.gz')
|
24
28
|
|
25
|
-
# Get data from a previously saved and
|
29
|
+
# Get data from a previously saved and unzipped file
|
26
30
|
data = RailFeeds::NetworkRail::CORPUS.load_file('PATH TO FILE.json')
|
27
31
|
|
28
32
|
# Get data by fetching it from the web
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Network Rail SMART
|
1
|
+
# Network Rail - SMART
|
2
2
|
|
3
3
|
The SMART data is a periodically updated list of mappings between train describer berths
|
4
4
|
and locations, allowing for TD events to be translated into arrival/departure from location
|
@@ -18,8 +18,12 @@ reachable in each direction.
|
|
18
18
|
|
19
19
|
```ruby
|
20
20
|
# Download the SMART data and get the data from it
|
21
|
+
RailFeeds::NetworkRail::Credentials.configure(
|
22
|
+
username: 'YOUR USERNAME HERE',
|
23
|
+
password: 'YOUR PASSWORD HERE'
|
24
|
+
)
|
21
25
|
temp_file = RailFeeds::NetworkRail::SMART.fetch
|
22
|
-
step_data = RailFeeds::NetworkRail::SMART.load_file(temp_file
|
26
|
+
step_data = RailFeeds::NetworkRail::SMART.load_file(temp_file)
|
23
27
|
|
24
28
|
# Get the SMART data from a previously saved file
|
25
29
|
step_data = RailFeeds::NetworkRail::SMART.load_file('PATH TO FILE.json.gz')
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Network Rail Schedule
|
1
|
+
# Network Rail - Schedule
|
2
2
|
|
3
3
|
The schedule files from Network Rail contain:
|
4
4
|
|
@@ -36,14 +36,14 @@ RailFeeds::NetworkRail::Credentials.configure(
|
|
36
36
|
# 2. Fetch some files
|
37
37
|
fetcher = RailFeeds::NetworkRail::Schedule::Fetcher.new
|
38
38
|
|
39
|
-
fetcher.fetch_all_full(:cif) do |
|
39
|
+
fetcher.fetch_all_full(:cif) do |full_file_as_an_io|
|
40
40
|
...
|
41
41
|
end
|
42
42
|
|
43
|
-
fetcher.fetch_all_update('fri', :cif) do |
|
43
|
+
fetcher.fetch_all_update('fri', :cif) do |update_file_as_an_io|
|
44
44
|
...
|
45
45
|
end
|
46
|
-
# Each of the file variables will contain
|
46
|
+
# Each of the file variables will contain an IO object which can be used to read
|
47
47
|
# the CIF data (or passed to the parser to make use of). The files will be deleted
|
48
48
|
# at the end of the block.
|
49
49
|
```
|
@@ -34,6 +34,36 @@ module RailFeeds
|
|
34
34
|
@password = password.to_s.clone
|
35
35
|
end
|
36
36
|
|
37
|
+
# Get an array of [username, password].
|
38
|
+
# @return [Array<String>]
|
39
|
+
def to_a
|
40
|
+
[username, password]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get a hash of { username: ?, password: ? }.
|
44
|
+
# @return [Hash<Symbol => String>]
|
45
|
+
def to_h
|
46
|
+
{
|
47
|
+
username: username,
|
48
|
+
password: password
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get an array of [username, password].
|
53
|
+
# @return [Array<String>]
|
54
|
+
def self.to_a
|
55
|
+
[username, password]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Get a hash of { username: ?, password: ? }.
|
59
|
+
# @return [Hash<Symbol => String>]
|
60
|
+
def self.to_h
|
61
|
+
{
|
62
|
+
username: username,
|
63
|
+
password: password
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
37
67
|
def self.username
|
38
68
|
@username.clone
|
39
69
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailFeeds
|
4
|
+
# A wrapper class for ::Net::HTTP
|
5
|
+
class HTTPClient
|
6
|
+
include Logging
|
7
|
+
|
8
|
+
# Initialize a new http client.
|
9
|
+
# @param [RailFeeds::Credentials] credentials
|
10
|
+
# The credentials for connecting to the feed.
|
11
|
+
# @param [Logger] logger
|
12
|
+
# The logger for outputting evetns, if nil the global logger will be used.
|
13
|
+
def initialize(credentials: nil, logger: nil)
|
14
|
+
@credentials = credentials
|
15
|
+
self.logger = logger unless logger.nil?
|
16
|
+
end
|
17
|
+
|
18
|
+
# Fetch path from server.
|
19
|
+
# @param [String] url The URL to fetch.
|
20
|
+
# @yield contents
|
21
|
+
# @yieldparam [IO] file Either a Tempfile or StringIO.
|
22
|
+
def fetch(url, options = {})
|
23
|
+
logger.debug "fetching #{url.inspect}"
|
24
|
+
options[:http_basic_authentication] = @credentials.to_a
|
25
|
+
yield URI(url).open(options)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Fetch path from server and unzip it.
|
29
|
+
# @param [String] url The URL to fetch. For child classes this is just the path.
|
30
|
+
# @yield contents
|
31
|
+
# @yieldparam [Zlib::GzipReader] reader The unzippable content of the file.
|
32
|
+
def fetch_unzipped(url)
|
33
|
+
logger.debug "get_unzipped(#{url.inspect})"
|
34
|
+
fetch(url) do |gz_file|
|
35
|
+
reader = Zlib::GzipReader.new gz_file
|
36
|
+
yield reader
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Download path from server.
|
41
|
+
# @param [String] url The URL to download. For child classes this is just the path.
|
42
|
+
# @param [String] file The path to the file to save the contents in.
|
43
|
+
def download(url, file)
|
44
|
+
logger.debug "download(#{url.inspect}, #{file.inspect})"
|
45
|
+
fetch(url) do |src|
|
46
|
+
File.open(file, 'w') do |dst|
|
47
|
+
IO.copy_stream src, dst
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
attr_reader :credentials
|
55
|
+
end
|
56
|
+
end
|
data/lib/rail_feeds/logging.rb
CHANGED
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailFeeds
|
4
|
+
module NationalRail
|
5
|
+
# A Class to store username & password required to access national rail feeds
|
6
|
+
# Can be used to set a global default but create new instances with
|
7
|
+
# specific ones for a specific use.
|
8
|
+
class Credentials < RailFeeds::Credentials
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailFeeds
|
4
|
+
module NationalRail
|
5
|
+
# A wrapper class for ::Net::HTTP
|
6
|
+
class HTTPClient < RailFeeds::HTTPClient
|
7
|
+
def initialize(credentials: nil, **args)
|
8
|
+
credentials ||= RailFeeds::NationalRail::Credentials
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
# Fetch path from server.
|
13
|
+
# @param [String] path The path to fetch.
|
14
|
+
# @yield contents
|
15
|
+
# @yieldparam [IO] file Either a Tempfile or StringIO.
|
16
|
+
def fetch(path)
|
17
|
+
super "https://datafeeds.nationalrail.co.uk/#{path}", 'X-Auth-Token' => auth_token
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# rubocop:disable Metrics/AbcSize
|
23
|
+
def auth_token
|
24
|
+
return @auth_token if !@auth_token.nil? && @auth_token_expires_at >= Time.now
|
25
|
+
|
26
|
+
logger.info 'Getting an auth token for national rail.'
|
27
|
+
response = Net::HTTP.post_form(
|
28
|
+
URI('https://datafeeds.nationalrail.co.uk/authenticate'),
|
29
|
+
credentials.to_h
|
30
|
+
)
|
31
|
+
response.value # Raise an exception if not successful
|
32
|
+
data = JSON.parse(response.body)
|
33
|
+
logger.debug "Got auth token data: #{data.inspect}"
|
34
|
+
token = data.fetch('token')
|
35
|
+
|
36
|
+
# Token expires in 1 hour. Using 55 minutes provides a safety margin.
|
37
|
+
@auth_token_expires_at = Time.now + (55 * 60)
|
38
|
+
logger.debug "Auth token expires at #{@auth_token_expires_at}."
|
39
|
+
|
40
|
+
@auth_token = token
|
41
|
+
end
|
42
|
+
# rubocop:enable Metrics/AbcSize
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailFeeds
|
4
|
+
module NationalRail
|
5
|
+
module KnowledgeBase
|
6
|
+
# A module for accessing the national service indicator
|
7
|
+
# from the national rail knowledge base.
|
8
|
+
module NationalServiceIndicator
|
9
|
+
TOC = Struct.new(
|
10
|
+
:code, :name, :status, :twitter_account, :additional_info, :service_groups
|
11
|
+
) do
|
12
|
+
def to_s
|
13
|
+
"#{code} - #{name}\n" \
|
14
|
+
"#{status}\n#{service_groups.join("\n")}\n" \
|
15
|
+
"@#{twitter_account} - #{additional_info}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Status = Struct.new(:title, :description, :image) do
|
20
|
+
def to_s
|
21
|
+
"#{title} - #{description} - #{image}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
ServiceGroup = Struct.new(:disruption_id, :name, :detail, :url) do
|
26
|
+
def to_s
|
27
|
+
"#{name} - #{detail}\n#{disruption_id} #{url}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Download the current data.
|
32
|
+
# @param [RailFeeds::NationalRail::Credentials] credentials
|
33
|
+
# @param [String] file
|
34
|
+
# The path to the file to save the .xml download in.
|
35
|
+
def self.download(file, credentials = Credentials)
|
36
|
+
client = HTTPClient.new(credentials: credentials)
|
37
|
+
client.download 'darwin/api/staticfeeds/4.0/serviceIndicators', file
|
38
|
+
end
|
39
|
+
|
40
|
+
# Fetch the current data.
|
41
|
+
# @param [RailFeeds::NationalRail::Credentials] credentials
|
42
|
+
# @return [IO]
|
43
|
+
def self.fetch(credentials = Credentials, &block)
|
44
|
+
client = HTTPClient.new(credentials: credentials)
|
45
|
+
client.fetch 'darwin/api/staticfeeds/4.0/serviceIndicators', &block
|
46
|
+
end
|
47
|
+
|
48
|
+
# Load data from either a .json or .json.gz file.
|
49
|
+
# @param [String] file The path of the file to open.
|
50
|
+
# @return
|
51
|
+
# [Array<RailFeeds::NationalRail::KnowledgeBase::NationalServiceIndicator::TOC>]
|
52
|
+
def self.load_file(file)
|
53
|
+
parse_xml File.read(file)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Load data from the internet.
|
57
|
+
# @param [RailFeeds::NationalRail::Credentials] credentials
|
58
|
+
# The credentials to authenticate with.
|
59
|
+
# @return
|
60
|
+
# [Array<RailFeeds::NationalRail::KnowledgeBase::NationalServiceIndicator::TOC>]
|
61
|
+
def self.fetch_data(credentials = Credentials)
|
62
|
+
fetch(credentials: credentials) do |file|
|
63
|
+
parse_xml file.read
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# rubocop:disable Metrics/AbcSize
|
68
|
+
# rubocop:disable Metrics/MethodLength
|
69
|
+
def self.parse_xml(xml)
|
70
|
+
options = Nokogiri::XML::ParseOptions.new.nonet.noent.noblanks
|
71
|
+
doc = Nokogiri::XML.parse(xml, nil, nil, options)
|
72
|
+
doc.xpath('/xmlns:NSI/xmlns:TOC').map do |toc_node|
|
73
|
+
TOC.new(
|
74
|
+
toc_node.xpath('./xmlns:TocCode').first&.content,
|
75
|
+
toc_node.xpath('./xmlns:TocName').first&.content,
|
76
|
+
Status.new(
|
77
|
+
toc_node.xpath('./xmlns:Status').first&.content,
|
78
|
+
toc_node.xpath('./xmlns:StatusDescription').first&.content,
|
79
|
+
toc_node.xpath('./xmlns:StatusImage').first&.content
|
80
|
+
),
|
81
|
+
toc_node.xpath('./xmlns:TwitterAccount').first&.content,
|
82
|
+
toc_node.xpath('./xmlns:AdditionalInfo').first&.content,
|
83
|
+
toc_node.xpath('./xmlns:ServiceGroup').map do |service_group|
|
84
|
+
ServiceGroup.new(
|
85
|
+
service_group.xpath('./xmlns:CurrentDisruption').first&.content,
|
86
|
+
service_group.xpath('./xmlns:GroupName').first&.content,
|
87
|
+
service_group.xpath('./xmlns:CustomDetail').first&.content,
|
88
|
+
service_group.xpath('./xmlns:CustomURL').first&.content
|
89
|
+
)
|
90
|
+
end
|
91
|
+
)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
# rubocop:enable Metrics/AbcSize
|
95
|
+
# rubocop:enable Metrics/MethodLength
|
96
|
+
private_class_method :parse_xml
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'json'
|
4
|
-
|
5
3
|
module RailFeeds
|
6
4
|
module NetworkRail
|
7
5
|
# A module for getting information out of the CORPUS data.
|
@@ -14,15 +12,15 @@ module RailFeeds
|
|
14
12
|
# @param [RailFeeds::NetworkRail::Credentials] credentials
|
15
13
|
# @param [String] file
|
16
14
|
# The path to the file to save the .json.gz download in.
|
17
|
-
def self.download(file, credentials
|
15
|
+
def self.download(file, credentials = Credentials)
|
18
16
|
client = HTTPClient.new(credentials: credentials)
|
19
17
|
client.download 'ntrod/SupportingFileAuthenticate?type=CORPUS', file
|
20
18
|
end
|
21
19
|
|
22
20
|
# Fetch the current CORPUS data.
|
23
21
|
# @param [RailFeeds::NetworkRail::Credentials] credentials
|
24
|
-
# @return [
|
25
|
-
def self.fetch(credentials
|
22
|
+
# @return [IO]
|
23
|
+
def self.fetch(credentials = Credentials)
|
26
24
|
client = HTTPClient.new(credentials: credentials)
|
27
25
|
client.fetch 'ntrod/SupportingFileAuthenticate?type=CORPUS'
|
28
26
|
end
|
@@ -42,7 +40,7 @@ module RailFeeds
|
|
42
40
|
# @param [RailFeeds::NetworkRail::Credentials] credentials
|
43
41
|
# The credentials to authenticate with.
|
44
42
|
# @return [Array<RailFeeds::NetworkRail::CORPUS::Data>]
|
45
|
-
def self.fetch_data(credentials
|
43
|
+
def self.fetch_data(credentials = Credentials)
|
46
44
|
client = HTTPClient.new(credentials: credentials)
|
47
45
|
client.fetch_unzipped('ntrod/SupportingFileAuthenticate?type=CORPUS') do |file|
|
48
46
|
break parse_json file.read
|
@@ -69,6 +67,7 @@ module RailFeeds
|
|
69
67
|
|
70
68
|
def self.nilify(item)
|
71
69
|
return nil if item.nil? || item.empty?
|
70
|
+
|
72
71
|
item
|
73
72
|
end
|
74
73
|
private_class_method :nilify
|
@@ -6,17 +6,6 @@ module RailFeeds
|
|
6
6
|
# Can be used to set a global default but create new instances with
|
7
7
|
# specific ones for a specific use.
|
8
8
|
class Credentials < RailFeeds::Credentials
|
9
|
-
# Get an array of [username, password].
|
10
|
-
# @return [Array<String>]
|
11
|
-
def to_a
|
12
|
-
[username, password]
|
13
|
-
end
|
14
|
-
|
15
|
-
# Get an array of [username, password].
|
16
|
-
# @return [Array<String>]
|
17
|
-
def self.to_a
|
18
|
-
[username, password]
|
19
|
-
end
|
20
9
|
end
|
21
10
|
end
|
22
11
|
end
|
@@ -1,56 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'open-uri'
|
4
|
-
|
5
3
|
module RailFeeds
|
6
4
|
module NetworkRail
|
7
5
|
# A wrapper class for ::Net::HTTP
|
8
|
-
class HTTPClient
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
# Initialize a new http client.
|
14
|
-
# @param [RailFeeds::NetworkRail::Credentials] credentials
|
15
|
-
# The credentials for connecting to the feed.
|
16
|
-
# @param [Logger] logger
|
17
|
-
# The logger for outputting evetns, if nil the global logger will be used.
|
18
|
-
def initialize(credentials: Credentials, logger: nil)
|
19
|
-
@credentials = credentials
|
20
|
-
self.logger = logger unless logger.nil?
|
6
|
+
class HTTPClient < RailFeeds::HTTPClient
|
7
|
+
def initialize(credentials: nil, **args)
|
8
|
+
credentials ||= RailFeeds::NetworkRail::Credentials
|
9
|
+
super
|
21
10
|
end
|
22
11
|
|
23
|
-
# Fetch path from
|
12
|
+
# Fetch path from server.
|
24
13
|
# @param [String] path The path to fetch.
|
25
|
-
# @yield
|
26
|
-
# @yieldparam [
|
14
|
+
# @yield contents
|
15
|
+
# @yieldparam [IO] file Either a Tempfile or StringIO.
|
27
16
|
def fetch(path)
|
28
|
-
|
29
|
-
uri = URI("https://#{HOST}/#{path}")
|
30
|
-
yield uri.open(http_basic_authentication: @credentials.to_a)
|
31
|
-
end
|
32
|
-
|
33
|
-
# Fetch path from network rail server and unzip it.
|
34
|
-
# @param [String] path The path to fetch.
|
35
|
-
# @yield [reader] Once the block has run the temp file will be deleted.
|
36
|
-
# @yieldparam [Zlib::GzipReader] reader The unzippable content of the file.
|
37
|
-
def fetch_unzipped(path)
|
38
|
-
logger.debug "get_unzipped(#{path.inspect})"
|
39
|
-
fetch(path) do |gz_file|
|
40
|
-
yield Zlib::GzipReader.open(gz_file.path)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# Download path from netwrok rail server.
|
45
|
-
# @param [String] path The path to download.
|
46
|
-
# @param [String] file The path to the file to save the contents in.
|
47
|
-
def download(path, file)
|
48
|
-
logger.debug "download(#{path.inspect}, #{file.inspect})"
|
49
|
-
fetch(path) do |src|
|
50
|
-
File.open(file, 'w') do |dst|
|
51
|
-
IO.copy_stream src, dst
|
52
|
-
end
|
53
|
-
end
|
17
|
+
super "https://datafeeds.networkrail.co.uk/#{path}"
|
54
18
|
end
|
55
19
|
end
|
56
20
|
end
|
@@ -181,7 +181,7 @@ module RailFeeds
|
|
181
181
|
# rubocop:enable Metrics/AbcSize
|
182
182
|
|
183
183
|
# rubocop:disable Metrics/MethodLength
|
184
|
-
def to_json
|
184
|
+
def to_json(**opts)
|
185
185
|
{
|
186
186
|
'JsonAssociationV1' => {
|
187
187
|
'transaction_type' => 'Create',
|
@@ -198,7 +198,7 @@ module RailFeeds
|
|
198
198
|
'diagram_type' => 'T',
|
199
199
|
'CIF_stp_indicator' => stp_indicator_to_cif
|
200
200
|
}
|
201
|
-
}.to_json
|
201
|
+
}.to_json(**opts)
|
202
202
|
end
|
203
203
|
# rubocop:enable Metrics/MethodLength
|
204
204
|
end
|
@@ -52,7 +52,7 @@ module RailFeeds
|
|
52
52
|
# @param [IO] file
|
53
53
|
# The file to load data from.
|
54
54
|
def load_cif_file(file)
|
55
|
-
@parser.
|
55
|
+
@parser.parse_file file
|
56
56
|
|
57
57
|
logger.info "Currently have #{associations.count} associations, " \
|
58
58
|
"#{tiplocs.count} tiplocs, #{trains.count} trains."
|
@@ -92,7 +92,7 @@ module RailFeeds
|
|
92
92
|
# The credentials for connecting to the feed.
|
93
93
|
# @return [RailFeeds::NetworkRail::Schedule::Header::CIF]
|
94
94
|
# The header of the last file added.
|
95
|
-
def fetch_data(credentials
|
95
|
+
def fetch_data(credentials = Credentials)
|
96
96
|
fetcher = Fetcher.new credentials: credentials
|
97
97
|
|
98
98
|
method = if last_header.nil? ||
|
@@ -198,6 +198,7 @@ module RailFeeds
|
|
198
198
|
trains[train_schedule.uid] ||= []
|
199
199
|
index = trains[train_schedule.uid].index train_schedule
|
200
200
|
return do_train_schedule_create(parser, train_schedule) if index.nil?
|
201
|
+
|
201
202
|
trains[train_schedule.uid][index] = train_schedule
|
202
203
|
end
|
203
204
|
|