coursegen 0.0.2

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 (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
+