atco 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.
data/README.mdown ADDED
@@ -0,0 +1,27 @@
1
+ ## ATCO-CIF
2
+
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
+
5
+ * **Official spec:** [http://www.pti.org.uk/CIF/atco-cif-spec.pdf](http://www.pti.org.uk/CIF/atco-cif-spec.pdf)
6
+
7
+ ### USAGE
8
+
9
+ Currently this library is under-development and has several things left to do before it is perfect (see the [todo.mdown](http://github.com/davidjrice/atco/blob/master/todo.mdown) list ).
10
+
11
+ * clone this library
12
+ * start an irb session
13
+ * put the cif file in ./data (needs to change from being hardcoded)
14
+
15
+ Code example, for more detailed internal api usage see the spec files.
16
+
17
+ require 'lib/atco'
18
+
19
+ result = Atco.parse('filename.cif')
20
+ result = Atco.parse('SVRTMAO009A-20091005.cif) # an example data file
21
+
22
+ => {
23
+ :header => {...},
24
+ :locations => [...],
25
+ :journies => {...}
26
+ }
27
+
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ begin
2
+ require 'jeweler'
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
12
+ rescue LoadError
13
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install jeweler"
14
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/lib/atco.rb ADDED
@@ -0,0 +1,180 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'open3'
5
+ require 'tempfile'
6
+ require 'location'
7
+
8
+ module Atco
9
+ VERSION = '0.0.1'
10
+
11
+ class << self
12
+
13
+ @path = nil
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
+ }
24
+
25
+ def parse(file)
26
+ @path = File.expand_path(file)
27
+ data = File.readlines(@path)
28
+
29
+ objects = []
30
+ current_journey = nil
31
+ current_location = nil
32
+ locations = []
33
+ journeys = {}
34
+ header = nil
35
+
36
+ data.each do |line|
37
+ if line == data.first
38
+ header = parse_header(line)
39
+ next
40
+ end
41
+ @@methods.each do |method,identifier|
42
+ object = self.send("parse_#{method}", line)
43
+ if object[:record_identity] && object[:record_identity] == identifier
44
+ current_journey = object if object[:record_identity] && object[:record_identity] == @@methods[:journey_header]
45
+ if object[:record_identity] && ( object[:record_identity] == @@methods[:location] || object[:record_identity] == @@methods[:additional_location_info] )
46
+ if object[:record_identity] == @@methods[:location]
47
+ current_location = object
48
+ else
49
+ locations << Location.new(current_location, object)
50
+ end
51
+ end
52
+
53
+ if current_journey
54
+ if journeys[current_journey[:unique_journey_identifier]]
55
+ journeys[current_journey[:unique_journey_identifier]] << object
56
+ else
57
+ journeys[current_journey[:unique_journey_identifier]] = [object]
58
+ end
59
+ end
60
+ objects << object
61
+ end
62
+ end
63
+ end
64
+ return {:header => header, :locations => locations, :journeys => journeys}
65
+ end
66
+
67
+ def parse_header(string)
68
+ {
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
+ }
75
+ end
76
+
77
+ def parse_bank_holiday(string)
78
+ {
79
+ :record_identity => string[0,2],
80
+ :transaction_type => string[2,1],
81
+ :date_of_bank_holiday => string[3,8]
82
+ }
83
+ end
84
+
85
+ def parse_operator(string)
86
+ {
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])
92
+ }
93
+ end
94
+
95
+ def parse_additional_location_info(string)
96
+ {
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])
102
+ }
103
+ end
104
+
105
+ def parse_location(string)
106
+ {
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]
112
+ }
113
+ end
114
+
115
+ def parse_destination(string)
116
+ {
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]
123
+ }
124
+ end
125
+
126
+ def parse_intermediate(string)
127
+ {
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]
136
+ }
137
+ end
138
+
139
+ def parse_origin(string)
140
+ {
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]
147
+ }
148
+ end
149
+
150
+ def parse_journey_header(string)
151
+ {
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]
172
+ }
173
+ end
174
+
175
+ def parse_value(value)
176
+ return value.strip if value
177
+ end
178
+ end
179
+
180
+ end
data/lib/location.rb ADDED
@@ -0,0 +1,23 @@
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
data/spec/atco_spec.rb ADDED
@@ -0,0 +1,150 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'json'
3
+
4
+ describe Atco do
5
+
6
+ before(:all) do
7
+
8
+ end
9
+
10
+ it "should output file for debugging!" do
11
+ result = Atco.parse('spec/fixtures/example.cif')
12
+ File.open('test.output', 'w+') do |f|
13
+ f.flush
14
+ f.write(JSON.pretty_generate(result))
15
+ end
16
+ #fixture = JSON.parse(File.read('spec/fixtures/example.json'))
17
+ end
18
+
19
+ it "should parse header from fixture" do
20
+ result = Atco.parse('spec/fixtures/example.cif')
21
+ result[:header].should == {
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
+ }
28
+ end
29
+
30
+ it "should parse locations from fixture" do
31
+ result = Atco.parse('spec/fixtures/example.cif')
32
+ result[:header].should == {
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
+ }
39
+ end
40
+
41
+ it "should parse header" do
42
+ Atco.parse_header("ATCO-CIF0500Electronic Registration MIA 4.20.18 20090915113809\r\n").should == {
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
+ }
49
+ end
50
+
51
+ it "should parse bank holiday" do
52
+ Atco.parse_bank_holiday("QHN20061225").should == {
53
+ :record_identity => 'QH',
54
+ :transaction_type => 'N',
55
+ :date_of_bank_holiday => '20061225'
56
+ }
57
+ end
58
+
59
+ it "should parse operator" do
60
+ Atco.parse_operator("QPNTM Translink Metro Translink Metro \r\n").should == {
61
+ :record_identity => 'QP',
62
+ :transaction_type => 'N',
63
+ :operator => 'TM',
64
+ :operator_short_form => 'Translink Metro',
65
+ :operator_legal_name => 'Translink Metro'
66
+ }
67
+ end
68
+
69
+ it "should parse additional location information" do
70
+ Atco.parse_additional_location_info("QBN700000001252 328622 367433 \r\n").should == {
71
+ :record_identity => 'QB',
72
+ :transaction_type => 'N',
73
+ :location => '700000001252',
74
+ :grid_reference_easting => '328622',
75
+ :grid_reference_northing => '367433'
76
+ }
77
+ end
78
+
79
+ it "should parse location" do
80
+ Atco.parse_location("QLN700000001252Conway (River Rd) 1\r\n").should == {
81
+ :record_identity => 'QL',
82
+ :transaction_type => 'N',
83
+ :location => '700000001252',
84
+ :full_location => 'Conway (River Rd)',
85
+ :gazetteer_code => '1'
86
+ }
87
+ end
88
+
89
+ # QT7000000012520605 T1F0
90
+ it "should parse destination" do
91
+ Atco.parse_destination("QT7000000012520605 T1F0\r\n").should == {
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
+ }
99
+ end
100
+
101
+ it "should parse intermediate" do
102
+ Atco.parse_intermediate("QI70000000125607120712B T1F0\r\n").should == {
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
+ }
112
+ end
113
+
114
+ it "should parse origin" do
115
+ Atco.parse_origin("QO7000000012520730 T1F0\r\n").should == {
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
+ }
123
+ end
124
+
125
+ it "should parse journey header" do
126
+ Atco.parse_journey_header("QSNTM 13986520091005 1111100 9A 9018 0 I\r\n").should == {
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
+ }
148
+ end
149
+
150
+ end
@@ -0,0 +1,2 @@
1
+ require 'rubygems'
2
+ require File.dirname(__FILE__) + '/../lib/atco'
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: atco
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - David Rice
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-02-22 00:00:00 +00:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Simple and opinionated library for parsing ATCO-CIF files with Ruby.
22
+ email: me@davidjrice.co.uk
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README.mdown
29
+ files:
30
+ - README.mdown
31
+ - Rakefile
32
+ - VERSION
33
+ - lib/atco.rb
34
+ - lib/location.rb
35
+ has_rdoc: true
36
+ homepage: http://github.com/davidjrice/atco
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --charset=UTF-8
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ segments:
49
+ - 0
50
+ version: "0"
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.3.6
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: Simple and opinionated library for parsing ATCO-CIF files with Ruby.
65
+ test_files:
66
+ - spec/atco_spec.rb
67
+ - spec/spec_helper.rb