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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +2 -0
  6. data/README.md +107 -0
  7. data/Rakefile +7 -0
  8. data/bin/tblifts +5 -0
  9. data/lib/tower_bridge_lifts.rb +13 -0
  10. data/lib/tower_bridge_lifts/application.rb +80 -0
  11. data/lib/tower_bridge_lifts/base.rb +118 -0
  12. data/lib/tower_bridge_lifts/helpers.rb +24 -0
  13. data/lib/tower_bridge_lifts/lift.rb +49 -0
  14. data/lib/tower_bridge_lifts/server.rb +36 -0
  15. data/lib/tower_bridge_lifts/templates/lifts.erb +21 -0
  16. data/lib/tower_bridge_lifts/templates/lifts_compact.erb +17 -0
  17. data/lib/tower_bridge_lifts/templates/lifts_group.erb +23 -0
  18. data/lib/tower_bridge_lifts/templates/lifts_group_compact.erb +20 -0
  19. data/lib/tower_bridge_lifts/templates/status.erb +9 -0
  20. data/lib/tower_bridge_lifts/version.rb +4 -0
  21. data/lib/tower_bridge_lifts/views.rb +114 -0
  22. data/reload +4 -0
  23. data/spec/spec_helper.rb +3 -0
  24. data/spec/tower_bridge_lifts_spec.rb +32 -0
  25. data/tower_bridge_lifts.gemspec +31 -0
  26. data/web/apidoc.html +87 -0
  27. data/web/apidoc.yaml +163 -0
  28. data/web/css/print.css +1367 -0
  29. data/web/css/reset.css +125 -0
  30. data/web/css/screen.css +1497 -0
  31. data/web/css/style.css +249 -0
  32. data/web/css/tblifts.css +4 -0
  33. data/web/css/typography.css +14 -0
  34. data/web/fonts/DroidSans-Bold.ttf +0 -0
  35. data/web/fonts/DroidSans.ttf +0 -0
  36. data/web/images/collapse.gif +0 -0
  37. data/web/images/expand.gif +0 -0
  38. data/web/images/explorer_icons.png +0 -0
  39. data/web/images/logo_small.png +0 -0
  40. data/web/images/throbber.gif +0 -0
  41. data/web/images/wordnik_api.png +0 -0
  42. data/web/index.rhtml +42 -0
  43. data/web/lib/backbone-min.js +15 -0
  44. data/web/lib/es5-shim.js +2065 -0
  45. data/web/lib/handlebars-4.0.5.js +4608 -0
  46. data/web/lib/highlight.9.1.0.pack.js +2 -0
  47. data/web/lib/highlight.9.1.0.pack_extended.js +34 -0
  48. data/web/lib/jquery-1.8.0.min.js +2 -0
  49. data/web/lib/jquery.ba-bbq.min.js +18 -0
  50. data/web/lib/jquery.slideto.min.js +1 -0
  51. data/web/lib/jquery.wiggle.min.js +8 -0
  52. data/web/lib/js-yaml.min.js +3 -0
  53. data/web/lib/jsoneditor.min.js +11 -0
  54. data/web/lib/lodash.min.js +102 -0
  55. data/web/lib/marked.js +1272 -0
  56. data/web/lib/object-assign-pollyfill.js +23 -0
  57. data/web/lib/sanitize-html.min.js +6 -0
  58. data/web/lib/swagger-oauth.js +347 -0
  59. data/web/o2c.html +20 -0
  60. data/web/swagger-ui.js +25067 -0
  61. 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
@@ -0,0 +1,7 @@
1
+ .DS_Store
2
+ *.gem
3
+ /.bundle/
4
+ /.yardoc
5
+ /Gemfile.lock
6
+ /spec/reports/
7
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.12.5
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
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
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ task default: :spec
5
+ task test: :spec
6
+ desc 'Run all specs in spec directory (excluding plugin specs)'
7
+ RSpec::Core::RakeTask.new(:spec)
data/bin/tblifts ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'tower_bridge_lifts'
4
+
5
+ TowerBridgeLifts::Application.new.run
@@ -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