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