tableau 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 78a2bd4d06b44a5c5fac93cc6eb728f0b405e9e7
4
+ data.tar.gz: 44763957d728478811a5f91c9509f334f3966752
5
+ SHA512:
6
+ metadata.gz: 28b0d5f1082b31434cf4debb8b463a2c4bd823c9ab93b6e9ac6a51a70bcd9ff1672639f0d6c375cfa2c892f3a27cecf54df162702f60b95c44244dbc3b7c890d
7
+ data.tar.gz: f0e570d93e64733397d197f6d64d356312796b317534abe3bd88f44e92152c5f039835864e08f79f95ebebd980d077f6bca606b32df09ab7a5e3f9777020092b
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 Matt Ryder
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Tableau'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ Bundler::GemHelper.install_tasks
18
+
data/lib/tableau.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'json'
2
+ require 'tableau/timetable'
3
+ require 'tableau/moduleparser'
4
+ require 'tableau/tablebuilder'
5
+
6
+ module Tableau
7
+ class << self
8
+
9
+ def generate(table_id, module_codes)
10
+ timetable = Tableau::Timetable.new(table_id, module_codes)
11
+ builder = Tableau::TableBuilder.new(timetable)
12
+ builder.to_html
13
+ end
14
+
15
+ # Return the Name, Code and Types (2Prac / PracA / PracB etc) from the timetable
16
+ def module_info(module_code)
17
+ Tableau::ModuleParser.new(module_code).module_info
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,136 @@
1
+ require 'tableau/timetable'
2
+ require 'tableau/module'
3
+ require 'tableau/class'
4
+ require 'tableau/uribuilder'
5
+ require 'tableau/classarray'
6
+
7
+ module Tableau
8
+ class BaseParser
9
+
10
+ @@COURSE_DESCRIPTION_XPATH = '/html/body/table[1]//td//td/text()'
11
+ @@WEEKS_REGEX = /^([\d]{2}-[\d]{2})|^([\d]{2})/
12
+
13
+ # Create a new instance of the BaseParser, with an ID to lookup
14
+ def initalize(lookup_id)
15
+ @time, @day = Time.new(2013, 1, 1, 9, 0, 0), 0
16
+ @lookup_id = lookup_id
17
+ end
18
+
19
+ # Parse the module table for any classes
20
+ def parse_table(table_rows)
21
+ classes = Tableau::ClassArray.new
22
+ @day = 0
23
+
24
+ # delete the time header row
25
+ table_rows.delete(table_rows.first)
26
+
27
+ table_rows.each do |row|
28
+ @time = Time.new(2013, 1, 1, 9, 0, 0)
29
+
30
+ # drop the 'Day' cell from the row
31
+ row_items = row.xpath('td')
32
+ row_items.delete(row_items.first)
33
+
34
+ row_items.each do |cell|
35
+ if cell.attribute('colspan')
36
+ intervals = cell.attribute('colspan').value
37
+ classes << create_class(cell)
38
+ else intervals = 1
39
+ end
40
+
41
+ inc_time(intervals)
42
+ end
43
+
44
+ @day += 1
45
+ end
46
+
47
+ classes
48
+ end
49
+
50
+ # Create a Class from the given data element
51
+ def create_class(class_element)
52
+ begin
53
+ tt_class = Tableau::Class.new(@day, @time)
54
+ data = class_element.xpath('table/tr/td//text()')
55
+ raise "Misformed cell for #{module_id}" if data.count < 4
56
+ rescue Exception => e
57
+ p "EXCEPTION: #{e.message}", "Data Parsed:", data
58
+ return nil
59
+ end
60
+
61
+ # If the weeks are in the 2nd index, it's a core timetable
62
+ if @@WEEKS_REGEX.match(data[1].text())
63
+ tt_class.code = data[0].text().match(/^[A-Za-z0-9\-]+/).to_s
64
+ tt_class.type = data[0].text().gsub(tt_class.code, '').gsub('/', '')
65
+
66
+ tt_class.weeks = create_class_weeks(data[1].text())
67
+ tt_class.location = data[2].text()
68
+ tt_class.name = data[3].text()
69
+ else # this is a module timetable, laid out differently
70
+ if data[0].to_s != ""
71
+ tt_class.code = data[0].text().match(/^[A-Za-z0-9\-]+/).to_s
72
+ tt_class.type = data[0].text().gsub(tt_class.code, '').gsub('/', '')
73
+ end
74
+
75
+ tt_class.location = data[1].text()
76
+ tt_class.name = data[2].text()
77
+ tt_class.weeks = create_class_weeks(data[3].text())
78
+ end
79
+
80
+ # Same attribute on both timetables, DRY'd here
81
+ tt_class.tutor = data[4] ? data[4].text() : nil
82
+
83
+ if intervals = class_element.attribute('colspan').value
84
+ tt_class.intervals = intervals.to_i
85
+ end
86
+
87
+ tt_class
88
+ end
89
+
90
+ # Create the week range array for the given week string
91
+ def create_class_weeks(week_data)
92
+ week_span_regex = /([\d]{2}-[\d]{2})/
93
+ week_start_regex = /^[0-9]{2}/
94
+ week_end_regex = /[0-9]{2}$/
95
+ week_single_regex = /[\d]{2}/
96
+
97
+ class_weeks = Array.new
98
+
99
+ week_data.scan(@@WEEKS_REGEX).each do |weekspan|
100
+
101
+ # if it's a 28-39 week span
102
+ if weekspan =~ week_span_regex
103
+ start = week_start_regex.match(weekspan)[0].to_i
104
+ finish = week_end_regex.match(weekspan)[0].to_i
105
+
106
+ while start <= finish
107
+ class_weeks << start
108
+ start += 1
109
+ end
110
+
111
+ # some single week (30, 31, 32 etc) support
112
+ elsif weekspan =~ week_single_regex
113
+ class_weeks << week_single_regex.match(weekspan)[0].to_i
114
+ end
115
+ end
116
+
117
+ class_weeks
118
+ end
119
+
120
+ # Increments the @time by 15 minute intervals
121
+ def inc_time(intervals)
122
+ intervals.to_i.times { @time += 900 }
123
+ end
124
+
125
+ # Get the ID string in the first <table> element (usually <:code> - <:name>)
126
+ def get_info(timetable_data)
127
+ timetable_data.xpath(@@COURSE_DESCRIPTION_XPATH).to_html
128
+ end
129
+
130
+ # Returns the XPath for the nth table
131
+ def xpath_for_table(table_count)
132
+ "/html/body/table[#{3 * table_count - 1}]/tr"
133
+ end
134
+
135
+ end
136
+ end
@@ -0,0 +1,34 @@
1
+ module Tableau
2
+ class Class
3
+
4
+ attr_accessor :type, :location, :name, :tutor,
5
+ :day, :time, :intervals, :weeks,
6
+ :code
7
+
8
+ def defaults
9
+ def_opts = {
10
+ type: '',
11
+ location: '',
12
+ name: '',
13
+ tutor: '',
14
+ intervals: 4,
15
+ day: 0,
16
+ code: 0,
17
+ time: Time.new
18
+ }
19
+ end
20
+
21
+ def initialize(day, time, options = {})
22
+ @day = day
23
+ @time = time
24
+ @weeks = Array.new
25
+ defaults.merge!(options)
26
+ end
27
+
28
+ # Duration of the class in hours
29
+ def duration
30
+ @intervals / 4
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,30 @@
1
+ module Tableau
2
+ class ClassArray < Array
3
+
4
+ def initialize
5
+ super
6
+ end
7
+
8
+ # Returns an array of all the classes for the day
9
+ def classes_for_day(day)
10
+ days_classes = ClassArray.new
11
+ self.each { |c| days_classes << c if c.day == day }
12
+ days_classes.count > 0 ? days_classes : nil
13
+ end
14
+
15
+ # Returns the earliest class in the module
16
+ def earliest_class
17
+ earliest = self.first
18
+ self.each { |c| earliest = c if c.time < earliest.time }
19
+ earliest
20
+ end
21
+
22
+ # Returns the latest class in the module
23
+ def latest_class
24
+ latest = self.first
25
+ self.each { |c| latest = c if c.time > latest.time }
26
+ latest
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,40 @@
1
+ module Tableau
2
+ class Module
3
+
4
+ attr_reader :module_id, :name, :classes
5
+
6
+ def initialize(id, options = {})
7
+ @module_id = id
8
+
9
+ @name = options[:name] || ''
10
+ @classes = options[:classes] || Tableau::ClassArray.new
11
+ end
12
+
13
+ # Add a class to the module
14
+ def add_class(new_class)
15
+ @classes << new_class
16
+ end
17
+
18
+ # Returns an array of all the classes for the day
19
+ def classes_for_day(day)
20
+ days_classes = Tableau::ClassArray.new
21
+ @classes.each { |c| days_classes << c if c.day == day }
22
+ days_classes.count > 0 ? days_classes : nil
23
+ end
24
+
25
+ # Returns the earliest class in the module
26
+ def earliest_class
27
+ earliest = @classes.first
28
+ @classes.each { |c| earliest = c if c.time < earliest.time }
29
+ earliest
30
+ end
31
+
32
+ # Returns the latest class in the module
33
+ def latest_class
34
+ latest = @classes.first
35
+ @classes.each { |c| latest = c if c.time > latest.time }
36
+ latest
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,51 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+ require 'tableau/baseparser'
4
+
5
+ module Tableau
6
+ class ModuleParser < Tableau::BaseParser
7
+
8
+ @@MODULE_ID_REGEX = /^CE[\d]{5}-[1-8]/
9
+ attr_reader :raw_timetable
10
+
11
+ # Create a new ModuleParser, with an optional module code
12
+ def initialize(module_code = nil)
13
+ begin
14
+ timetable_response = Tableau::UriBuilder.new(module_code, module_lookup: true).read
15
+ @raw_timetable = Nokogiri::HTML(timetable_response) if timetable_response
16
+ rescue OpenURI::HTTPError
17
+ return nil
18
+ end
19
+ end
20
+
21
+ def module_info
22
+ mod, types = parse, Set.new
23
+ mod.classes.each { |c| types.add?(c.type) }
24
+
25
+ return { name: mod.name, code: mod.module_id, types: types }
26
+ end
27
+
28
+ def parse
29
+ raise "No module timetable loaded!" unless @raw_timetable
30
+
31
+ #Get the ID and Name from the first <table>
32
+ raw_info = @raw_timetable.xpath(@@COURSE_DESCRIPTION_XPATH).to_html
33
+ module_id = @@MODULE_ID_REGEX.match(raw_info).to_s
34
+ module_name = raw_info.gsub(module_id, '')
35
+
36
+ mod = Tableau::Module.new(module_id, name: module_name)
37
+ table_count = 1
38
+ table_data = @raw_timetable.xpath(xpath_for_table(table_count))
39
+
40
+ # Iterate through each timetable until xpath returns no more timetable tables
41
+ while !table_data.empty?
42
+ tables_classes = parse_table(table_data)
43
+ tables_classes.each { |c| mod.classes << c }
44
+ table_data = @raw_timetable.xpath(xpath_for_table(table_count += 1))
45
+ end
46
+
47
+ mod #return the module to the caller
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,134 @@
1
+ module Tableau
2
+ class TableBuilder
3
+
4
+ def css_defaults
5
+ css_defaults = {
6
+ body_color: '#F4F5F6',
7
+ border_color: 'rgba(0, 0, 0, 0.25)',
8
+ class_color: '#a9b1b9',
9
+ header_bg_color: '#CFD3D7',
10
+ header_fg_color: '#25292D',
11
+ empty_color: '#EBEDEE'
12
+ }
13
+ end
14
+
15
+ def initialize(timetable, css_options = {})
16
+ @timetable = timetable
17
+ @css = css_defaults.merge(css_options)
18
+ end
19
+
20
+ def build_css
21
+ %Q{
22
+ body {
23
+ background-color: #{@css[:body_color]};
24
+ }
25
+
26
+ table {
27
+ border-collapse: collapse;
28
+ border: 1px solid #{@css[:border_color]};
29
+ }
30
+
31
+ #time {
32
+ background-color: #{@css[:header_bg_color]};
33
+ color: #{@css[:header_fg_color]};
34
+ }
35
+
36
+ #time > th {
37
+ font-weight: lighter;
38
+ border-left: 1px solid #{@css[:border_color]};
39
+ }
40
+
41
+ .dh {
42
+ background-color: #{@css[:header_bg_color]};
43
+ color: #{@css[:header_fg_color]};
44
+ }
45
+
46
+ td {
47
+ background-color: #{@css[:empty_color]};
48
+ border: 1px solid #{@css[:border_color]};
49
+ padding: 5px;
50
+ }
51
+
52
+ .class_item {
53
+ background-color: #{@css[:class_color]};
54
+ }
55
+ }
56
+ end
57
+
58
+ def day_row(day, classes, end_time = nil)
59
+ days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']
60
+ time = Time.new(2013, 1, 1, 9, 0 , 0)
61
+ end_time ||= Time.new(2013, 1, 1, 21, 0, 0)
62
+
63
+ day_row = %Q{<td class="dh">#{days[day]}</td>}
64
+
65
+ if !classes || classes.count == 0
66
+ while time < end_time
67
+ day_row += "<td></td>"
68
+ time += 900 # 15 mins in seconds
69
+ end
70
+ else
71
+ while time < end_time
72
+ class_at_time = @timetable.class_for_time(day, time)
73
+ if class_at_time
74
+ day_row += make_class(class_at_time)
75
+ time += (900 * class_at_time.intervals)
76
+ else
77
+ day_row += "<td></td>"
78
+ time += 900 # 15 mins in seconds
79
+ end
80
+ end
81
+ end
82
+ day_row
83
+ end
84
+
85
+ # Create the HTML for a class item on the table
86
+ def make_class(klass)
87
+ %Q{
88
+ <td class="class_item" colspan="#{klass.intervals}">
89
+ <p>#{klass.name} - #{klass.location}</p>
90
+ <p>#{klass.type}</p>
91
+ </td>}
92
+ end
93
+
94
+ # HTML5 representation of the timetable
95
+ def to_html
96
+ time_header, rows = '<th></th>', Array.new
97
+ end_time = Time.new(2013, 1, 1, 21, 0, 0)
98
+
99
+ # make the time row
100
+ @time = Time.new(2013, 1, 1, 9, 0, 0)
101
+ while @time < end_time
102
+ time_header += "<th>#{@time.strftime("%-k:%M")}</th>"
103
+ @time += 900
104
+ end
105
+
106
+ #make each day row
107
+ (0..4).each do |day|
108
+ classes = @timetable.classes_for_day(day)
109
+ rows << day_row(day, classes)
110
+ end
111
+
112
+ rows_str, id_str = '', "id=\"#{@timetable.name}\""
113
+ rows.each{ |r| rows_str += "<tr class=\"day\">\n#{r}\n</tr>\n" }
114
+
115
+ %Q{
116
+ <!DOCTYPE html>
117
+ <html>
118
+ <head>
119
+ <title>#{@timetable.name || 'Timetable' } - Timetablr.co</title>
120
+ <style>#{build_css}</style>
121
+ </head>
122
+ <body>
123
+ <h3>#{@timetable.name}</h3>
124
+ <table #{id_str if @timetable.name}>
125
+ <tr id="time">#{time_header}</tr>
126
+ #{rows_str}
127
+ </table>
128
+ </body>
129
+ </html>
130
+ }
131
+ end
132
+
133
+ end
134
+ end
@@ -0,0 +1,114 @@
1
+ module Tableau
2
+ class Timetable
3
+
4
+ attr_accessor :name, :modules
5
+
6
+ # Create a new Timetable, with a Timetable Name and Student Set ID
7
+ def initialize(timetable_name)
8
+ @name = name || "Timetable"
9
+ @modules = Array.new
10
+ end
11
+
12
+ # Pushes an existing module into the timetable
13
+ def push_module(mod)
14
+ @modules << mod
15
+ end
16
+
17
+ # Adds a Module to the Timetable via the Parser
18
+ def add_module(module_code)
19
+ @module = Tableau::ModuleParser.new(module_code).parse
20
+ @modules << @module if @module
21
+ end
22
+
23
+ # Mass-adds an array of modules objects to the timetable
24
+ def add_modules(modules)
25
+ modules.each { |mod_code| add_module(mod_code) }
26
+ end
27
+
28
+ # Removes a class from the timetable
29
+ def remove_class(rem_class)
30
+ @modules.each do |m|
31
+ if m.name == rem_class.name
32
+ m.classes.delete(rem_class)
33
+ break
34
+ end
35
+ end
36
+ end
37
+
38
+ # Returns an array of the given day's classes
39
+ def classes_for_day(day)
40
+ classes = Tableau::ClassArray.new
41
+
42
+ @modules.each do |mod|
43
+ cfd = mod.classes_for_day(day)
44
+ cfd.each { |cl| classes << cl } if cfd
45
+ end
46
+
47
+ classes.count > 0 ? classes : nil
48
+ end
49
+
50
+ # Returns the class at the given day & time
51
+ def class_for_time(day, time)
52
+ cfd = self.classes_for_day(day)
53
+ cfd.each { |c| return c if c.time == time }
54
+ nil
55
+ end
56
+
57
+ # Returns the module with given Module Code
58
+ def module_for_name(module_name)
59
+ modules.each { |m| return m if m.name }
60
+ nil
61
+ end
62
+
63
+ # Return the Tableau::Module that matches a given code
64
+ def module_for_code(mod_code)
65
+ @modules.each { |m| return m if m.module_id == mod_code } if @modules
66
+ nil
67
+ end
68
+
69
+ # Returns the earliest class on the timetable
70
+ def earliest_class
71
+ earliest_classes = Tableau::ClassArray.new
72
+ @modules.each { |m| earliest_classes << m.earliest_class }
73
+
74
+ earliest = earliest_classes.first
75
+ earliest_classes.each { |c| earliest = c if c.time < earliest.time }
76
+ earliest
77
+ end
78
+
79
+ # Returns the latest class on the timetable
80
+ def latest_class
81
+ latest_classes = Tableau::ClassArray.new
82
+ @modules.each { |m| latest_classes << m.latest_class }
83
+
84
+ latest = latest_classes.first
85
+ latest_classes.each { |c| latest = c if c.time > latest.time }
86
+ latest
87
+ end
88
+
89
+ # Returns an array of time conflicts found in the timetable
90
+ def conflicts
91
+ conflicts = Tableau::ClassArray.new
92
+
93
+ (0..4).each do |day|
94
+ days_classes = self.classes_for_day(day)
95
+ next if !days_classes || days_classes.count == 0
96
+
97
+ # get the last element index
98
+ last = days_classes.count - 1
99
+
100
+ for i in 0..last
101
+ i_c = days_classes[i]
102
+ time_range = i_c.time..(i_c.time + 3600 * i_c.duration)
103
+
104
+ for j in (i+1)..last
105
+ if time_range.cover?(days_classes[j].time)
106
+ conflicts << [days_classes[i], days_classes[j]]
107
+ end
108
+ end
109
+ end
110
+ end
111
+ conflicts # return the conflicts
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,54 @@
1
+ require 'tableau/baseparser'
2
+
3
+ module Tableau
4
+ class TimetableParser < Tableau::BaseParser
5
+
6
+ @@TIMETABLE_CODE_REGEX = /^[A-Za-z0-9\(\)]+/
7
+
8
+ # Create a new TimetableParser, with a Student Set ID (aka Core Timetable)
9
+ def initialize(student_set_id = nil)
10
+ begin
11
+ timetable_response = Tableau::UriBuilder.new(student_set_id).read
12
+ @raw_timetable = Nokogiri::HTML(timetable_response) if timetable_response
13
+ rescue OpenURI::HTTPError
14
+ return nil
15
+ end
16
+ end
17
+
18
+ # Get a summary of information about this timetable
19
+ def timetable_info
20
+ end
21
+
22
+ # Parse the Timetable for all Modules within
23
+ # Returns: A Tableau::Timetable
24
+ def parse
25
+ raise "No Timetable loaded!" unless @raw_timetable
26
+
27
+ table_info = @@TIMETABLE_CODE_REGEX.match(get_info(@raw_timetable))
28
+ timetable = Tableau::Timetable.new(table_info)
29
+
30
+ table_count = 1
31
+ table_data = @raw_timetable.xpath(xpath_for_table(table_count))
32
+
33
+ while !table_data.empty?
34
+ table_classes = parse_table(table_data)
35
+ sort_classes(timetable, table_classes)
36
+ table_data = @raw_timetable.xpath(xpath_for_table(table_count += 1))
37
+ end
38
+ timetable
39
+ end
40
+
41
+ # Sort all the parsed classes into modules
42
+ def sort_classes(timetable, classes)
43
+ classes.each do |c|
44
+ if !(cmodule = timetable.module_for_code(c.code))
45
+ cmodule = Tableau::Module.new(c.code)
46
+ timetable.push_module(cmodule)
47
+ end
48
+
49
+ cmodule.add_class(c)
50
+ end
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,43 @@
1
+ module Tableau
2
+ class UriBuilder
3
+
4
+ def defaults
5
+ @options = {
6
+ root: 'crwnmis3.staffs.ac.uk/Reporting/',
7
+ timetable_type: 'Individual',
8
+ lookup_type: 'Student+Sets',
9
+ timetable_template: 'Design+Template',
10
+ weeks: '10-25', # 10-25 for Semester 1, 26-42 for Semester 2
11
+ days: '1-5', # Mon - Fri
12
+ period_from: '5', # 15 min intervals since 8AM
13
+ period_to: '52',
14
+ optional_params: '&width=0&height=0' #optional params
15
+ }
16
+ end
17
+
18
+ def initialize(lookup_id, options = {})
19
+ @lookup_id = lookup_id
20
+ defaults.merge!(options)
21
+
22
+ if options[:module_lookup]
23
+ @options[:lookup_type] = "Modules"
24
+ @options[:timetable_template] = "Module%20Individual%20SOC"
25
+ end
26
+
27
+ @options[:weeks] = ENV["TABLEAU_SEMESTER"] == 1 ? '10-25' : '26-42'
28
+ end
29
+
30
+ def read
31
+ open(self.to_s){ |io| io.read }
32
+ end
33
+
34
+ def to_s
35
+ "http://#{@options[:root]}#{@options[:timetable_type]};#{@options[:lookup_type]};name;" +
36
+ "#{@lookup_id}?&template=#{@options[:timetable_template].gsub(' ', '%20')}" +
37
+ "&weeks=#{@options[:weeks]}&days=#{@options[:days]}" +
38
+ "&periods=#{@options[:period_from]}-#{@options[:period_to]}" +
39
+ "#{@options[:optional_params]}"
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module Tableau
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :tableau do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tableau
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matt Ryder
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 4.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 4.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: nokogiri
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-spork
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: debugger
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Parses, analyses and creates Timetables for Staffordshire University
112
+ students.Handles both Course and individual Module timetables.
113
+ email:
114
+ - matt@mattryder.co.uk
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - lib/tableau/timetable.rb
120
+ - lib/tableau/class.rb
121
+ - lib/tableau/uribuilder.rb
122
+ - lib/tableau/timetableparser.rb
123
+ - lib/tableau/moduleparser.rb
124
+ - lib/tableau/baseparser.rb
125
+ - lib/tableau/classarray.rb
126
+ - lib/tableau/module.rb
127
+ - lib/tableau/tablebuilder.rb
128
+ - lib/tableau/version.rb
129
+ - lib/tasks/tableau_tasks.rake
130
+ - lib/tableau.rb
131
+ - MIT-LICENSE
132
+ - Rakefile
133
+ homepage: http://www.github.com/MattRyder/tableau
134
+ licenses: []
135
+ metadata: {}
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubyforge_project:
152
+ rubygems_version: 2.0.3
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Parses, analyses and creates Timetables for Staffordshire University students.Handles
156
+ both Course and individual Module timetables.
157
+ test_files: []