odeon_uk 1.1.5 → 2.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 +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +8 -1
- data/README.md +1 -0
- data/Rakefile +13 -9
- data/lib/odeon_uk/cinema.rb +14 -42
- data/lib/odeon_uk/film.rb +23 -5
- data/lib/odeon_uk/internal/film_with_screenings_parser.rb +133 -46
- data/lib/odeon_uk/internal/showtimes_page.rb +36 -0
- data/lib/odeon_uk/internal/title_sanitizer.rb +49 -0
- data/lib/odeon_uk/internal/website.rb +36 -0
- data/lib/odeon_uk/screening.rb +56 -22
- data/lib/odeon_uk/version.rb +2 -2
- data/lib/odeon_uk.rb +4 -2
- data/odeon_uk.gemspec +0 -1
- data/test/fixture_updater.rb +72 -0
- data/test/fixtures/{odeon-bfi-imax.html → cinema/bfi_imax.html} +988 -1130
- data/test/fixtures/{odeon-brighton.html → cinema/brighton.html} +1035 -761
- data/test/fixtures/{odeon-london-leicester-square.html → cinema/leicester_square.html} +470 -766
- data/test/fixtures/showtimes/brighton/film_first.html +92 -0
- data/test/fixtures/showtimes/brighton/film_last.html +100 -0
- data/test/fixtures/showtimes/brighton.html +2071 -0
- data/test/fixtures/showtimes/liverpool_one/film_first_dbox.html +188 -0
- data/test/fixtures/showtimes/manchester/film_first_imax.html +182 -0
- data/test/fixtures/{odeon-sitemap.html → sitemap.html} +572 -444
- data/test/lib/odeon_uk/cinema_test.rb +129 -231
- data/test/lib/odeon_uk/film_test.rb +50 -3
- data/test/lib/odeon_uk/internal/film_with_screenings_parser_test.rb +79 -123
- data/test/lib/odeon_uk/internal/showtimes_page_test.rb +51 -0
- data/test/lib/odeon_uk/internal/title_sanitizer_test.rb +105 -0
- data/test/lib/odeon_uk/internal/website_test.rb +59 -0
- data/test/lib/odeon_uk/screening_test.rb +66 -32
- metadata +32 -53
- data/test/fixtures/brighton-showtimes/about-time.html +0 -175
- data/test/fixtures/brighton-showtimes/autism-friendly-planes.html +0 -75
- data/test/fixtures/brighton-showtimes/bolshoi-spartacus-live.html +0 -67
- data/test/fixtures/brighton-showtimes/cinemagic-echo-planet.html +0 -67
- data/test/fixtures/brighton-showtimes/globe-on-screen-twelfth-night.html +0 -67
- data/test/fixtures/brighton-showtimes/met-opera-eugene-onegin.html +0 -67
- data/test/fixtures/brighton-showtimes/national-theatre-live-frankenstein.html +0 -78
- data/test/fixtures/brighton-showtimes/nt-live-war-horse.html +0 -67
- data/test/fixtures/brighton-showtimes/royal-opera-house-turandot.html +0 -66
- data/test/fixtures/brighton-showtimes/rsc-richard-ii.html +0 -67
- data/test/fixtures/brighton-showtimes/star-trek-into-darkness-2d.html +0 -83
- data/test/fixtures/brighton-showtimes/ukjff-from-cable-street-to-brick-lane.html +0 -67
- data/test/fixtures/manchester-showtimes/thor-the-dark-world.html +0 -300
- data/test/fixtures/odeon-brighton-showtimes.html +0 -1238
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 59c9821b04dd9e8262c25557b82d95b51a9fdf7d
|
4
|
+
data.tar.gz: b1950e1710b9a4bcda1dbe85611cb26ad3604c48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc7ef841b0748d993a87b43e77cebf818cefa48698a3cd461bb57178f96dc677b5f0014d488d49857e76512912034a5e7c37092dcf1109ee5eb5fddb6d5a81aa
|
7
|
+
data.tar.gz: 6be57bd2533423a9895ec6f9a4f6aed25c45721886ac244cbed0ab13720f3a31899d045f3d5fd09b936d556d732ece8f5636948b5787565fcb025e28506bc839
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,11 @@
|
|
1
|
-
|
1
|
+
## 2.0.0, _22nd September 2014_
|
2
|
+
|
3
|
+
- add website utility class
|
4
|
+
- break up film parsing into internal classes
|
5
|
+
- Film#at builds films not cinema
|
6
|
+
- fixture update script
|
7
|
+
- title sanitizer class
|
8
|
+
- updated fixtures
|
2
9
|
- Added changelog
|
3
10
|
|
4
11
|
## 1.1.3, _8th Nov 2013_
|
data/README.md
CHANGED
@@ -5,6 +5,7 @@ A simple gem to parse the [Odeon UK website](http://odeon.co.uk) and spit out us
|
|
5
5
|
[](http://badge.fury.io/rb/odeon_uk)
|
6
6
|
[](https://codeclimate.com/github/andycroll/odeon_uk)
|
7
7
|
[](https://travis-ci.org/andycroll/odeon_uk)
|
8
|
+
[](http://inch-ci.org/github/andycroll/odeon_uk)
|
8
9
|
|
9
10
|
## Installation
|
10
11
|
|
data/Rakefile
CHANGED
@@ -1,20 +1,24 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
|
-
require
|
2
|
+
require 'bundler/gem_tasks'
|
3
3
|
|
4
4
|
require 'rake/testtask'
|
5
5
|
|
6
6
|
Rake::TestTask.new do |t|
|
7
7
|
t.libs << 'lib/odeon_uk'
|
8
|
-
t.test_files = FileList[
|
8
|
+
t.test_files = FileList[
|
9
|
+
'test/lib/odeon_uk/*_test.rb',
|
10
|
+
'test/lib/odeon_uk/internal/*_test.rb'
|
11
|
+
]
|
9
12
|
t.verbose = true
|
10
13
|
end
|
11
14
|
|
12
|
-
|
13
|
-
|
15
|
+
# http://erniemiller.org/2014/02/05/7-lines-every-gems-rakefile-should-have/
|
16
|
+
task :console do
|
17
|
+
require 'irb'
|
18
|
+
require 'irb/completion'
|
19
|
+
require 'odeon_uk'
|
20
|
+
ARGV.clear
|
21
|
+
IRB.start
|
14
22
|
end
|
15
23
|
|
16
|
-
task :
|
17
|
-
system "gem push odeon_uk-#{OdeonUk::VERSION}"
|
18
|
-
end
|
19
|
-
|
20
|
-
task :default => :test
|
24
|
+
task default: :test
|
data/lib/odeon_uk/cinema.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
module OdeonUk
|
2
|
-
|
3
2
|
# The object representing a cinema on the Odeon UK website
|
4
3
|
class Cinema
|
5
|
-
|
6
4
|
# @return [String] the brand of the cinema
|
7
5
|
attr_reader :brand
|
8
6
|
# @return [Integer] the numeric id of the cinema on the Odeon website
|
@@ -21,16 +19,13 @@ module OdeonUk
|
|
21
19
|
def initialize(id, name, url)
|
22
20
|
@brand = 'Odeon'
|
23
21
|
@id = id.to_i
|
24
|
-
@name = name.gsub('London - ','')
|
25
|
-
@slug = @name.downcase.gsub(/[^0-9a-z ]/,'').gsub(/\s+/, '-')
|
22
|
+
@name = name.gsub('London - ', '').gsub(' - ', ': ')
|
23
|
+
@slug = @name.downcase.gsub(/[^0-9a-z ]/, '').gsub(/\s+/, '-')
|
26
24
|
@url = (url[0] == '/') ? "http://www.odeon.co.uk#{url}" : url
|
27
25
|
end
|
28
26
|
|
29
27
|
# Return basic cinema information for all cinemas
|
30
28
|
# @return [Array<OdeonUk::Cinema>]
|
31
|
-
# @example
|
32
|
-
# OdeonUk::Cinema.all
|
33
|
-
# #=> [<OdeonUk::Cinema brand="Odeon" name="Odeon Tunbridge Wells" slug="odeon-tunbridge-wells" id=23 url="...">, #=> <OdeonUk::Cinema brand="Odeon" name="Odeon Brighton" slug="odeon-brighton" chain_id="71" url="...">, ...]
|
34
29
|
def self.all
|
35
30
|
cinema_links.map do |link|
|
36
31
|
new_from_link link
|
@@ -38,11 +33,12 @@ module OdeonUk
|
|
38
33
|
end
|
39
34
|
|
40
35
|
# Find a single cinema
|
41
|
-
# @param [Integer, String] id the cinema id of the format 71/'71' as used on
|
36
|
+
# @param [Integer, String] id the cinema id of the format 71/'71' as used on
|
37
|
+
# the odeon.co.uk website
|
42
38
|
# @return [OdeonUk::Cinema, nil]
|
43
39
|
# @example
|
44
40
|
# OdeonUk::Cinema.find('71')
|
45
|
-
# #=> <OdeonUk::Cinema brand="Odeon" name="Brighton" slug="brighton"
|
41
|
+
# #=> <OdeonUk::Cinema brand="Odeon" name="Brighton" slug="brighton" ...>
|
46
42
|
def self.find(id)
|
47
43
|
id = id.to_i
|
48
44
|
return nil unless id > 0
|
@@ -55,7 +51,10 @@ module OdeonUk
|
|
55
51
|
# @example
|
56
52
|
# cinema = OdeonUk::Cinema.find('71')
|
57
53
|
# cinema.adr
|
58
|
-
# #=> { street_address: 'Kingswest',
|
54
|
+
# #=> { street_address: 'Kingswest',
|
55
|
+
# locality: 'Brighton',
|
56
|
+
# postal_code: 'BN1 2RE',
|
57
|
+
# country_name: 'United Kingdom' }
|
59
58
|
def adr
|
60
59
|
{
|
61
60
|
street_address: street_address,
|
@@ -68,15 +67,8 @@ module OdeonUk
|
|
68
67
|
|
69
68
|
# Films with showings scheduled at this cinema
|
70
69
|
# @return [Array<OdeonUk::Film>]
|
71
|
-
# @example
|
72
|
-
# cinema = OdeonUk::Cinema.find('71')
|
73
|
-
# cinema.films
|
74
|
-
# #=> [<OdeonUk::Film name="Iron Man 3">, <OdeonUk::Film name="Star Trek Into Darkness">]
|
75
70
|
def films
|
76
|
-
|
77
|
-
parser = OdeonUk::Internal::FilmWithScreeningsParser.new node.to_s
|
78
|
-
OdeonUk::Film.new parser.film_name
|
79
|
-
end.uniq
|
71
|
+
Film.at(id)
|
80
72
|
end
|
81
73
|
|
82
74
|
# The name of the cinema including the brand
|
@@ -99,7 +91,6 @@ module OdeonUk
|
|
99
91
|
address_node.text.match(/\w+(\s\w+){0,}\s+(\w+(\s\w+){0,})/)[2]
|
100
92
|
end
|
101
93
|
|
102
|
-
|
103
94
|
# Post code of the cinema
|
104
95
|
# @return [String]
|
105
96
|
# @example
|
@@ -117,14 +108,7 @@ module OdeonUk
|
|
117
108
|
# cinema.screenings
|
118
109
|
# #=> [<OdeonUk::Screening film_name="Iron Man 3" cinema_name="Brighton" when="..." variant="...">, <OdeonUk::Screening ...>]
|
119
110
|
def screenings
|
120
|
-
|
121
|
-
parser = OdeonUk::Internal::FilmWithScreeningsParser.new node.to_s
|
122
|
-
parser.showings.map do |screening_type, times_urls|
|
123
|
-
times_urls.map do |array|
|
124
|
-
OdeonUk::Screening.new parser.film_name, self.name, array[0], array[1], screening_type
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end.flatten
|
111
|
+
Screening.at(id)
|
128
112
|
end
|
129
113
|
|
130
114
|
# Screenings for particular film
|
@@ -155,7 +139,7 @@ module OdeonUk
|
|
155
139
|
private
|
156
140
|
|
157
141
|
def self.cinema_links
|
158
|
-
links = parsed_sitemap.css('.sitemap
|
142
|
+
links = parsed_sitemap.css('.sitemap a[href*=cinemas]')
|
159
143
|
links.select { |link| link.get_attribute('href').match(/\/\d+\/$/) }
|
160
144
|
end
|
161
145
|
|
@@ -171,7 +155,7 @@ module OdeonUk
|
|
171
155
|
end
|
172
156
|
|
173
157
|
def self.sitemap_response
|
174
|
-
@sitemap_response ||=
|
158
|
+
@sitemap_response ||= OdeonUk::Internal::Website.new.sitemap
|
175
159
|
end
|
176
160
|
|
177
161
|
def address_node
|
@@ -179,23 +163,11 @@ module OdeonUk
|
|
179
163
|
end
|
180
164
|
|
181
165
|
def cinema_response
|
182
|
-
@cinema_response ||=
|
166
|
+
@cinema_response ||= OdeonUk::Internal::Website.new.cinema(@id)
|
183
167
|
end
|
184
168
|
|
185
169
|
def parsed_cinema
|
186
170
|
Nokogiri::HTML(cinema_response)
|
187
171
|
end
|
188
|
-
|
189
|
-
def film_nodes
|
190
|
-
parsed_showtimes.css('.film-detail')
|
191
|
-
end
|
192
|
-
|
193
|
-
def parsed_showtimes
|
194
|
-
Nokogiri::HTML(showtimes_response)
|
195
|
-
end
|
196
|
-
|
197
|
-
def showtimes_response
|
198
|
-
@showtimes_response ||= HTTParty.get("http://www.odeon.co.uk/showtimes/week/#{@id}?siteId=#{@id}")
|
199
|
-
end
|
200
172
|
end
|
201
173
|
end
|
data/lib/odeon_uk/film.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module OdeonUk
|
2
|
-
|
3
2
|
# The object representing a film on the Odeon UK website
|
4
3
|
class Film
|
5
4
|
include Comparable
|
@@ -16,11 +15,20 @@ module OdeonUk
|
|
16
15
|
@slug = name.downcase.gsub(/[^0-9a-z ]/,'').gsub(/\s+/, '-')
|
17
16
|
end
|
18
17
|
|
18
|
+
# All currently listed films showing at a cinema
|
19
|
+
# @param [Integer] cinema_id id of the cinema on the website
|
20
|
+
# @return [Array<OdeonUk::Film>]
|
21
|
+
def self.at(cinema_id)
|
22
|
+
showtimes_page(cinema_id).to_a.map do |html|
|
23
|
+
new(screenings_parser(html).film_name)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
19
27
|
# Allows sort on objects
|
20
28
|
# @param [OdeonUk::Film] other another film object
|
21
29
|
# @return [Integer] -1, 0 or 1
|
22
|
-
def <=>
|
23
|
-
|
30
|
+
def <=>(other)
|
31
|
+
slug <=> other.slug
|
24
32
|
end
|
25
33
|
|
26
34
|
# Check an object is the same as another object.
|
@@ -28,7 +36,7 @@ module OdeonUk
|
|
28
36
|
# @return [Boolean] True if both objects are the same exact object, or if
|
29
37
|
# they are of the same type and share an equal slug
|
30
38
|
# @note Guided by http://woss.name/2011/01/20/equality-comparison-and-ordering-in-ruby/
|
31
|
-
def eql?
|
39
|
+
def eql?(other)
|
32
40
|
self.class == other.class && self == other
|
33
41
|
end
|
34
42
|
|
@@ -41,7 +49,17 @@ module OdeonUk
|
|
41
49
|
# @return [Integer] hash of slug
|
42
50
|
# @note Guided by http://woss.name/2011/01/20/equality-comparison-and-ordering-in-ruby/
|
43
51
|
def hash
|
44
|
-
|
52
|
+
slug.hash
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def self.screenings_parser(html)
|
58
|
+
OdeonUk::Internal::FilmWithScreeningsParser.new(html)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.showtimes_page(cinema_id)
|
62
|
+
OdeonUk::Internal::ShowtimesPage.new(cinema_id)
|
45
63
|
end
|
46
64
|
end
|
47
65
|
end
|
@@ -1,67 +1,154 @@
|
|
1
1
|
module OdeonUk
|
2
|
-
|
3
2
|
# Internal utility classes: Do not use
|
4
3
|
# @api private
|
5
4
|
module Internal
|
6
|
-
|
7
5
|
# Parses a chunk of HTML to derive movie showing data
|
8
6
|
class FilmWithScreeningsParser
|
7
|
+
# CSS selector for a film title inside an individual film HTML
|
8
|
+
NAME_CSS = '.presentation-info h4 a'
|
9
|
+
# CSS selector for a group of showtimes inside an individual film HTML
|
10
|
+
SHOWTIMES_CSS = '.times-all.accordion-group'
|
9
11
|
|
10
|
-
# @param [String]
|
11
|
-
def initialize(
|
12
|
-
@
|
12
|
+
# @param [String] html a chunk of html
|
13
|
+
def initialize(html)
|
14
|
+
@html = html
|
13
15
|
end
|
14
16
|
|
15
17
|
# The film name
|
16
18
|
# @return [String]
|
17
19
|
def film_name
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
20
|
+
@film_name ||= TitleSanitizer.new(raw_film_name).sanitized
|
21
|
+
end
|
22
|
+
|
23
|
+
# array containing hashes of screening attributes
|
24
|
+
# @return [Array<Hash>]
|
25
|
+
def to_a
|
26
|
+
doc.css(SHOWTIMES_CSS).map do |node|
|
27
|
+
dimension_parser(node).to_a.map do |hash|
|
28
|
+
hash.merge(film_name: film_name)
|
29
|
+
end
|
30
|
+
end.flatten
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def doc
|
36
|
+
@doc ||= Nokogiri::HTML(@html)
|
37
|
+
end
|
38
|
+
|
39
|
+
def raw_film_name
|
40
|
+
@raw_film_name ||= doc.css(NAME_CSS).children.first.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
def dimension_parser(node)
|
44
|
+
DimensionNodeParser.new(node)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# parses chunk of screenings for a particular screening dimension
|
49
|
+
class DimensionNodeParser
|
50
|
+
# CSS selector for a single screening inside a 'group of showtimes' HTML
|
51
|
+
SCREENING_CSS = '.show'
|
52
|
+
# CSS selector for showing technology for a 'group of showtimes' HTML
|
53
|
+
TECH_CSS = '.tech a'
|
54
|
+
|
55
|
+
# @param [Nokigiri::Node] node a Nokogiri node object
|
56
|
+
def initialize(node)
|
57
|
+
@node = node
|
58
|
+
end
|
59
|
+
|
60
|
+
# array containing hashes of screening attributes for dimension
|
61
|
+
# @return [Array<Hash>]
|
62
|
+
def to_a
|
63
|
+
screening_hashes.map do |hash|
|
64
|
+
hash.merge(
|
65
|
+
dimension: dimension,
|
66
|
+
variant: add_imax(hash[:variant])
|
67
|
+
)
|
37
68
|
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def add_imax(original)
|
74
|
+
imax? ? "#{original} imax".strip : original
|
75
|
+
end
|
76
|
+
|
77
|
+
def dimension
|
78
|
+
tech.match(/[23]d/)[0]
|
79
|
+
end
|
80
|
+
|
81
|
+
def imax?
|
82
|
+
!!tech.match(/imax/)
|
83
|
+
end
|
38
84
|
|
39
|
-
|
40
|
-
|
41
|
-
name = name.gsub /\s+\z/, '' # remove trailing spaces
|
85
|
+
def screening_hashes
|
86
|
+
screening_nodes.map { |node| ScreeningNodeParser.new(node).to_hash }
|
42
87
|
end
|
43
88
|
|
44
|
-
|
89
|
+
def screening_nodes
|
90
|
+
@node.css(SCREENING_CSS)
|
91
|
+
end
|
92
|
+
|
93
|
+
def tech
|
94
|
+
@tech ||= @node.css(TECH_CSS).text.gsub('in ', '').downcase
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# parses a single screening
|
99
|
+
class ScreeningNodeParser
|
100
|
+
# regex for time format
|
101
|
+
TIME_REGEX = %r(\d+/\d+/\d+ \d{2}\:\d{2})
|
102
|
+
# regex for D-Box screenings
|
103
|
+
DBOX_REGEX = /D-Box/
|
104
|
+
|
105
|
+
# @param [Nokigiri::Node] node a Nokogiri node object
|
106
|
+
def initialize(node)
|
107
|
+
@node = node
|
108
|
+
end
|
109
|
+
|
110
|
+
# hashes of screening attributes
|
45
111
|
# @return [Hash]
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
@nokogiri_html.css('.times-all.accordion-group').inject({}) do |result, variant_node|
|
54
|
-
variant = variant_node.css('.tech a').text.gsub('in ', '').upcase
|
55
|
-
|
56
|
-
times_url = variant_node.css('.performance-detail').map do |screening_node|
|
57
|
-
[
|
58
|
-
tz.local_to_utc(Time.parse(screening_node['title'].match(/\d+\/\d+\/\d+ \d{2}\:\d{2}/).to_s + ' UTC')),
|
59
|
-
"http://www.odeon.co.uk#{screening_node['href']}"
|
60
|
-
]
|
61
|
-
end
|
112
|
+
def to_hash
|
113
|
+
{
|
114
|
+
booking_url: booking_url,
|
115
|
+
time: utc_time,
|
116
|
+
variant: variant
|
117
|
+
}
|
118
|
+
end
|
62
119
|
|
63
|
-
|
64
|
-
|
120
|
+
private
|
121
|
+
|
122
|
+
def booking_url
|
123
|
+
link_attr('href').to_s
|
124
|
+
end
|
125
|
+
|
126
|
+
def link_attr(attribute)
|
127
|
+
@node.css('a').attribute(attribute)
|
128
|
+
end
|
129
|
+
|
130
|
+
def info
|
131
|
+
@node.css('i').to_s
|
132
|
+
end
|
133
|
+
|
134
|
+
def time
|
135
|
+
Time.parse(time_string)
|
136
|
+
end
|
137
|
+
|
138
|
+
def time_string
|
139
|
+
link_attr('title').to_s.match(TIME_REGEX).to_s + ' UTC'
|
140
|
+
end
|
141
|
+
|
142
|
+
def tz
|
143
|
+
@tz ||= TZInfo::Timezone.get('Europe/London')
|
144
|
+
end
|
145
|
+
|
146
|
+
def utc_time
|
147
|
+
tz.local_to_utc(time)
|
148
|
+
end
|
149
|
+
|
150
|
+
def variant
|
151
|
+
info.match(DBOX_REGEX) ? 'd-box' : ''
|
65
152
|
end
|
66
153
|
end
|
67
154
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module OdeonUk
|
2
|
+
# Internal utility classes: Do not use
|
3
|
+
# @api private
|
4
|
+
module Internal
|
5
|
+
# Parses a chunk of HTML to derive showing data for a single films
|
6
|
+
class ShowtimesPage
|
7
|
+
# css selector for film html chunks
|
8
|
+
FILM_CSS = '.film-detail'
|
9
|
+
|
10
|
+
# @param [Integer] cinema_id cineworld cinema id
|
11
|
+
def initialize(cinema_id)
|
12
|
+
@cinema_id = cinema_id
|
13
|
+
end
|
14
|
+
|
15
|
+
# break up the showtimes page into individual chunks for each film
|
16
|
+
# @return [Array<String>] html chunks for a film and it's screenings
|
17
|
+
def to_a
|
18
|
+
film_nodes.map { |node| node.to_s.gsub(/^\s+/, '') }
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def film_nodes
|
24
|
+
@film_nodes ||= showtimes_doc.css(FILM_CSS)
|
25
|
+
end
|
26
|
+
|
27
|
+
def showtimes
|
28
|
+
@showtimes ||= OdeonUk::Internal::Website.new.showtimes(@cinema_id)
|
29
|
+
end
|
30
|
+
|
31
|
+
def showtimes_doc
|
32
|
+
@showtimes_doc ||= Nokogiri::HTML(showtimes)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module OdeonUk
|
2
|
+
# Internal utility classes: Do not use
|
3
|
+
# @api private
|
4
|
+
module Internal
|
5
|
+
# Sanitize and standardize film titles
|
6
|
+
class TitleSanitizer
|
7
|
+
# strings and regex to be removed
|
8
|
+
REMOVE = [
|
9
|
+
/\s+[23][dD]/, # dimension
|
10
|
+
'Autism Friendly Screening -', # autism screening
|
11
|
+
/\ACinemagic \d{1,4} \-/, # cinemagic
|
12
|
+
/\(encore.+\)/i, # encore for NT Live
|
13
|
+
/\(live\)/i, # live in brackets
|
14
|
+
'UKJFF -', # UK Jewish festival prefix
|
15
|
+
]
|
16
|
+
|
17
|
+
# regexes and their replacements
|
18
|
+
REPLACE = {
|
19
|
+
/Bolshoi - (.*)/ => 'Bolshoi: ',
|
20
|
+
/Globe On Screen: (.*)/ => 'Globe: ',
|
21
|
+
/Met Opera - (.*)/ => 'Met Opera: ',
|
22
|
+
/National Theatre Live - (.*)/ => 'National Theatre: ',
|
23
|
+
/NT Live - (.*)/ => 'National Theatre: ',
|
24
|
+
/ROH - (.*)/ => 'Royal Opera House: ',
|
25
|
+
/(.*)\- RSC Live \d{1,4}/ => 'Royal Shakespeare Company: '
|
26
|
+
}
|
27
|
+
|
28
|
+
# @param [String] title a film title
|
29
|
+
def initialize(title)
|
30
|
+
@title = title
|
31
|
+
end
|
32
|
+
|
33
|
+
# sanitized and standardized title
|
34
|
+
# @return [String] title
|
35
|
+
def sanitized
|
36
|
+
@sanitzed ||= begin
|
37
|
+
sanitized = @title
|
38
|
+
REMOVE.each do |pattern|
|
39
|
+
sanitized.gsub! pattern, ''
|
40
|
+
end
|
41
|
+
REPLACE.each do |pattern, prefix|
|
42
|
+
sanitized.gsub!(pattern) { |_| prefix + $1 }
|
43
|
+
end
|
44
|
+
sanitized.squeeze(' ').strip
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
module OdeonUk
|
4
|
+
# Internal utility classes: Do not use
|
5
|
+
# @api private
|
6
|
+
module Internal
|
7
|
+
# Utility class to make calls to the odeon website
|
8
|
+
class Website
|
9
|
+
# cinema page
|
10
|
+
# @param [Integer] cinema_id website id of the cinema
|
11
|
+
# @return [String] html of the page
|
12
|
+
def cinema(cinema_id)
|
13
|
+
get("cinemas/odeon/#{cinema_id}/")
|
14
|
+
end
|
15
|
+
|
16
|
+
# showtimes page for a single cinema
|
17
|
+
# @param [Integer] cinema_id website id of the cinema
|
18
|
+
# @return [String] html of the page
|
19
|
+
def showtimes(cinema_id)
|
20
|
+
get("showtimes/week/#{cinema_id}/?siteId=#{cinema_id}")
|
21
|
+
end
|
22
|
+
|
23
|
+
# sitemap page
|
24
|
+
# @return [String] html of the page
|
25
|
+
def sitemap
|
26
|
+
get('sitemap/')
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def get(path)
|
32
|
+
URI("http://www.odeon.co.uk/#{path}").read
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|