curriculum-generator 1.0.0

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