rail_feeds 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|