linkedin2cv 0.0.1 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d62bfd1c1f730f996da843ce11a63b400f3c7cbb
4
- data.tar.gz: 72139b2c8f2d664429de8b33c876ade5463207ba
3
+ metadata.gz: c48a59e31c94af34237c92f71a6872892289589c
4
+ data.tar.gz: 1dccf11adb9d8481312779c47dd0ecba6a919d46
5
5
  SHA512:
6
- metadata.gz: d18f5df837e060d5b5fb79bff0ec69b2c50fda290e2f9027d2aa78e3e38355cb6f0e6f0f3494648194601943f0180bbe0d8e1f442b394000990781ed9d8d2f03
7
- data.tar.gz: 8f918db4c0368dcac90545a67222c7654bb6d0f66da04b1bb9827ac1ebc4667da39999c61dd0e844950671fbf4d0095dfa3a85d7308e7d17ab7600f6ff9614d7
6
+ metadata.gz: a286e40bdea56ddde934ead557fc7eff9e36ec3d6986a7642edb9a1e7d4a6a8408246a0388a731977ddf8d4c778171078567b3b528b0915b7e6edc5c33b0a6f1
7
+ data.tar.gz: 335e4ba1a22918848c0327021cb39babe78b6819c9d796243884b5cba8420207ecfbbfc67e070098994cf64bd193ab1c8b516300cb2cdf93f180fd6ded3c6623
@@ -1,2 +1,2 @@
1
1
  https://github.com/heroku/heroku-buildpack-ruby.git
2
- https://github.com/mefellows/heroku-buildpack-nodejs
2
+ https://github.com/mefellows/heroku-buildpack-tex.git
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  .sass-cache/
3
3
  *.gem
