atco 0.0.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/{README.mdown → README.md} +22 -12
- data/Rakefile +10 -12
- data/VERSION +1 -1
- data/lib/atco/journey.rb +84 -0
- data/lib/atco/location.rb +25 -0
- data/lib/atco/stop.rb +40 -0
- data/lib/atco.rb +108 -106
- data/spec/atco_spec.rb +135 -111
- data/spec/fixtures/example.cif +30 -0
- data/spec/fixtures/example.json +98 -0
- data/spec/spec_helper.rb +5 -1
- metadata +65 -48
- data/lib/location.rb +0 -23
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 91d4d697f6d487218aa3def133f0104257141ed8fb18b93be2c3f0c52ac92a6e
|
4
|
+
data.tar.gz: 213d51b686a0076c69f26b6d18173dd2b767eaf24db17f7e0cdc011406b94705
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fb0ccef50c6a0490c9b87b0b01a7179bec8ca755d594f52193ef6220ef6733e2c8ed600f46043c3a0a38038f3290a8687f1828adfe893dac7dad5c7413b23c89
|
7
|
+
data.tar.gz: 6ba15c31b4cfb1873577af0fe50c62c6325780d9f9c3c6ddb124cfe91397dd2808382757f1198f492ea6fbbe2a21131e010b49ac80a757f33041a3ff57db1d9f
|
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,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
1
6
|
begin
|
2
|
-
|
3
|
-
Jeweler::Tasks.new do |s|
|
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
|
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
|
+
task default: :spec
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
1.0.0
|
data/lib/atco/journey.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Atco
|
4
|
+
class Journey
|
5
|
+
attr_accessor :vehicle_type, :registration_number, :identifier, :operator, :route_number, :first_date_of_operation,
|
6
|
+
:running_board, :last_date_of_operation, :school_term_time, :route_direction, :bank_holidays, :stops
|
7
|
+
|
8
|
+
def initialize(data)
|
9
|
+
@mondays = parse_boolean_int data[:operates_on_mondays]
|
10
|
+
@tuesdays = parse_boolean_int data[:operates_on_tuesdays]
|
11
|
+
@wednesdays = parse_boolean_int data[:operates_on_wednesdays]
|
12
|
+
@thursdays = parse_boolean_int data[:operates_on_thursdays]
|
13
|
+
@fridays = parse_boolean_int data[:operates_on_fridays]
|
14
|
+
@saturdays = parse_boolean_int data[:operates_on_saturdays]
|
15
|
+
@sundays = parse_boolean_int data[:operates_on_sundays]
|
16
|
+
|
17
|
+
@vehicle_type = data[:vehicle_type]
|
18
|
+
@registration_number = data[:registration_number]
|
19
|
+
@identifier = data[:unique_journey_identifier]
|
20
|
+
@operator = data[:operator]
|
21
|
+
@route_number = data[:route_number]
|
22
|
+
@route_direction = data[:route_direction]
|
23
|
+
@first_date_of_operation = data[:first_date_of_operation]
|
24
|
+
@last_date_of_operation = data[:last_date_of_operation]
|
25
|
+
@running_board = data[:running_board]
|
26
|
+
@school_term_time = data[:school_term_time]
|
27
|
+
@bank_holidays = data[:bank_holidays]
|
28
|
+
|
29
|
+
@stops = []
|
30
|
+
# stops.each do |s|
|
31
|
+
# @stops << Stop.new(s)
|
32
|
+
# end
|
33
|
+
end
|
34
|
+
|
35
|
+
def mondays?
|
36
|
+
@mondays
|
37
|
+
end
|
38
|
+
|
39
|
+
def tuesdays?
|
40
|
+
@tuesdays
|
41
|
+
end
|
42
|
+
|
43
|
+
def wednesdays?
|
44
|
+
@wednesdays
|
45
|
+
end
|
46
|
+
|
47
|
+
def thursdays?
|
48
|
+
@thursdays
|
49
|
+
end
|
50
|
+
|
51
|
+
def fridays?
|
52
|
+
@fridays
|
53
|
+
end
|
54
|
+
|
55
|
+
def saturdays?
|
56
|
+
@saturdays
|
57
|
+
end
|
58
|
+
|
59
|
+
def sundays?
|
60
|
+
@sundays
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse_boolean_int(string)
|
64
|
+
string && string == '1' ? true : false
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_json(*args)
|
68
|
+
{
|
69
|
+
vehicle_type: @vehicle_type,
|
70
|
+
registration_number: @registration_number,
|
71
|
+
identifier: @identifier,
|
72
|
+
operator: @operator,
|
73
|
+
route_number: @route_number,
|
74
|
+
first_date_of_operation: @first_date_of_operation,
|
75
|
+
running_board: @running_board,
|
76
|
+
last_date_of_operation: @last_date_of_operation,
|
77
|
+
school_term_time: @school_term_time,
|
78
|
+
route_direction: @route_direction,
|
79
|
+
bank_holidays: @bank_holidays,
|
80
|
+
stops: @stops
|
81
|
+
}.to_json(*args)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Atco
|
4
|
+
class Location
|
5
|
+
attr_accessor :name, :identifier, :easting, :northing, :gazeteer_code
|
6
|
+
|
7
|
+
def initialize(location_header, additional_location_information)
|
8
|
+
@name = location_header[:full_location]
|
9
|
+
@identifier = location_header[:record_identity]
|
10
|
+
@easting = additional_location_information[:grid_reference_easting]
|
11
|
+
@northing = additional_location_information[:grid_reference_northing]
|
12
|
+
@gazeteer_code = location_header[:gazetteer_code]
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_json(*a)
|
16
|
+
{
|
17
|
+
name: @name,
|
18
|
+
identifier: @identifier,
|
19
|
+
easting: @easting,
|
20
|
+
northing: @northing,
|
21
|
+
gazeteer_code: @gazeteer_code
|
22
|
+
}.to_json(*a)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/atco/stop.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Atco
|
4
|
+
class Stop
|
5
|
+
attr_accessor :bay_number, :location, :timing_point_indicator, :fare_stage_indicator, :published_departure_time,
|
6
|
+
:record_identity
|
7
|
+
|
8
|
+
def origin?
|
9
|
+
@record_identity == 'QO'
|
10
|
+
end
|
11
|
+
|
12
|
+
def intermediate?
|
13
|
+
@record_identity == 'QI'
|
14
|
+
end
|
15
|
+
|
16
|
+
def destination?
|
17
|
+
@record_identity == 'QT'
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(data)
|
21
|
+
@bay_number = data[:bay_number]
|
22
|
+
@location = data[:location]
|
23
|
+
@timing_point_indicator = data[:timing_point_indicator]
|
24
|
+
@fare_stage_indicator = data[:fare_stage_indicator]
|
25
|
+
@published_departure_time = data[:published_departure_time]
|
26
|
+
@record_identity = data[:record_identity]
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_json(*args)
|
30
|
+
{
|
31
|
+
record_identity: @record_identity,
|
32
|
+
location: @location,
|
33
|
+
published_departure_time: @published_departure_time,
|
34
|
+
timing_point_indicator: @timing_point_indicator,
|
35
|
+
fare_stage_indicator: @fare_stage_indicator,
|
36
|
+
bay_number: @bay_number
|
37
|
+
}.to_json(*args)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/atco.rb
CHANGED
@@ -1,180 +1,182 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__)) unless
|
4
|
+
$LOAD_PATH.include?(File.dirname(__FILE__)) || $LOAD_PATH.include?(__dir__)
|
3
5
|
|
4
6
|
require 'open3'
|
5
|
-
require 'tempfile'
|
6
|
-
require 'location'
|
7
|
+
require 'tempfile'
|
8
|
+
require 'atco/location'
|
9
|
+
require 'atco/journey'
|
10
|
+
require 'atco/stop'
|
7
11
|
|
8
12
|
module Atco
|
9
13
|
VERSION = '0.0.1'
|
10
|
-
|
14
|
+
|
11
15
|
class << self
|
12
|
-
|
13
16
|
@path = nil
|
14
17
|
@@methods = {
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
18
|
+
bank_holiday: 'QH',
|
19
|
+
operator: 'QP',
|
20
|
+
additional_location_info: 'QB',
|
21
|
+
location: 'QL',
|
22
|
+
destination: 'QT',
|
23
|
+
intermediate: 'QI',
|
24
|
+
origin: 'QO',
|
25
|
+
journey_header: 'QS'
|
23
26
|
}
|
24
|
-
|
27
|
+
|
25
28
|
def parse(file)
|
26
29
|
@path = File.expand_path(file)
|
27
30
|
data = File.readlines(@path)
|
28
|
-
|
31
|
+
|
29
32
|
objects = []
|
30
33
|
current_journey = nil
|
31
34
|
current_location = nil
|
32
35
|
locations = []
|
33
36
|
journeys = {}
|
34
37
|
header = nil
|
35
|
-
|
38
|
+
|
36
39
|
data.each do |line|
|
37
40
|
if line == data.first
|
38
41
|
header = parse_header(line)
|
39
42
|
next
|
40
43
|
end
|
41
|
-
@@methods.each do |method,identifier|
|
42
|
-
object =
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
44
|
+
@@methods.each do |method, identifier|
|
45
|
+
object = send("parse_#{method}", line)
|
46
|
+
next unless object[:record_identity] && object[:record_identity] == identifier
|
47
|
+
|
48
|
+
current_journey = object if object[:record_identity] && object[:record_identity] == @@methods[:journey_header]
|
49
|
+
if object[:record_identity] && (object[:record_identity] == @@methods[:location] || object[:record_identity] == @@methods[:additional_location_info])
|
50
|
+
if object[:record_identity] == @@methods[:location]
|
51
|
+
current_location = object
|
52
|
+
else
|
53
|
+
locations << Location.new(current_location, object)
|
51
54
|
end
|
55
|
+
end
|
52
56
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
end
|
57
|
+
if current_journey
|
58
|
+
if journeys[current_journey[:unique_journey_identifier]]
|
59
|
+
journeys[current_journey[:unique_journey_identifier]].stops << Stop.new(object)
|
60
|
+
else
|
61
|
+
journeys[current_journey[:unique_journey_identifier]] = Journey.new(object)
|
59
62
|
end
|
60
|
-
objects << object
|
61
63
|
end
|
64
|
+
objects << object
|
62
65
|
end
|
63
66
|
end
|
64
|
-
|
67
|
+
{ header: header, locations: locations, journeys: journeys }
|
65
68
|
end
|
66
|
-
|
69
|
+
|
67
70
|
def parse_header(string)
|
68
71
|
{
|
69
|
-
:
|
70
|
-
:
|
71
|
-
:
|
72
|
-
:
|
73
|
-
:
|
74
|
-
}
|
72
|
+
file_type: string[0, 8],
|
73
|
+
version: "#{string[8, 2].to_i}.#{string[10, 2].to_i}",
|
74
|
+
file_originator: string[12, 32].strip!,
|
75
|
+
source_product: string[44, 16].strip!,
|
76
|
+
production_datetime: string[60, 14]
|
77
|
+
}
|
75
78
|
end
|
76
|
-
|
79
|
+
|
77
80
|
def parse_bank_holiday(string)
|
78
81
|
{
|
79
|
-
:
|
80
|
-
:
|
81
|
-
:
|
82
|
+
record_identity: string[0, 2],
|
83
|
+
transaction_type: string[2, 1],
|
84
|
+
date_of_bank_holiday: string[3, 8]
|
82
85
|
}
|
83
86
|
end
|
84
|
-
|
87
|
+
|
85
88
|
def parse_operator(string)
|
86
89
|
{
|
87
|
-
:
|
88
|
-
:
|
89
|
-
:
|
90
|
-
:
|
91
|
-
:
|
90
|
+
record_identity: string[0, 2],
|
91
|
+
transaction_type: string[2, 1],
|
92
|
+
operator: parse_value(string[3, 4]),
|
93
|
+
operator_short_form: parse_value(string[7, 24]),
|
94
|
+
operator_legal_name: parse_value(string[31, 48])
|
92
95
|
}
|
93
96
|
end
|
94
97
|
|
95
98
|
def parse_additional_location_info(string)
|
96
99
|
{
|
97
|
-
:
|
98
|
-
:
|
99
|
-
:
|
100
|
-
:
|
101
|
-
:
|
100
|
+
record_identity: string[0, 2],
|
101
|
+
transaction_type: string[2, 1],
|
102
|
+
location: string[3, 12].strip,
|
103
|
+
grid_reference_easting: parse_value(string[15, 8]),
|
104
|
+
grid_reference_northing: parse_value(string[23, 8])
|
102
105
|
}
|
103
106
|
end
|
104
107
|
|
105
108
|
def parse_location(string)
|
106
109
|
{
|
107
|
-
:
|
108
|
-
:
|
109
|
-
:
|
110
|
-
:
|
111
|
-
:
|
110
|
+
record_identity: string[0, 2],
|
111
|
+
transaction_type: string[2, 1],
|
112
|
+
location: parse_value(string[3, 12]),
|
113
|
+
full_location: parse_value(string[15, 48]),
|
114
|
+
gazetteer_code: string[63, 1]
|
112
115
|
}
|
113
116
|
end
|
114
117
|
|
115
118
|
def parse_destination(string)
|
116
119
|
{
|
117
|
-
:
|
118
|
-
:
|
119
|
-
:
|
120
|
-
:
|
121
|
-
:
|
122
|
-
:
|
120
|
+
record_identity: string[0, 2],
|
121
|
+
location: string[2, 12],
|
122
|
+
published_arrival_time: string[14, 4],
|
123
|
+
bay_number: parse_value(string[18, 3]),
|
124
|
+
timing_point_indicator: string[21, 2],
|
125
|
+
fare_stage_indicator: string[23, 2]
|
123
126
|
}
|
124
127
|
end
|
125
128
|
|
126
129
|
def parse_intermediate(string)
|
127
130
|
{
|
128
|
-
:
|
129
|
-
:
|
130
|
-
:
|
131
|
-
:
|
132
|
-
:
|
133
|
-
:
|
134
|
-
:
|
135
|
-
:
|
131
|
+
record_identity: string[0, 2],
|
132
|
+
location: string[2, 12],
|
133
|
+
published_arrival_time: string[14, 4],
|
134
|
+
published_departure_time: string[18, 4],
|
135
|
+
activity_flag: string[22, 1],
|
136
|
+
bay_number: parse_value(string[23, 3]),
|
137
|
+
timing_point_indicator: string[26, 2],
|
138
|
+
fare_stage_indicator: string[28, 2]
|
136
139
|
}
|
137
140
|
end
|
138
141
|
|
139
142
|
def parse_origin(string)
|
140
143
|
{
|
141
|
-
:
|
142
|
-
:
|
143
|
-
:
|
144
|
-
:
|
145
|
-
:
|
146
|
-
:
|
144
|
+
record_identity: string[0, 2],
|
145
|
+
location: string[2, 12],
|
146
|
+
published_departure_time: string[14, 4],
|
147
|
+
bay_number: parse_value(string[18, 3]),
|
148
|
+
timing_point_indicator: string[21, 2],
|
149
|
+
fare_stage_indicator: string[23, 2]
|
147
150
|
}
|
148
151
|
end
|
149
152
|
|
150
153
|
def parse_journey_header(string)
|
151
154
|
{
|
152
|
-
:
|
153
|
-
:
|
154
|
-
:
|
155
|
-
:
|
156
|
-
:
|
157
|
-
:
|
158
|
-
:
|
159
|
-
:
|
160
|
-
:
|
161
|
-
:
|
162
|
-
:
|
163
|
-
:
|
164
|
-
:
|
165
|
-
:
|
166
|
-
:
|
167
|
-
:
|
168
|
-
:
|
169
|
-
:
|
170
|
-
:
|
171
|
-
:
|
155
|
+
record_identity: string[0, 2],
|
156
|
+
transaction_type: string[2, 1],
|
157
|
+
operator: string[3, 4].strip,
|
158
|
+
unique_journey_identifier: string[7, 6],
|
159
|
+
first_date_of_operation: parse_value(string[13, 8]),
|
160
|
+
last_date_of_operation: parse_value(string[21, 8]),
|
161
|
+
operates_on_mondays: string[29, 1],
|
162
|
+
operates_on_tuesdays: string[30, 1],
|
163
|
+
operates_on_wednesdays: string[31, 1],
|
164
|
+
operates_on_thursdays: string[32, 1],
|
165
|
+
operates_on_fridays: string[33, 1],
|
166
|
+
operates_on_saturdays: string[34, 1],
|
167
|
+
operates_on_sundays: string[35, 1],
|
168
|
+
school_term_time: parse_value(string[36, 1]),
|
169
|
+
bank_holidays: parse_value(string[37, 1]),
|
170
|
+
route_number: parse_value(string[38, 4]),
|
171
|
+
running_board: parse_value(string[42, 6]),
|
172
|
+
vehicle_type: parse_value(string[48, 8]),
|
173
|
+
registration_number: parse_value(string[56, 8]),
|
174
|
+
route_direction: string[64, 1]
|
172
175
|
}
|
173
176
|
end
|
174
|
-
|
177
|
+
|
175
178
|
def parse_value(value)
|
176
|
-
|
179
|
+
value&.strip
|
177
180
|
end
|
178
181
|
end
|
179
|
-
|
180
|
-
end
|
182
|
+
end
|
data/spec/atco_spec.rb
CHANGED
@@ -1,150 +1,174 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "#{File.dirname(__FILE__)}/spec_helper"
|
2
4
|
require 'json'
|
3
5
|
|
4
6
|
describe Atco do
|
5
|
-
|
6
7
|
before(:all) do
|
7
|
-
|
8
8
|
end
|
9
|
-
|
10
|
-
it
|
9
|
+
|
10
|
+
it 'should output file for debugging!' do
|
11
11
|
result = Atco.parse('spec/fixtures/example.cif')
|
12
12
|
File.open('test.output', 'w+') do |f|
|
13
13
|
f.flush
|
14
14
|
f.write(JSON.pretty_generate(result))
|
15
15
|
end
|
16
|
-
#fixture = JSON.parse(File.read('spec/fixtures/example.json'))
|
17
16
|
end
|
18
|
-
|
19
|
-
it
|
17
|
+
|
18
|
+
it 'should parse header from fixture' do
|
20
19
|
result = Atco.parse('spec/fixtures/example.cif')
|
21
|
-
result[:header]
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
20
|
+
expect result[:header] == {
|
21
|
+
file_type: 'ATCO-CIF',
|
22
|
+
file_originator: 'Electronic Registration',
|
23
|
+
source_product: 'MIA 4.20.18',
|
24
|
+
version: '5.0',
|
25
|
+
production_datetime: '20090915113809'
|
27
26
|
}
|
28
27
|
end
|
29
|
-
|
30
|
-
it
|
28
|
+
|
29
|
+
it 'should parse locations from fixture' do
|
31
30
|
result = Atco.parse('spec/fixtures/example.cif')
|
32
|
-
result[:header]
|
33
|
-
:
|
34
|
-
:
|
35
|
-
:
|
36
|
-
:
|
37
|
-
:
|
31
|
+
expect result[:header] == {
|
32
|
+
file_type: 'ATCO-CIF',
|
33
|
+
file_originator: 'Electronic Registration',
|
34
|
+
source_product: 'MIA 4.20.18',
|
35
|
+
version: '5.0',
|
36
|
+
production_datetime: '20090915113809'
|
38
37
|
}
|
39
38
|
end
|
40
|
-
|
41
|
-
it
|
42
|
-
Atco.parse_header("ATCO-CIF0500Electronic Registration MIA 4.20.18 20090915113809\r\n")
|
43
|
-
:
|
44
|
-
:
|
45
|
-
:
|
46
|
-
:
|
47
|
-
:
|
39
|
+
|
40
|
+
it 'should parse header' do
|
41
|
+
expect Atco.parse_header("ATCO-CIF0500Electronic Registration MIA 4.20.18 20090915113809\r\n") == {
|
42
|
+
file_type: 'ATCO-CIF',
|
43
|
+
version: '5.0',
|
44
|
+
file_originator: 'Electronic Registration',
|
45
|
+
source_product: 'MIA 4.20.18',
|
46
|
+
production_datetime: '20090915113809'
|
48
47
|
}
|
49
48
|
end
|
50
|
-
|
51
|
-
it
|
52
|
-
Atco.parse_bank_holiday(
|
53
|
-
:
|
54
|
-
:
|
55
|
-
:
|
49
|
+
|
50
|
+
it 'should parse bank holiday' do
|
51
|
+
expect Atco.parse_bank_holiday('QHN20061225') == {
|
52
|
+
record_identity: 'QH',
|
53
|
+
transaction_type: 'N',
|
54
|
+
date_of_bank_holiday: '20061225'
|
56
55
|
}
|
57
56
|
end
|
58
|
-
|
59
|
-
it
|
60
|
-
Atco.parse_operator("QPNTM Translink Metro Translink Metro \r\n")
|
61
|
-
:
|
62
|
-
:
|
63
|
-
:
|
64
|
-
:
|
65
|
-
:
|
57
|
+
|
58
|
+
it 'should parse operator' do
|
59
|
+
expect Atco.parse_operator("QPNTM Translink Metro Translink Metro \r\n") == {
|
60
|
+
record_identity: 'QP',
|
61
|
+
transaction_type: 'N',
|
62
|
+
operator: 'TM',
|
63
|
+
operator_short_form: 'Translink Metro',
|
64
|
+
operator_legal_name: 'Translink Metro'
|
66
65
|
}
|
67
66
|
end
|
68
|
-
|
69
|
-
it
|
70
|
-
Atco.parse_additional_location_info("QBN700000001252 328622 367433 \r\n")
|
71
|
-
:
|
72
|
-
:
|
73
|
-
:
|
74
|
-
:
|
75
|
-
:
|
67
|
+
|
68
|
+
it 'should parse additional location information' do
|
69
|
+
expect Atco.parse_additional_location_info("QBN700000001252 328622 367433 \r\n") == {
|
70
|
+
record_identity: 'QB',
|
71
|
+
transaction_type: 'N',
|
72
|
+
location: '700000001252',
|
73
|
+
grid_reference_easting: '328622',
|
74
|
+
grid_reference_northing: '367433'
|
76
75
|
}
|
77
76
|
end
|
78
|
-
|
79
|
-
it
|
80
|
-
Atco.parse_location("QLN700000001252Conway (River Rd) 1\r\n")
|
81
|
-
:
|
82
|
-
:
|
83
|
-
:
|
84
|
-
:
|
85
|
-
:
|
77
|
+
|
78
|
+
it 'should parse location' do
|
79
|
+
expect Atco.parse_location("QLN700000001252Conway (River Rd) 1\r\n") == {
|
80
|
+
record_identity: 'QL',
|
81
|
+
transaction_type: 'N',
|
82
|
+
location: '700000001252',
|
83
|
+
full_location: 'Conway (River Rd)',
|
84
|
+
gazetteer_code: '1'
|
86
85
|
}
|
87
86
|
end
|
88
87
|
|
89
88
|
# QT7000000012520605 T1F0
|
90
|
-
it
|
91
|
-
Atco.parse_destination("QT7000000012520605 T1F0\r\n")
|
92
|
-
:
|
93
|
-
:
|
94
|
-
:
|
95
|
-
:
|
96
|
-
:
|
97
|
-
:
|
89
|
+
it 'should parse destination' do
|
90
|
+
expect Atco.parse_destination("QT7000000012520605 T1F0\r\n") == {
|
91
|
+
record_identity: 'QT',
|
92
|
+
location: '700000001252',
|
93
|
+
published_arrival_time: '0605',
|
94
|
+
bay_number: '',
|
95
|
+
timing_point_indicator: 'T1',
|
96
|
+
fare_stage_indicator: 'F0'
|
98
97
|
}
|
99
98
|
end
|
100
|
-
|
101
|
-
it
|
102
|
-
Atco.parse_intermediate("QI70000000125607120712B T1F0\r\n")
|
103
|
-
:
|
104
|
-
:
|
105
|
-
:
|
106
|
-
:
|
107
|
-
:
|
108
|
-
:
|
109
|
-
:
|
110
|
-
:
|
99
|
+
|
100
|
+
it 'should parse intermediate' do
|
101
|
+
expect Atco.parse_intermediate("QI70000000125607120712B T1F0\r\n") == {
|
102
|
+
record_identity: 'QI',
|
103
|
+
location: '700000001256',
|
104
|
+
published_arrival_time: '0712',
|
105
|
+
published_departure_time: '0712',
|
106
|
+
activity_flag: 'B',
|
107
|
+
bay_number: '',
|
108
|
+
timing_point_indicator: 'T1',
|
109
|
+
fare_stage_indicator: 'F0'
|
111
110
|
}
|
112
111
|
end
|
113
|
-
|
114
|
-
it
|
115
|
-
Atco.parse_origin("QO7000000012520730 T1F0\r\n")
|
116
|
-
:
|
117
|
-
:
|
118
|
-
:
|
119
|
-
:
|
120
|
-
:
|
121
|
-
:
|
112
|
+
|
113
|
+
it 'should parse origin' do
|
114
|
+
expect Atco.parse_origin("QO7000000012520730 T1F0\r\n") == {
|
115
|
+
record_identity: 'QO',
|
116
|
+
location: '700000001252',
|
117
|
+
published_departure_time: '0730',
|
118
|
+
bay_number: '',
|
119
|
+
timing_point_indicator: 'T1',
|
120
|
+
fare_stage_indicator: 'F0'
|
122
121
|
}
|
123
122
|
end
|
124
123
|
|
125
|
-
it
|
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
|
-
:
|
124
|
+
it 'should parse journey header' do
|
125
|
+
expect Atco.parse_journey_header("QSNTM 13986520091005 1111100 9A 9018 0 I\r\n") == {
|
126
|
+
record_identity: 'QS',
|
127
|
+
transaction_type: 'N',
|
128
|
+
operator: 'TM',
|
129
|
+
unique_journey_identifier: '139865',
|
130
|
+
first_date_of_operation: '20091005',
|
131
|
+
last_date_of_operation: '',
|
132
|
+
operates_on_mondays: '1',
|
133
|
+
operates_on_tuesdays: '1',
|
134
|
+
operates_on_wednesdays: '1',
|
135
|
+
operates_on_thursdays: '1',
|
136
|
+
operates_on_fridays: '1',
|
137
|
+
operates_on_saturdays: '0',
|
138
|
+
operates_on_sundays: '0',
|
139
|
+
school_term_time: '',
|
140
|
+
bank_holidays: '',
|
141
|
+
route_number: '9A',
|
142
|
+
running_board: '9018',
|
143
|
+
vehicle_type: '0',
|
144
|
+
registration_number: '',
|
145
|
+
route_direction: 'I'
|
147
146
|
}
|
148
147
|
end
|
149
148
|
|
150
|
-
|
149
|
+
describe 'with example.cif' do
|
150
|
+
before(:all) do
|
151
|
+
@atco = Atco.parse('spec/fixtures/example.cif')
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'should parse 1 journey' do
|
155
|
+
expect @atco[:journeys].size == 1
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'should parse journeys into Atco::Joruney objects' do
|
159
|
+
expect(@atco[:journeys]['139748']).to be_a_kind_of(Atco::Journey)
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should parse 6 stops for joureny 139748' do
|
163
|
+
expect @atco[:journeys]['139748'].stops.size == 6
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should parse 6 stops for joureny 139748' do
|
167
|
+
expect @atco[:journeys]['139748'].stops.size == 6
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'should parse 2 locations' do
|
171
|
+
expect @atco[:locations].size == 2
|
172
|
+
end
|
173
|
+
end
|
174
|
+
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,67 +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
|
-
- 1
|
9
|
-
version: 0.0.1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
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-04 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: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '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: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
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
51
|
- VERSION
|
33
52
|
- lib/atco.rb
|
34
|
-
- lib/
|
35
|
-
|
53
|
+
- lib/atco/journey.rb
|
54
|
+
- lib/atco/location.rb
|
55
|
+
- lib/atco/stop.rb
|
56
|
+
- spec/atco_spec.rb
|
57
|
+
- spec/fixtures/example.cif
|
58
|
+
- spec/fixtures/example.json
|
59
|
+
- spec/spec_helper.rb
|
36
60
|
homepage: http://github.com/davidjrice/atco
|
37
|
-
licenses:
|
38
|
-
|
61
|
+
licenses:
|
62
|
+
- MIT
|
63
|
+
metadata: {}
|
39
64
|
post_install_message:
|
40
|
-
rdoc_options:
|
41
|
-
- --charset=UTF-8
|
42
|
-
require_paths:
|
65
|
+
rdoc_options:
|
66
|
+
- "--charset=UTF-8"
|
67
|
+
require_paths:
|
43
68
|
- lib
|
44
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
-
requirements:
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
46
71
|
- - ">="
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
-
requirements:
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
53
76
|
- - ">="
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
|
56
|
-
- 0
|
57
|
-
version: "0"
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
58
79
|
requirements: []
|
59
|
-
|
60
|
-
rubyforge_project:
|
61
|
-
rubygems_version: 1.3.6
|
80
|
+
rubygems_version: 3.5.3
|
62
81
|
signing_key:
|
63
|
-
specification_version:
|
64
|
-
summary:
|
65
|
-
test_files:
|
66
|
-
- spec/atco_spec.rb
|
67
|
-
- spec/spec_helper.rb
|
82
|
+
specification_version: 4
|
83
|
+
summary: Parse ATCO .cif files to JSON with Ruby
|
84
|
+
test_files: []
|
data/lib/location.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
class Location
|
2
|
-
|
3
|
-
attr_accessor :name, :identifier, :easting, :northing, :gazeteer_code
|
4
|
-
|
5
|
-
def initialize(location_header, additional_location_information)
|
6
|
-
@name = location_header[:full_location]
|
7
|
-
@identifier = location_header[:record_identity]
|
8
|
-
@easting = additional_location_information[:grid_reference_easting]
|
9
|
-
@northing = additional_location_information[:grid_reference_northing]
|
10
|
-
@gazeteer_code = location_header[:gazetteer_code]
|
11
|
-
end
|
12
|
-
|
13
|
-
def to_json(*a)
|
14
|
-
{
|
15
|
-
:name => @name,
|
16
|
-
:identifier => @identifier,
|
17
|
-
:easting => @easting,
|
18
|
-
:northing => @northing,
|
19
|
-
:gazeteer_code => @gazeteer_code
|
20
|
-
}.to_json(*a)
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|