OGTraf 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6791fcdbb6bee86906c6e14792a247e2daa29eb96c4bc95a443cb7c18deed138
4
+ data.tar.gz: bbe5d1094ea389bfa067477011c5e759faa9227734689131ddac263d2953807d
5
+ SHA512:
6
+ metadata.gz: 38f6ed583d443925f144940f272d7bbcdcb7fd93199f25ca5cf6f774c43d41072f53a77c1288c2da50b287efe31d98d14fa97ef936d92ba4013fccefb7117be4
7
+ data.tar.gz: f0b0dbffcced85e577061ae99e699eed2c22b38b4bed086d49180e25e738f04a9891b7a572c4ebfce0ebcb789509dd328d2a58eff56fbc8145454bcb9f94262d
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ogtraf'
4
+ require 'time'
5
+ require 'thor'
6
+
7
+ # Implements the OGTraf CLI
8
+ class OGTrafCLI < Thor
9
+ class_option :verbose, type: :boolean, default: false, aliases: '-v'
10
+
11
+ desc 'journey FROM TO', 'Plans a journey from FROM to TO'
12
+ option :walk, default: false, type: :boolean, desc: 'Is walking allowed?'
13
+ option :num, default: 5, type: :numeric, desc: 'Number of entries wanted',
14
+ aliases: '-n'
15
+
16
+ option :time, default: nil, type: :string, desc: 'Date+Time to search from',
17
+ aliases: '-t'
18
+
19
+ def journey(from, to)
20
+ from_s = OGTraf.stops(from).first
21
+ to_s = OGTraf.stops(to).first
22
+ time = Time.now
23
+ time = Time.parse(options[:date]) if options[:date]
24
+
25
+ puts "Next #{options[:num]} journeys from #{from_s} to #{to_s} after #{time}:"
26
+
27
+ shown = 0
28
+ until shown >= options[:num]
29
+ avail = OGTraf.journey(
30
+ from, to,
31
+ date: time,
32
+ span: :after,
33
+ walk: options[:walk],
34
+ verbose: options[:verbose]
35
+ )
36
+
37
+ avail.each do |j|
38
+ next if j.departure < Time.now
39
+
40
+ time_left = j.departure - Time.now
41
+
42
+ hours = (time_left / 3600).floor
43
+ minutes = ((time_left % 3600) / 60).floor
44
+
45
+ puts
46
+ puts "In #{format('%02d:%02d', hours, minutes)}:" if hours > 0
47
+ puts "In #{minutes} minutes:" if hours.zero?
48
+
49
+ j.connections.each do |c|
50
+ if c.line.walk
51
+ puts format('- (%s - %s) walk to %s',
52
+ c.departure.strftime('%H:%M'),
53
+ c.arrival.strftime('%H:%M'),
54
+ c.to.to_s)
55
+ else
56
+ puts format('- (%s - %s) %s from %s to %s',
57
+ c.departure.strftime('%H:%M'),
58
+ c.arrival.strftime('%H:%M'),
59
+ c.line.to_s,
60
+ c.from.to_s,
61
+ c.to.to_s)
62
+ end
63
+ end
64
+
65
+ shown += 1
66
+ break if shown >= options[:num]
67
+
68
+ time = j.departure
69
+ end
70
+ end
71
+ end
72
+
73
+ desc 'stop STOP', 'Finds a stop by the given name'
74
+ option :limit, default: 5, type: :numeric, desc: 'Number of entries wanted',
75
+ aliases: '-l'
76
+ def stop(name)
77
+ puts 'Found stops:'
78
+ puts OGTraf.stops(
79
+ name,
80
+ verbose: options[:verbose]
81
+ ).take(options[:limit]).map(&:name)
82
+ end
83
+ end
84
+
85
+ OGTrafCLI.start
@@ -0,0 +1,141 @@
1
+ require 'cgi'
2
+ require 'json'
3
+ require 'logging'
4
+ require 'net/http'
5
+ require 'ogtraf/connection'
6
+ require 'ogtraf/departure'
7
+ require 'ogtraf/deviation'
8
+ require 'ogtraf/journey'
9
+ require 'ogtraf/line'
10
+ require 'ogtraf/stop'
11
+ require 'ogtraf/version'
12
+ require 'uri'
13
+
14
+ module OGTraf
15
+ module Priority
16
+ SHORTEST_TIME = 0
17
+ FEWER_CHANGES = 1
18
+ end
19
+
20
+ def self.stops(name, options = {})
21
+ query = {
22
+ pointType: nil
23
+ }.merge(options)
24
+
25
+ query[:q] = CGI.escape name.to_s
26
+
27
+ uri = URI('https://rest.ostgotatrafiken.se/stops/Find')
28
+ uri.query = URI.encode_www_form(query)
29
+
30
+ j = run_query(uri)
31
+ j.map { |v| Stop.new v }
32
+ end
33
+
34
+ def self.departures(departure_start, options = {})
35
+ query = {
36
+ date: nil,
37
+ delay: 0,
38
+ maxNumberOfResultPerColumn: 8,
39
+ columnsPerPageCount: 1,
40
+ pagesCount: 1,
41
+ lines: nil,
42
+ trafficTypes: nil,
43
+ stopPoints: nil
44
+ }.merge(options)
45
+
46
+ departure_start = stops(departure_start).first unless departure_start.is_a? Stop
47
+
48
+ raise 'Date must be a Time' unless query[:date].is_a? Time
49
+
50
+ query.merge!(
51
+ date: query[:date].strftime('%Y-%m-%d+%H:%M'),
52
+ stopAreaId: departure_start.id
53
+ )
54
+
55
+ uri = URI('https://rest.ostgotatrafiken.se/stopdepartures/departures')
56
+ uri.query = URI.encode_www_form(query)
57
+
58
+ j = run_query(uri)
59
+ j[:groups].first.map { |v| Departure.new v[:Line] }
60
+ end
61
+
62
+ def self.journey(journey_start, journey_end, options = {})
63
+ query = {
64
+ date: Time.now,
65
+ direction: 0,
66
+ span: :default,
67
+ traffictypes: 0,
68
+ changetime: 0,
69
+ priority: Priority::SHORTEST_TIME,
70
+ walk: false
71
+ }.merge(options)
72
+
73
+ journey_start = stops(journey_start).first unless journey_start.is_a? Stop
74
+ journey_end = stops(journey_end).first unless journey_end.is_a? Stop
75
+
76
+ raise 'Date must be a Time' unless query[:date].is_a? Time
77
+
78
+ query.merge!(
79
+ time: query[:date].strftime('%H:%M'),
80
+ date: query[:date].strftime('%Y-%m-%d'),
81
+
82
+ startId: journey_start.id,
83
+ startType: journey_start.type,
84
+ startLl: journey_start.gps_ll,
85
+ startName: journey_start.name,
86
+ endId: journey_end.id,
87
+ endType: journey_end.type,
88
+ endLl: journey_end.gps_ll,
89
+ endName: journey_end.name
90
+ )
91
+
92
+ uri = URI('https://rest.ostgotatrafiken.se/journey/Find')
93
+ uri.query = URI.encode_www_form(query)
94
+
95
+ j = run_query(uri, error: true)
96
+ j[:Journeys].map { |v| Journey.new v }
97
+ end
98
+
99
+ def self.run_query(uri, _options = {})
100
+ logger.debug uri
101
+
102
+ h = Net::HTTP.new(uri.host, uri.port)
103
+ h.use_ssl = uri.scheme == 'https'
104
+
105
+ r = h.start do |http|
106
+ http.request(Net::HTTP::Get.new(uri))
107
+ end
108
+
109
+ logger.debug r
110
+
111
+ begin
112
+ j = JSON.parse(r.body, symbolize_names: true)
113
+
114
+ logger.debug j
115
+
116
+ raise j unless r.is_a? Net::HTTPSuccess
117
+
118
+ if j.is_a? Array
119
+ first = j.first
120
+ if first.key? :ErrorCode
121
+ raise first[:ErrorText] unless first[:ErrorCode].zero?
122
+ end
123
+ end
124
+
125
+ j
126
+ rescue JSON::ParserError
127
+ raise "HTTP #{r.code} Response with unparseable JSON."
128
+ end
129
+ end
130
+
131
+ def self.debug!
132
+ logger.level = :debug
133
+ end
134
+
135
+ def self.logger
136
+ @logger ||= Logging.logger[self].tap do |logger|
137
+ logger.add_appenders Logging.appenders.stdout
138
+ logger.level = :info
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,29 @@
1
+ require 'time'
2
+
3
+ module OGTraf
4
+ class Connection
5
+ attr_reader :departure, :real_departure, :arrival, :real_arrival, :from,
6
+ :to, :line, :deviations
7
+
8
+ def initialize(datablock)
9
+ @departure = Time.parse datablock[:DepDateTime]
10
+ @real_departure = @departure
11
+
12
+ @arrival = Time.parse datablock[:ArrDateTime]
13
+ @real_arrival = @arrival
14
+
15
+ if datablock[:RealTime] && datablock[:RealTime][:RealTimeInfo]
16
+ datablock[:RealTime][:RealTimeInfo].each do |rti|
17
+ @departure += rti[:DepTimeDeviation] * 60
18
+ @arrival += rti[:ArrTimeDeviation] * 60
19
+ end
20
+ end
21
+
22
+ @from = Stop.new datablock[:From]
23
+ @to = Stop.new datablock[:To]
24
+
25
+ @line = Line.new datablock[:Line]
26
+ @deviations = datablock[:Deviations].map { |d| Deviation.new d }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ module OGTraf
2
+ #
3
+ class Departure
4
+ attr_reader :from, :to, :time, :real_time, :name, :stops
5
+
6
+ def initialize(datablock)
7
+ @from = datablock[:from]
8
+ @to = datablock[:to]
9
+ @time = datablock[:time]
10
+ @real_time = datablock[:real_time]
11
+ @name = datablock[:name]
12
+ @stops = datablock[:stops]
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ require 'time'
2
+
3
+ module OGTraf
4
+ #
5
+ class Deviation
6
+ attr_reader :header, :summary, :details, :text, :from, :to
7
+
8
+ def initialize(datablock)
9
+ @header = datablock[:Header]
10
+ @summary = datablock[:Summary] || datablock[:PublicNote]
11
+ @details = datablock[:Details]
12
+ @text = datablock[:ShortText]
13
+
14
+ @from = Time.now
15
+ @to = Time.now
16
+
17
+ datablock[:DeviationScopes].each do |dev|
18
+ from = Time.parse dev[:FromDateTime]
19
+ @from = from if @from > from
20
+ to = Time.parse dev[:ToDateTime]
21
+ @to = to if @to < to
22
+ end
23
+ end
24
+
25
+ def to_s
26
+ @header.to_s
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,42 @@
1
+ require 'time'
2
+
3
+ module OGTraf
4
+ #
5
+ class Journey
6
+ attr_reader :departure, :arrival, :from, :to,
7
+ :real_departure, :real_arrival,
8
+ :connections
9
+
10
+ def initialize(datablock)
11
+ @real_departure = Time.parse(datablock[:Departure])
12
+ @real_arrival = Time.parse(datablock[:Arrival])
13
+
14
+ @from = Stop.new datablock[:UsedStartPlace]
15
+ @to = Stop.new datablock[:UsedEndPlace]
16
+
17
+ @connections = datablock[:Routelinks].map { |r| Connection.new r }
18
+
19
+ @departure = @connections.first.departure
20
+ arrival_point = @connections.find { |c| c.to == @to }
21
+ @arrival = @real_arrival
22
+ @arrival = arrival_point.arrival unless arrival_point.nil?
23
+ end
24
+
25
+ def on_time?(side = :arr, allowed_drift = 60)
26
+ raise ArgumentError unless
27
+ %i[both dep departure arr arrival].include? side
28
+
29
+ if side == :both
30
+ return (@departure - @real_departure).abs < allowed_drift &&
31
+ (@arrival - @real_arrival).abs < allowed_drift
32
+ end
33
+
34
+ case side
35
+ when :dep, :departure
36
+ (@departure - @real_departure).abs < allowed_drift
37
+ when :arr, :arrival
38
+ (@arrival - @real_arrival).abs < allowed_drift
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+ module OGTraf
2
+ #
3
+ class Line
4
+ attr_reader :name, :number, :note, :operator, :type, :towards, :train_nr,
5
+ :walk
6
+
7
+ def initialize(datablock)
8
+ @name = datablock[:LineName]
9
+ @number = datablock[:LineNrReal]
10
+ @note = datablock[:FootNote]
11
+ @operator = {
12
+ id: datablock[:OperatorId],
13
+ name: datablock[:OperatorName]
14
+ }
15
+ @type = datablock[:LineTypeName].to_sym
16
+ @towards = datablock[:Towards]
17
+ @train_nr = datablock[:TrainNo]
18
+ @walk = datablock[:IsWalk]
19
+ end
20
+
21
+ def to_s
22
+ @name.to_s
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,32 @@
1
+ module OGTraf
2
+ #
3
+ class Stop
4
+ include Comparable
5
+ attr_reader :name, :id, :gps, :clean_name, :type
6
+
7
+ def initialize(datablock)
8
+ @name = datablock[:PlaceName]
9
+ @id = datablock[:Id]
10
+ if datablock.key?(:Ll) && !datablock[:Ll].nil?
11
+ @gps = {
12
+ lon: datablock[:Ll][0],
13
+ lat: datablock[:Ll][1]
14
+ }
15
+ end
16
+ @clean_name = datablock[:OgtStopUrlSegment]
17
+ @type = datablock[:OgtType].to_sym
18
+ end
19
+
20
+ def gps_ll
21
+ [@gps[:lon], @gps[:lat]].join ','
22
+ end
23
+
24
+ def to_s
25
+ @name.to_s
26
+ end
27
+
28
+ def <=>(other)
29
+ @id <=> other.id
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module OGTraf
2
+ VERSION = '0.1.0'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: OGTraf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alexander "Ace" Olofsson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-08-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: logging
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.19'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.19'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.9'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.9'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.41'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.41'
97
+ description: Allows querying Östgötatrafiken's APIs
98
+ email: ace@haxalot.com
99
+ executables:
100
+ - ogtraf
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - bin/ogtraf
105
+ - lib/ogtraf.rb
106
+ - lib/ogtraf/connection.rb
107
+ - lib/ogtraf/departure.rb
108
+ - lib/ogtraf/deviation.rb
109
+ - lib/ogtraf/journey.rb
110
+ - lib/ogtraf/line.rb
111
+ - lib/ogtraf/stop.rb
112
+ - lib/ogtraf/version.rb
113
+ homepage:
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.7.7
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: Östgötatrafiken
137
+ test_files: []