OGTraf 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.
@@ -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: []