4
4
  bin/*
5
- !bin/placeholder
5
+ !bin/linkedin2cv
6
6
  .env
7
- .token
7
+ .token
8
+ vendor/
data/Procfile CHANGED
@@ -1,3 +1 @@
1
- # web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb
2
- web: bundle exec thin start -p $PORT
3
- #web: bundle exec puma -p $PORT
1
+ web: bundle exec thin start -p $PORT
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  LinkedIn 2 CV
2
2
  ==============
3
3
 
4
- Turn your LinkedIn Profile into a professional resume in many formats (PDF / HTML5 / Latex / Asciidoc)
4
+ Turn your LinkedIn Profile into a professional resume in many formats (PDF / HTML5 / LaTeX / Asciidoc)
5
5
 
6
6
  ## Motivations
7
7
 
@@ -11,7 +11,7 @@ Oh, and don't bother using the LinkedIn 'Export to PDF' feature - it sucks.
11
11
 
12
12
  ## Getting Started
13
13
 
14
- First, install [MacTex](http://www.tug.org/mactex/) so that you can produce LaTeX documents. Then, simply run the converter:
14
+ First, install a Tex distribution ([MacTex](http://www.tug.org/mactex/) or [TexLive](http://www.tug.org/texlive/)) so that you can produce LaTeX documents. Then, simply run the converter:
15
15
 
16
16
  gem install linkedin2resume
17
17
  linkedin2cv convert --format latex --options config.yml matt.fellows
@@ -6,6 +6,7 @@ require 'sinatra/param'
6
6
  require "sinatra/json"
7
7
  require "json"
8
8
  require "sinatra-websocket"
9
+ require "linkedin2cv/converter"
9
10
 
10
11
  #
11
12
  # Public: API for the Application
@@ -14,8 +15,16 @@ module Linkedin2CV
14
15
  module Routes
15
16
  class API < Sinatra::Application
16
17
  helpers Sinatra::Param
18
+ use Rack::Logger
17
19
 
18
20
  configure do
21
+ set :views, 'app/views'
22
+ set :public_folder, 'public/dist'
23
+ set :api, ENV['LINKEDIN_API_KEY']
24
+ set :secret, ENV["LINKEDIN_API_SECRET"]
25
+ set :logging, true
26
+ set :server, 'thin'
27
+ set :port, 5000
19
28
  set :json_encoder, :to_json
20
29
  set :sockets, []
21
30
  end
@@ -36,8 +45,23 @@ module Linkedin2CV
36
45
  # Public: Main API entry point to run the Linkedin2CV service
37
46
  #
38
47
  #
39
- get '/api/linkedin2cv /:uri' do
48
+ get '/api/cv/:name' do
40
49
 
50
+ param :format, String, default: 'latex'
51
+ param :name, String, required: true
52
+ param :access_token, String, required: true
53
+
54
+ logger.debug("Request received to create a CV. Format: #{params[:format]}, Name: #{params[:name]}, Token: #{params[:access_token]}")
55
+
56
+ options = {}
57
+ options['output_file'] = "/tmp/#{params[:name]}"
58
+ options['format'] = params[:format]
59
+
60
+ converter = Linkedin2CV::Converter.new(params[:access_token])
61
+ converter.create_resume(options)
62
+
63
+ content_type "application/pdf"
64
+ File.read("#{options['output_file']}.pdf")
41
65
  end
42
66
 
43
67
 
@@ -90,9 +114,9 @@ module Linkedin2CV
90
114
  # Override this for custom behaviour.
91
115
  #
92
116
  def checkPong(msg)
93
- log.debug("Checking ping pong for a ping: " + msg['message'])
117
+ logger.debug("Checking ping pong for a ping: " + msg['message'])
94
118
  if (msg['message'] == 'ping')
95
- log.debug("Ping hit!")
119
+ logger.debug("Ping hit!")
96
120
  response_obj = {'message' => 'pong'}
97
121
  end
98
122
  end
@@ -99,12 +99,13 @@ module Linkedin2CV
99
99
  token = client.request_access_token(code, redirect_uri: "http://#{request.host}:#{request.port}/auth/callback")
100
100
  session[:atoken] = token.token
101
101
 
102
- # Store in env for command line!
103
- file = File.new('.token', 'w')
104
- file.write(token.token)
105
- file.close
106
-
102
+ # CLI Only behaviour
107
103
  if !ENV['CLI_ONLY'].nil?
104
+ # Store in env for command line!
105
+ file = File.new('.token', 'w')
106
+ file.write(token.token)
107
+ file.close
108
+
108
109
  logger.info "Got access token, shutting down!: #{session[:atoken]}"
109
110
  Thread.current.thread_variable_set('access_token', token.token)
110
111
  Thread.kill(Thread.current)
@@ -1,5 +1,5 @@
1
- <!doctype html><!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]--><!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]--><!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]--><!--[if gt IE 8]><!--><html class="no-js"><!--<![endif]--><head><meta charset="utf-8"><title>Linkedin2CV Generator | Generate a Linkedin2CV for your website</title><meta name="description" content=""><meta name="viewport" content="width=device-width"><!-- Place favicon.ico and apple-touch-icon.png in the root directory --><link rel="stylesheet" href="styles/vendor.css"><link rel="stylesheet" href="styles/f24dfdad.main.css"><body ng-app="linkedin2cv GeneratorApp"><!--[if lt IE 7]>
2
- <p class="browselinkedin2cv ">You are using an <strong>outdated</strong> browser. Please <a href="http://browselinkedin2cv .com/">upgrade your browser</a> to improve your experience.</p>
1
+ <!doctype html><!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]--><!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]--><!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]--><!--[if gt IE 8]><!--><html class="no-js"><!--<![endif]--><head><meta charset="utf-8"><title>Linkedin2CV Generator | Generate a Linkedin2CV for your website</title><meta name="description" content=""><meta name="viewport" content="width=device-width"><!-- Place favicon.ico and apple-touch-icon.png in the root directory --><link rel="stylesheet" href="styles/vendor.css"><link rel="stylesheet" href="styles/f24dfdad.main.css"><body ng-app="linkedin2cvGeneratorApp"><!--[if lt IE 7]>
2
+ <p class="browselinkedin2cv">You are using an <strong>outdated</strong> browser. Please <a href="http://browselinkedin2cv.com/">upgrade your browser</a> to improve your experience.</p>
3
3
  <![endif]--><!-- Add your site or application content here --><div class="container" ng-view=""></div><!-- Google Analytics: change UA-XXXXX-X to be your site's ID --><script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
4
4
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
5
5
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
@@ -2,10 +2,8 @@
2
2
  %a{:href => "/auth/logout"} Logout
3
3
  %p= profile.headline
4
4
  %br
5
+ %a{:href => "/api/cv/#{session[:atoken]}?access_token=#{session[:atoken]}"} Download CV
5
6
  %div= "Your token is #{session[:atoken]}"
6
7
 
7
8
  -else
8
9
  %a{:href => "/auth"} Login using LinkedIn
9
-
10
- -if session[:authorize_url]
11
- %p="yo, auth url #{session[:authorize_url]}"
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
5
+ $: << File.join(File.dirname(__FILE__), "..", "lib")
6
+ require 'linkedin2cv/cli/command'
7
+
8
+ # Setup the app to work from a base Runner/Command etc.
9
+ Linkedin2CV::MainCommand.run
@@ -1,4 +1,5 @@
1
1
  require 'linkedin-oauth2'
2
+ require 'linkedin2cv/logging'
2
3
  require 'asciidoctor'
3
4
  require 'launchy'
4
5
  require 'tempfile'
@@ -13,17 +14,20 @@ module Linkedin2CV
13
14
  class Converter
14
15
  include Logging
15
16
 
16
- TOKEN_WAIT_TIME = 15
17
17
  API_KEY = ENV['LINKEDIN_API_KEY']
18
18
  API_SECRET = ENV["LINKEDIN_API_SECRET"]
19
19
 
20
- def initialize
20
+ def initialize(token = nil)
21
21
  @display_fields = ["company", "publication", "patent", "language", "skills", "certification", "education", "course", "volunteer", "recommendations"]
22
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
+ @token = token
23
24
 
24
25
  # Confirm API keys setup
26
+ raise StandardError, "API_KEY not set" unless !API_KEY.nil?
27
+ raise StandardError, "API_SECRET not set" unless !API_SECRET.nil?
25
28
 
26
29
  # Setup default options to be merged with supplied ones
30
+
27
31
  end
28
32
 
29
33
  # Public: Create a resume given a LinkedIn Profile
@@ -42,25 +46,33 @@ module Linkedin2CV
42
46
 
43
47
  # Currently only supports one LaTeX template
44
48
  renderer = Linkedin2CV::LatexRenderer.new
45
- renderer.latex_pdf(profile, options)
49
+ renderer.render(profile, options)
46
50
  else
47
51
  puts "Sorry mate, I don't yet support the '#{options['format']}' format'"
48
52
  end
49
53
  end
50
54
 
55
+ # Public: Authenticate & Authorize the end user (via OAuth2) and fetch access token
56
+ #
57
+ #
51
58
  def auth
52
- thread = nil
53
- if !File.exists?('.token')
54
- auth_thread = prompt_user_authorisation
59
+ if @token.nil?
60
+ # If in CLI Mode, spawn a thread, run the API and open a browser window
61
+
62
+ thread = nil
63
+ if !File.exists?('.token')
64
+ auth_thread = prompt_user_authorisation
55
65
 
56
- puts "Waiting to retrieve LinkedIn OAuth token"
57
- sleep 1 until auth_thread.thread_variable?('access_token')
66
+ puts "Waiting to retrieve LinkedIn OAuth token"
67
+ sleep 1 until auth_thread.thread_variable?('access_token')
68
+ end
69
+
70
+ # Get Token from environment
71
+ @token = fetch_token_from_tempfile
72
+ puts "I have a token for you: #{@token}"
58
73
  end
59
74
 
60
- # Get Token from environment
61
- token = fetch_token_from_tempfile
62
- puts "I have a token for you: #{token}"
63
- token
75
+ @token
64
76
  end
65
77
 
66
78
  # Public: Fetch Profile Data
@@ -73,15 +85,7 @@ module Linkedin2CV
73
85
 
74
86
  # Get Client info based on fields provided
75
87
  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
-
88
+ client.profile(:fields => @profile_fields)
85
89
  end
86
90
 
87
91
  # Public: Invoke the API and browser session for end user authentication.
@@ -1,52 +1,35 @@
1
1
  module Linkedin2CV
2
2
  class LatexRenderer
3
3
 
4
- # Public: Produce a Latex PDF
4
+ # Public: Render a LaTeX document
5
5
  #
6
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'
7
+ def render(profile, options)
8
+ output = render_latex(profile, options)
32
9
  output_filename = "#{options['output_file']}.latex"
33
- template = Tilt.new('templates/cv.erb')
34
-
35
- output = template.render(self, :profile => profile, :options => options)
10
+ render_pdf(output, output_filename)
11
+ end
36
12
 
13
+ # Public: Produce a Latex PDF
14
+ #
15
+ #
16
+ def render_pdf(output, output_filename)
37
17
  output_file = File.new(output_filename, 'w')
38
18
  output_file.write(output)
39
19
  output_file.close
40
20
 
41
21
  # Make sure this variable is escaped, clearly.....
42
- exec("pdflatex #{output_filename}")
22
+ system("pdflatex -output-directory=/tmp/ #{output_filename}")
43
23
  end
44
24
 
45
- # Public: Produce a Latex
25
+ # Convert a LinkedIn Profile into a LaTeX source document.
46
26
  #
47
27
  #
48
- def latex
49
-
28
+ def render_latex(profile, options)
29
+ require 'tilt/erb'
30
+ template = Tilt.new('templates/cv.erb')
31
+ output = template.render(self, :profile => profile, :options => options)
32
+ output
50
33
  end
51
34
  end
52
35
  end
@@ -62,3 +45,28 @@ class ERB::Compiler
62
45
  end
63
46
 
64
47
  end
48
+
49
+ # Public: Prepare a string for LaTeX rendering.
50
+ #
51
+ # Escapes special chars, replaces etc.
52
+ def clean_latex(s)
53
+ # Clean &
54
+ s = s.gsub(/(?<!\\)\&(?!\\)/, '\\\&')
55
+
56
+ # Clean $
57
+ s = s.gsub(/(?<!\\)\$(?!\\)/, '\\\$')
58
+
59
+ # Clean %
60
+ s = s.gsub(/(?<!\\)%(?!\\)/, '\\\%')
61
+
62
+ # # Clean ~
63
+ s = s.gsub(/\~/, '\\\textasciitilde')
64
+
65
+ # # Clean >
66
+ s = s.gsub(/\>/, '\\\textgreater')
67
+
68
+ # # Clean <
69
+ s = s.gsub(/\</, '\\\textless')
70
+
71
+ s
72
+ end
@@ -1,3 +1,3 @@
1
1
  module Linkedin2CV
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["matt.fellows@onegeek.com.au"]
11
11
  spec.summary = "Linkedin2CV"
12
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"
13
+ spec.homepage = "http://linkedin2cv.herokuapp.com"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
@@ -17,9 +17,9 @@
17
17
  <link rel="stylesheet" href="styles/main.css">
18
18
  <!-- endbuild -->
19
19
  </head>
20
- <body ng-app="linkedin2cv App">
20
+ <body ng-app="linkedin2cvApp">
21
21
  <!--[if lt IE 7]>
22
- <p class="browselinkedin2cv ">You are using an <strong>outdated</strong> browser. Please <a href="http://browselinkedin2cv .com/">upgrade your browser</a> to improve your experience.</p>
22
+ <p class="browselinkedin2cv">You are using an <strong>outdated</strong> browser. Please <a href="http://browselinkedin2cv.com/">upgrade your browser</a> to improve your experience.</p>
23
23
  <![endif]-->
24
24
 
25
25
  <!-- Add your site or application content here -->
@@ -67,7 +67,7 @@
67
67
  <!-- build:js({.tmp,app}) scripts/scripts.js -->
68
68
  <script src="scripts/app.js"></script>
69
69
  <script src="scripts/controllers/main.js"></script>
70
- <script src="scripts/services/linkedin2cv api.js"></script>
70
+ <script src="scripts/services/linkedin2cvapi.js"></script>
71
71
  <!-- endbuild -->
72
72
  </body>
73
73
  </html>
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  angular
4
- .module('linkedin2cv App', [
4
+ .module('linkedin2cvApp', [
5
5
  'ngCookies',
6
6
  'ngResource',
7
7
  'ngSanitize',
8
8
  'ngRoute',
9
- 'linkedin2cv Services'
9
+ 'linkedin2cvServices'
10
10
  ])
11
11
  .config(function ($routeProvider) {
12
12
  $routeProvider
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- angular.module('linkedin2cv App')
3
+ angular.module('linkedin2cvApp')
4
4
  .controller('MainCtrl', ['$scope', '$http', 'Linkedin2CV', function ($scope, $http, Linkedin2CV) {
5
5
 
6
6
  // Default website
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var linkedin2cv Services = angular.module('linkedin2cv Services', ['ngResource']);
3
+ var linkedin2cvServices = angular.module('linkedin2cvServices', ['ngResource']);
4
4
 
5
- linkedin2cv Services.factory('Linkedin2CV', ['$q', '$rootScope', '$interval', function($q, $rootScope, $interval) {
5
+ linkedin2cvServices.factory('Linkedin2CV', ['$q', '$rootScope', '$interval', function($q, $rootScope, $interval) {
6
6
  // We return this object to anything injecting our service
7
7
  var Service = {};
8
8
 
@@ -6,7 +6,7 @@ $icon-font-path: "/bower_components/bootstrap-sass-official/vendor/assets/fonts/
6
6
  @import "../bower_components/bootstrap-sass-official/vendor/assets/stylesheets/bootstrap.scss";
7
7
  // endbower
8
8
 
9
- .browselinkedin2cv {
9
+ .browselinkedin2cv {
10
10
  margin: 0.2em 0;
11
11
  background: #ccc;
12
12
  color: #000;
@@ -1,7 +1,7 @@
1
1
  <div class="header">
2
2
  <ul class="nav nav-pills pull-right">
3
3
  <li class="active"><a ng-href="#">Home</a></li>
4
- <li><a ng-href="http://github.com/mefellows/linkedin2cv -generator">About</a></li>
4
+ <li><a ng-href="http://github.com/mefellows/linkedin2cv-generator">About</a></li>
5
5
  </ul>
6
6
  <h1 class="text-muted">Linkedin2CV App</h1>
7
7
  </div>
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "linkedin2cv -generator",
2
+ "name": "linkedin2cv",
3
3
  "version": "0.0.0",
4
4
  "dependencies": {
5
5
  "angular": "1.2.15",