linkedin2cv 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.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.buildpacks +2 -0
  3. data/.bundle/config +1 -0
  4. data/.gitignore +7 -0
  5. data/.node +1 -0
  6. data/.travis.yml +21 -0
  7. data/Gemfile +5 -0
  8. data/Gemfile.lock +119 -0
  9. data/Procfile +3 -0
  10. data/README.md +32 -0
  11. data/Rakefile +6 -0
  12. data/Vagrantfile +16 -0
  13. data/app/routes/api.rb +101 -0
  14. data/app/routes/web.rb +132 -0
  15. data/app/views/hello.erb +36 -0
  16. data/app/views/home.erb +12 -0
  17. data/app/views/index.haml +11 -0
  18. data/app.rb +26 -0
  19. data/config.ru +6 -0
  20. data/config.yml +31 -0
  21. data/lib/linkedin2cv/cli/command.rb +46 -0
  22. data/lib/linkedin2cv/converter.rb +116 -0
  23. data/lib/linkedin2cv/logging.rb +35 -0
  24. data/lib/linkedin2cv/renderer/latex_renderer.rb +64 -0
  25. data/lib/linkedin2cv/version.rb +3 -0
  26. data/linkedin2cv.gemspec +49 -0
  27. data/public/.bowerrc +3 -0
  28. data/public/.editorconfig +21 -0
  29. data/public/.gitattributes +1 -0
  30. data/public/.gitignore +5 -0
  31. data/public/.jshintrc +24 -0
  32. data/public/.travis.yml +7 -0
  33. data/public/Gruntfile.js +487 -0
  34. data/public/app/.buildignore +1 -0
  35. data/public/app/.htaccess +543 -0
  36. data/public/app/404.html +157 -0
  37. data/public/app/favicon.ico +0 -0
  38. data/public/app/images/yeoman.png +0 -0
  39. data/public/app/index.html +73 -0
  40. data/public/app/robots.txt +3 -0
  41. data/public/app/scripts/app.js +20 -0
  42. data/public/app/scripts/controllers/main.js +21 -0
  43. data/public/app/scripts/services/linkedin2cv.js +93 -0
  44. data/public/app/styles/main.scss +95 -0
  45. data/public/app/views/main.html +23 -0
  46. data/public/bower.json +19 -0
  47. data/public/karma-e2e.conf.js +54 -0
  48. data/public/karma.conf.js +56 -0
  49. data/public/package.json +41 -0
  50. data/public/test/.jshintrc +36 -0
  51. data/public/test/runner.html +10 -0
  52. data/public/test/spec/controllers/main.js +22 -0
  53. data/public/test/spec/services/happyapi.js +18 -0
  54. data/public/test/spec/services/happyservice.js +18 -0
  55. data/spec/converter_spec.rb +83 -0
  56. data/spec/mocks/config.yml +31 -0
  57. data/spec/mocks/profile.json +866 -0
  58. data/spec/spec_helper.rb +13 -0
  59. data/templates/cv.erb +327 -0
  60. data/templates/foo.asciidoc +11 -0
  61. data/templates/foo.latex +230 -0
  62. data/test.rb +109 -0
  63. data/test.sh +6 -0
  64. data/teust.rb +75 -0
  65. metadata +434 -0
