rail_feeds 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +3 -0
- data/.rubocop.yml +31 -0
- data/.travis.yml +26 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +6 -0
- data/Guardfile +25 -0
- data/LICENSE.md +32 -0
- data/README.md +77 -0
- data/Rakefile +3 -0
- data/doc/guides/Logging.md +13 -0
- data/doc/guides/Network Rail/CORPUS.md +34 -0
- data/doc/guides/Network Rail/SMART.md +39 -0
- data/doc/guides/Network Rail/Schedule.md +138 -0
- data/file +0 -0
- data/lib/rail_feeds/credentials.rb +45 -0
- data/lib/rail_feeds/logging.rb +51 -0
- data/lib/rail_feeds/network_rail/corpus.rb +77 -0
- data/lib/rail_feeds/network_rail/credentials.rb +22 -0
- data/lib/rail_feeds/network_rail/http_client.rb +57 -0
- data/lib/rail_feeds/network_rail/schedule/association.rb +208 -0
- data/lib/rail_feeds/network_rail/schedule/data.rb +215 -0
- data/lib/rail_feeds/network_rail/schedule/days.rb +95 -0
- data/lib/rail_feeds/network_rail/schedule/fetcher.rb +193 -0
- data/lib/rail_feeds/network_rail/schedule/header/cif.rb +102 -0
- data/lib/rail_feeds/network_rail/schedule/header/json.rb +79 -0
- data/lib/rail_feeds/network_rail/schedule/header.rb +22 -0
- data/lib/rail_feeds/network_rail/schedule/parser/cif.rb +141 -0
- data/lib/rail_feeds/network_rail/schedule/parser/json.rb +87 -0
- data/lib/rail_feeds/network_rail/schedule/parser.rb +108 -0
- data/lib/rail_feeds/network_rail/schedule/stp_indicator.rb +72 -0
- data/lib/rail_feeds/network_rail/schedule/tiploc.rb +100 -0
- data/lib/rail_feeds/network_rail/schedule/train_schedule/change_en_route.rb +158 -0
- data/lib/rail_feeds/network_rail/schedule/train_schedule/location/intermediate.rb +119 -0
- data/lib/rail_feeds/network_rail/schedule/train_schedule/location/origin.rb +91 -0
- data/lib/rail_feeds/network_rail/schedule/train_schedule/location/terminating.rb +72 -0
- data/lib/rail_feeds/network_rail/schedule/train_schedule/location.rb +76 -0
- data/lib/rail_feeds/network_rail/schedule/train_schedule.rb +392 -0
- data/lib/rail_feeds/network_rail/schedule.rb +33 -0
- data/lib/rail_feeds/network_rail/smart.rb +186 -0
- data/lib/rail_feeds/network_rail/stomp_client.rb +77 -0
- data/lib/rail_feeds/network_rail.rb +16 -0
- data/lib/rail_feeds/version.rb +14 -0
- data/lib/rail_feeds.rb +10 -0
- data/rail_feeds.gemspec +32 -0
- data/spec/fixtures/network_rail/schedule/data/full.yaml +60 -0
- data/spec/fixtures/network_rail/schedule/data/starting.yaml +131 -0
- data/spec/fixtures/network_rail/schedule/data/update-gap.yaml +10 -0
- data/spec/fixtures/network_rail/schedule/data/update-next.yaml +13 -0
- data/spec/fixtures/network_rail/schedule/data/update-old.yaml +10 -0
- data/spec/fixtures/network_rail/schedule/data/update.yaml +112 -0
- data/spec/fixtures/network_rail/schedule/parser/train_create.json +1 -0
- data/spec/fixtures/network_rail/schedule/parser/train_delete.json +1 -0
- data/spec/fixtures/network_rail/schedule/train_schedule/json-data.yaml +67 -0
- data/spec/rail_feeds/credentials_spec.rb +46 -0
- data/spec/rail_feeds/logging_spec.rb +81 -0
- data/spec/rail_feeds/network_rail/corpus_spec.rb +92 -0
- data/spec/rail_feeds/network_rail/credentials_spec.rb +22 -0
- data/spec/rail_feeds/network_rail/http_client_spec.rb +88 -0
- data/spec/rail_feeds/network_rail/schedule/association_spec.rb +205 -0
- data/spec/rail_feeds/network_rail/schedule/data_spec.rb +219 -0
- data/spec/rail_feeds/network_rail/schedule/days_shared.rb +99 -0
- data/spec/rail_feeds/network_rail/schedule/days_spec.rb +4 -0
- data/spec/rail_feeds/network_rail/schedule/fetcher_spec.rb +228 -0
- data/spec/rail_feeds/network_rail/schedule/header/cif_spec.rb +72 -0
- data/spec/rail_feeds/network_rail/schedule/header/json_spec.rb +51 -0
- data/spec/rail_feeds/network_rail/schedule/header_spec.rb +19 -0
- data/spec/rail_feeds/network_rail/schedule/parser/cif_spec.rb +197 -0
- data/spec/rail_feeds/network_rail/schedule/parser/json_spec.rb +172 -0
- data/spec/rail_feeds/network_rail/schedule/parser_spec.rb +34 -0
- data/spec/rail_feeds/network_rail/schedule/stp_indicator_shared.rb +49 -0
- data/spec/rail_feeds/network_rail/schedule/stp_indicator_spec.rb +4 -0
- data/spec/rail_feeds/network_rail/schedule/tiploc_spec.rb +77 -0
- data/spec/rail_feeds/network_rail/schedule/train_schedule/change_en_route_spec.rb +121 -0
- data/spec/rail_feeds/network_rail/schedule/train_schedule/location/intermediate_spec.rb +95 -0
- data/spec/rail_feeds/network_rail/schedule/train_schedule/location/origin_spec.rb +87 -0
- data/spec/rail_feeds/network_rail/schedule/train_schedule/location/terminating_spec.rb +81 -0
- data/spec/rail_feeds/network_rail/schedule/train_schedule/location_spec.rb +35 -0
- data/spec/rail_feeds/network_rail/schedule/train_schedule_spec.rb +284 -0
- data/spec/rail_feeds/network_rail/schedule_spec.rb +41 -0
- data/spec/rail_feeds/network_rail/smart_spec.rb +194 -0
- data/spec/rail_feeds/network_rail/stomp_client_spec.rb +151 -0
- data/spec/rail_feeds/network_rail_spec.rb +7 -0
- data/spec/rail_feeds_spec.rb +11 -0
- data/spec/spec_helper.rb +47 -0
- metadata +282 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailFeeds
|
4
|
+
module NetworkRail
|
5
|
+
module Schedule
|
6
|
+
class TrainSchedule
|
7
|
+
class Location
|
8
|
+
# A class for holding info about a particular train's terminating location
|
9
|
+
class Terminating < Location
|
10
|
+
# @!attribute [rw] path
|
11
|
+
# @return [String]
|
12
|
+
# @!attribute [rw] scheduled_arrival
|
13
|
+
# @return [String] The scheduled time for arriving at the location.
|
14
|
+
# @!attribute [rw] public_arrival
|
15
|
+
# @return [String] The public arrival time (HHMM).
|
16
|
+
|
17
|
+
attr_accessor :scheduled_arrival, :public_arrival, :path
|
18
|
+
|
19
|
+
def initialize(**attributes)
|
20
|
+
attributes.each do |attribute, value|
|
21
|
+
send "#{attribute}=", value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# rubocop:disable Metrics/AbcSize
|
26
|
+
# Initialize a new terminating from a CIF file line
|
27
|
+
def self.from_cif(line)
|
28
|
+
fail ArgumentError, "Invalid line:\n#{line}" unless line[0..1].eql?('LT')
|
29
|
+
|
30
|
+
new(
|
31
|
+
tiploc: line[2..8].strip,
|
32
|
+
tiploc_suffix: line[9].to_i,
|
33
|
+
scheduled_arrival: line[10..14].strip,
|
34
|
+
public_arrival: line[15..18].strip,
|
35
|
+
platform: line[19..21].strip,
|
36
|
+
path: line[22..24].strip,
|
37
|
+
activity: line[25..36].strip
|
38
|
+
)
|
39
|
+
end
|
40
|
+
# rubocop:enable Metrics/AbcSize
|
41
|
+
|
42
|
+
def to_cif
|
43
|
+
format('%-80.80s', [
|
44
|
+
'LT',
|
45
|
+
format('%-7.7s', tiploc),
|
46
|
+
format('%-1.1s', tiploc_suffix),
|
47
|
+
format('%-5.5s', scheduled_arrival),
|
48
|
+
format('%-4.4s', public_arrival),
|
49
|
+
format('%-3.3s', platform),
|
50
|
+
format('%-3.3s', path),
|
51
|
+
format('%-12.12s', activity)
|
52
|
+
].join) + "\n"
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_hash_for_json
|
56
|
+
{
|
57
|
+
location_type: 'LT',
|
58
|
+
record_identity: 'LT',
|
59
|
+
tiploc_code: tiploc,
|
60
|
+
tiploc_instance: tiploc_suffix,
|
61
|
+
arrival: scheduled_arrival,
|
62
|
+
public_arrival: public_arrival,
|
63
|
+
platform: platform,
|
64
|
+
path: path
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'location/intermediate'
|
4
|
+
require_relative 'location/origin'
|
5
|
+
require_relative 'location/terminating'
|
6
|
+
|
7
|
+
module RailFeeds
|
8
|
+
module NetworkRail
|
9
|
+
module Schedule
|
10
|
+
class TrainSchedule
|
11
|
+
# A class for holding information about a particular train's particular location.
|
12
|
+
class Location
|
13
|
+
# @!attribute [rw] tiploc
|
14
|
+
# @return [String] The location where the change occurs.
|
15
|
+
# @!attribute [rw] tiploc_suffix
|
16
|
+
# @return [String]
|
17
|
+
# @!attribute [rw] platform
|
18
|
+
# @return [String]
|
19
|
+
# @!attribute [rw] activity
|
20
|
+
# @return [String]
|
21
|
+
|
22
|
+
attr_accessor :tiploc, :tiploc_suffix, :platform, :activity
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
fail 'This class should never be instantiated'
|
26
|
+
end
|
27
|
+
|
28
|
+
# Initialize a new location from a CIF file line
|
29
|
+
# (will be of the appropriate sub class)
|
30
|
+
def self.from_cif(line)
|
31
|
+
case line[0..1]
|
32
|
+
when 'LO'
|
33
|
+
Origin.from_cif line
|
34
|
+
when 'LI'
|
35
|
+
Intermediate.from_cif line
|
36
|
+
when 'LT'
|
37
|
+
Terminating.from_cif line
|
38
|
+
else
|
39
|
+
fail ArgumentError, "Improper line type #{line[0..1]}: #{line}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def ==(other)
|
44
|
+
hash == other&.hash
|
45
|
+
end
|
46
|
+
|
47
|
+
def hash
|
48
|
+
"#{tiploc}-#{tiploc_suffix}"
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def self.parse_allowance(value)
|
54
|
+
half = value[-1].eql?('H')
|
55
|
+
value = value.to_f
|
56
|
+
half ? value + 0.5 : value
|
57
|
+
end
|
58
|
+
private_class_method :parse_allowance
|
59
|
+
|
60
|
+
def allowance_cif(value)
|
61
|
+
i = value.to_i
|
62
|
+
f = value.to_f - i
|
63
|
+
f.eql?(0.5) ? "#{i}H" : i.to_s
|
64
|
+
end
|
65
|
+
|
66
|
+
def allowance_json(value)
|
67
|
+
return nil if value.nil?
|
68
|
+
i = value.to_i
|
69
|
+
f = value.to_f - i
|
70
|
+
f.eql?(0.5) ? "#{i}H" : i.to_s
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,392 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
require_relative 'train_schedule/change_en_route'
|
7
|
+
require_relative 'train_schedule/location'
|
8
|
+
|
9
|
+
module RailFeeds
|
10
|
+
module NetworkRail
|
11
|
+
module Schedule
|
12
|
+
# rubocop:disable Metrics/ClassLength
|
13
|
+
# A class for holding information about a particular train
|
14
|
+
class TrainSchedule
|
15
|
+
include Comparable
|
16
|
+
include Schedule::Days
|
17
|
+
include Schedule::STPIndicator
|
18
|
+
|
19
|
+
# @!attribute [rw] uid
|
20
|
+
# @return [String] The unique train identifier (letter then 5 numbers).
|
21
|
+
# Along with start_date uniquely identifies a schedule.
|
22
|
+
# @!attribute [rw] category
|
23
|
+
# @return [String]
|
24
|
+
# @!attribute [rw] status
|
25
|
+
# @return [String]
|
26
|
+
# @!attribute [rw] stp_indicator
|
27
|
+
# @return [String]
|
28
|
+
# * C - cancellation of permanent schedule
|
29
|
+
# * N - new STP schedule
|
30
|
+
# * O - STP overlay of permanent schedule
|
31
|
+
# * P - permanent
|
32
|
+
# @!attribute [rw] reservation_headcode
|
33
|
+
# @return [Integer, nil] The train's headcode in the NRS
|
34
|
+
# (National Reservation System).
|
35
|
+
# @!attribute [rw] signalling_headcode
|
36
|
+
# @return [String, nil] The headcode used in signalling the service.
|
37
|
+
# Will be nil for annonymous freight services.
|
38
|
+
# @!attribute [rw] service_code
|
39
|
+
# @return [Integer] Used for attribution of revenue.
|
40
|
+
# @!attribute [rw] start_date
|
41
|
+
# @return [Date] When the schedule starts.
|
42
|
+
# @!attribute [rw] end_date
|
43
|
+
# @return [Date] When the schedule ends.
|
44
|
+
# @!attribute [rw] days
|
45
|
+
# @return [Array<Boolean>] The days on which the service runs.
|
46
|
+
# [Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday]
|
47
|
+
# @!attribute [rw] bank_holiday_running
|
48
|
+
# @return [String, nil] Whether the service runs on bank holidays.
|
49
|
+
# * X - does not run on specified bank holiday Mondays.
|
50
|
+
# * G - does not run on Glasgow bank holidays.
|
51
|
+
# @!attribute [rw] power_type
|
52
|
+
# @return [String]
|
53
|
+
# @!attribute [rw] timing_load
|
54
|
+
# @return [String, nil]
|
55
|
+
# @!attribute [rw] speed
|
56
|
+
# @return [Integer] The planned speed (miles per hour).
|
57
|
+
# @!attribute [rw] operating_characteristics
|
58
|
+
# @return [String, nil]
|
59
|
+
# @!attribute [rw] seating_class
|
60
|
+
# @return [String, nil] The seating classes available.
|
61
|
+
# * B or blank - First and Standard
|
62
|
+
# * S - Standard only
|
63
|
+
# @!attribute [rw] sleeping_class
|
64
|
+
# @return [String, nil] The sleeping classes available.
|
65
|
+
# * B - First and Standard
|
66
|
+
# * F - First only
|
67
|
+
# * S - Standard only
|
68
|
+
# @!attribute [rw] reservations
|
69
|
+
# @return [String, nil] The reservation recommendations.
|
70
|
+
# * A - Reservations Compulsory
|
71
|
+
# * E - Reservations for bicycles essential
|
72
|
+
# * R - Reservations recommended
|
73
|
+
# * S - Reservations possible from any station
|
74
|
+
# * W - Wheelchair only reservations
|
75
|
+
# @!attribute [rw] catering
|
76
|
+
# @return [String, nil] The catering available, any 2 of:
|
77
|
+
# * H - Hot food available
|
78
|
+
# * C - Buffet service
|
79
|
+
# * R - Restaurant
|
80
|
+
# * F - Restaurant for first class passengers
|
81
|
+
# * M - Meal included for first class passengers
|
82
|
+
# * T - Trolly service
|
83
|
+
# @!attribute [rw] branding
|
84
|
+
# @return [String, nil] The service brand.
|
85
|
+
# * E - Eurostar
|
86
|
+
# @!attribute [rw] uic
|
87
|
+
# @return [Integer, nil] For train services running to/from continental Europe.
|
88
|
+
# @!attribute [rw] atoc
|
89
|
+
# @!attribute [rw] applicable_timetable
|
90
|
+
# @return [Boolean, nil] Whether the service is subject to
|
91
|
+
# performance monitoring (Applicable Timetable Service).
|
92
|
+
# @!attribute [rw] journey
|
93
|
+
# @return [Array<Location, ChangeEnRoute>] The combined locations and changes.
|
94
|
+
# change will precede location.
|
95
|
+
|
96
|
+
attr_accessor :uid, :category, :status,
|
97
|
+
:reservation_headcode, :signalling_headcode, :service_code,
|
98
|
+
:start_date, :end_date, :bank_holiday_running,
|
99
|
+
:power_type, :timing_load, :speed, :operating_characteristics,
|
100
|
+
:seating_class, :sleeping_class, :reservations, :catering,
|
101
|
+
:branding, :uic, :atoc, :applicable_timetable, :journey
|
102
|
+
# Attributes from modules :days, :stp_indicator
|
103
|
+
|
104
|
+
# Initialize a new train
|
105
|
+
def initialize(**attributes)
|
106
|
+
@journey = []
|
107
|
+
attributes.each do |attribute, value|
|
108
|
+
send "#{attribute}=", value
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Add details from a CIF schedule file line to this train schedule
|
113
|
+
def update_from_cif(line)
|
114
|
+
type = line[0..1]
|
115
|
+
|
116
|
+
if type.eql?('BS')
|
117
|
+
update_basic_information line
|
118
|
+
elsif type.eql?('BX')
|
119
|
+
update_extra_information line
|
120
|
+
|
121
|
+
elsif %w[LO LI LT].include?(type)
|
122
|
+
journey.push Location.from_cif(line)
|
123
|
+
|
124
|
+
elsif type.eql?('CR')
|
125
|
+
journey.push ChangeEnRoute.from_cif(line)
|
126
|
+
|
127
|
+
else
|
128
|
+
fail ArgumentError, "Improper line type #{line[0..1]}: #{line}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# rubocop:disable Metrics/AbcSize
|
133
|
+
# rubocop:disable Metrics/LineLength
|
134
|
+
# rubocop:disable Metrics/MethodLength
|
135
|
+
# Create a new train schedule from json data.
|
136
|
+
def self.from_json(json)
|
137
|
+
data = ::JSON.parse(json)['JsonScheduleV1']
|
138
|
+
|
139
|
+
locations = data.dig('schedule_segment', 'schedule_location')&.map do |location|
|
140
|
+
location_from_json location
|
141
|
+
end
|
142
|
+
|
143
|
+
new(
|
144
|
+
uid: data['CIF_train_uid'],
|
145
|
+
category: Schedule.nil_or_strip(data.dig('schedule_segment', 'CIF_train_category')),
|
146
|
+
status: Schedule.nil_or_strip(data['train_status']),
|
147
|
+
reservation_headcode: Schedule.nil_or_i(data.dig('schedule_segment', 'CIF_headcode')),
|
148
|
+
signalling_headcode: Schedule.nil_or_strip(data.dig('schedule_segment', 'signalling_id')),
|
149
|
+
service_code: Schedule.nil_or_strip(data.dig('schedule_segment', 'CIF_train_service_code')),
|
150
|
+
start_date: Date.parse(data['schedule_start_date']),
|
151
|
+
end_date: data['schedule_end_date'] ? Date.parse(data['schedule_end_date']) : nil,
|
152
|
+
days: days_from_cif(data['schedule_days_runs']),
|
153
|
+
bank_holiday_running: Schedule.nil_or_strip(data['CIF_bank_holiday_running']),
|
154
|
+
power_type: Schedule.nil_or_strip(data.dig('schedule_segment', 'CIF_power_type')),
|
155
|
+
timing_load: Schedule.nil_or_strip(data.dig('schedule_segment', 'CIF_timing_load')),
|
156
|
+
speed: Schedule.nil_or_i(data.dig('schedule_segment', 'CIF_speed')),
|
157
|
+
operating_characteristics: Schedule.nil_or_strip(data.dig('schedule_segment', 'CIF_operating_characteristics')),
|
158
|
+
seating_class: Schedule.nil_or_strip(data.dig('schedule_segment', 'CIF_train_class')),
|
159
|
+
sleeping_class: Schedule.nil_or_strip(data.dig('schedule_segment', 'CIF_sleepers')),
|
160
|
+
reservations: Schedule.nil_or_strip(data.dig('schedule_segment', 'CIF_reservations')),
|
161
|
+
catering: Schedule.nil_or_strip(data.dig('schedule_segment', 'CIF_catering_code')),
|
162
|
+
branding: Schedule.nil_or_strip(data.dig('schedule_segment', 'CIF_service_branding')),
|
163
|
+
uic: Schedule.nil_or_i(data.dig('new_schedule_segment', 'uic_code')),
|
164
|
+
atoc: data['atoc_code'],
|
165
|
+
applicable_timetable: data['applicable_timetable'].eql?('Y'),
|
166
|
+
stp_indicator: stp_indicator_from_cif(data['CIF_stp_indicator']),
|
167
|
+
journey: locations
|
168
|
+
)
|
169
|
+
end
|
170
|
+
# rubocop:enable Metrics/AbcSize
|
171
|
+
# rubocop:enable Metrics/LineLength
|
172
|
+
# rubocop:enable Metrics/MethodLength
|
173
|
+
|
174
|
+
def to_cif
|
175
|
+
[
|
176
|
+
basic_to_cif,
|
177
|
+
extra_to_cif,
|
178
|
+
*journey.map(&:to_cif)
|
179
|
+
].join
|
180
|
+
end
|
181
|
+
|
182
|
+
# rubocop:disable Metrics/AbcSize
|
183
|
+
# rubocop:disable Metrics/MethodLength
|
184
|
+
def to_json
|
185
|
+
{
|
186
|
+
'JsonScheduleV1' => {
|
187
|
+
'CIF_bank_holiday_running' => bank_holiday_running,
|
188
|
+
'CIF_stp_indicator' => stp_indicator_to_cif,
|
189
|
+
'CIF_train_uid' => uid,
|
190
|
+
'applicable_timetable' => (applicable_timetable ? 'Y' : 'N'),
|
191
|
+
'atoc_code' => atoc,
|
192
|
+
'schedule_start_date' => start_date.strftime('%Y-%m-%d'),
|
193
|
+
'train_status' => status,
|
194
|
+
'transaction_type' => 'Create',
|
195
|
+
'schedule_days_runs' => days_to_cif,
|
196
|
+
'schedule_end_date' => end_date.strftime('%Y-%m-%d'),
|
197
|
+
'new_schedule_segment' => {
|
198
|
+
'traction_class' => '',
|
199
|
+
'uic_code' => uic.to_s
|
200
|
+
},
|
201
|
+
'schedule_segment' => {
|
202
|
+
'signalling_id' => signalling_headcode,
|
203
|
+
'CIF_train_category' => category,
|
204
|
+
'CIF_headcode' => reservation_headcode.to_s,
|
205
|
+
'CIF_course_indicator' => nil,
|
206
|
+
'CIF_train_service_code' => service_code,
|
207
|
+
'CIF_business_sector' => '??',
|
208
|
+
'CIF_power_type' => power_type,
|
209
|
+
'CIF_timing_load' => timing_load,
|
210
|
+
'CIF_speed' => speed.to_s.rjust(3, '0'),
|
211
|
+
'CIF_operating_characteristics' => operating_characteristics,
|
212
|
+
'CIF_train_class' => seating_class,
|
213
|
+
'CIF_sleepers' => sleeping_class,
|
214
|
+
'CIF_reservations' => reservations,
|
215
|
+
'CIF_connection_indicator' => nil,
|
216
|
+
'CIF_catering_code' => catering,
|
217
|
+
'CIF_service_branding' => branding,
|
218
|
+
'schedule_location' => journey.map(&:to_hash_for_json).reject(&:nil?)
|
219
|
+
}
|
220
|
+
}
|
221
|
+
}.to_json
|
222
|
+
end
|
223
|
+
# rubocop:enable Metrics/AbcSize
|
224
|
+
# rubocop:enable Metrics/MethodLength
|
225
|
+
|
226
|
+
def hash
|
227
|
+
"#{uid}-#{start_date&.strftime('%Y%m%d')}"
|
228
|
+
end
|
229
|
+
|
230
|
+
def ==(other)
|
231
|
+
hash == other&.hash
|
232
|
+
end
|
233
|
+
|
234
|
+
def <=>(other)
|
235
|
+
values = [start_date, uid]
|
236
|
+
other_values = [other&.start_date, other&.uid]
|
237
|
+
values <=> other_values
|
238
|
+
end
|
239
|
+
|
240
|
+
private
|
241
|
+
|
242
|
+
# rubocop:disable Metrics/AbcSize
|
243
|
+
# rubocop:disable Metrics/MethodLength
|
244
|
+
def update_basic_information(line)
|
245
|
+
self.uid = line[3..8].strip
|
246
|
+
self.start_date = Schedule.make_date line[9..14]
|
247
|
+
self.end_date = Schedule.make_date line[15..20], allow_nil: line[2].eql?('D')
|
248
|
+
self.days = days_from_cif line[21..27]
|
249
|
+
self.bank_holiday_running = Schedule.nil_or_strip line[28]
|
250
|
+
self.status = Schedule.nil_or_strip(line[29])
|
251
|
+
self.category = Schedule.nil_or_strip(line[30..31])
|
252
|
+
self.signalling_headcode = Schedule.nil_or_strip line[32..35]
|
253
|
+
self.reservation_headcode = Schedule.nil_or_i line[36..39]
|
254
|
+
self.service_code = Schedule.nil_or_i line[41..48]
|
255
|
+
self.power_type = Schedule.nil_or_strip(line[50..52])
|
256
|
+
self.timing_load = Schedule.nil_or_strip line[53..56].strip
|
257
|
+
self.speed = Schedule.nil_or_i(line[57..59])
|
258
|
+
self.operating_characteristics = Schedule.nil_or_strip line[60..65].strip
|
259
|
+
self.seating_class = Schedule.nil_or_strip line[66]
|
260
|
+
self.sleeping_class = Schedule.nil_or_strip line[67]
|
261
|
+
self.reservations = Schedule.nil_or_strip line[68]
|
262
|
+
self.catering = Schedule.nil_or_strip(line[70..73])
|
263
|
+
self.branding = Schedule.nil_or_strip line[74..77].strip
|
264
|
+
self.stp_indicator = stp_indicator_from_cif line[79]
|
265
|
+
end
|
266
|
+
# rubocop:enable Metrics/AbcSize
|
267
|
+
# rubocop:enable Metrics/MethodLength
|
268
|
+
|
269
|
+
def update_extra_information(line)
|
270
|
+
self.uic = Schedule.nil_or_i line[6..10]
|
271
|
+
self.atoc = line[11..12]
|
272
|
+
self.applicable_timetable = line[13].eql?('Y')
|
273
|
+
end
|
274
|
+
|
275
|
+
# rubocop:disable Metrics/AbcSize
|
276
|
+
# rubocop:disable Metrics/MethodLength
|
277
|
+
def basic_to_cif
|
278
|
+
format('%-80.80s', [
|
279
|
+
'BSN',
|
280
|
+
format('%-6.6s', uid),
|
281
|
+
# rubocop:disable Style/FormatStringToken
|
282
|
+
format('%-6.6s', start_date&.strftime('%y%m%d')),
|
283
|
+
format('%-6.6s', end_date&.strftime('%y%m%d')),
|
284
|
+
# rubocop:enable Style/FormatStringToken
|
285
|
+
days_to_cif,
|
286
|
+
format('%-1.1s', bank_holiday_running),
|
287
|
+
format('%-1.1s', status),
|
288
|
+
format('%-2.2s', category),
|
289
|
+
format('%-4.4s', signalling_headcode),
|
290
|
+
format('%-4.4s', reservation_headcode),
|
291
|
+
'1',
|
292
|
+
format('%-8.8s', service_code),
|
293
|
+
' ',
|
294
|
+
format('%-3.3s', power_type),
|
295
|
+
format('%-4.4s', timing_load),
|
296
|
+
format('%-3.3s', speed),
|
297
|
+
format('%-6.6s', operating_characteristics),
|
298
|
+
format('%-1.1s', seating_class),
|
299
|
+
format('%-1.1s', sleeping_class),
|
300
|
+
format('%-1.1s', reservations),
|
301
|
+
' ',
|
302
|
+
format('%-4.4s', catering),
|
303
|
+
format('%-4.4s', branding),
|
304
|
+
' ',
|
305
|
+
stp_indicator_to_cif
|
306
|
+
].join) + "\n"
|
307
|
+
end
|
308
|
+
# rubocop:enable Metrics/AbcSize
|
309
|
+
# rubocop:enable Metrics/MethodLength
|
310
|
+
|
311
|
+
def extra_to_cif
|
312
|
+
format('%-80.80s', [
|
313
|
+
'BX ',
|
314
|
+
format('%-5.5s', uic),
|
315
|
+
format('%-2.2s', atoc),
|
316
|
+
format('%-1.1s', (applicable_timetable ? 'Y' : 'N'))
|
317
|
+
].join) + "\n"
|
318
|
+
end
|
319
|
+
|
320
|
+
def self.location_from_json(hash)
|
321
|
+
return lo_from_json(hash) if hash['record_identity'].eql?('LO')
|
322
|
+
return li_from_json(hash) if hash['record_identity'].eql?('LI')
|
323
|
+
return lt_from_json(hash) if hash['record_identity'].eql?('LT')
|
324
|
+
nil
|
325
|
+
end
|
326
|
+
private_class_method :location_from_json
|
327
|
+
|
328
|
+
def self.lo_from_json(hash)
|
329
|
+
Location::Origin.new(
|
330
|
+
tiploc: hash['tiploc_code'],
|
331
|
+
tiploc_suffix: hash['tiploc_instance'],
|
332
|
+
platform: Schedule.nil_or_strip(hash['platform']),
|
333
|
+
activity: :unknown,
|
334
|
+
scheduled_departure: hash['departure'],
|
335
|
+
public_departure: hash['public_departure'],
|
336
|
+
line: Schedule.nil_or_strip(hash['line']),
|
337
|
+
engineering_allowance: parse_allowance(hash['engineering_allowance']),
|
338
|
+
pathing_allowance: parse_allowance(hash['pathing_allowance']),
|
339
|
+
performance_allowance: parse_allowance(hash['performance_allowance'])
|
340
|
+
)
|
341
|
+
end
|
342
|
+
private_class_method :lo_from_json
|
343
|
+
|
344
|
+
# rubocop:disable Metrics/AbcSize
|
345
|
+
# rubocop:disable Metrics/MethodLength
|
346
|
+
def self.li_from_json(hash)
|
347
|
+
Location::Intermediate.new(
|
348
|
+
tiploc: hash['tiploc_code'],
|
349
|
+
tiploc_suffix: hash['tiploc_instance'],
|
350
|
+
platform: Schedule.nil_or_strip(hash['platform']),
|
351
|
+
activity: :unknown,
|
352
|
+
scheduled_arrival: hash['arrival'],
|
353
|
+
public_arrival: hash['public_arrival'],
|
354
|
+
scheduled_departure: hash['departure'],
|
355
|
+
public_departure: hash['public_departure'],
|
356
|
+
scheduled_pass: hash['pass'],
|
357
|
+
line: Schedule.nil_or_strip(hash['line']),
|
358
|
+
path: Schedule.nil_or_strip(hash['path']),
|
359
|
+
engineering_allowance: parse_allowance(hash['engineering_allowance']),
|
360
|
+
pathing_allowance: parse_allowance(hash['pathing_allowance']),
|
361
|
+
performance_allowance: parse_allowance(hash['performance_allowance'])
|
362
|
+
)
|
363
|
+
end
|
364
|
+
private_class_method :li_from_json
|
365
|
+
# rubocop:enable Metrics/MethodLength
|
366
|
+
# rubocop:enable Metrics/AbcSize
|
367
|
+
|
368
|
+
def self.lt_from_json(hash)
|
369
|
+
Location::Terminating.new(
|
370
|
+
tiploc: hash['tiploc_code'],
|
371
|
+
tiploc_suffix: hash['tiploc_instance'],
|
372
|
+
platform: Schedule.nil_or_strip(hash['platform']),
|
373
|
+
activity: :unknown,
|
374
|
+
scheduled_arrival: hash['arrival'],
|
375
|
+
public_arrival: hash['public_arrival'],
|
376
|
+
path: Schedule.nil_or_strip(hash['path'])
|
377
|
+
)
|
378
|
+
end
|
379
|
+
private_class_method :lt_from_json
|
380
|
+
|
381
|
+
def self.parse_allowance(value)
|
382
|
+
return nil if value.nil? || value.empty?
|
383
|
+
half = value[-1].eql?('H')
|
384
|
+
value = value.to_f
|
385
|
+
half ? value + 0.5 : value
|
386
|
+
end
|
387
|
+
private_class_method :parse_allowance
|
388
|
+
end
|
389
|
+
# rubocop:enable Metrics/ClassLength
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'schedule/days'
|
4
|
+
require_relative 'schedule/stp_indicator'
|
5
|
+
require_relative 'schedule/association'
|
6
|
+
require_relative 'schedule/header'
|
7
|
+
require_relative 'schedule/tiploc'
|
8
|
+
require_relative 'schedule/train_schedule'
|
9
|
+
require_relative 'schedule/fetcher'
|
10
|
+
require_relative 'schedule/parser'
|
11
|
+
require_relative 'schedule/data'
|
12
|
+
|
13
|
+
module RailFeeds
|
14
|
+
module NetworkRail
|
15
|
+
module Schedule # :nodoc:
|
16
|
+
def self.nil_or_i(value)
|
17
|
+
return nil if value.to_s.strip.empty?
|
18
|
+
value.to_i
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.nil_or_strip(value)
|
22
|
+
return nil if value.to_s.strip.empty?
|
23
|
+
value.strip
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.make_date(value, allow_nil: false)
|
27
|
+
return nil if allow_nil && value.strip.empty?
|
28
|
+
return Date.new(9999, 12, 31) if value.eql?('999999')
|
29
|
+
Date.strptime(value, '%y%m%d')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|