coursegen 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +2 -0
  7. data/bin/cg +4 -0
  8. data/coursegen.gemspec +29 -0
  9. data/lib/coursegen/cli.rb +49 -0
  10. data/lib/coursegen/course/helpers/content_helpers.rb +139 -0
  11. data/lib/coursegen/course/helpers/lecture_helpers.rb +9 -0
  12. data/lib/coursegen/course/helpers/logging_helpers.rb +14 -0
  13. data/lib/coursegen/course/helpers/navigation_helpers.rb +64 -0
  14. data/lib/coursegen/course/helpers/sidebar_helpers.rb +63 -0
  15. data/lib/coursegen/course/lib/citem.rb +88 -0
  16. data/lib/coursegen/course/lib/helpers_.rb +9 -0
  17. data/lib/coursegen/course/lib/lectures.rb +91 -0
  18. data/lib/coursegen/course/lib/schedule_def.rb +13 -0
  19. data/lib/coursegen/course/lib/scheduler.rb +77 -0
  20. data/lib/coursegen/course/lib/search_data_generator.rb +21 -0
  21. data/lib/coursegen/course/lib/section.rb +74 -0
  22. data/lib/coursegen/course/lib/section_def.rb +10 -0
  23. data/lib/coursegen/course/lib/toc.rb +160 -0
  24. data/lib/coursegen/templates.rb +54 -0
  25. data/lib/coursegen/version.rb +3 -0
  26. data/lib/coursegen.rb +23 -0
  27. data/templates/Rules +98 -0
  28. data/templates/cg_config.rb +12 -0
  29. data/templates/cg_config.rb_sample +46 -0
  30. data/templates/content/bootstrap/css/custom.css +187 -0
  31. data/templates/content/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
  32. data/templates/content/bootstrap/js/custom.js +7 -0
  33. data/templates/content/content/index.md.erb +4 -0
  34. data/templates/layouts/body_footer.html +8 -0
  35. data/templates/layouts/body_header.html.erb +6 -0
  36. data/templates/layouts/course.html +59 -0
  37. data/templates/layouts/main_navbar.html.erb +21 -0
  38. data/templates/lib/default.rb +5 -0
  39. data/test1/Rules +40 -0
  40. data/test1/content/index.html +14 -0
  41. data/test1/content/stylesheet.css +101 -0
  42. data/test1/layouts/default.html +29 -0
  43. data/test1/lib/default.rb +2 -0
  44. data/test1/nanoc.yaml +62 -0
  45. metadata +159 -0