@@ -0,0 +1,116 @@
1
+ require 'linkedin-oauth2'
2
+ require 'asciidoctor'
3
+ require 'launchy'
4
+ require 'tempfile'
5
+ require 'tilt'
6
+ require 'yaml'
7
+ require 'erb'
8
+ require 'net/http'
9
+
10
+
11
+ module Linkedin2CV
12
+
13
+ class Converter
14
+ include Logging
15
+
16
+ TOKEN_WAIT_TIME = 15
17
+ API_KEY = ENV['LINKEDIN_API_KEY']
18
+ API_SECRET = ENV["LINKEDIN_API_SECRET"]
19
+
20
+ def initialize
21
+ @display_fields = ["company", "publication", "patent", "language", "skills", "certification", "education", "course", "volunteer", "recommendations"]
22
+ @profile_fields = ["projects:(start-date,end-date,description,id,name,url),main-address,phone-numbers,email-address,first-name,last-name,maiden-name,formatted-name,phonetic-first-name,phonetic-last-name,formatted-phonetic-name,headline,location:(name,country:(code)),industry,current-status,current-share,num-connections,num-connections-capped,summary,specialties,positions,picture-url,api-standard-profile-request:(url,headers),public-profile-url,last-modified-timestamp,proposal-comments,associations,interests,publications:(id,title,publisher:(name),authors:(id,name,person),date,url,summary),patents,languages:(id,language:(name),proficiency:(level,name)),skills:(id,skill:(name)),certifications:(id,name,authority:(name),number,start-date,end-date),educations:(id,school-name,field-of-study,start-date,end-date,degree,activities,notes),courses:(id,name,number),three-current-positions:(id,title,summary,start-date,end-date,is-current,company),three-past-positions:(id,title,summary,start-date,end-date,is-current,company),num-recommenders,recommendations-received:(id,recommendation-type,recommendation-text,recommender),mfeed-rss-url,following,job-bookmarks,suggestions,date-of-birth,member-url-resources:(url,name),related-profile-views,honors-awards"]
23
+
24
+ # Confirm API keys setup
25
+
26
+ # Setup default options to be merged with supplied ones
27
+ end
28
+
29
+ # Public: Create a resume given a LinkedIn Profile
30
+ #
31
+ # Takes
32
+ def create_resume(options = {})
33
+
34
+ # Get Profile
35
+ profile = get_profile
36
+
37
+ # Find stylist (LaTeX / Asciidoc etc.)
38
+ # Find output factory/plugin (fetch from Github?)
39
+ case options['format']
40
+ when 'latex'
41
+ require 'linkedin2cv/renderer/latex_renderer'
42
+
43
+ # Currently only supports one LaTeX template
44
+ renderer = Linkedin2CV::LatexRenderer.new
45
+ renderer.latex_pdf(profile, options)
46
+ else
47
+ puts "Sorry mate, I don't yet support the '#{options['format']}' format'"
48
+ end
49
+ end
50
+
51
+ def auth
52
+ thread = nil
53
+ if !File.exists?('.token')
54
+ auth_thread = prompt_user_authorisation
55
+
56
+ puts "Waiting to retrieve LinkedIn OAuth token"
57
+ sleep 1 until auth_thread.thread_variable?('access_token')
58
+ end
59
+
60
+ # Get Token from environment
61
+ token = fetch_token_from_tempfile
62
+ puts "I have a token for you: #{token}"
63
+ token
64
+ end
65
+
66
+ # Public: Fetch Profile Data
67
+ #
68
+ #
69
+ def get_profile
70
+
71
+ # Auth
72
+ token = auth
73
+
74
+ # Get Client info based on fields provided
75
+ client = LinkedIn::Client.new(API_KEY, API_SECRET, token)
76
+ user = client.profile(:fields => @profile_fields)
77
+
78
+ end
79
+
80
+ # Public: Fetches a template for rendering via Asciidoctor
81
+ #
82
+ #
83
+ def lookup_template
84
+
85
+ end
86
+
87
+ # Public: Invoke the API and browser session for end user authentication.
88
+ #
89
+ # Returns a sub-process containing the API session.
90
+ #
91
+ def prompt_user_authorisation
92
+
93
+ require './app/routes/web'
94
+
95
+ # Start local API
96
+ Launchy.open("http://localhost:5000/cli/auth")
97
+
98
+ auth_thread = Thread.new do
99
+ Linkedin2CV::Routes::Web.run!
100
+ end
101
+
102
+ auth_thread
103
+ end
104
+
105
+ # Public: Fetch the token from a temporary oauth token file.
106
+ #
107
+ #
108
+ def fetch_token_from_tempfile
109
+ # Store in env for command line!
110
+ file = File.new('.token', 'r')
111
+ token = file.read
112
+ file.close
113
+ token
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,35 @@
1
+ require 'log4r'
2
+
3
+ # Public: Logging module to mixin with classes.
4
+ #
5
+ #
6
+ module Logging
7
+
8
+ def log
9
+ @log ||= Logging.logger_for(self.class.name)
10
+ end
11
+
12
+ # Use a hash class-ivar to cache a unique Logger per class:
13
+ @loggers = {}
14
+
15
+ class << self
16
+ include Log4r
17
+
18
+ def logger_for(classname)
19
+ @loggers[classname] ||= configure_logger_for(classname)
20
+ end
21
+
22
+ def configure_logger_for(classname)
23
+ logger = Logger.new classname.to_s.gsub(/[^a-zA-Z0-9]/, '.').downcase.gsub(/\.+/, '.')
24
+
25
+ # Log to file or stdout?
26
+ if (ENV['LOG_OUTPUT_FILENAME'] && !ENV['LOG_OUTPUT_FILENAME'].empty?)
27
+ puts ENV['LOG_OUTPUT_FILENAME']
28
+ logger.outputters << Log4r::FileOutputter.new('linkedin2cvlog', :filename => ENV['LOG_OUTPUT_FILENAME'])
29
+ elsif
30
+ logger.outputters << Log4r::StdoutOutputter.new('linkedin2cvlog')
31
+ end
32
+ logger
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,64 @@
1
+ module Linkedin2CV
2
+ class LatexRenderer
3
+
4
+ # Public: Produce a Latex PDF
5
+ #
6
+ #
7
+ def latex_pdf(profile, options)
8
+
9
+ def clean_latex(s)
10
+ # Clean &
11
+ s = s.gsub(/(?<!\\)\&(?!\\)/, '\\\&')
12
+
13
+ # Clean $
14
+ s = s.gsub(/(?<!\\)\$(?!\\)/, '\\\$')
15
+
16
+ # Clean %
17
+ s = s.gsub(/(?<!\\)%(?!\\)/, '\\\%')
18
+
19
+ # # Clean ~
20
+ s = s.gsub(/\~/, '\\\textasciitilde')
21
+
22
+ # # Clean >
23
+ s = s.gsub(/\>/, '\\\textgreater')
24
+
25
+ # # Clean <
26
+ s = s.gsub(/\</, '\\\textless')
27
+
28
+ s
29
+ end
30
+
31
+ require 'tilt/erb'
32
+ output_filename = "#{options['output_file']}.latex"
33
+ template = Tilt.new('templates/cv.erb')
34
+
35
+ output = template.render(self, :profile => profile, :options => options)
36
+
37
+ output_file = File.new(output_filename, 'w')
38
+ output_file.write(output)
39
+ output_file.close
40
+
41
+ # Make sure this variable is escaped, clearly.....
42
+ exec("pdflatex #{output_filename}")
43
+ end
44
+
45
+ # Public: Produce a Latex
46
+ #
47
+ #
48
+ def latex
49
+
50
+ end
51
+ end
52
+ end
53
+
54
+ # Monkey patching jiggery-pokery to escape special LaTeX chars
55
+ # automatically from ERB <%= %> statements
56
+ class ERB::Compiler
57
+ alias_method :old_add_insert_cmd, :add_insert_cmd
58
+ alias_method :old_add_put_cmd, :add_put_cmd
59
+
60
+ def add_insert_cmd(out, content)
61
+ out.push("#{@insert_cmd}(clean_latex( (#{content}).to_s ))")
62
+ end
63
+
64
+ end
@@ -0,0 +1,3 @@
1
+ module Linkedin2CV
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,49 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'linkedin2cv/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "linkedin2cv"
8
+ spec.version = Linkedin2CV::VERSION
9
+ spec.authors = ["mefellows"]
10
+ spec.email = ["matt.fellows@onegeek.com.au"]
11
+ spec.summary = "Linkedin2CV"
12
+ spec.description = "Turn your LinkedIn Profile into a professional resume in many formats (PDF / HTML5 / LaTeX / Asciidoc)"
13
+ spec.homepage = "https://github.com/mefellows/linkedin2cv"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+
25
+ # Needs to be runtime for deployment on Heroku
26
+ spec.add_runtime_dependency "rspec"
27
+ spec.add_runtime_dependency "rake"
28
+ spec.add_runtime_dependency "clamp"
29
+ spec.add_runtime_dependency "json"
30
+ spec.add_runtime_dependency "log4r"
31
+ spec.add_runtime_dependency "sinatra"
32
+ spec.add_runtime_dependency "sinatra-contrib"
33
+ spec.add_runtime_dependency "sinatra-param"
34
+ spec.add_runtime_dependency "eventmachine"
35
+ spec.add_runtime_dependency "em-websocket", '=0.3.6'
36
+ spec.add_runtime_dependency "thin"
37
+ spec.add_runtime_dependency "foreman"
38
+ spec.add_runtime_dependency "dotenv"
39
+ spec.add_runtime_dependency "sinatra-websocket"
40
+
41
+ # Specific App dependencies
42
+ spec.add_runtime_dependency "linkedin-oauth2"
43
+ spec.add_runtime_dependency "haml"
44
+ spec.add_runtime_dependency "asciidoctor"
45
+ spec.add_runtime_dependency "launchy"
46
+ spec.add_runtime_dependency "tilt"
47
+ # spec.add_development_dependency "pact"
48
+ spec.add_development_dependency "webmock"
49
+ end
data/public/.bowerrc ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "directory": "app/bower_components"
3
+ }
@@ -0,0 +1,21 @@
1
+ # EditorConfig helps developers define and maintain consistent
2
+ # coding styles between different editors and IDEs
3
+ # editorconfig.org
4
+
5
+ root = true
6
+
7
+
8
+ [*]
9
+
10
+ # Change these settings to your own preference
11
+ indent_style = space
12
+ indent_size = 2
13
+
14
+ # We recommend you to keep these unchanged
15
+ end_of_line = lf
16
+ charset = utf-8
17
+ trim_trailing_whitespace = true
18
+ insert_final_newline = true
19
+
20
+ [*.md]
21
+ trim_trailing_whitespace = false
@@ -0,0 +1 @@
1
+ * text=auto
data/public/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ node_modules
2
+ dist
3
+ .tmp
4
+ .sass-cache
5
+ app/bower_components
data/public/.jshintrc ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "node": true,
3
+ "browser": true,
4
+ "esnext": true,
5
+ "bitwise": true,
6
+ "camelcase": true,
7
+ "curly": true,
8
+ "eqeqeq": true,
9
+ "immed": true,
10
+ "indent": 2,
11
+ "latedef": true,
12
+ "newcap": true,
13
+ "noarg": true,
14
+ "quotmark": "single",
15
+ "regexp": true,
16
+ "undef": true,
17
+ "unused": true,
18
+ "strict": true,
19
+ "trailing": true,
20
+ "smarttabs": true,
21
+ "globals": {
22
+ "angular": false
23
+ }
24
+ }
@@ -0,0 +1,7 @@
1
+ language: node_js
2
+ node_js:
3
+ - '0.8'
4
+ - '0.10'
5
+ before_script:
6
+ - 'npm install -g bower grunt-cli'
7
+ - 'bower install'