tower_bridge_lifts 1.0.0
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/.gitignore +7 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +2 -0
- data/README.md +107 -0
- data/Rakefile +7 -0
- data/bin/tblifts +5 -0
- data/lib/tower_bridge_lifts.rb +13 -0
- data/lib/tower_bridge_lifts/application.rb +80 -0
- data/lib/tower_bridge_lifts/base.rb +118 -0
- data/lib/tower_bridge_lifts/helpers.rb +24 -0
- data/lib/tower_bridge_lifts/lift.rb +49 -0
- data/lib/tower_bridge_lifts/server.rb +36 -0
- data/lib/tower_bridge_lifts/templates/lifts.erb +21 -0
- data/lib/tower_bridge_lifts/templates/lifts_compact.erb +17 -0
- data/lib/tower_bridge_lifts/templates/lifts_group.erb +23 -0
- data/lib/tower_bridge_lifts/templates/lifts_group_compact.erb +20 -0
- data/lib/tower_bridge_lifts/templates/status.erb +9 -0
- data/lib/tower_bridge_lifts/version.rb +4 -0
- data/lib/tower_bridge_lifts/views.rb +114 -0
- data/reload +4 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/tower_bridge_lifts_spec.rb +32 -0
- data/tower_bridge_lifts.gemspec +31 -0
- data/web/apidoc.html +87 -0
- data/web/apidoc.yaml +163 -0
- data/web/css/print.css +1367 -0
- data/web/css/reset.css +125 -0
- data/web/css/screen.css +1497 -0
- data/web/css/style.css +249 -0
- data/web/css/tblifts.css +4 -0
- data/web/css/typography.css +14 -0
- data/web/fonts/DroidSans-Bold.ttf +0 -0
- data/web/fonts/DroidSans.ttf +0 -0
- data/web/images/collapse.gif +0 -0
- data/web/images/expand.gif +0 -0
- data/web/images/explorer_icons.png +0 -0
- data/web/images/logo_small.png +0 -0
- data/web/images/throbber.gif +0 -0
- data/web/images/wordnik_api.png +0 -0
- data/web/index.rhtml +42 -0
- data/web/lib/backbone-min.js +15 -0
- data/web/lib/es5-shim.js +2065 -0
- data/web/lib/handlebars-4.0.5.js +4608 -0
- data/web/lib/highlight.9.1.0.pack.js +2 -0
- data/web/lib/highlight.9.1.0.pack_extended.js +34 -0
- data/web/lib/jquery-1.8.0.min.js +2 -0
- data/web/lib/jquery.ba-bbq.min.js +18 -0
- data/web/lib/jquery.slideto.min.js +1 -0
- data/web/lib/jquery.wiggle.min.js +8 -0
- data/web/lib/js-yaml.min.js +3 -0
- data/web/lib/jsoneditor.min.js +11 -0
- data/web/lib/lodash.min.js +102 -0
- data/web/lib/marked.js +1272 -0
- data/web/lib/object-assign-pollyfill.js +23 -0
- data/web/lib/sanitize-html.min.js +6 -0
- data/web/lib/swagger-oauth.js +347 -0
- data/web/o2c.html +20 -0
- data/web/swagger-ui.js +25067 -0
- metadata +191 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2b539efb659f1637b3e60c99c07e35b1e9fc0c70
|
4
|
+
data.tar.gz: 9eb11ff0612123417622b655a6ef4e1ee90721c4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a25aa0e328e6d4775778e01a2533a3c74369db2b32385e2856c6d3f132ba9868463babbe8b3723c91fc9a87b39a6b5e782f305f8969baa771c097db2dafa6291
|
7
|
+
data.tar.gz: 0e71ad2fd65f61e0d98d6bdb20d0372e69c252efb7ac3f616cec59f574e7524245b1dfa3430e4016860c71e98577b6902c14f08dfd79064f9a83017f4072e71d
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
# Tower Bridge Lifts
|
2
|
+
|
3
|
+
Provides lift times information, parsed from TowerBridge.org.uk
|
4
|
+
|
5
|
+
[missing_demo_link](http://www.github.com)
|
6
|
+
|
7
|
+
|
8
|
+
## Description
|
9
|
+
This repo was born as I couldn't find an API to access bridge info to another side project and didn't hear back from the official website mantainers.
|
10
|
+
It parses data from the official page (http://www.towerbridge.org.uk/lift-times) and makes it available in other formats.
|
11
|
+
|
12
|
+
Bascule and traffic information are inferred from the lift times and might not correspond to reality.
|
13
|
+
(Please ping me if you'd like to collaborate and improve them using computer vision on this live stream
|
14
|
+
http://www.ustream.tv/towerbridge ;-)
|
15
|
+
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add it to your application's Gemfile:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem 'tower_bridge_lifts'
|
23
|
+
```
|
24
|
+
|
25
|
+
and..
|
26
|
+
|
27
|
+
$ bundle
|
28
|
+
|
29
|
+
OR from the command line:
|
30
|
+
|
31
|
+
$ gem install tower_bridge_lifts
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
### IRB or inside your own application:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
require 'tower_bridge_lifts'
|
39
|
+
|
40
|
+
bridge = TowerBridgeLifts::Base.new
|
41
|
+
puts bridge.status
|
42
|
+
puts bridge.time
|
43
|
+
puts bridge.lifts
|
44
|
+
puts bridge.next_lift
|
45
|
+
puts bridge.updated
|
46
|
+
|
47
|
+
```
|
48
|
+
### Command line wrapper:
|
49
|
+
|
50
|
+
```
|
51
|
+
$ tblifts --help
|
52
|
+
|
53
|
+
Tower Bridge Lifts - provides lift times information, parsed from towerbridge.org.uk
|
54
|
+
|
55
|
+
Usage:
|
56
|
+
tblifts [COMMAND] [OPTIONS]
|
57
|
+
|
58
|
+
Commands:
|
59
|
+
<none> | lifts shows all scheduled lifts
|
60
|
+
next | next_lift shows the next lift only
|
61
|
+
bascules shows the bascules position
|
62
|
+
time shows local time (London)
|
63
|
+
traffic shows traffic status accross the bridge
|
64
|
+
status shows all above
|
65
|
+
server serves an api in server mode
|
66
|
+
|
67
|
+
Options:
|
68
|
+
-d, --date=date filter by date (DD-MM-YY | today | tomorrow)
|
69
|
+
-c, --compact shows compact
|
70
|
+
-g, --group group lifts by date
|
71
|
+
-l, --lines=num lines to display
|
72
|
+
|
73
|
+
-p, --port=port sets the port on server mode
|
74
|
+
|
75
|
+
-h, --help displays help
|
76
|
+
-v, --version displays version
|
77
|
+
|
78
|
+
For more info, please visit http://github.com/aaparmeggiani/tower_bridge_lifts
|
79
|
+
|
80
|
+
$ tblifts --date=today
|
81
|
+
01-Jan-16 Sun 08:00 ⬆︎ A Vessel
|
82
|
+
01-Jan-16 Sun 13:00 ⬇︎ Another Vessel
|
83
|
+
|
84
|
+
$tblifts --group --lines=2
|
85
|
+
01-Jan-16 Sun [2] 08:00 13:00
|
86
|
+
02-Jan-16 Mon [4] 08:00 08:30 12:00 15:00 15:00
|
87
|
+
```
|
88
|
+
### Server mode:
|
89
|
+
|
90
|
+
tblifts can also operate as a daemon, serving json through RESTful http requests.
|
91
|
+
|
92
|
+
```
|
93
|
+
$ tblifts server -p 3000
|
94
|
+
<tblifts listening on port 3000>
|
95
|
+
|
96
|
+
$ curl http://localhost:3000/api/v1/status
|
97
|
+
<json answer>
|
98
|
+
```
|
99
|
+
API docs + sandbox can be found [here_missing_link](http://github.com).
|
100
|
+
|
101
|
+
## Contributing
|
102
|
+
|
103
|
+
Bug reports and pull requests are welcome through this repo.
|
104
|
+
|
105
|
+
## License
|
106
|
+
|
107
|
+
MIT
|
data/Rakefile
ADDED
data/bin/tblifts
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
module TowerBridgeLifts
|
3
|
+
DEBUG = false
|
4
|
+
end
|
5
|
+
|
6
|
+
require "tower_bridge_lifts/version"
|
7
|
+
require "tower_bridge_lifts/base"
|
8
|
+
require "tower_bridge_lifts/lift"
|
9
|
+
require "tower_bridge_lifts/views"
|
10
|
+
require "tower_bridge_lifts/helpers"
|
11
|
+
require "tower_bridge_lifts/application"
|
12
|
+
require "tower_bridge_lifts/server"
|
13
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
module TowerBridgeLifts
|
5
|
+
class Application
|
6
|
+
|
7
|
+
def run
|
8
|
+
options = {}
|
9
|
+
parser = OptionParser.new do |opt|
|
10
|
+
opt.banner = ""
|
11
|
+
opt.separator "Tower Bridge Lifts - provides lift times information, parsed from towerbridge.org.uk"
|
12
|
+
opt.separator ""
|
13
|
+
opt.separator "Usage:"
|
14
|
+
opt.separator " tblifts [COMMAND] [OPTIONS]"
|
15
|
+
opt.separator ""
|
16
|
+
opt.separator "Commands:"
|
17
|
+
opt.separator " <none> | lifts shows all scheduled lifts"
|
18
|
+
opt.separator " next | next_lift shows the next lift only"
|
19
|
+
opt.separator " bascules shows the bascules position"
|
20
|
+
opt.separator " time shows local time (London)"
|
21
|
+
opt.separator " traffic shows traffic status accross the bridge"
|
22
|
+
opt.separator " status shows all above"
|
23
|
+
opt.separator " server serves an api in server mode"
|
24
|
+
opt.separator ""
|
25
|
+
opt.separator "Options:"
|
26
|
+
|
27
|
+
opt.on('-d', '--date=date' , 'filter by date (DD-MM-YY | today | tomorrow)') do |date|
|
28
|
+
date = Time.now.strftime('%d-%m-%y') if date == 'today'
|
29
|
+
date = (Time.now + 24*60*60).strftime('%d-%m-%y') if date == 'tomorrow'
|
30
|
+
options[:date] = date
|
31
|
+
end
|
32
|
+
opt.on('-c', '--compact' , 'shows compact') {options[:compact] = true}
|
33
|
+
opt.on('-g', '--group' , 'group lifts by date') {options[:group] = true}
|
34
|
+
opt.on('-l', '--lines=num' , 'lines to display') {|num| options[:count] = num.to_i }
|
35
|
+
opt.separator ""
|
36
|
+
opt.on('-p', '--port=port' , 'sets the port on server mode') {|port| options[:port] = port.to_i}
|
37
|
+
opt.separator ""
|
38
|
+
opt.on('-h', '--help' , 'displays help') { puts opt; exit }
|
39
|
+
opt.on('-v', '--version' , 'displays version') { puts TowerBridgeLifts::VERSION; exit }
|
40
|
+
opt.separator ""
|
41
|
+
opt.separator "For more info, please visit http://github.com/aaparmeggiani/tower_bridge_lifts"
|
42
|
+
opt.separator ""
|
43
|
+
end
|
44
|
+
|
45
|
+
begin
|
46
|
+
parser.parse!
|
47
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
|
48
|
+
puts "#{$!.to_s.capitalize}\n"
|
49
|
+
exit
|
50
|
+
end
|
51
|
+
|
52
|
+
if DEBUG
|
53
|
+
puts "command: #{command}"
|
54
|
+
puts "options: #{options}"
|
55
|
+
puts "----"
|
56
|
+
end
|
57
|
+
|
58
|
+
command = case ARGV[0]
|
59
|
+
when nil then 'lifts'
|
60
|
+
when 'next' then 'next_lift'
|
61
|
+
else ARGV[0]
|
62
|
+
end
|
63
|
+
|
64
|
+
options = {} unless ['lifts', 'server'].include?(command)
|
65
|
+
|
66
|
+
unless Base::ALLOWED_COMMANDS.include?(command)
|
67
|
+
puts "Unknown Command: #{command}"
|
68
|
+
exit
|
69
|
+
end
|
70
|
+
|
71
|
+
if command == 'server'
|
72
|
+
Server.run!(port: options[:port])
|
73
|
+
else
|
74
|
+
puts View.new.render(Base.new, command, options, :txt)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
@@ -0,0 +1,118 @@
|
|
1
|
+
|
2
|
+
require 'open-uri'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'tzinfo'
|
5
|
+
|
6
|
+
module TowerBridgeLifts
|
7
|
+
class Base
|
8
|
+
attr_accessor :error, :updated
|
9
|
+
attr_accessor :lifts if ENV['RACK_ENV'] == 'test'
|
10
|
+
|
11
|
+
LIFTS_URL = 'http://www.towerbridge.org.uk/lift-times/'
|
12
|
+
ALLOWED_COMMANDS = %w(lifts next_lift bascules time traffic status server)
|
13
|
+
EXPIRE_TIME = 60 * 15
|
14
|
+
|
15
|
+
# Lift timings (seconds)
|
16
|
+
T_CLEAR_UP = 60 # time to clear the bridge after the traffic is stopped
|
17
|
+
T_MOVE_UP = 60 # time to move the bascules up
|
18
|
+
T_UP = 60 # time the bridge stays open
|
19
|
+
T_MOVE_DOWN = 60 # time to move the bascules down
|
20
|
+
T_CLEAR_DOWN = 60 # time to clear the bridge before traffic is allowed
|
21
|
+
T_FULL_LIFT = T_CLEAR_UP + T_MOVE_UP + T_UP + T_MOVE_DOWN + T_CLEAR_DOWN
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@tz = TZInfo::Timezone.get('Europe/London')
|
25
|
+
@lifts = []
|
26
|
+
@error = nil
|
27
|
+
@updated = nil
|
28
|
+
fetch
|
29
|
+
end
|
30
|
+
|
31
|
+
def fetch
|
32
|
+
error = nil
|
33
|
+
page = Nokogiri::HTML(open(LIFTS_URL))
|
34
|
+
page.css(('table tbody tr')).each do |line|
|
35
|
+
td = line.css('td').map{|td| td.text()}
|
36
|
+
@lifts << Lift.new(
|
37
|
+
timestamp: Time.parse("#{td[1]} #{year?(td[1])} #{td[2]}"),
|
38
|
+
vessel: td[3],
|
39
|
+
direction: { "Up river" => :up_river, "Down river" => :down_river }[td[4]]
|
40
|
+
)
|
41
|
+
end
|
42
|
+
rescue
|
43
|
+
error = "Unable to fetch data: #{$!}"
|
44
|
+
ensure
|
45
|
+
@error = error
|
46
|
+
@updated = Time.now
|
47
|
+
end
|
48
|
+
|
49
|
+
def expired?
|
50
|
+
Time.now > ( @updated + EXPIRE_TIME)
|
51
|
+
end
|
52
|
+
|
53
|
+
def status
|
54
|
+
{
|
55
|
+
time: time,
|
56
|
+
lifts_count: @lifts.count,
|
57
|
+
next_lift: next_lift,
|
58
|
+
bascules: bascules,
|
59
|
+
traffic: traffic,
|
60
|
+
updated: @updated,
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def time
|
65
|
+
@tz.now
|
66
|
+
end
|
67
|
+
|
68
|
+
def next_lift
|
69
|
+
return if @lifts.empty?
|
70
|
+
@lifts.find{ |lift| (lift.timestamp + T_FULL_LIFT) >= time }
|
71
|
+
end
|
72
|
+
|
73
|
+
def bascules
|
74
|
+
return :down unless next_lift
|
75
|
+
t_start = next_lift.timestamp
|
76
|
+
t_end = t_start + T_FULL_LIFT
|
77
|
+
t_now = time
|
78
|
+
case
|
79
|
+
when t_now < t_start + T_CLEAR_UP then :down
|
80
|
+
when t_now >= t_end - T_CLEAR_DOWN then :down
|
81
|
+
when t_now >= t_end - T_CLEAR_DOWN - T_MOVE_DOWN then :moving_down
|
82
|
+
when t_now >= t_end - T_CLEAR_DOWN - T_MOVE_DOWN - T_UP then :up
|
83
|
+
when t_now >= t_end - T_CLEAR_DOWN - T_MOVE_DOWN - T_UP - T_MOVE_UP then :moving_up
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def traffic
|
88
|
+
return :allowed unless next_lift
|
89
|
+
( time < self.next_lift.timestamp ) ? :allowed : :blocked
|
90
|
+
end
|
91
|
+
|
92
|
+
def lifts(options=nil)
|
93
|
+
collection = @lifts
|
94
|
+
return collection if collection.empty? || !options
|
95
|
+
collection = collection.select{|lift| lift.timestamp.strftime('%d-%m-%y') == options[:date] } if options[:date]
|
96
|
+
collection = collection[0...options[:count]] if options[:count]
|
97
|
+
collection = collection.group_by{|lift| lift.date_weekday} if options[:group]
|
98
|
+
collection
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def year?(str)
|
104
|
+
date = Time.parse(str)
|
105
|
+
now = time
|
106
|
+
month_i(date) >= month_i(now) ? year_i(now) : (year_i(now) + 1)
|
107
|
+
end
|
108
|
+
|
109
|
+
def month_i(ts)
|
110
|
+
ts.strftime('%m').to_i
|
111
|
+
end
|
112
|
+
|
113
|
+
def year_i(ts)
|
114
|
+
ts.strftime('%Y').to_i
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
def table_status
|
5
|
+
@view.render(@tblifts, 'status', {}, :html)
|
6
|
+
end
|
7
|
+
|
8
|
+
def table_lifts
|
9
|
+
@view.render(@tblifts, 'lifts', {}, :html)
|
10
|
+
end
|
11
|
+
|
12
|
+
def table_lifts_compact
|
13
|
+
@view.render(@tblifts, 'lifts', {compact: true}, :html)
|
14
|
+
end
|
15
|
+
|
16
|
+
def table_lifts_group
|
17
|
+
@view.render(@tblifts, 'lifts', {group: true}, :html)
|
18
|
+
end
|
19
|
+
|
20
|
+
def table_lifts_group_compact
|
21
|
+
@view.render(@tblifts, 'lifts', {group: true, compact: true}, :html)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
module TowerBridgeLifts
|
3
|
+
class Lift
|
4
|
+
attr_accessor :timestamp, :vessel, :direction
|
5
|
+
|
6
|
+
def initialize(timestamp: nil, vessel: nil, direction: nil )
|
7
|
+
raise "Hey!!" if timestamp.nil?
|
8
|
+
@timestamp, @vessel, @direction = timestamp, vessel, direction
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
[ date_weekday_time, dir, vessel ].join(' ')
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_h
|
16
|
+
Hash[ instance_variables.map{|v| [v[1..-1].to_sym, instance_variable_get(v)]} ]
|
17
|
+
end
|
18
|
+
|
19
|
+
def empty?
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
### Decorators
|
24
|
+
def date
|
25
|
+
timestamp.strftime('%d-%b-%y')
|
26
|
+
end
|
27
|
+
|
28
|
+
def weekday
|
29
|
+
timestamp.strftime('%a')
|
30
|
+
end
|
31
|
+
|
32
|
+
def time
|
33
|
+
timestamp.strftime('%H:%M')
|
34
|
+
end
|
35
|
+
|
36
|
+
def date_weekday
|
37
|
+
[date, weekday].join(' ')
|
38
|
+
end
|
39
|
+
|
40
|
+
def date_weekday_time
|
41
|
+
[date, weekday, time].join(' ')
|
42
|
+
end
|
43
|
+
|
44
|
+
def dir
|
45
|
+
{ :up_river => "⬆︎", :down_river => "⬇︎" }[@direction]
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|