curriculum-generator 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.ruby-version +1 -0
  4. data/CGen.gemspec +35 -0
  5. data/Gemfile +5 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +62 -0
  8. data/Rakefile +3 -0
  9. data/bin/cgen.rb +86 -0
  10. data/lib/CGen.rb +27 -0
  11. data/lib/cgen/compiler.rb +96 -0
  12. data/lib/cgen/curriculum.rb +57 -0
  13. data/lib/cgen/data_loader/yaml_data_loader.rb +31 -0
  14. data/lib/cgen/data_loader.rb +6 -0
  15. data/lib/cgen/generator/basic_generator.rb +37 -0
  16. data/lib/cgen/generator/cv_column.rb +32 -0
  17. data/lib/cgen/generator/cv_double_item.rb +42 -0
  18. data/lib/cgen/generator/cv_entry.rb +33 -0
  19. data/lib/cgen/generator/cv_item.rb +29 -0
  20. data/lib/cgen/generator/cv_item_with_comment.rb +32 -0
  21. data/lib/cgen/generator/cv_list_double_item.rb +31 -0
  22. data/lib/cgen/generator/cv_list_item.rb +23 -0
  23. data/lib/cgen/generator/list.rb +25 -0
  24. data/lib/cgen/generator/macro_substitution.rb +11 -0
  25. data/lib/cgen/generator/specific/education.rb +95 -0
  26. data/lib/cgen/generator/specific/self_assessment.rb +61 -0
  27. data/lib/cgen/generator/specific/work_experience.rb +81 -0
  28. data/lib/cgen/generator.rb +10 -0
  29. data/lib/cgen/util/latex_to_pdf.rb +61 -0
  30. data/lib/cgen/util/logging.rb +27 -0
  31. data/lib/cgen/util/shell_command.rb +48 -0
  32. data/lib/cgen/util.rb +6 -0
  33. data/lib/cgen/version.rb +6 -0
  34. data/static/bundled_templates/moderncv/.gitkeep +0 -0
  35. data/static/bundled_templates/moderncv/deps.yml +15 -0
  36. data/static/bundled_templates/moderncv/main.tex +208 -0
  37. data/static/bundled_templates/moderncv/publications.bib +40 -0
  38. data/static/bundled_templates/moderncv/resources/AuthorPhoto.png +0 -0
  39. metadata +194 -0
