atco 0.0.2 → 1.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/{README.mdown → README.md} +22 -12
- data/Rakefile +17 -12
- data/lib/atco/journey.rb +56 -37
- data/lib/atco/location.rb +12 -13
- data/lib/atco/stop.rb +25 -15
- data/lib/atco/version.rb +5 -0
- data/lib/atco.rb +113 -117
- data/spec/atco_spec.rb +131 -106
- data/spec/fixtures/example.cif +30 -0
- data/spec/fixtures/example.json +98 -0
- data/spec/spec_helper.rb +6 -2
- metadata +63 -48
- data/VERSION +0 -1
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 215cf54f3257e776c20d54d0177731f06cf62eda409e7763457c709757aa1a52
|
4
|
+
data.tar.gz: 97ebd8cf82c779b872f23204079a230aae0d9b159bacdf3db5042cada877a197
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 69b1de96881df1044138cbebe82ea3e22053c47d36aa38dbb39c1018683675eb44e27619be1d29346d5ef156f924c3ee8e408808aec11b7940ad2ca4877dfc25
|
7
|
+
data.tar.gz: d14f0b410610dbaf1d2e4e80ce41703becb6fddc99c06bbe3a88500d65bde6b3b44bdfa7768405682c654abf8cb0ecb5470a7589b7f6b978f0933db2e230736e
|
data/{README.mdown → README.md}
RENAMED
@@ -2,11 +2,14 @@
|
|
2
2
|
|
3
3
|
ATCO-CIF is the format of choice for UK public transport authorities. This is a ruby library that reads **.cif** files and gives you JSON back.
|
4
4
|
|
5
|
+
* ATCO (Association of Transport Coordinating Officers)
|
6
|
+
* CIF (Common Interface File)
|
7
|
+
|
5
8
|
* **Official spec:** [http://www.pti.org.uk/CIF/atco-cif-spec.pdf](http://www.pti.org.uk/CIF/atco-cif-spec.pdf)
|
6
9
|
|
7
10
|
### USAGE
|
8
11
|
|
9
|
-
Currently this library is under-development and has several things left to do before it is perfect (see the [todo.
|
12
|
+
Currently this library is under-development and has several things left to do before it is perfect (see the [todo.md](http://github.com/davidjrice/atco/blob/master/TODO.md) list ).
|
10
13
|
|
11
14
|
* clone this library
|
12
15
|
* start an irb session
|
@@ -14,14 +17,21 @@ Currently this library is under-development and has several things left to do be
|
|
14
17
|
|
15
18
|
Code example, for more detailed internal api usage see the spec files.
|
16
19
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem install atco
|
23
|
+
irb
|
24
|
+
|
25
|
+
|
26
|
+
require 'rubygems'
|
27
|
+
require 'atco'
|
28
|
+
|
29
|
+
result = Atco.parse('filename.cif')
|
30
|
+
result = Atco.parse('SVRTMAO009A-20091005.cif) # an example data file in the repo
|
31
|
+
|
32
|
+
=> {
|
33
|
+
header: {…},
|
34
|
+
locations: […],
|
35
|
+
journies: {…}
|
36
|
+
}
|
37
|
+
```
|
data/Rakefile
CHANGED
@@ -1,14 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
|
1
5
|
begin
|
2
|
-
require
|
3
|
-
|
4
|
-
s.name = "atco"
|
5
|
-
s.summary = "Simple and opinionated library for parsing ATCO-CIF files with Ruby."
|
6
|
-
s.email = "me@davidjrice.co.uk"
|
7
|
-
s.homepage = "http://github.com/davidjrice/atco"
|
8
|
-
s.description = "Simple and opinionated library for parsing ATCO-CIF files with Ruby."
|
9
|
-
s.authors = ["David Rice"]
|
10
|
-
s.files = FileList["[A-Z]*", "{bin,generators,lib,test}/**/*", 'lib/jeweler/templates/.gitignore']
|
11
|
-
end
|
6
|
+
require "rspec/core/rake_task"
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
12
8
|
rescue LoadError
|
13
|
-
puts "
|
14
|
-
end
|
9
|
+
puts "RSpec, or one of its dependencies, is not available. Install it with: bundle install"
|
10
|
+
end
|
11
|
+
|
12
|
+
begin
|
13
|
+
require "rubocop/rake_task"
|
14
|
+
RuboCop::RakeTask.new
|
15
|
+
rescue LoadError
|
16
|
+
puts "rubocop, or one of its dependencies, is not available. Install it with: bundle install"
|
17
|
+
end
|
18
|
+
|
19
|
+
task default: %i[spec rubocop]
|
data/lib/atco/journey.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Atco
|
2
|
-
|
4
|
+
# Atco::Journey is a data class to abstract ATCO-CIF Journey records.
|
3
5
|
class Journey
|
4
|
-
|
5
|
-
|
6
|
-
:first_date_of_operation, :running_board, :last_date_of_operation, :school_term_time, :route_direction, :bank_holidays
|
7
|
-
|
8
|
-
attr_accessor :stops
|
6
|
+
attr_accessor :vehicle_type, :registration_number, :identifier, :operator, :route_number, :first_date_of_operation,
|
7
|
+
:running_board, :last_date_of_operation, :school_term_time, :route_direction, :bank_holidays, :stops
|
9
8
|
|
10
|
-
def initialize(data)
|
9
|
+
def initialize(data) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
11
10
|
@mondays = parse_boolean_int data[:operates_on_mondays]
|
12
11
|
@tuesdays = parse_boolean_int data[:operates_on_tuesdays]
|
13
12
|
@wednesdays = parse_boolean_int data[:operates_on_wednesdays]
|
@@ -15,7 +14,7 @@ module Atco
|
|
15
14
|
@fridays = parse_boolean_int data[:operates_on_fridays]
|
16
15
|
@saturdays = parse_boolean_int data[:operates_on_saturdays]
|
17
16
|
@sundays = parse_boolean_int data[:operates_on_sundays]
|
18
|
-
|
17
|
+
|
19
18
|
@vehicle_type = data[:vehicle_type]
|
20
19
|
@registration_number = data[:registration_number]
|
21
20
|
@identifier = data[:unique_journey_identifier]
|
@@ -27,40 +26,60 @@ module Atco
|
|
27
26
|
@running_board = data[:running_board]
|
28
27
|
@school_term_time = data[:school_term_time]
|
29
28
|
@bank_holidays = data[:bank_holidays]
|
30
|
-
|
29
|
+
|
31
30
|
@stops = []
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
# stops.each do |s|
|
32
|
+
# @stops << Stop.new(s)
|
33
|
+
# end
|
34
|
+
end
|
35
|
+
|
36
|
+
def mondays?
|
37
|
+
@mondays
|
38
|
+
end
|
39
|
+
|
40
|
+
def tuesdays?
|
41
|
+
@tuesdays
|
42
|
+
end
|
43
|
+
|
44
|
+
def wednesdays?
|
45
|
+
@wednesdays
|
46
|
+
end
|
47
|
+
|
48
|
+
def thursdays?
|
49
|
+
@thursdays
|
50
|
+
end
|
51
|
+
|
52
|
+
def fridays?
|
53
|
+
@fridays
|
54
|
+
end
|
55
|
+
|
56
|
+
def saturdays?
|
57
|
+
@saturdays
|
58
|
+
end
|
59
|
+
|
60
|
+
def sundays?
|
61
|
+
@sundays
|
35
62
|
end
|
36
63
|
|
37
|
-
def mondays?; @mondays; end
|
38
|
-
def tuesdays?; @tuesdays; end
|
39
|
-
def wednesdays?; @wednesdays; end
|
40
|
-
def thursdays?; @thursdays; end
|
41
|
-
def fridays?; @fridays; end
|
42
|
-
def saturdays?; @saturdays; end
|
43
|
-
def sundays?; @sundays; end
|
44
|
-
|
45
64
|
def parse_boolean_int(string)
|
46
|
-
|
65
|
+
string && string == "1"
|
47
66
|
end
|
48
|
-
|
49
|
-
def to_json(*
|
67
|
+
|
68
|
+
def to_json(*attrs) # rubocop:disable Metrics/MethodLength
|
50
69
|
{
|
51
|
-
:
|
52
|
-
:
|
53
|
-
:
|
54
|
-
:
|
55
|
-
:
|
56
|
-
:
|
57
|
-
:
|
58
|
-
:
|
59
|
-
:
|
60
|
-
:
|
61
|
-
:
|
62
|
-
:
|
63
|
-
}.to_json(*
|
70
|
+
vehicle_type: @vehicle_type,
|
71
|
+
registration_number: @registration_number,
|
72
|
+
identifier: @identifier,
|
73
|
+
operator: @operator,
|
74
|
+
route_number: @route_number,
|
75
|
+
first_date_of_operation: @first_date_of_operation,
|
76
|
+
running_board: @running_board,
|
77
|
+
last_date_of_operation: @last_date_of_operation,
|
78
|
+
school_term_time: @school_term_time,
|
79
|
+
route_direction: @route_direction,
|
80
|
+
bank_holidays: @bank_holidays,
|
81
|
+
stops: @stops
|
82
|
+
}.to_json(*attrs)
|
64
83
|
end
|
65
84
|
end
|
66
|
-
end
|
85
|
+
end
|
data/lib/atco/location.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Atco
|
2
|
-
|
4
|
+
# Atco::Location is a data class to abstract ATCO-CIF Location records.
|
3
5
|
class Location
|
4
|
-
|
5
6
|
attr_accessor :name, :identifier, :easting, :northing, :gazeteer_code
|
6
7
|
|
7
8
|
def initialize(location_header, additional_location_information)
|
@@ -12,16 +13,14 @@ module Atco
|
|
12
13
|
@gazeteer_code = location_header[:gazetteer_code]
|
13
14
|
end
|
14
15
|
|
15
|
-
def to_json(*
|
16
|
-
{
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
}.to_json(*
|
16
|
+
def to_json(*attrs)
|
17
|
+
{
|
18
|
+
name: @name,
|
19
|
+
identifier: @identifier,
|
20
|
+
easting: @easting,
|
21
|
+
northing: @northing,
|
22
|
+
gazeteer_code: @gazeteer_code
|
23
|
+
}.to_json(*attrs)
|
23
24
|
end
|
24
|
-
|
25
25
|
end
|
26
|
-
|
27
|
-
end
|
26
|
+
end
|
data/lib/atco/stop.rb
CHANGED
@@ -1,13 +1,23 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module Atco
|
4
|
+
# Atco::Stop is a class to abstract ATCO-CIF Origin, Intermediate and Destination (Stop) records.
|
3
5
|
class Stop
|
6
|
+
attr_accessor :bay_number, :location, :timing_point_indicator, :fare_stage_indicator, :published_departure_time,
|
7
|
+
:record_identity
|
8
|
+
|
9
|
+
def origin?
|
10
|
+
@record_identity == "QO"
|
11
|
+
end
|
4
12
|
|
5
|
-
|
13
|
+
def intermediate?
|
14
|
+
@record_identity == "QI"
|
15
|
+
end
|
16
|
+
|
17
|
+
def destination?
|
18
|
+
@record_identity == "QT"
|
19
|
+
end
|
6
20
|
|
7
|
-
def origin?; @record_identity == "QO"; end
|
8
|
-
def intermediate?; @record_identity == "QI"; end
|
9
|
-
def destination?; @record_identity == "QT"; end
|
10
|
-
|
11
21
|
def initialize(data)
|
12
22
|
@bay_number = data[:bay_number]
|
13
23
|
@location = data[:location]
|
@@ -17,15 +27,15 @@ module Atco
|
|
17
27
|
@record_identity = data[:record_identity]
|
18
28
|
end
|
19
29
|
|
20
|
-
def to_json(*
|
30
|
+
def to_json(*attrs)
|
21
31
|
{
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
28
|
-
}.to_json(*
|
32
|
+
record_identity: @record_identity,
|
33
|
+
location: @location,
|
34
|
+
published_departure_time: @published_departure_time,
|
35
|
+
timing_point_indicator: @timing_point_indicator,
|
36
|
+
fare_stage_indicator: @fare_stage_indicator,
|
37
|
+
bay_number: @bay_number
|
38
|
+
}.to_json(*attrs)
|
29
39
|
end
|
30
40
|
end
|
31
|
-
end
|
41
|
+
end
|
data/lib/atco/version.rb
ADDED
data/lib/atco.rb
CHANGED
@@ -1,183 +1,179 @@
|
|
1
|
-
|
2
|
-
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
require
|
5
|
-
require
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
require "open3"
|
4
|
+
require "tempfile"
|
5
|
+
require_relative "atco/location"
|
6
|
+
require_relative "atco/journey"
|
7
|
+
require_relative "atco/stop"
|
8
|
+
require_relative "atco/version"
|
9
9
|
|
10
|
-
|
11
|
-
module Atco
|
12
|
-
|
13
|
-
|
14
|
-
class << self
|
15
|
-
|
10
|
+
# Public: Atco is a module that provides a parser for the ATCO-CIF data format.
|
11
|
+
module Atco # rubocop:disable Metrics/ModuleLength
|
12
|
+
class << self # rubocop:disable Metrics/ClassLength
|
16
13
|
@path = nil
|
17
|
-
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
}
|
27
|
-
|
28
|
-
def parse(file)
|
14
|
+
METHODS = {
|
15
|
+
bank_holiday: "QH",
|
16
|
+
operator: "QP",
|
17
|
+
additional_location_info: "QB",
|
18
|
+
location: "QL",
|
19
|
+
destination: "QT",
|
20
|
+
intermediate: "QI",
|
21
|
+
origin: "QO",
|
22
|
+
journey_header: "QS"
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
def parse(file) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
29
26
|
@path = File.expand_path(file)
|
30
27
|
data = File.readlines(@path)
|
31
|
-
|
28
|
+
|
32
29
|
objects = []
|
33
30
|
current_journey = nil
|
34
31
|
current_location = nil
|
35
32
|
locations = []
|
36
33
|
journeys = {}
|
37
34
|
header = nil
|
38
|
-
|
35
|
+
|
39
36
|
data.each do |line|
|
40
37
|
if line == data.first
|
41
38
|
header = parse_header(line)
|
42
39
|
next
|
43
40
|
end
|
44
|
-
|
45
|
-
object =
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
41
|
+
METHODS.each do |method, identifier|
|
42
|
+
object = send("parse_#{method}", line)
|
43
|
+
next unless object[:record_identity] && object[:record_identity] == identifier
|
44
|
+
|
45
|
+
current_journey = object if object[:record_identity] && object[:record_identity] == METHODS[:journey_header]
|
46
|
+
if object[:record_identity] && (object[:record_identity] == METHODS[:location] || object[:record_identity] == METHODS[:additional_location_info]) # rubocop:disable Layout/LineLength
|
47
|
+
if object[:record_identity] == METHODS[:location]
|
48
|
+
current_location = object
|
49
|
+
else
|
50
|
+
locations << Location.new(current_location, object)
|
54
51
|
end
|
52
|
+
end
|
55
53
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
54
|
+
if current_journey
|
55
|
+
if journeys[current_journey[:unique_journey_identifier]]
|
56
|
+
journeys[current_journey[:unique_journey_identifier]].stops << Stop.new(object)
|
57
|
+
else
|
58
|
+
journeys[current_journey[:unique_journey_identifier]] = Journey.new(object)
|
62
59
|
end
|
63
|
-
objects << object
|
64
60
|
end
|
61
|
+
objects << object
|
65
62
|
end
|
66
63
|
end
|
67
|
-
|
64
|
+
{ header: header, locations: locations, journeys: journeys }
|
68
65
|
end
|
69
|
-
|
66
|
+
|
70
67
|
def parse_header(string)
|
71
68
|
{
|
72
|
-
:
|
73
|
-
:
|
74
|
-
:
|
75
|
-
:
|
76
|
-
:
|
77
|
-
}
|
69
|
+
file_type: string[0, 8],
|
70
|
+
version: "#{string[8, 2].to_i}.#{string[10, 2].to_i}",
|
71
|
+
file_originator: string[12, 32].strip!,
|
72
|
+
source_product: string[44, 16].strip!,
|
73
|
+
production_datetime: string[60, 14]
|
74
|
+
}
|
78
75
|
end
|
79
|
-
|
76
|
+
|
80
77
|
def parse_bank_holiday(string)
|
81
78
|
{
|
82
|
-
:
|
83
|
-
:
|
84
|
-
:
|
79
|
+
record_identity: string[0, 2],
|
80
|
+
transaction_type: string[2, 1],
|
81
|
+
date_of_bank_holiday: string[3, 8]
|
85
82
|
}
|
86
83
|
end
|
87
|
-
|
84
|
+
|
88
85
|
def parse_operator(string)
|
89
86
|
{
|
90
|
-
:
|
91
|
-
:
|
92
|
-
:
|
93
|
-
:
|
94
|
-
:
|
87
|
+
record_identity: string[0, 2],
|
88
|
+
transaction_type: string[2, 1],
|
89
|
+
operator: parse_value(string[3, 4]),
|
90
|
+
operator_short_form: parse_value(string[7, 24]),
|
91
|
+
operator_legal_name: parse_value(string[31, 48])
|
95
92
|
}
|
96
93
|
end
|
97
94
|
|
98
95
|
def parse_additional_location_info(string)
|
99
96
|
{
|
100
|
-
:
|
101
|
-
:
|
102
|
-
:
|
103
|
-
:
|
104
|
-
:
|
97
|
+
record_identity: string[0, 2],
|
98
|
+
transaction_type: string[2, 1],
|
99
|
+
location: string[3, 12].strip,
|
100
|
+
grid_reference_easting: parse_value(string[15, 8]),
|
101
|
+
grid_reference_northing: parse_value(string[23, 8])
|
105
102
|
}
|
106
103
|
end
|
107
104
|
|
108
105
|
def parse_location(string)
|
109
106
|
{
|
110
|
-
:
|
111
|
-
:
|
112
|
-
:
|
113
|
-
:
|
114
|
-
:
|
107
|
+
record_identity: string[0, 2],
|
108
|
+
transaction_type: string[2, 1],
|
109
|
+
location: parse_value(string[3, 12]),
|
110
|
+
full_location: parse_value(string[15, 48]),
|
111
|
+
gazetteer_code: string[63, 1]
|
115
112
|
}
|
116
113
|
end
|
117
114
|
|
118
115
|
def parse_destination(string)
|
119
116
|
{
|
120
|
-
:
|
121
|
-
:
|
122
|
-
:
|
123
|
-
:
|
124
|
-
:
|
125
|
-
:
|
117
|
+
record_identity: string[0, 2],
|
118
|
+
location: string[2, 12],
|
119
|
+
published_arrival_time: string[14, 4],
|
120
|
+
bay_number: parse_value(string[18, 3]),
|
121
|
+
timing_point_indicator: string[21, 2],
|
122
|
+
fare_stage_indicator: string[23, 2]
|
126
123
|
}
|
127
124
|
end
|
128
125
|
|
129
126
|
def parse_intermediate(string)
|
130
127
|
{
|
131
|
-
:
|
132
|
-
:
|
133
|
-
:
|
134
|
-
:
|
135
|
-
:
|
136
|
-
:
|
137
|
-
:
|
138
|
-
:
|
128
|
+
record_identity: string[0, 2],
|
129
|
+
location: string[2, 12],
|
130
|
+
published_arrival_time: string[14, 4],
|
131
|
+
published_departure_time: string[18, 4],
|
132
|
+
activity_flag: string[22, 1],
|
133
|
+
bay_number: parse_value(string[23, 3]),
|
134
|
+
timing_point_indicator: string[26, 2],
|
135
|
+
fare_stage_indicator: string[28, 2]
|
139
136
|
}
|
140
137
|
end
|
141
138
|
|
142
139
|
def parse_origin(string)
|
143
140
|
{
|
144
|
-
:
|
145
|
-
:
|
146
|
-
:
|
147
|
-
:
|
148
|
-
:
|
149
|
-
:
|
141
|
+
record_identity: string[0, 2],
|
142
|
+
location: string[2, 12],
|
143
|
+
published_departure_time: string[14, 4],
|
144
|
+
bay_number: parse_value(string[18, 3]),
|
145
|
+
timing_point_indicator: string[21, 2],
|
146
|
+
fare_stage_indicator: string[23, 2]
|
150
147
|
}
|
151
148
|
end
|
152
149
|
|
153
|
-
def parse_journey_header(string)
|
150
|
+
def parse_journey_header(string) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
154
151
|
{
|
155
|
-
:
|
156
|
-
:
|
157
|
-
:
|
158
|
-
:
|
159
|
-
:
|
160
|
-
:
|
161
|
-
:
|
162
|
-
:
|
163
|
-
:
|
164
|
-
:
|
165
|
-
:
|
166
|
-
:
|
167
|
-
:
|
168
|
-
:
|
169
|
-
:
|
170
|
-
:
|
171
|
-
:
|
172
|
-
:
|
173
|
-
:
|
174
|
-
:
|
152
|
+
record_identity: string[0, 2],
|
153
|
+
transaction_type: string[2, 1],
|
154
|
+
operator: string[3, 4].strip,
|
155
|
+
unique_journey_identifier: string[7, 6],
|
156
|
+
first_date_of_operation: parse_value(string[13, 8]),
|
157
|
+
last_date_of_operation: parse_value(string[21, 8]),
|
158
|
+
operates_on_mondays: string[29, 1],
|
159
|
+
operates_on_tuesdays: string[30, 1],
|
160
|
+
operates_on_wednesdays: string[31, 1],
|
161
|
+
operates_on_thursdays: string[32, 1],
|
162
|
+
operates_on_fridays: string[33, 1],
|
163
|
+
operates_on_saturdays: string[34, 1],
|
164
|
+
operates_on_sundays: string[35, 1],
|
165
|
+
school_term_time: parse_value(string[36, 1]),
|
166
|
+
bank_holidays: parse_value(string[37, 1]),
|
167
|
+
route_number: parse_value(string[38, 4]),
|
168
|
+
running_board: parse_value(string[42, 6]),
|
169
|
+
vehicle_type: parse_value(string[48, 8]),
|
170
|
+
registration_number: parse_value(string[56, 8]),
|
171
|
+
route_direction: string[64, 1]
|
175
172
|
}
|
176
173
|
end
|
177
|
-
|
174
|
+
|
178
175
|
def parse_value(value)
|
179
|
-
|
176
|
+
value&.strip
|
180
177
|
end
|
181
178
|
end
|
182
|
-
|
183
|
-
end
|
179
|
+
end
|
data/spec/atco_spec.rb
CHANGED
@@ -1,150 +1,175 @@
|
|
1
|
-
|
2
|
-
require 'json'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
require "#{File.dirname(__FILE__)}/spec_helper"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
describe Atco do # rubocop:disable Metrics/BlockLength
|
7
|
+
it "has a version number" do
|
8
|
+
expect(Atco::VERSION).not_to be nil
|
8
9
|
end
|
9
|
-
|
10
|
+
|
10
11
|
it "should output file for debugging!" do
|
11
|
-
result = Atco.parse(
|
12
|
-
File.open(
|
12
|
+
result = Atco.parse("spec/fixtures/example.cif")
|
13
|
+
File.open("test.output", "w+") do |f|
|
13
14
|
f.flush
|
14
15
|
f.write(JSON.pretty_generate(result))
|
15
16
|
end
|
16
|
-
#fixture = JSON.parse(File.read('spec/fixtures/example.json'))
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
it "should parse header from fixture" do
|
20
|
-
result = Atco.parse(
|
21
|
-
result[:header]
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
20
|
+
result = Atco.parse("spec/fixtures/example.cif")
|
21
|
+
expect result[:header] == {
|
22
|
+
file_type: "ATCO-CIF",
|
23
|
+
file_originator: "Electronic Registration",
|
24
|
+
source_product: "MIA 4.20.18",
|
25
|
+
version: "5.0",
|
26
|
+
production_datetime: "20090915113809"
|
27
27
|
}
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
it "should parse locations from fixture" do
|
31
|
-
result = Atco.parse(
|
32
|
-
result[:header]
|
33
|
-
:
|
34
|
-
:
|
35
|
-
:
|
36
|
-
:
|
37
|
-
:
|
31
|
+
result = Atco.parse("spec/fixtures/example.cif")
|
32
|
+
expect result[:header] == {
|
33
|
+
file_type: "ATCO-CIF",
|
34
|
+
file_originator: "Electronic Registration",
|
35
|
+
source_product: "MIA 4.20.18",
|
36
|
+
version: "5.0",
|
37
|
+
production_datetime: "20090915113809"
|
38
38
|
}
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
it "should parse header" do
|
42
|
-
Atco.parse_header("ATCO-CIF0500Electronic Registration MIA 4.20.18 20090915113809\r\n")
|
43
|
-
:
|
44
|
-
:
|
45
|
-
:
|
46
|
-
:
|
47
|
-
:
|
42
|
+
expect Atco.parse_header("ATCO-CIF0500Electronic Registration MIA 4.20.18 20090915113809\r\n") == {
|
43
|
+
file_type: "ATCO-CIF",
|
44
|
+
version: "5.0",
|
45
|
+
file_originator: "Electronic Registration",
|
46
|
+
source_product: "MIA 4.20.18",
|
47
|
+
production_datetime: "20090915113809"
|
48
48
|
}
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
51
|
it "should parse bank holiday" do
|
52
|
-
Atco.parse_bank_holiday("QHN20061225")
|
53
|
-
:
|
54
|
-
:
|
55
|
-
:
|
52
|
+
expect Atco.parse_bank_holiday("QHN20061225") == {
|
53
|
+
record_identity: "QH",
|
54
|
+
transaction_type: "N",
|
55
|
+
date_of_bank_holiday: "20061225"
|
56
56
|
}
|
57
57
|
end
|
58
|
-
|
58
|
+
|
59
59
|
it "should parse operator" do
|
60
|
-
Atco.parse_operator("QPNTM Translink Metro Translink Metro \r\n")
|
61
|
-
:
|
62
|
-
:
|
63
|
-
:
|
64
|
-
:
|
65
|
-
:
|
60
|
+
expect Atco.parse_operator("QPNTM Translink Metro Translink Metro \r\n") == {
|
61
|
+
record_identity: "QP",
|
62
|
+
transaction_type: "N",
|
63
|
+
operator: "TM",
|
64
|
+
operator_short_form: "Translink Metro",
|
65
|
+
operator_legal_name: "Translink Metro"
|
66
66
|
}
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
it "should parse additional location information" do
|
70
|
-
Atco.parse_additional_location_info("QBN700000001252 328622 367433 \r\n")
|
71
|
-
:
|
72
|
-
:
|
73
|
-
:
|
74
|
-
:
|
75
|
-
:
|
70
|
+
expect Atco.parse_additional_location_info("QBN700000001252 328622 367433 \r\n") == {
|
71
|
+
record_identity: "QB",
|
72
|
+
transaction_type: "N",
|
73
|
+
location: "700000001252",
|
74
|
+
grid_reference_easting: "328622",
|
75
|
+
grid_reference_northing: "367433"
|
76
76
|
}
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
it "should parse location" do
|
80
|
-
Atco.parse_location("QLN700000001252Conway (River Rd) 1\r\n")
|
81
|
-
:
|
82
|
-
:
|
83
|
-
:
|
84
|
-
:
|
85
|
-
:
|
80
|
+
expect Atco.parse_location("QLN700000001252Conway (River Rd) 1\r\n") == {
|
81
|
+
record_identity: "QL",
|
82
|
+
transaction_type: "N",
|
83
|
+
location: "700000001252",
|
84
|
+
full_location: "Conway (River Rd)",
|
85
|
+
gazetteer_code: "1"
|
86
86
|
}
|
87
87
|
end
|
88
88
|
|
89
89
|
# QT7000000012520605 T1F0
|
90
90
|
it "should parse destination" do
|
91
|
-
Atco.parse_destination("QT7000000012520605 T1F0\r\n")
|
92
|
-
:
|
93
|
-
:
|
94
|
-
:
|
95
|
-
:
|
96
|
-
:
|
97
|
-
:
|
91
|
+
expect Atco.parse_destination("QT7000000012520605 T1F0\r\n") == {
|
92
|
+
record_identity: "QT",
|
93
|
+
location: "700000001252",
|
94
|
+
published_arrival_time: "0605",
|
95
|
+
bay_number: "",
|
96
|
+
timing_point_indicator: "T1",
|
97
|
+
fare_stage_indicator: "F0"
|
98
98
|
}
|
99
99
|
end
|
100
|
-
|
100
|
+
|
101
101
|
it "should parse intermediate" do
|
102
|
-
Atco.parse_intermediate("QI70000000125607120712B T1F0\r\n")
|
103
|
-
:
|
104
|
-
:
|
105
|
-
:
|
106
|
-
:
|
107
|
-
:
|
108
|
-
:
|
109
|
-
:
|
110
|
-
:
|
102
|
+
expect Atco.parse_intermediate("QI70000000125607120712B T1F0\r\n") == {
|
103
|
+
record_identity: "QI",
|
104
|
+
location: "700000001256",
|
105
|
+
published_arrival_time: "0712",
|
106
|
+
published_departure_time: "0712",
|
107
|
+
activity_flag: "B",
|
108
|
+
bay_number: "",
|
109
|
+
timing_point_indicator: "T1",
|
110
|
+
fare_stage_indicator: "F0"
|
111
111
|
}
|
112
112
|
end
|
113
|
-
|
113
|
+
|
114
114
|
it "should parse origin" do
|
115
|
-
Atco.parse_origin("QO7000000012520730 T1F0\r\n")
|
116
|
-
:
|
117
|
-
:
|
118
|
-
:
|
119
|
-
:
|
120
|
-
:
|
121
|
-
:
|
115
|
+
expect Atco.parse_origin("QO7000000012520730 T1F0\r\n") == {
|
116
|
+
record_identity: "QO",
|
117
|
+
location: "700000001252",
|
118
|
+
published_departure_time: "0730",
|
119
|
+
bay_number: "",
|
120
|
+
timing_point_indicator: "T1",
|
121
|
+
fare_stage_indicator: "F0"
|
122
122
|
}
|
123
123
|
end
|
124
124
|
|
125
125
|
it "should parse journey header" do
|
126
|
-
Atco.parse_journey_header("QSNTM 13986520091005 1111100 9A 9018 0 I\r\n")
|
127
|
-
:
|
128
|
-
:
|
129
|
-
:
|
130
|
-
:
|
131
|
-
:
|
132
|
-
:
|
133
|
-
:
|
134
|
-
:
|
135
|
-
:
|
136
|
-
:
|
137
|
-
:
|
138
|
-
:
|
139
|
-
:
|
140
|
-
:
|
141
|
-
:
|
142
|
-
:
|
143
|
-
:
|
144
|
-
:
|
145
|
-
:
|
146
|
-
:
|
126
|
+
expect Atco.parse_journey_header("QSNTM 13986520091005 1111100 9A 9018 0 I\r\n") == {
|
127
|
+
record_identity: "QS",
|
128
|
+
transaction_type: "N",
|
129
|
+
operator: "TM",
|
130
|
+
unique_journey_identifier: "139865",
|
131
|
+
first_date_of_operation: "20091005",
|
132
|
+
last_date_of_operation: "",
|
133
|
+
operates_on_mondays: "1",
|
134
|
+
operates_on_tuesdays: "1",
|
135
|
+
operates_on_wednesdays: "1",
|
136
|
+
operates_on_thursdays: "1",
|
137
|
+
operates_on_fridays: "1",
|
138
|
+
operates_on_saturdays: "0",
|
139
|
+
operates_on_sundays: "0",
|
140
|
+
school_term_time: "",
|
141
|
+
bank_holidays: "",
|
142
|
+
route_number: "9A",
|
143
|
+
running_board: "9018",
|
144
|
+
vehicle_type: "0",
|
145
|
+
registration_number: "",
|
146
|
+
route_direction: "I"
|
147
147
|
}
|
148
148
|
end
|
149
149
|
|
150
|
-
|
150
|
+
describe "with example.cif" do
|
151
|
+
before(:all) do
|
152
|
+
@atco = Atco.parse("spec/fixtures/example.cif")
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should parse 1 journey" do
|
156
|
+
expect @atco[:journeys].size == 1
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should parse journeys into Atco::Joruney objects" do
|
160
|
+
expect(@atco[:journeys]["139748"]).to be_a_kind_of(Atco::Journey)
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should parse 6 stops for joureny 139748" do
|
164
|
+
expect @atco[:journeys]["139748"].stops.size == 6
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should parse 6 stops for joureny 139748" do
|
168
|
+
expect @atco[:journeys]["139748"].stops.size == 6
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should parse 2 locations" do
|
172
|
+
expect @atco[:locations].size == 2
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
ATCO-CIF0500Electronic Registration MIA 4.20.18 20090915113809
|
2
|
+
QLN700000001433Donegal Square East 1
|
3
|
+
QBN700000001433 333935 373979
|
4
|
+
GS00001433 N Belfast Metro Ops 7000
|
5
|
+
GR00001433Donegall Square East 7000
|
6
|
+
QLN700000001627Great Northern Mall 1
|
7
|
+
QBN700000001627 333575 373717
|
8
|
+
GS00001627 N Belfast Metro Ops 7000
|
9
|
+
GR00001627Great Victoria Street 7000 7000
|
10
|
+
QPNTM Translink Metro Translink Metro
|
11
|
+
QQ
|
12
|
+
ZLTMAO009AT05O
|
13
|
+
ZD20091005 Monday to Friday FFFFF00
|
14
|
+
ZSTMAO009A9A City Centre - Conway
|
15
|
+
ZAN700000001433 P1W1T1
|
16
|
+
ZAN700000001627 P1W1T1
|
17
|
+
QSNTM 13974820091005 1111100 9A 9011 0 O
|
18
|
+
ZJTMAO009A004 1 370 000CB 545 2237 C FFFFF00L0
|
19
|
+
QO7000000014330545 T1F0
|
20
|
+
ZECH0 054500054500P0W0
|
21
|
+
QI70000000162705460546B T1F0
|
22
|
+
ZECH0 054600054600P0W0
|
23
|
+
QI70000000124806020602B T1F0
|
24
|
+
ZECH0 060200060200P0W0
|
25
|
+
QI70000000125006030603B T0F0
|
26
|
+
ZECH0 060348060348P0W0
|
27
|
+
QT7000000012520605 T1F0
|
28
|
+
ZECH0 060500060500P0W0
|
29
|
+
QVN0 Bus
|
30
|
+
QHN20071226
|
@@ -0,0 +1,98 @@
|
|
1
|
+
{
|
2
|
+
"header": {
|
3
|
+
"file_type": "ATCO-CIF",
|
4
|
+
"file_originator": "Electronic Registration",
|
5
|
+
"source_product": "MIA 4.20.18",
|
6
|
+
"version": "5.0",
|
7
|
+
"production_datetime": "20090915113809"
|
8
|
+
},
|
9
|
+
"locations": [
|
10
|
+
[
|
11
|
+
{
|
12
|
+
"record_identity": "QL",
|
13
|
+
"location": "700000001433",
|
14
|
+
"transaction_type": "N",
|
15
|
+
"full_location": "Donegal Square East",
|
16
|
+
"gazetteer_code": "1"
|
17
|
+
},
|
18
|
+
{
|
19
|
+
"record_identity": "QB",
|
20
|
+
"location": "700000001433",
|
21
|
+
"transaction_type": "N",
|
22
|
+
"grid_reference_easting": "333935",
|
23
|
+
"grid_reference_northing": "373979"
|
24
|
+
}
|
25
|
+
],
|
26
|
+
[
|
27
|
+
{
|
28
|
+
"record_identity": "QL",
|
29
|
+
"location": "700000001627",
|
30
|
+
"transaction_type": "N",
|
31
|
+
"full_location": "Great Northern Mall",
|
32
|
+
"gazetteer_code": "1"
|
33
|
+
},
|
34
|
+
{
|
35
|
+
"record_identity": "QB",
|
36
|
+
"location": "700000001627",
|
37
|
+
"transaction_type": "N",
|
38
|
+
"grid_reference_easting": "333575",
|
39
|
+
"grid_reference_northing": "373717"
|
40
|
+
}
|
41
|
+
]
|
42
|
+
],
|
43
|
+
"journeys": {
|
44
|
+
"139748": [
|
45
|
+
{
|
46
|
+
"record_identity": "QO",
|
47
|
+
"published_departure_time": "0545",
|
48
|
+
"location": "700000001433",
|
49
|
+
"bay_number": "",
|
50
|
+
"timing_point_indicator": "T1",
|
51
|
+
"fare_stage_indicator": "F0"
|
52
|
+
},
|
53
|
+
{
|
54
|
+
"record_identity": "QI",
|
55
|
+
"published_departure_time": "0546",
|
56
|
+
"location": "700000001627",
|
57
|
+
"published_arrival_time": "0546",
|
58
|
+
"activity_flag": "B",
|
59
|
+
"bay_number": "",
|
60
|
+
"timing_point_indicator": "T1",
|
61
|
+
"fare_stage_indicator": "F0"
|
62
|
+
},
|
63
|
+
{
|
64
|
+
"record_identity": "QI",
|
65
|
+
"published_departure_time": "0602",
|
66
|
+
"location": "700000001248",
|
67
|
+
"published_arrival_time": "0602",
|
68
|
+
"activity_flag": "B",
|
69
|
+
"bay_number": "",
|
70
|
+
"timing_point_indicator": "T1",
|
71
|
+
"fare_stage_indicator": "F0"
|
72
|
+
},
|
73
|
+
{
|
74
|
+
"record_identity": "QI",
|
75
|
+
"published_departure_time": "0603",
|
76
|
+
"location": "700000001250",
|
77
|
+
"published_arrival_time": "0603",
|
78
|
+
"activity_flag": "B",
|
79
|
+
"bay_number": "",
|
80
|
+
"timing_point_indicator": "T0",
|
81
|
+
"fare_stage_indicator": "F0"
|
82
|
+
},
|
83
|
+
{
|
84
|
+
"record_identity": "QT",
|
85
|
+
"location": "700000001252",
|
86
|
+
"published_arrival_time": "0605",
|
87
|
+
"bay_number": "",
|
88
|
+
"timing_point_indicator": "T1",
|
89
|
+
"fare_stage_indicator": "F0"
|
90
|
+
},
|
91
|
+
{
|
92
|
+
"record_identity": "QH",
|
93
|
+
"transaction_type": "N",
|
94
|
+
"date_of_bank_holiday": "20071226"
|
95
|
+
}
|
96
|
+
]
|
97
|
+
}
|
98
|
+
}
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,69 +1,84 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: atco
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
segments:
|
6
|
-
- 0
|
7
|
-
- 0
|
8
|
-
- 2
|
9
|
-
version: 0.0.2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
10
5
|
platform: ruby
|
11
|
-
authors:
|
6
|
+
authors:
|
12
7
|
- David Rice
|
13
8
|
autorequire:
|
14
9
|
bindir: bin
|
15
10
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
11
|
+
date: 2024-04-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '13.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '13.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.9'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.9'
|
41
|
+
description: Simple and opinionated library for parsing ATCO .cif files to JSON with
|
42
|
+
Ruby
|
22
43
|
email: me@davidjrice.co.uk
|
23
44
|
executables: []
|
24
|
-
|
25
45
|
extensions: []
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
- README.mdown
|
46
|
+
extra_rdoc_files:
|
47
|
+
- README.md
|
48
|
+
files:
|
49
|
+
- README.md
|
31
50
|
- Rakefile
|
32
|
-
- VERSION
|
33
51
|
- lib/atco.rb
|
34
52
|
- lib/atco/journey.rb
|
35
53
|
- lib/atco/location.rb
|
36
54
|
- lib/atco/stop.rb
|
37
|
-
|
55
|
+
- lib/atco/version.rb
|
56
|
+
- spec/atco_spec.rb
|
57
|
+
- spec/fixtures/example.cif
|
58
|
+
- spec/fixtures/example.json
|
59
|
+
- spec/spec_helper.rb
|
38
60
|
homepage: http://github.com/davidjrice/atco
|
39
|
-
licenses:
|
40
|
-
|
61
|
+
licenses:
|
62
|
+
- MIT
|
63
|
+
metadata: {}
|
41
64
|
post_install_message:
|
42
|
-
rdoc_options:
|
43
|
-
- --charset=UTF-8
|
44
|
-
require_paths:
|
65
|
+
rdoc_options:
|
66
|
+
- "--charset=UTF-8"
|
67
|
+
require_paths:
|
45
68
|
- lib
|
46
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
-
requirements:
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
48
71
|
- - ">="
|
49
|
-
- !ruby/object:Gem::Version
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
-
requirements:
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 2.7.0
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
55
76
|
- - ">="
|
56
|
-
- !ruby/object:Gem::Version
|
57
|
-
|
58
|
-
- 0
|
59
|
-
version: "0"
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
60
79
|
requirements: []
|
61
|
-
|
62
|
-
rubyforge_project:
|
63
|
-
rubygems_version: 1.3.6
|
80
|
+
rubygems_version: 3.5.3
|
64
81
|
signing_key:
|
65
|
-
specification_version:
|
66
|
-
summary:
|
67
|
-
test_files:
|
68
|
-
- spec/atco_spec.rb
|
69
|
-
- spec/spec_helper.rb
|
82
|
+
specification_version: 4
|
83
|
+
summary: Parse ATCO .cif files to JSON with Ruby
|
84
|
+
test_files: []
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.0.2
|