@@ -0,0 +1,77 @@
1
+ WEEKDAYS = { sunday: 0, monday: 1, tuesday: 2,
2
+ wednesday: 3, thursday: 4, friday: 5, saturday: 6 }
3
+
4
+ # Calculate days on which each event occurs, based on the configuration info
5
+ class Scheduler
6
+ def self.add_weeks(the_date, number)
7
+ the_date.to_date + Integer(number) * 7
8
+ end
9
+
10
+ def setup_from_args(start: nil, weekdays: nil, number: nil, skips: [])
11
+ if start.nil?
12
+ @start = nil
13
+ return
14
+ end
15
+ convert_and_verify_arguments(start, weekdays, number, skips)
16
+ @weekdays.sort!
17
+ recalc_event_map
18
+ end
19
+
20
+ def setup_from_schedule_def(sdef)
21
+ if sdef.nil?
22
+ @start = nil
23
+ return
24
+ end
25
+ setup_from_args(start: sdef.first_day, weekdays: sdef.weekdays,
26
+ number: sdef.number, skips: sdef.skips)
27
+ end
28
+
29
+ def event_date_by_index(ind)
30
+ null? ? nil : @event_dates[ind]
31
+ end
32
+
33
+ def null?
34
+ @start_date.nil?
35
+ end
36
+
37
+ private
38
+
39
+ def recalc_event_map
40
+ return if self.null?
41
+ @event_dates = []
42
+ wkdy_of_start = @start_date.cwday
43
+ wkday_index = @weekdays.find_index(wkdy_of_start)
44
+ curr_event_date = @start_date
45
+ @number.times do
46
+ |i|
47
+ @event_dates << curr_event_date unless @skips.include? curr_event_date
48
+ if @weekdays.length == 1
49
+ curr_event_date += 7
50
+ elsif wkday_index < @weekdays.length-1
51
+ wkday_index += 1
52
+ curr_event_date += @weekdays[wkday_index] - @weekdays[wkday_index-1]
53
+ elsif wkday_index == @weekdays.length-1
54
+ wkday_index = 0 # wrap
55
+ curr_event_date += 7 + @weekdays.first - @weekdays.last
56
+ end
57
+ end
58
+ end
59
+
60
+ def convert_and_verify_arguments(start, weekdays, number, skips)
61
+ @number = number + skips.length
62
+ @start_date = string_to_date(start)
63
+ @skips = skips.map { |d| string_to_date(d) } rescue raise(ArgumentError, "Scheduler: Invalid skip date")
64
+
65
+ raise ArgumentError, "Scheduler: invalid weekdays" unless weekdays.all? { |wd| WEEKDAYS.include? wd }
66
+
67
+ @weekdays = weekdays.map { |wd| WEEKDAYS[wd]}
68
+ raise ArgumentError, "Scheduler: Start date is not on one of the weekdays" unless @weekdays.include? @start_date.cwday
69
+ raise ArgumentError, "Scheduler: Skip date is not on a valid weekday" if !@skips.reduce(true) { |accum, skip| accum && @weekdays.include?(skip.cwday) }
70
+ end
71
+
72
+ private
73
+
74
+ def string_to_date(string_date)
75
+ Date.strptime(string_date, "%b-%d-%Y") rescue binding.pry
76
+ end
77
+ end
@@ -0,0 +1,21 @@
1
+ class SearchIndex
2
+ attr_reader :index
3
+ def initialize all_items
4
+ all_citems = all_items.map { |itm| Toc.instance.n2c(itm) }
5
+ skiplist = Regexp.union([/\/search_index\/.*/, /\/bootstrap\/.*/, /\/config\/.*/, /\/tipuesearch\/.*/])
6
+ @index = all_citems.select { |citem| citem.type == "page" && !citem.nitem.binary? && !citem.identifier.match(skiplist)}.map do
7
+ |item|
8
+ # puts "indexing: #{item.title}, match: #{item.identifier.match(skiplist) ? "true" : "false"}"
9
+ nok_parse = Nokogiri::HTML(item.nitem.compiled_content).at('body')
10
+ nok_parse_inner_text = nok_parse.nil? ? "" : nok_parse.inner_text
11
+ { title: clean_string(item.title),
12
+ text: clean_string(nok_parse_inner_text),
13
+ tags: "",
14
+ loc: item.nitem.rep_named(:default).path }
15
+ end
16
+ end
17
+
18
+ def clean_string str
19
+ str.nil? ? "" : str.gsub(/(\s+|\"|\“|\”)/, " ")
20
+ end
21
+ end
@@ -0,0 +1,74 @@
1
+ require 'forwardable'
2
+ require 'pry'
3
+
4
+ class Section
5
+ extend Forwardable
6
+ attr_reader :section
7
+ def_delegators :@citems, :[], :count, :each, :sort!, :reduce
8
+
9
+ def initialize sect, citems
10
+ @section = sect
11
+ @citems = section_filter(citems)
12
+ sort_pages
13
+ end
14
+
15
+ def find_index(citem)
16
+ @citems.find_index(citem)
17
+ end
18
+
19
+ def find_by_short_name(sname)
20
+ matches = @citems.select { |c| sname == c.short_name}
21
+ binding.pry if matches.length != 1
22
+ raise RuntimeError,"#{sname}: invalid reference in section \"#{@section}\"" if matches.length == 0
23
+ raise RunimeError, "#{sname}: duplicate referenced in section \"#{@section}\"" if matches.length != 1
24
+ matches[0]
25
+ end
26
+
27
+ def [](ind)
28
+ @citems[ind]
29
+ end
30
+
31
+ def next_for(citem)
32
+ index = @citems.find_index(citem)
33
+ raise ArgumentError, "invalid citem in next_for" if index.nil?
34
+ new_index = [index, @citems.length-2].min
35
+ @citems[new_index+1]
36
+ end
37
+
38
+ def previous_for(citem)
39
+ index = @citems.find_index(citem)
40
+ raise ArgumentError, "invalid citem in next_for" if index.nil?
41
+ new_index = [index, 1].max
42
+ @citems[new_index-1]
43
+ end
44
+
45
+ def has_subsections?
46
+ false
47
+ end
48
+
49
+ def has_lecture_numbers?
50
+ false
51
+ end
52
+
53
+ protected
54
+
55
+ def lookup_citem_by_identifier identifier
56
+ res = @citems.select { |i| i.identifier == identifier }
57
+ binding.pry if res.length != 1
58
+ raise RuntimeError, "lookup by identifier failed #{identifier}" if res.length != 1
59
+ res[0]
60
+ end
61
+
62
+
63
+ def section_filter citems
64
+ filtered_citems = citems.map do
65
+ |citem|
66
+ citem.section == @section && !citem.hidden? ? citem : nil
67
+ end
68
+ filtered_citems.compact
69
+ end
70
+
71
+ def sort_pages
72
+ @citems.sort! { |a,b| a.order <=> b.order } rescue binding.pry
73
+ end
74
+ end
@@ -0,0 +1,10 @@
1
+ # Sidebar configuration
2
+
3
+ class SectionDef
4
+ attr_accessor :title, :selector, :options
5
+ def initialize(title, selector, options = {})
6
+ @title = title
7
+ @selector = selector
8
+ @options = options
9
+ end
10
+ end
@@ -0,0 +1,160 @@
1
+ require 'singleton'
2
+ class Toc
3
+
4
+ include Enumerable
5
+ include Singleton
6
+
7
+ def prepare items, config
8
+ raise "Toc.prepare called twice!" unless @sections.nil?
9
+ @section_config = config
10
+ build_mapping_table items
11
+ build_citem_table
12
+ build_sections @citems
13
+ @info = {}
14
+ end
15
+
16
+ def build_sections items
17
+ @sections = {}
18
+ @section_config.each do
19
+ |sect|
20
+ selector = sect.selector.to_s
21
+ if sect.options[:type] == :lecture
22
+ schedule = Scheduler.new
23
+ schedule.setup_from_schedule_def(sect.options[:schedule])
24
+ @sections[selector] = Lectures.new(selector, items, schedule)
25
+ elsif sect.options[:type] == :section
26
+ @sections[selector] = Section.new(selector, items)
27
+ else
28
+ raise ArgumentError.new("Invalid section option")
29
+ end
30
+ end
31
+ end
32
+
33
+ def build_mapping_table items
34
+ @map_n2c = {}
35
+ items.each do
36
+ |nitem|
37
+ citem = CItem.new(nitem)
38
+ @map_n2c[nitem] = citem
39
+ end
40
+ end
41
+
42
+ def build_citem_table
43
+ @citems = @map_n2c.map { |k, v| v}
44
+ end
45
+
46
+ def n2c nitem
47
+ @map_n2c[nitem]
48
+ end
49
+
50
+ def lookup_citem the_sect, item_short_name
51
+ section(the_sect).find_by_short_name(item_short_name)
52
+ end
53
+
54
+ def reset
55
+ @sections = nil
56
+ end
57
+
58
+ def section selector
59
+ section = @sections[selector]
60
+ raise RuntimeError, "Invalid Section: #{selector}" if section.nil?
61
+ section
62
+ end
63
+
64
+
65
+ def find_next_forn(nitem)
66
+ p = find_next_for(n2c(nitem))
67
+ binding.pry if p.nil?
68
+ p
69
+ end
70
+
71
+ def find_previous_forn(nitem)
72
+ p = find_previous_for(n2c(nitem))
73
+ binding.pry if p.nil?
74
+ p
75
+ end
76
+
77
+ def find_next_for(citem)
78
+ section(citem.section).next_for(citem)
79
+ end
80
+
81
+ def find_previous_for(citem)
82
+ section(citem.section).previous_for(citem)
83
+ end
84
+
85
+ # def section_for nitem
86
+ # sec = @sections[n2c(nitem).section]
87
+ # binding.pry if sec.nil?
88
+ # sec
89
+ # end
90
+
91
+ def citem_section citem
92
+ @sections[citem.section]
93
+ end
94
+
95
+ def record_inclusion host_item, included_item
96
+ @info[included_item.identifier] = host_item
97
+ end
98
+
99
+ private
100
+
101
+ def subsections(section, &block)
102
+ items = sort_section_by_subsection_name section
103
+ item_iterator = items.chunk do |item|
104
+ subsection_name section.to_s, item.identifier
105
+ end
106
+ item_iterator.each do |subsection_item|
107
+ block.call(subsection_item)
108
+ end
109
+ end
110
+
111
+
112
+
113
+ def index_in_section item
114
+ section_for(item).find_index(item)
115
+ end
116
+
117
+ def each_by(section_selector, &block)
118
+ sub_toc = @sections[section_selector]
119
+ sub_toc.sort! { |a,b| a[:order] <=> b[:order] }.each do |member|
120
+ block.call(member)
121
+ end
122
+ end
123
+
124
+ def sort_section_by_order sect
125
+ @sections[sect].sort! { |a,b| a[:order] <=> b[:order] }
126
+ end
127
+
128
+ def sort_section_by_subsection_name(sect)
129
+ items = @sections[sect]
130
+ items.sort! do
131
+ |item1, item2|
132
+ subsection_name(sect.to_s, item1.identifier) <=> subsection_name(sect.to_s, item2.identifier)
133
+ end
134
+ items
135
+ end
136
+
137
+ def subsection_name section_name, item_name
138
+ item_matcher = item_name.match('\A/'+section_name+'/([^/]*)/([^/]*)/\z')
139
+ if !item_matcher.nil?
140
+ item_matcher[1]
141
+ else
142
+ ""
143
+ end
144
+ end
145
+
146
+ def lookup_inclusion included_item
147
+ @info[included_item.identifier]
148
+ end
149
+
150
+ def lookup_by_index section, index
151
+ sect = section.section
152
+ if index >= @sections[sect].count
153
+ index = @sections[sect].count-1
154
+ elsif index < 0
155
+ index = 0
156
+ end
157
+ @sections[sect][index]
158
+ end
159
+
160
+ end
@@ -0,0 +1,54 @@
1
+ require 'pry'
2
+
3
+ module CourseGen
4
+ class Templates < Thor::Group
5
+ include Thor::Actions
6
+
7
+ def generate_all
8
+ copy_template_dir("layouts", "layouts")
9
+ copy_template_dir("content/bootstrap", "content/bootstrap")
10
+ copy_template_dir("content/content", "content/content")
11
+ delete_target_file("lib/default.rb")
12
+ copy_template_dir("lib", "lib")
13
+ delete_target_file("Rules")
14
+ copy_template_file("Rules", "Rules")
15
+ copy_template_file("cg_config.rb", "cg_config.rb")
16
+ copy_template_file("cg_config.rb_sample", "cg_config.rb_sample")
17
+ delete_target_file("content/stylesheet.css")
18
+ delete_target_file("content/index.html")
19
+ end
20
+
21
+ def valid_cg_directory?
22
+ valid = true
23
+ list = ["Rules", "nanoc.yaml", "content", "lib"]
24
+ list.each do |filename|
25
+ if !File.exists?(filename)
26
+ valid = false
27
+ say("Required file not found: #{filename}")
28
+ end
29
+ end
30
+ valid
31
+ end
32
+
33
+ def self.source_root
34
+ Pathname.new(File.dirname(__FILE__)).parent.parent.to_s
35
+ end
36
+
37
+ def initialize
38
+ super
39
+ destination_root = Dir.getwd
40
+ end
41
+
42
+ def copy_template_dir from, to
43
+ directory("templates/#{from}", "#{to}")
44
+ end
45
+
46
+ def copy_template_file from, to
47
+ template("templates/#{from}", "#{to}")
48
+ end
49
+
50
+ def delete_target_file(to)
51
+ remove_file(to)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,3 @@
1
+ module Coursegen
2
+ VERSION = "0.0.2"
3
+ end
data/lib/coursegen.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'nanoc/toolbox'
2
+ require 'pry'
3
+ require 'nokogiri'
4
+
5
+ require "coursegen/version"
6
+ require "coursegen/course/helpers/sidebar_helpers"
7
+ require "coursegen/course/helpers/lecture_helpers"
8
+ require "coursegen/course/helpers/content_helpers"
9
+ require "coursegen/course/helpers/logging_helpers"
10
+ require "coursegen/course/helpers/navigation_helpers"
11
+ require 'coursegen/course/lib/section'
12
+ require "coursegen/course/lib/toc"
13
+ require 'coursegen/course/lib/citem'
14
+ require 'coursegen/course/lib/helpers_'
15
+ require 'coursegen/course/lib/schedule_def'
16
+ require 'coursegen/course/lib/section_def'
17
+ require 'coursegen/course/lib/scheduler'
18
+ require 'coursegen/course/lib/lectures'
19
+ require 'coursegen/course/lib/search_data_generator'
20
+
21
+ module Coursegen
22
+ # Your code goes here...
23
+ end
data/templates/Rules ADDED
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # A few helpful tips about the Rules file:
4
+ #
5
+ # * The string given to #compile and #route are matching patterns for
6
+ # identifiers--not for paths. Therefore, you can’t match on extension.
7
+ #
8
+ # * The order of rules is important: for each item, only the first matching
9
+ # rule is applied.
10
+ #
11
+ # * Item identifiers start and end with a slash (e.g. “/about/” for the file
12
+ # “content/about.html”). To select all children, grandchildren, … of an
13
+ # item, use the pattern “/about/*/”; “/about/*” will also select the parent,
14
+ # because “*” matches zero or more characters.
15
+
16
+ require 'pry'
17
+ require './cg_config.rb'
18
+
19
+ preprocess do
20
+ Toc.instance.prepare @items, SECTION_CONFIG
21
+ end
22
+
23
+ compile '/bootstrap/*' do
24
+ nil
25
+ end
26
+
27
+ compile '/tipuesearch/*' do
28
+ nil
29
+ end
30
+
31
+ compile '/content/scripts/*' do
32
+ nil
33
+ end
34
+
35
+ compile "/search_index/tipuesearch_content/" do
36
+ filter :erb
37
+ end
38
+
39
+ compile '*' do
40
+ if item.binary? || item[:status] == "hidden"
41
+ nil
42
+ elsif item[:status] == "hidden"
43
+ nil
44
+ elsif item[:type] == "subsection"
45
+ nil
46
+ elsif item[:extension] == "haml"
47
+ filter :haml;
48
+ layout 'course'
49
+ else
50
+ item[:extension].split('.').reverse.each do
51
+ |f|
52
+ case f
53
+ when 'md', 'markdown'
54
+ filter :kramdown, coderay_tab_width: 3
55
+ when 'erb'
56
+ filter :erb
57
+ end
58
+ end
59
+ layout 'course'
60
+ end
61
+ end
62
+
63
+ route '/bootstrap/*' do
64
+ @item.identifier.chop + '.' + @item[:extension]
65
+ end
66
+
67
+ route '/tipuesearch/*' do
68
+ @item.identifier.chop + '.' + @item[:extension]
69
+ end
70
+
71
+ route '/content/' do
72
+ '/index.html'
73
+ end
74
+
75
+ route "/search_index/tipuesearch_content/" do
76
+ '/tipuesearch/js/tipuesearch_content.js'
77
+ end
78
+
79
+ route '*' do
80
+ if item[:extension].nil?
81
+ raise RuntimeError, "Missing required extension: \".#{item.identifier}\""
82
+ elsif item.binary?
83
+ # Write item with identifier /foo/ to /foo.ext
84
+ item.identifier.chop + '.' + item[:extension] rescue binding.pry
85
+ elsif item.identifier == "/search_index/tipuesearch_content/"
86
+ item.identifier.chop + '.' + item[:extension] rescue binding.pry
87
+ elsif item[:status] == "hidden"
88
+ nil
89
+ elsif item[:type] == "subsection"
90
+ nil
91
+ elsif item[:extension] != "css"
92
+ # Write item with identifier /foo/ to /foo/index.html
93
+ item.identifier + 'index.html'
94
+ end
95
+ end
96
+
97
+ layout '*', :erb
98
+
@@ -0,0 +1,12 @@
1
+ # Copyright string
2
+ COPYRIGHT_STRING = "Copyright (2013-2014) R. Pito Salas, pitosalas@gmail.com"
3
+
4
+ # Course short name
5
+ COURSE_SHORT_NAME = "Cosi 000"
6
+ COURSE_LONG_NAME = "Not a course"
7
+
8
+ # Sections in the right hand margin of the page
9
+ SECTION_CONFIG = [
10
+ SectionDef.new("Background", "background", type: :section)
11
+ ]
12
+
@@ -0,0 +1,46 @@
1
+ require './lib/schedule_def.rb'
2
+ require './lib/section_def.rb'
3
+
4
+ # URL for AWS Deployment of the course
5
+ AWS_BUCKET = "cosi236b.courses.salas.com"
6
+
7
+ # Local directory path for directory of topics
8
+ TOPICS_PATH = "/mydev/curriculous-topics"
9
+
10
+ # Local directory path for directory of content
11
+ CONTENT_PATH = "/mydev/cosi236b"
12
+
13
+ # Copyright string
14
+ COPYRIGHT_STRING = "Copyright (2013-2014) R. Pito Salas, pitosalas@gmail.com"
15
+
16
+ # Course short name
17
+ COURSE_SHORT_NAME = "COSI 236B"
18
+ COURSE_LONG_NAME = "Software Engineering"
19
+
20
+ # Schedule information. Note that Monday is day 0
21
+ LECTURES_SCHEDULE_CONFIG = ScheduleDef.new(
22
+ first_day: "jan-14-2014",
23
+ weekdays: [:tuesday, :friday],
24
+ number: 26,
25
+ skips: ["feb-18-2014", "feb-21-2014", "apr-15-2014", "apr-18-2014", "apr-22-2014"])
26
+
27
+ LABS_SCHEDULE_CONFIG = ScheduleDef.new(
28
+ first_day: "jan-16-2014",
29
+ weekdays: [:thursday],
30
+ number: 13,
31
+ skips: ["feb-20-2014", "apr-17-2014"])
32
+
33
+ # Sections in the right hand margin of the page
34
+ SECTION_CONFIG = [
35
+ SectionDef.new("Lectures", "lectures", type: :lecture, schedule: LECTURES_SCHEDULE_CONFIG),
36
+ SectionDef.new("Labs", "lab", type: :lecture, schedule: LABS_SCHEDULE_CONFIG),
37
+ SectionDef.new("Incubator", "incubator", type: :section),
38
+ SectionDef.new("PA", "pa", type: :section),
39
+ SectionDef.new("Crib Sheets", "cribsheets", type: :section),
40
+ SectionDef.new("Background", "background", type: :section),
41
+ SectionDef.new("Root", "root", hidden: true, type: :section),
42
+ SectionDef.new("Topics", "topics", hidden: true, type: :section),
43
+ SectionDef.new("Search", "search_results", hidden: true, type: :section)
44
+ ]
45
+
46
+