@@ -0,0 +1,32 @@
1
+ class CGen::Generator::CvItemWithComment < CGen::Generator::BasicGenerator
2
+
3
+ def initialize(param, data, lang)
4
+ super(param, data, lang)
5
+ end
6
+
7
+ def generate
8
+ value = get_value(param)
9
+ value = Array[value] unless value.is_a?(Array)
10
+
11
+ inst = self
12
+ value.collect do |elem|
13
+ result = Either.chain do
14
+ bind -> { elem.is_a?(Hash) }
15
+ bind -> {
16
+ elem.has_key?('title') &&
17
+ elem.has_key?('content') &&
18
+ elem.has_key?('comment')
19
+ }
20
+ bind -> {
21
+ inst.get_cv_item_with_comment(elem['title'], elem['content'], elem['comment'])
22
+ }
23
+ end
24
+ result.success? ? result.fetch : ''
25
+ end.join("\n")
26
+ end
27
+
28
+ def get_cv_item_with_comment(title, content, comment)
29
+ "\\cvitemwithcomment{#{title}}{#{content}}{#{comment}}"
30
+ end
31
+
32
+ end
@@ -0,0 +1,31 @@
1
+ class CGen::Generator::CvListDoubleItem < CGen::Generator::BasicGenerator
2
+
3
+ def initialize(param, data, lang)
4
+ super(param, data, lang)
5
+ end
6
+
7
+ def generate
8
+ value = get_value(param)
9
+ unless value.is_a?(Array)
10
+ value = Array[value]
11
+ end
12
+ value.collect do |elem|
13
+ instance = self
14
+ result = Either.chain do
15
+ bind -> { elem.is_a?(Hash) }
16
+ bind -> { elem.has_key?('item_0') && elem.has_key?('item_1') }
17
+ bind -> {
18
+ instance.get_cv_list_double_item(elem['item_0'], elem['item_1'])
19
+ }
20
+ end
21
+ result.success? ? result.fetch : ''
22
+ end.join("\n")
23
+ end
24
+
25
+ protected
26
+
27
+ def get_cv_list_double_item(item_0, item_1)
28
+ "\\cvlistdoubleitem{#{item_0}}{#{item_1}}"
29
+ end
30
+
31
+ end
@@ -0,0 +1,23 @@
1
+ class CGen::Generator::CvListItem < CGen::Generator::BasicGenerator
2
+
3
+ def initialize(param, data, lang)
4
+ super(param, data, lang)
5
+ end
6
+
7
+ def generate
8
+ value = get_value(param)
9
+ unless value.is_a?(Array)
10
+ value = Array[value]
11
+ end
12
+ value.collect do |content|
13
+ get_cv_list_item(content)
14
+ end.join("\n")
15
+ end
16
+
17
+ protected
18
+
19
+ def get_cv_list_item(content)
20
+ "\\cvlistitem{#{content}}"
21
+ end
22
+
23
+ end
@@ -0,0 +1,25 @@
1
+ class CGen::Generator::List < CGen::Generator::BasicGenerator
2
+
3
+ def initialize(param, data, lang)
4
+ super(param, data, lang)
5
+ end
6
+
7
+ def generate
8
+ value = get_value(param)
9
+ unless value.is_a?(Array)
10
+ value = Array[value]
11
+ end
12
+ '\begin{itemize}' +
13
+ value.collect do |item|
14
+ get_list(item)
15
+ end.join('') +
16
+ '\end{itemize}'
17
+ end
18
+
19
+ protected
20
+
21
+ def get_list(item)
22
+ "\\item #{item}"
23
+ end
24
+
25
+ end
@@ -0,0 +1,11 @@
1
+ class CGen::Generator::MacroSubstitution < CGen::Generator::BasicGenerator
2
+
3
+ def initialize(param, data, lang)
4
+ super(param, data, lang)
5
+ end
6
+
7
+ def generate
8
+ get_value(param)
9
+ end
10
+
11
+ end
@@ -0,0 +1,95 @@
1
+ class CGen::Generator::Education < CGen::Generator::BasicGenerator
2
+
3
+ def initialize(param, data, lang)
4
+ super(param, data, lang)
5
+ end
6
+
7
+ def generate
8
+ value = get_value(param)
9
+ value.collect do |elem|
10
+ get_education(elem)
11
+ end.join('')
12
+ end
13
+
14
+ protected
15
+
16
+ def get_education(context)
17
+ context.merge!({'titles' => get_value('titles')})
18
+
19
+ input = <<-CODE
20
+
21
+ \\cventry
22
+
23
+ <% if @date.nil? %>
24
+ {}
25
+ <% elsif @date.is_a?(Hash) && @date.has_key?('from') && @date.has_key?('to') %>
26
+ {<%= @date['from'] %>\\\\<%= @date['to'] %>}
27
+ <% else %>
28
+ {<%= @date %>}
29
+ <% end %>
30
+
31
+ {<%= @title.nil? ? '' : @title %>}
32
+
33
+ {}{}{}
34
+
35
+ {\\begin{itemize}
36
+
37
+ <% if @qualification %>
38
+ \\item \\textit{<%= @titles['S_4']['qualification'] %>}: <%= @qualification %>
39
+ <% end %>
40
+
41
+ <% if @organisation %>
42
+ \\item \\textit{<%= @titles['S_4']['organisation'] %>}: <%= @organisation %>
43
+ <% end %>
44
+
45
+ <% if @level %>
46
+ \\item \\textit{<%= @titles['S_4']['level'] %>}: <%= @level %>
47
+ <% end %>
48
+
49
+ <% if @lessons %>
50
+ \\item \\textit{<%= @titles['S_4']['lessons'] %>}: <%= @lessons %>
51
+ <% end %>
52
+
53
+ <% if @validity %>
54
+ \\item \\textit{<%= @titles['S_4']['validity'] %>}: <%= @validity %>
55
+ <% end %>
56
+
57
+ <% if @location %>
58
+ \\item \\textit{<%= @titles['S_4']['location'] %>}: <%= @location %>
59
+ <% end %>
60
+
61
+ <% if @teacher %>
62
+ \\item \\textit{<%= @titles['S_4']['teacher'] %>}: <%= @teacher %>
63
+ <% end %>
64
+
65
+ <% if @skills_covered %>
66
+
67
+ <% unless @skills_covered.is_a?(Array) %>
68
+ <% @skills_covered = Array[@skills_covered] %>
69
+ <% end %>
70
+
71
+ <% @skills_covered.compact! %>
72
+
73
+ <% if @skills_covered.length > 0 %>
74
+
75
+ \\item \\textit{<%= @titles['S_4']['skills_covered'] %>}:
76
+ \\begin{itemize}
77
+
78
+ <% @skills_covered.each do |skill_covered| %>
79
+ \\item <%= skill_covered %>
80
+ <% end %>
81
+
82
+ \\end{itemize}
83
+
84
+ <% end %>
85
+
86
+ <% end %>
87
+
88
+ \\end{itemize}}
89
+
90
+ CODE
91
+
92
+ evaluate(input, context).gsub(/^\s+/,'').gsub(/\n/,'') + "\n\n"
93
+ end
94
+
95
+ end
@@ -0,0 +1,61 @@
1
+ class CGen::Generator::SelfAssessment < CGen::Generator::BasicGenerator
2
+
3
+ def initialize(param, data, lang)
4
+ super(param, data, lang)
5
+ end
6
+
7
+ def generate
8
+ get_self_assessment(get_value(param))
9
+ end
10
+
11
+ protected
12
+
13
+ def get_self_assessment(context)
14
+ context.merge!({'titles' => get_value('titles')})
15
+
16
+ input = <<-CODE
17
+
18
+ \\cvitem{<%= @titles['S_5']['self_assessment'] %>}
19
+
20
+ {
21
+ \\scriptsize
22
+ \\begin{tabular}{l|l|l|l|l|l|l|l|l|l|l}
23
+
24
+ \\multicolumn{1}{c|}{<%= ' ' %>} &
25
+ \\multicolumn{4}{|c|}{\\textbf{<%= @titles['S_5']['understanding'] %>}} &
26
+ \\multicolumn{4}{|c|}{\\textbf{<%= @titles['S_5']['speaking'] %>}} &
27
+ \\multicolumn{2}{|c|}{\\textbf{<%= @titles['S_5']['writing'] %>}} \\\\
28
+
29
+ \\multicolumn{1}{c|}{<%= ' ' %>} &
30
+ \\multicolumn{2}{|c|}{<%= @titles['S_5']['listening'] %>} &
31
+ \\multicolumn{2}{|c|}{<%= @titles['S_5']['reading'] %>} &
32
+ \\multicolumn{2}{|c|}{<%= @titles['S_5']['spoken_interaction'] %>} &
33
+ \\multicolumn{2}{|c|}{<%= @titles['S_5']['spoken_production'] %>} &
34
+ \\multicolumn{2}{|c|}{<%= @titles['S_5']['written_production'] %>} \\\\
35
+ \\hline
36
+
37
+ <% @languages.each do |language| %>
38
+ \\hline
39
+ \\textbf{<%= language['name'] %>} &
40
+ <%= language['listening']['level'] %> &
41
+ <%= language['listening']['description'] %> &
42
+ <%= language['reading']['level'] %> &
43
+ <%= language['reading']['description'] %> &
44
+ <%= language['spoken_interaction']['level'] %> &
45
+ <%= language['spoken_interaction']['description'] %> &
46
+ <%= language['spoken_production']['level'] %> &
47
+ <%= language['spoken_production']['description'] %> &
48
+ <%= language['written_production']['level'] %> &
49
+ <%= language['written_production']['description'] %> \\\\
50
+ <% end %>
51
+ \\hline
52
+
53
+ \\end{tabular}
54
+ }
55
+
56
+ CODE
57
+
58
+ evaluate(input, context).gsub(/^\s+/,'').gsub(/\n/,'') + "\n\n"
59
+ end
60
+
61
+ end
@@ -0,0 +1,81 @@
1
+ class CGen::Generator::WorkExperience < CGen::Generator::BasicGenerator
2
+
3
+ def initialize(param, data, lang)
4
+ super(param, data, lang)
5
+ end
6
+
7
+ def generate
8
+ value = get_value(param)
9
+ value.collect do |elem|
10
+ get_work_experience(elem)
11
+ end.join("\n")
12
+ end
13
+
14
+ protected
15
+
16
+ def get_work_experience(context)
17
+
18
+ context.merge!({'titles' => get_value('titles')})
19
+
20
+ input = <<-CODE
21
+
22
+ \\cventry
23
+
24
+ <% if @date.nil? %>
25
+ {}
26
+ <% elsif @date.is_a?(Hash) && @date.has_key?('from') && @date.has_key?('to') %>
27
+ {<%= @date['from'] %>\\\\<%= @date['to'] %>}
28
+ <% else %>
29
+ {<%= @date %>}
30
+ <% end %>
31
+
32
+ {<%= @occupation.nil? ? '' : @occupation %>}
33
+
34
+ <% if @employer_name %>
35
+
36
+ {
37
+ <%= @employer_name %>
38
+ <% if @type_of_business %>
39
+ (<%= @type_of_business %>)
40
+ <% end %>
41
+ }
42
+
43
+ <% if @employer_address %>
44
+ {<%= @employer_address %>}
45
+ <% end %>
46
+
47
+ <% end %>
48
+
49
+ {}
50
+
51
+ {
52
+ <% if @main_activities %>
53
+
54
+ <% unless @main_activities.is_a?(Array) %>
55
+ <% @main_activities = Array[@main_activities] %>
56
+ <% end %>
57
+
58
+ <% @main_activities.compact! %>
59
+
60
+ <% if @main_activities.length > 0 %>
61
+
62
+ \\textit{<%= @titles['S_3']['main_activities'] %>}:
63
+ \\begin{itemize}
64
+
65
+ <% @main_activities.each do |main_activity| %>
66
+ \\item <%= main_activity %>
67
+ <% end %>
68
+
69
+ \\end{itemize}
70
+
71
+ <% end %>
72
+
73
+ <% end %>
74
+ }
75
+
76
+ CODE
77
+
78
+ evaluate(input, context).gsub(/^\s+/,'').gsub(/\n/,'') + "\n\n"
79
+ end
80
+
81
+ end
@@ -0,0 +1,10 @@
1
+ # All of the classes under cgen/generator should be namespaced with this module
2
+ module CGen::Generator; end
3
+
4
+
5
+ # Require the basic generator before the others, since they will assume the BasicGenerator class is already available
6
+ require 'cgen/generator/basic_generator'
7
+ # Require the 'generic' generators
8
+ Dir.glob(File.join(File.dirname(__FILE__), 'generator/*.rb')) { |mod| require(mod) if mod != 'basic_generator' }
9
+ # Require the 'specific' generators
10
+ Dir.glob(File.join(File.dirname(__FILE__), 'generator/specific/*.rb')) { |mod| require(mod) }
@@ -0,0 +1,61 @@
1
+ class CGen::Util::LatexToPdf
2
+
3
+ def initialize(
4
+ input_file_name, input_dir, resources_pths, out_dir, log_file,
5
+ halt_on_error=true, shell_enabled=true, interpreter='xelatex', additional_args=[])
6
+ @interpreter = interpreter
7
+ @halt_on_error = !!halt_on_error
8
+ @shell_enabled = !!shell_enabled
9
+ @out_dir = out_dir
10
+ @input_file_name = input_file_name
11
+ @input_dir = input_dir
12
+ @resources_pths = resources_pths
13
+ @log_file = log_file
14
+ @additional_args = additional_args.is_a?(Array) ? additional_args : []
15
+ end
16
+
17
+ def generate
18
+
19
+ # Store the starting resources for later cleanup
20
+ starting_resources = resources_files
21
+
22
+ # Create the command used to generate the PDF
23
+ gen_pdf_cmd = CGen::Util::ShellCommand.new(get_tex_cmd, @input_dir, @log_file)
24
+ # Create the command used to generate the bibliography
25
+ gen_bib_cmd = CGen::Util::ShellCommand.new(get_bibtex_cmd, @out_dir, @log_file)
26
+
27
+ Either.chain do
28
+ bind -> { gen_pdf_cmd.run }
29
+ bind -> { gen_bib_cmd.run }
30
+ bind -> { gen_pdf_cmd.run }
31
+ bind -> { gen_pdf_cmd.run }
32
+ bind -> {
33
+ # ==> Cleanup resources files
34
+ dirty_files = resources_files - starting_resources
35
+ dirty_files.length > 0 ? system("rm #{dirty_files.join(' ')}") : false
36
+ }
37
+ end
38
+ end
39
+
40
+ protected
41
+
42
+ def get_tex_cmd
43
+ args = %w(-synctex=1 -interaction=batchmode) # default arguments
44
+ args << '-halt-on-error' if @halt_on_error # halt on error
45
+ args += %w(-shell-escape --enable-write18) if @shell_enabled # shell escape
46
+ args << "-output-directory=#{@out_dir}" # output directory
47
+ args += @additional_args # add the additional arguments to the computed ones
48
+ args << @input_file_name
49
+
50
+ "#{@interpreter} #{args.join(' ')}" # build the tex command and return it
51
+ end
52
+
53
+ def get_bibtex_cmd
54
+ "BIBINPUTS=\"#{@input_dir}\" " + "bibtex #{@input_file_name}" # build the bibtex command and return it
55
+ end
56
+
57
+ def resources_files
58
+ Dir.glob(@resources_pths)
59
+ end
60
+
61
+ end
@@ -0,0 +1,27 @@
1
+ module CGen::Util::Logging
2
+
3
+ def self.log(name, opts={})
4
+ case name
5
+ when :fatal_error
6
+ puts "#{prefix} Fatal error: #{opts[:msg].light_black}. Aborting...".red
7
+ when :loading_curriculum_data
8
+ puts "#{prefix} Loading the curriculum data for ".cyan + opts[:trgt_lang].to_s.light_black
9
+ puts "#{indent} using ".cyan + opts[:master_lang].to_s.light_black + ' as the default'.cyan
10
+ when :executing_command
11
+ puts "#{prefix} Executing ".cyan + opts[:cmd].to_s.light_black
12
+ puts "#{indent} from the directory ".cyan + opts[:exec_dir].to_s.light_black if opts.has_key? :exec_dir
13
+ puts "#{indent} logging to ".cyan + opts[:log_file].to_s.light_black if opts.has_key? :log_file
14
+ else
15
+ # nothing to do
16
+ end
17
+ end
18
+
19
+ def self.prefix
20
+ '>>'
21
+ end
22
+
23
+ def self.indent
24
+ ' '
25
+ end
26
+
27
+ end
@@ -0,0 +1,48 @@
1
+ class CGen::Util::ShellCommand
2
+
3
+ def initialize(command, execution_dir, log_file=nil)
4
+ @command = command
5
+ @execution_dir = execution_dir
6
+ @log_file = log_file
7
+ end
8
+
9
+ def run
10
+ CGen::Util::Logging.log(:executing_command, cmd: @command, exec_dir: @execution_dir, log_file: @log_file)
11
+
12
+ status = true
13
+
14
+ Process.waitpid(
15
+ fork do
16
+ original_stdout, original_stderr = $stdout, $stderr
17
+ FileUtils.chdir @execution_dir do
18
+ File.open(@log_file, 'a') do |log_file|
19
+ $stderr = $stdout = log_file
20
+ system @command
21
+ $stdout, $stderr = original_stdout, original_stderr
22
+ end
23
+ end
24
+ end)
25
+
26
+ status # return
27
+ end
28
+
29
+ def self.exist?(command)
30
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
31
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
32
+ exts.collect do |ext|
33
+ exe = File.join(path, "#{command}#{ext}")
34
+ return true if File.executable? exe
35
+ end
36
+ end
37
+ false # return
38
+ end
39
+
40
+ def exist?
41
+ self.class.exist? @command
42
+ end
43
+
44
+ def find_executable_part(command)
45
+ command.split(' ').first
46
+ end
47
+
48
+ end
data/lib/cgen/util.rb ADDED
@@ -0,0 +1,6 @@
1
+ # All of the classes under cgen/util should be namespaced with this module
2
+ module CGen::Util; end
3
+
4
+
5
+ # Require all of the data loaders
6
+ Dir.glob(File.join(File.dirname(__FILE__), 'util/*.rb')) { |mod| require mod }
@@ -0,0 +1,6 @@
1
+ module CGen
2
+
3
+ # This is the current version of CGen
4
+ VERSION = '1.0.0'
5
+
6
+ end
File without changes
@@ -0,0 +1,15 @@
1
+ cmds:
2
+ - pygmentize
3
+
4
+ pkgs:
5
+ - fontawesome
6
+ - epigraph
7
+ - minted
8
+ - framed
9
+ - csquotes
10
+ - xcolor
11
+ - ulem
12
+ - todonotes
13
+
14
+ manual_deps:
15
+ - The FontAwesome system font (http://fortawesome.github.io/Font-Awesome)