diff --git a/.gitignore b/.gitignore deleted file mode 100644 index bfe0815..0000000 --- a/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -*.py[cod] - -# C extensions -*.so - -# Packages -*.egg -*.egg-info -dist -build -eggs -parts -bin -var -sdist -develop-eggs -.installed.cfg -lib -lib64 - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -.coverage -.tox -nosetests.xml - -# Translations -*.mo - -# Mr Developer -.mr.developer.cfg -.project -.pydevproject -.idea - -.settings/org.eclipse.core.resources.prefs diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 09521bc..0000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: python -python: - - 2.7 -script: python setup.py test -notifications: - - email: false \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index ca649ae..0000000 --- a/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright © 2013 Ozgur Vatansever (ozgurvt@gmail.com) - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of the Software, -and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index c4bf456..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -include README.rst \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 9967ef0..0000000 --- a/README.md +++ /dev/null @@ -1,448 +0,0 @@ -# Python LinkedIn - -Python interface to the LinkedIn API - -[![LinkedIn](http://developer.linkedin.com/sites/default/files/LinkedIn_Logo60px.png)](http://developer.linkedin.com) - -This library provides a pure Python interface to the LinkedIn **Profile**, **Group**, **Company**, **Jobs**, **Search**, **Share**, **Network** and **Invitation** REST APIs. - -[LinkedIn](http://developer.linkedin.com) provides a service that lets people bring their LinkedIn profiles and networks with them to your site or application via their OAuth based API. This library provides a lightweight interface over a complicated LinkedIn OAuth based API to make it for python programmers easy to use. - -## Installation - -[![Build Status](https://travis-ci.org/ozgur/python-linkedin.png?branch=master)](https://travis-ci.org/ozgur/python-linkedin) - -You can install **python-linkedin** library via pip: - - $ pip install python-linkedin - -## Authentication - -The LinkedIn REST API now supports the **OAuth 2.0** protocol for authentication. This package provides a full OAuth 2.0 implementation for connecting to LinkedIn as well as an option for using an OAuth 1.0a flow that can be helpful for development purposes or just accessing your own data. - -### HTTP API example - -Set `LINKEDIN_API_KEY` and `LINKEDIN_API_SECRET`, configure your app to redirect to `http://localhost:8080/code`, then execute: - - 0. `http_api.py` - 1. Visit `http://localhost:8080` in your browser, curl or similar - 2. A tab in your browser will open up, give LinkedIn permission there - 3. You'll then be presented with a list of available routes, hit any, e.g.: - 4. `curl -XGET http://localhost:8080/get_profile` - -### Developer Authentication - -To connect to LinkedIn as a developer or just to access your own data, you don't even have to implement an OAuth 2.0 flow that involves redirects. You can simply use the 4 credentials that are provided to you in your LinkedIn appliation as part of an OAuth 1.0a flow and immediately access your data. Here's how: - -```python -from linkedin import linkedin - -# Define CONSUMER_KEY, CONSUMER_SECRET, -# USER_TOKEN, and USER_SECRET from the credentials -# provided in your LinkedIn application - -# Instantiate the developer authentication class - -authentication = linkedin.LinkedInDeveloperAuthentication(CONSUMER_KEY, CONSUMER_SECRET, - USER_TOKEN, USER_SECRET, - RETURN_URL, linkedin.PERMISSIONS.enums.values()) - -# Pass it in to the app... - -application = linkedin.LinkedInApplication(authentication) - -# Use the app.... - -application.get_profile() -``` - - -### Production Authentication -In order to use the LinkedIn OAuth 2.0, you have an **application key** and **application secret**. You can get more detail from [here](http://developers.linkedin.com/documents/authentication). - -For debugging purposes you can use the credentials below. It belongs to my test application. Nothing's harmful. - -```python -KEY = 'wFNJekVpDCJtRPFX812pQsJee-gt0zO4X5XmG6wcfSOSlLocxodAXNMbl0_hw3Vl' -SECRET = 'daJDa6_8UcnGMw1yuq9TjoO_PMKukXMo8vEMo7Qv5J-G3SPgrAV0FqFCd0TNjQyG' -``` -You can also get those keys from [here](http://developer.linkedin.com/rest). - -LinkedIn redirects the user back to your website's URL after granting access (giving proper permissions) to your application. We call that url **RETURN URL**. Assuming your return url is **http://localhost:8000**, you can write something like this: - -```python -from linkedin import linkedin - -API_KEY = 'wFNJekVpDCJtRPFX812pQsJee-gt0zO4X5XmG6wcfSOSlLocxodAXNMbl0_hw3Vl' -API_SECRET = 'daJDa6_8UcnGMw1yuq9TjoO_PMKukXMo8vEMo7Qv5J-G3SPgrAV0FqFCd0TNjQyG' -RETURN_URL = 'http://localhost:8000' - -authentication = linkedin.LinkedInAuthentication(API_KEY, API_SECRET, RETURN_URL, linkedin.PERMISSIONS.enums.values()) -# Optionally one can send custom "state" value that will be returned from OAuth server -# It can be used to track your user state or something else (it's up to you) -# Be aware that this value is sent to OAuth server AS IS - make sure to encode or hash it -#authorization.state = 'your_encoded_message' -print authentication.authorization_url # open this url on your browser -application = linkedin.LinkedInApplication(authentication) -``` -When you grant access to the application, you will be redirected to the return url with the following query strings appended to your **RETURN_URL**: - -```python -"http://localhost:8000/?code=AQTXrv3Pe1iWS0EQvLg0NJA8ju_XuiadXACqHennhWih7iRyDSzAm5jaf3R7I8&state=ea34a04b91c72863c82878d2b8f1836c" -``` - -This means that the value of the **authorization_code** is **AQTXrv3Pe1iWS0EQvLg0NJA8ju_XuiadXACqHennhWih7iRyDSzAm5jaf3R7I8**. After setting it by hand, we can call the **.get_access_token()** to get the actual token. - -```python -authentication.authorization_code = 'AQTXrv3Pe1iWS0EQvLg0NJA8ju_XuiadXACqHennhWih7iRyDSzAm5jaf3R7I8' -authentication.get_access_token() -``` - -After you get the access token, you are now permitted to make API calls on behalf of the user who granted access to you app. In addition to that, in order to prevent from going through the OAuth flow for every consecutive request, -one can directly assign the access token obtained before to the application instance. - -```python -application = linkedin.LinkedInApplication(token='AQTFtPILQkJzXHrHtyQ0rjLe3W0I') -``` - -## Quick Usage From Python Interpreter - -For testing the library using an interpreter, you can benefit from the test server. - -```python -from linkedin import server -application = server.quick_api(KEY, SECRET) -``` -This will print the authorization url to the screen. Go into that URL using a browser to grant access to the application. After you do so, the method will return with an API object you can now use. - -## Profile API -The Profile API returns a member's LinkedIn profile. You can use this call to return one of two versions of a user's profile which are **public profile** and **standard profile**. For more information, check out the [documentation](http://developers.linkedin.com/documents/profile-api). - -```python -application.get_profile() -{u'firstName': u'ozgur', - u'headline': u'This is my headline', - u'lastName': u'vatansever', - u'siteStandardProfileRequest': {u'url': u'http://www.linkedin.com/profile/view?id=46113651&authType=name&authToken=Egbj&trk=api*a101945*s101945*'}} -``` - -There are many **field selectors** that enable the client fetch more information from the API. All of them used by each API are listed [here](http://developers.linkedin.com/documents/field-selectors). - -```python -application.get_profile(selectors=['id', 'first-name', 'last-name', 'location', 'distance', 'num-connections', 'skills', 'educations']) -{u'distance': 0, - u'educations': {u'_total': 1, - u'values': [{u'activities': u'This is my activity and society field', - u'degree': u'graduate', - u'endDate': {u'year': 2009}, - u'fieldOfStudy': u'computer science', - u'id': 42611838, - u'notes': u'This is my additional notes field', - u'schoolName': u'\u0130stanbul Bilgi \xdcniversitesi', - u'startDate': {u'year': 2004}}]}, - u'firstName': u'ozgur', - u'id': u'COjFALsKDP', - u'lastName': u'vatansever', - u'location': {u'country': {u'code': u'tr'}, u'name': u'Istanbul, Turkey'}, - u'numConnections': 13} -``` - -## Connections API -The Connections API returns a list of **1st degree** connections for a user who has granted access to their account. For more information, you check out its [documentation](http://developers.linkedin.com/documents/connections-api). - -To fetch your connections, you simply call **.get_connections()** method with proper GET querystring: - -```python -application.get_connections() -{u'_total': 13, - u'values': [{u'apiStandardProfileRequest': {u'headers': {u'_total': 1, - u'values': [{u'name': u'x-li-auth-token', u'value': u'name:16V1033'}]}, - u'url': u'http://api.linkedin.com/v1/people/lddvGtD5xk'}, - u'firstName': u'John', - u'headline': u'Ruby', - u'id': u'2323SDFSsfd34', - u'industry': u'Computer Software', - u'lastName': u'DOE', - u'location': {u'country': {u'code': u'tr'}, u'name': u'Istanbul, Turkey'}, - u'siteStandardProfileRequest': {u'url': u'http://www.linkedin.com/profile/view?id=049430532&authType=name&authToken=16V8&trk=api*a101945*s101945*'}}, - .... - -application.get_connections(selectors=['headline', 'first-name', 'last-name'], params={'start':10, 'count':5}) -``` - -## Search API -There are 3 types of Search APIs. One is the **People Search** API, second one is the **Company Search** API and the last one is **Jobs Search** API. - -The People Search API returns information about people. It lets you implement most of what shows up when you do a search for "People" in the top right box on LinkedIn.com. -You can get more information from [here](http://developers.linkedin.com/documents/people-search-api). - -```python -application.search_profile(selectors=[{'people': ['first-name', 'last-name']}], params={'keywords': 'apple microsoft'}) -# Search URL is https://api.linkedin.com/v1/people-search:(people:(first-name,last-name))?keywords=apple%20microsoft - -{u'people': {u'_count': 10, - u'_start': 0, - u'_total': 2, - u'values': [ - {u'firstName': u'John', u'lastName': 'Doe'}, - {u'firstName': u'Jane', u'lastName': u'Doe'} - ]}} -``` - -The Company Search API enables search across company pages. You can get more information from [here](http://developers.linkedin.com/documents/company-search). - -```python -application.search_company(selectors=[{'companies': ['name', 'universal-name', 'website-url']}], params={'keywords': 'apple microsoft'}) -# Search URL is https://api.linkedin.com/v1/company-search:(companies:(name,universal-name,website-url))?keywords=apple%20microsoft - -{u'companies': {u'_count': 10, - u'_start': 0, - u'_total': 1064, - u'values': [{u'name': u'Netflix', - u'universalName': u'netflix', - u'websiteUrl': u'http://netflix.com'}, - {u'name': u'Alliance Data', - u'universalName': u'alliance-data', - u'websiteUrl': u'www.alliancedata.com'}, - {u'name': u'GHA Technologies', - u'universalName': u'gha-technologies', - u'websiteUrl': u'www.gha-associates.com'}, - {u'name': u'Intelligent Decisions', - u'universalName': u'intelligent-decisions', - u'websiteUrl': u'http://www.intelligent.net'}, - {u'name': u'Mindfire Solutions', - u'universalName': u'mindfire-solutions', - u'websiteUrl': u'www.mindfiresolutions.com'}, - {u'name': u'Babel Media', - u'universalName': u'babel-media', - u'websiteUrl': u'http://www.babelmedia.com/'}, - {u'name': u'Milestone Technologies', - u'universalName': u'milestone-technologies', - u'websiteUrl': u'www.milestonepowered.com'}, - {u'name': u'Denali Advanced Integration', - u'universalName': u'denali-advanced-integration', - u'websiteUrl': u'www.denaliai.com'}, - {u'name': u'MicroAge', - u'universalName': u'microage', - u'websiteUrl': u'www.microage.com'}, - {u'name': u'TRUSTe', - u'universalName': u'truste', - u'websiteUrl': u'http://www.truste.com/'}]}} -``` - -The Job Search API enables search across LinkedIn's job postings. You can get more information from [here](http://developers.linkedin.com/documents/job-search-api). - -```python -application.search_job(selectors=[{'jobs': ['id', 'customer-job-code', 'posting-date']}], params={'title': 'python', 'count': 2}) -{u'jobs': {u'_count': 2, - u'_start': 0, - u'_total': 206747, - u'values': [{u'customerJobCode': u'0006YT23WQ', - u'id': 5174636, - u'postingDate': {u'day': 21, u'month': 3, u'year': 2013}}, - {u'customerJobCode': u'00023CCVC2', - u'id': 5174634, - u'postingDate': {u'day': 21, u'month': 3, u'year': 2013}}]}} -``` - -## Group API -The Groups API provides rich access to read and interact with LinkedIn’s groups functionality. You can get more information from [here](http://developers.linkedin.com/documents/groups-api). By the help of the interface, you can fetch group details, get your group memberships as well as your posts for a specific group which you are a member of. - -```python -application.get_group(41001) -{u'id': u'41001', u'name': u'Object Oriented Programming'} - -application.get_memberships(params={'count': 20}) -{u'_total': 1, - u'values': [{u'_key': u'25827', - u'group': {u'id': u'25827', u'name': u'Python Community'}, - u'membershipState': {u'code': u'member'}}]} - -application.get_posts(41001) - -application.get_post_comments( - %POST_ID%, - selectors=[ - {"creator": ["first-name", "last-name"]}, - "creation-timestamp", - "text" - ], - params={"start": 0, "count": 20} -) -``` - -You can also submit a new post into a specific group. - -```python -title = 'Scala for the Impatient' -summary = 'A new book has been published' -submitted_url = 'http://horstmann.com/scala/' -submitted_image_url = 'http://horstmann.com/scala/images/cover.png' -description = 'It is a great book for the keen beginners. Check it out!' - -application.submit_group_post(41001, title, summary, submitted_url, submitted_image_url, description) -``` - -## Company API -The Company API: - * Retrieves and displays one or more company profiles based on the company ID or universal name. - * Returns basic company profile data, such as name, website, and industry. - * Returns handles to additional company content, such as RSS stream and Twitter feed. - -You can query a company with either its **ID** or **Universal Name**. For more information, you can check out the documentation [here](http://developers.linkedin.com/documents/company-lookup-api-and-fields). - -```python -application.get_companies(company_ids=[1035], universal_names=['apple'], selectors=['name'], params={'is-company-admin': 'true'}) -# 1035 is Microsoft -# The URL is as follows: https://api.linkedin.com/v1/companies::(1035,universal-name=apple)?is-company-admin=true - -{u'_total': 2, - u'values': [{u'_key': u'1035', u'name': u'Microsoft'}, - {u'_key': u'universal-name=apple', u'name': u'Apple'}]} - -# Get the latest updates about Microsoft -application.get_company_updates(1035, params={'count': 2}) -{u'_count': 2, - u'_start': 0, - u'_total': 58, - u'values': [{u'isCommentable': True, - u'isLikable': True, - u'isLiked': False, - u'numLikes': 0, - u'timestamp': 1363855486620, - u'updateComments': {u'_total': 0}, - u'updateContent': {u'company': {u'id': 1035, u'name': u'Microsoft'}, - u'companyJobUpdate': {u'action': {u'code': u'created'}, - u'job': {u'company': {u'id': 1035, u'name': u'Microsoft'}, - u'description': u'Job Category: SalesLocation: Sacramento, CA, USJob ID: 812346-106756Division: Retail StoresStore...', - u'id': 5173319, - u'locationDescription': u'Sacramento, CA, US', - u'position': {u'title': u'Store Manager, Specialty Store'}, - u'siteJobRequest': {u'url': u'http://www.linkedin.com/jobs?viewJob=&jobId=5173319'}}}}, - u'updateKey': u'UNIU-c1035-5720424522989961216-FOLLOW_CMPY', - u'updateType': u'CMPY'}, - {u'isCommentable': True, - u'isLikable': True, - u'isLiked': False, - u'numLikes': 0, - u'timestamp': 1363855486617, - u'updateComments': {u'_total': 0}, - u'updateContent': {u'company': {u'id': 1035, u'name': u'Microsoft'}, - u'companyJobUpdate': {u'action': {u'code': u'created'}, - u'job': {u'company': {u'id': 1035, u'name': u'Microsoft'}, - u'description': u'Job Category: Software Engineering: TestLocation: Redmond, WA, USJob ID: 794953-81760Division:...', - u'id': 5173313, - u'locationDescription': u'Redmond, WA, US', - u'position': {u'title': u'Software Development Engineer in Test, Senior-IEB-MSCIS (794953)'}, - u'siteJobRequest': {u'url': u'http://www.linkedin.com/jobs?viewJob=&jobId=5173313'}}}}, - u'updateKey': u'UNIU-c1035-5720424522977378304-FOLLOW_CMPY', - u'updateType': u'CMPY'}]} -``` - -You can follow or unfollow a specific company as well. - -```python -application.follow_company(1035) -True - -application.unfollow_company(1035) -True -``` - -## Job API -The Jobs APIs provide access to view jobs and job data. You can get more information from its [documentation](http://developers.linkedin.com/documents/job-lookup-api-and-fields). - -```python -application.get_job(job_id=5174636) -{u'active': True, - u'company': {u'id': 2329, u'name': u'Schneider Electric'}, - u'descriptionSnippet': u"The Industrial Accounts Sales Manager is a quota carrying senior sales position principally responsible for generating new sales and growing company's share of wallet within the industrial business, contracting business and consulting engineering business. The primary objective is to build and establish strong and lasting relationships with technical teams and at executive level within specific in", - u'id': 5174636, - u'position': {u'title': u'Industrial Accounts Sales Manager'}, - u'postingTimestamp': 1363860033000} -``` - -You can also fetch you job bookmarks. - -```python -application.get_job_bookmarks() -{u'_total': 0} -``` - -## Share API -Network updates serve as one of the core experiences on LinkedIn, giving users the ability to share rich content to their professional network. You can get more information from [here](http://developers.linkedin.com/documents/share-api). - -``` -application.submit_share('Posting from the API using JSON', 'A title for your share', None, 'http://www.linkedin.com', 'http://d.pr/3OWS') -{'updateKey': u'UNIU-8219502-5705061301949063168-SHARE' - 'updateURL': 'http://www.linkedin.com/updates?discuss=&scope=8219502&stype=M&topic=5705061301949063168&type=U&a=aovi'} -``` - -## Network API -The Get Network Updates API returns the users network updates, which is the LinkedIn term for the user's feed. This call returns most of what shows up in the middle column of the LinkedIn.com home page, either for the member or the member's connections. You can get more information from [here](http://developers.linkedin.com/documents/get-network-updates-and-statistics-api). - -There are many network update types. You can look at them by importing **NETWORK_UPDATES** enumeration. - -```python -from linkedin.linkedin import NETWORK_UPDATES -print NETWORK_UPDATES.enums -{'APPLICATION': 'APPS', - 'CHANGED_PROFILE': 'PRFU', - 'COMPANY': 'CMPY', - 'CONNECTION': 'CONN', - 'EXTENDED_PROFILE': 'PRFX', - 'GROUP': 'JGRP', - 'JOB': 'JOBS', - 'PICTURE': 'PICT', - 'SHARED': 'SHAR', - 'VIRAL': 'VIRL'} - -update_types = (NETWORK_UPDATES.CONNECTION, NETWORK_UPDATES.PICTURE) -application.get_network_updates(update_types) - -{u'_total': 1, - u'values': [{u'isCommentable': True, - u'isLikable': True, - u'isLiked': False, - u'numLikes': 0, - u'timestamp': 1363470126509, - u'updateComments': {u'_total': 0}, - u'updateContent': {u'person': {u'apiStandardProfileRequest': {u'headers': {u'_total': 1, - u'values': [{u'name': u'x-li-auth-token', u'value': u'name:Egbj'}]}, - u'url': u'http://api.linkedin.com/v1/people/COjFALsKDP'}, - u'firstName': u'ozgur', - u'headline': u'This is my headline', - u'id': u'COjFALsKDP', - u'lastName': u'vatansever', - u'siteStandardProfileRequest': {u'url': u'http://www.linkedin.com/profile/view?id=46113651&authType=name&authToken=Egbj&trk=api*a101945*s101945*'}}}, - u'updateKey': u'UNIU-46113651-5718808205493026816-SHARE', - u'updateType': u'SHAR'}]} -``` - -## Invitation API -The Invitation API allows your users to invite people they find in your application to their LinkedIn network. You can get more information from [here](http://developers.linkedin.com/documents/invitation-api). - -```python -from linkedin.models import LinkedInRecipient, LinkedInInvitation -recipient = LinkedInRecipient(None, 'john.doe@python.org', 'John', 'Doe') -print recipient.json -{'person': {'_path': '/people/email=john.doe@python.org', - 'first-name': 'John', - 'last-name': 'Doe'}} - -invitation = LinkedInInvitation('Hello John', "What's up? Can I add you as a friend?", (recipient,), 'friend') -print invitation.json -{'body': "What's up? Can I add you as a friend?", - 'item-content': {'invitation-request': {'connect-type': 'friend'}}, - 'recipients': {'values': [{'person': {'_path': '/people/email=john.doe@python.org', - 'first-name': 'John', - 'last-name': 'Doe'}}]}, - 'subject': 'Hello John'} - -application.send_invitation(invitation) -True -``` - -## Throttle Limits - -LinkedIn API keys are throttled by default. You should take a look at the [Throttle Limits Documentation](http://developer.linkedin.com/documents/throttle-limits) to get more information about it. diff --git a/README.rst b/README.rst deleted file mode 100644 index df53601..0000000 --- a/README.rst +++ /dev/null @@ -1,92 +0,0 @@ -Python LinkedIn -================= - -Python interface to the LinkedIn API - -This library provides a pure Python interface to the LinkedIn **Profile**, **Group**, **Company**, **Jobs**, **Search**, **Share**, **Network** and **Invitation** REST APIs. - -`LinkedIn `_ provides a service that lets people bring their LinkedIn profiles and networks with them to your site or application via their OAuth based API. This library provides a lightweight interface over a complicated LinkedIn OAuth based API to make it for python programmers easy to use. - -Installation --------------------- - -You can install **python-linkedin** library via pip: - -.. code-block:: bash - - $ pip install python-linkedin - -Authentication ------------------------ - -LinkedIn REST API uses **Oauth 2.0** protocol for authentication. In order to use the LinkedIn API, you have an **application key** and **application secret**. You can get more detail from `here `_. - -For debugging purposes you can use the credentials below. It belongs to my test application. Nothing's harmful. - -.. code-block:: python - - KEY = 'wFNJekVpDCJtRPFX812pQsJee-gt0zO4X5XmG6wcfSOSlLocxodAXNMbl0_hw3Vl' - SECRET = 'daJDa6_8UcnGMw1yuq9TjoO_PMKukXMo8vEMo7Qv5J-G3SPgrAV0FqFCd0TNjQyG' - - -LinkedIn redirects the user back to your website's URL after granting access (giving proper permissions) to your application. We call that url **RETURN URL**. Assuming your return url is **http://localhost:8000**, you can write something like this: - -.. code-block:: python - - from linkedin import linkedin - - API_KEY = "wFNJekVpDCJtRPFX812pQsJee-gt0zO4X5XmG6wcfSOSlLocxodAXNMbl0_hw3Vl" - API_SECRET = "daJDa6_8UcnGMw1yuq9TjoO_PMKukXMo8vEMo7Qv5J-G3SPgrAV0FqFCd0TNjQyG" - RETURN_URL = "http://localhost:8000" - # Optionally one can send custom "state" value that will be returned from OAuth server - # It can be used to track your user state or something else (it's up to you) - # Be aware that this value is sent to OAuth server AS IS - make sure to encode or hash it - #authorization.state = 'your_encoded_message' - authentication = linkedin.LinkedInAuthentication(API_KEY, API_SECRET, RETURN_URL, linkedin.PERMISSIONS.enums.values()) - print authentication.authorization_url - application = linkedin.LinkedInApplication(authentication) - -When you grant access to the application, you will be redirected to the return url with the following query strings appended to your **RETURN_URL**: - -.. code-block:: python - - "http://localhost:8000/?code=AQTXrv3Pe1iWS0EQvLg0NJA8ju_XuiadXACqHennhWih7iRyDSzAm5jaf3R7I8&state=ea34a04b91c72863c82878d2b8f1836c" - - -This means that the value of the **authorization_code** is **AQTXrv3Pe1iWS0EQvLg0NJA8ju_XuiadXACqHennhWih7iRyDSzAm5jaf3R7I8**. After setting it by hand, we can call the **.get_access_token()** to get the actual token. - -.. code-block:: python - - authentication.authorization_code = "AQTXrv3Pe1iWS0EQvLg0NJA8ju_XuiadXACqHennhWih7iRyDSzAm5jaf3R7I8" - authentication.get_access_token() - - -After you get the access token, you are now permitted to make API calls on behalf of the user who granted access to you app. In addition to that, in order to prevent from going through the OAuth flow for every consecutive request, -one can directly assign the access token obtained before to the application instance. - - -.. code-block:: python - - application = linkedin.LinkedInApplication(token='AQTFtPILQkJzXHrHtyQ0rjLe3W0I') - - -Quick Usage From Python Interpreter ---------------------------------------------------------- - -For testing the library using an interpreter, use the quick helper. - -.. code-block:: python - - from linkedin import server - application = server.quick_api(KEY, SECRET) - -This will print the authorization url to the screen. Go into this URL using a browser, after you login, the method will return with an API object you can now use. - -.. code-block:: python - - application.get_profile() - - -More ------------------ -For more information, visit the `homepage `_ of the project. diff --git a/examples/__init__.py b/examples/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/examples/authentication.py b/examples/authentication.py deleted file mode 100644 index cb1a22b..0000000 --- a/examples/authentication.py +++ /dev/null @@ -1,12 +0,0 @@ -from linkedin.linkedin import (LinkedInAuthentication, LinkedInApplication, - PERMISSIONS) - - -if __name__ == '__main__': - API_KEY = 'wFNJekVpDCJtRPFX812pQsJee-gt0zO4X5XmG6wcfSOSlLocxodAXNMbl0_hw3Vl' - API_SECRET = 'daJDa6_8UcnGMw1yuq9TjoO_PMKukXMo8vEMo7Qv5J-G3SPgrAV0FqFCd0TNjQyG' - RETURN_URL = 'http://localhost:8000' - authentication = LinkedInAuthentication(API_KEY, API_SECRET, RETURN_URL, - PERMISSIONS.enums.values()) - print authentication.authorization_url - application = LinkedInApplication(authentication) diff --git a/examples/http_api.py b/examples/http_api.py deleted file mode 100644 index 84059d8..0000000 --- a/examples/http_api.py +++ /dev/null @@ -1,77 +0,0 @@ -__author__ = 'Samuel Marks ' -__version__ = '0.1.0' - -try: - from urllib.parse import urlparse -except ImportError: - from urlparse import urlparse - -from socketserver import ThreadingTCPServer -from http.server import SimpleHTTPRequestHandler - -from webbrowser import open_new_tab -from json import dumps -from os import environ - - -from linkedin.linkedin import LinkedInAuthentication, LinkedInApplication, PERMISSIONS - -PORT = 8080 - - -class LinkedInWrapper(object): - """ Simple namespacing """ - API_KEY = environ.get('LINKEDIN_API_KEY') - API_SECRET = environ.get('LINKEDIN_API_SECRET') - RETURN_URL = 'http://localhost:{0}/code'.format(globals()['PORT']) - authentication = LinkedInAuthentication(API_KEY, API_SECRET, RETURN_URL, PERMISSIONS.enums.values()) - application = LinkedInApplication(authentication) - - -liw = LinkedInWrapper() -run_already = False -params_to_d = lambda params: { - l[0]: l[1] for l in map(lambda j: j.split('='), urlparse(params).query.split('&')) -} - - -class CustomHandler(SimpleHTTPRequestHandler): - def json_headers(self, status_code=200): - self.send_response(status_code) - self.send_header('Content-type', 'application/json') - self.end_headers() - - def do_GET(self): - parsedurl = urlparse(self.path) - authed = liw.authentication.token is not None - - if parsedurl.path == '/code': - self.json_headers() - - liw.authentication.authorization_code = params_to_d(self.path).get('code') - self.wfile.write(dumps({'access_token': liw.authentication.get_access_token(), - 'routes': list(filter(lambda d: not d.startswith('_'), dir(liw.application)))}).encode('utf8')) - elif parsedurl.path == '/routes': - self.json_headers() - - self.wfile.write(dumps({'routes': list(filter(lambda d: not d.startswith('_'), dir(liw.application)))}).encode('utf8')) - elif not authed: - self.json_headers() - - if not globals()['run_already']: - open_new_tab(liw.authentication.authorization_url) - globals()['run_already'] = True - self.wfile.write(dumps({'path': self.path, 'authed': type(liw.authentication.token) is None}).encode('utf8')) - elif authed and len(parsedurl.path) and parsedurl.path[1:] in dir(liw.application): - self.json_headers() - self.wfile.write(dumps(getattr(liw.application, parsedurl.path[1:])()).encode('utf8')) - else: - self.json_headers(501) - self.wfile.write(dumps({'error': 'NotImplemented'}).encode('utf8')) - - -if __name__ == '__main__': - httpd = ThreadingTCPServer(('localhost', PORT), CustomHandler) - - print('Server started on port:{}'.format(PORT)) - httpd.serve_forever() diff --git a/images/body-bg.png b/images/body-bg.png new file mode 100644 index 0000000..d0618fe Binary files /dev/null and b/images/body-bg.png differ diff --git a/images/highlight-bg.jpg b/images/highlight-bg.jpg new file mode 100644 index 0000000..4c4a78e Binary files /dev/null and b/images/highlight-bg.jpg differ diff --git a/images/hr.png b/images/hr.png new file mode 100644 index 0000000..6c723a5 Binary files /dev/null and b/images/hr.png differ diff --git a/images/octocat-icon.png b/images/octocat-icon.png new file mode 100644 index 0000000..f0ba137 Binary files /dev/null and b/images/octocat-icon.png differ diff --git a/images/tar-gz-icon.png b/images/tar-gz-icon.png new file mode 100644 index 0000000..d50f34f Binary files /dev/null and b/images/tar-gz-icon.png differ diff --git a/images/zip-icon.png b/images/zip-icon.png new file mode 100644 index 0000000..162c425 Binary files /dev/null and b/images/zip-icon.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..30d92f9 --- /dev/null +++ b/index.html @@ -0,0 +1,426 @@ + + + + + + + + + + + Python-linkedin by ozgur + + + +
+
+ +
+

Python-linkedin

+

Python interface to the LinkedIn API

+
+ +
+ Download .zip + Download .tar.gz + View on GitHub +
+ +
+ +
+

Python LinkedIn

+ +

Python interface to the LinkedIn API

+ +

LinkedIn

+ +

This library provides a pure Python interface to the LinkedIn Profile, Group, Company, Jobs, Search, Share, Network and Invitation REST APIs.

+ +

LinkedIn provides a service that lets people bring their LinkedIn profiles and networks with them to your site or application via their OAuth based API. This library provides a lightweight interface over a complicated LinkedIn OAuth based API to make it for python programmers easy to use.

+ +

Installation

+ +

Build Status

+ +

You can install python-linkedin library via pip:

+ +
$ pip install python-linkedin
+
+ +

Authentication

+ +

LinkedIn REST API uses Oauth 2.0 protocol for authentication. In order to use the LinkedIn API, you have an application key and application secret. You can get more detail from here.

+ +

For debugging purposes you can use the credentials below. It belongs to my test application. Nothing's harmful.

+ +
KEY = 'wFNJekVpDCJtRPFX812pQsJee-gt0zO4X5XmG6wcfSOSlLocxodAXNMbl0_hw3Vl'
+SECRET = 'daJDa6_8UcnGMw1yuq9TjoO_PMKukXMo8vEMo7Qv5J-G3SPgrAV0FqFCd0TNjQyG'
+
+ +

You can also get those keys from here.

+ +

LinkedIn redirects the user back to your website's URL after granting access (giving proper permissions) to your application. We call that url RETURN URL. Assuming your return url is http://localhost:8000, you can write something like this:

+ +
from linkedin import linkedin
+
+API_KEY = 'wFNJekVpDCJtRPFX812pQsJee-gt0zO4X5XmG6wcfSOSlLocxodAXNMbl0_hw3Vl'
+API_SECRET = 'daJDa6_8UcnGMw1yuq9TjoO_PMKukXMo8vEMo7Qv5J-G3SPgrAV0FqFCd0TNjQyG'
+RETURN_URL = 'http://localhost:8000'
+
+authentication = linkedin.LinkedInAuthentication(API_KEY, API_SECRET, RETURN_URL, linkedin.PERMISSIONS.enums.values())
+print authentication.authorization_url  # open this url on your browser
+application = linkedin.LinkedInApplication(authentication)
+
+ +

When you grant access to the application, you will be redirected to the return url with the following query strings appended to your RETURN_URL:

+ +
"http://localhost:8000/?code=AQTXrv3Pe1iWS0EQvLg0NJA8ju_XuiadXACqHennhWih7iRyDSzAm5jaf3R7I8&state=ea34a04b91c72863c82878d2b8f1836c"
+
+ +

This means that the value of the authorization_code is AQTXrv3Pe1iWS0EQvLg0NJA8ju_XuiadXACqHennhWih7iRyDSzAm5jaf3R7I8. After setting it by hand, we can call the .get_access_token() to get the actual token.

+ +
authentication.authorization_code = 'AQTXrv3Pe1iWS0EQvLg0NJA8ju_XuiadXACqHennhWih7iRyDSzAm5jaf3R7I8'
+authentication.get_access_token()
+
+ +

Quick Usage From Python Interpreter

+ +

For testing the library using an interpreter, you can benefit from the test server.

+ +
from linkedin import server
+application = server.quick_api(KEY, SECRET)
+
+ +

This will print the authorization url to the screen. Go into that URL using a browser to grant access to the application. After you do so, the method will return with an API object you can now use.

+ +

Profile API

+ +

The Profile API returns a member's LinkedIn profile. You can use this call to return one of two versions of a user's profile which are public profile and standart profile. For more information, check out the documentation.

+ +
application.get_profile()
+{u'firstName': u'ozgur',
+ u'headline': u'This is my headline',
+ u'lastName': u'vatansever',
+ u'siteStandardProfileRequest': {u'url': u'http://www.linkedin.com/profile/view?id=46113651&authType=name&authToken=Egbj&trk=api*a101945*s101945*'}}
+
+ +

There are many field selectors that enable the client fetch more information from the API. All of them used by each API are listed here.

+ +
application.get_profile(selectors=['id', 'first-name', 'last-name', 'location', 'distance', 'num-connections', 'skills', 'educations'])
+{u'distance': 0,
+ u'educations': {u'_total': 1,
+  u'values': [{u'activities': u'This is my activity and society field',
+    u'degree': u'graduate',
+    u'endDate': {u'year': 2009},
+    u'fieldOfStudy': u'computer science',
+    u'id': 42611838,
+    u'notes': u'This is my additional notes field',
+    u'schoolName': u'\u0130stanbul Bilgi \xdcniversitesi',
+    u'startDate': {u'year': 2004}}]},
+ u'firstName': u'ozgur',
+ u'id': u'COjFALsKDP',
+ u'lastName': u'vatansever',
+ u'location': {u'country': {u'code': u'tr'}, u'name': u'Istanbul, Turkey'},
+ u'numConnections': 13}
+
+ +

Connections API

+ +

The Connections API returns a list of 1st degree connections for a user who has granted access to their account. For more information, you check out its documentation.

+ +

To fetch your connections, you simply call .get_connections() method with proper GET querystring:

+ +
application.get_connections()
+{u'_total': 13,
+ u'values': [{u'apiStandardProfileRequest': {u'headers': {u'_total': 1,
+     u'values': [{u'name': u'x-li-auth-token', u'value': u'name:16V1033'}]},
+    u'url': u'http://api.linkedin.com/v1/people/lddvGtD5xk'},
+   u'firstName': u'John',
+   u'headline': u'Ruby',
+   u'id': u'2323SDFSsfd34',
+   u'industry': u'Computer Software',
+   u'lastName': u'DOE',
+   u'location': {u'country': {u'code': u'tr'}, u'name': u'Istanbul, Turkey'},
+   u'siteStandardProfileRequest': {u'url': u'http://www.linkedin.com/profile/view?id=049430532&authType=name&authToken=16V8&trk=api*a101945*s101945*'}},
+   ....
+
+application.get_connections(selectors=['headline', 'first-name', 'last-name'], params={'start':10, 'count':5})
+
+ +

Search API

+ +

There are 3 types of Search APIs. One is the People Search API, second one is the Company Search API and the last one is Jobs Search API.

+ +

The People Search API returns information about people. It lets you implement most of what shows up when you do a search for "People" in the top right box on LinkedIn.com. +You can get more information from here.

+ +
application.search_profile(selectors=[{'people': ['first-name', 'last-name']}], params={'keywords': 'apple microsoft'})
+# Search URL is https://api.linkedin.com/v1/people-search:(people:(first-name,last-name))?keywords=apple%20microsoft
+
+{u'people': {u'_count': 10,
+  u'_start': 0,
+  u'_total': 2,
+  u'values': [
+   {u'firstName': u'John', u'lastName': 'Doe'},
+   {u'firstName': u'Jane', u'lastName': u'Doe'}
+  ]}}
+
+ +

The Company Search API enables search across company pages. You can get more information from here.

+ +
application.search_company(selectors=[{'companies': ['name', 'universal-name', 'website-url']}], params={'keywords': 'apple microsoft'})
+# Search URL is https://api.linkedin.com/v1/company-search:(companies:(name,universal-name,website-url))?keywords=apple%20microsoft
+
+{u'companies': {u'_count': 10,
+  u'_start': 0,
+  u'_total': 1064,
+  u'values': [{u'name': u'Netflix',
+    u'universalName': u'netflix',
+    u'websiteUrl': u'http://netflix.com'},
+   {u'name': u'Alliance Data',
+    u'universalName': u'alliance-data',
+    u'websiteUrl': u'www.alliancedata.com'},
+   {u'name': u'GHA Technologies',
+    u'universalName': u'gha-technologies',
+    u'websiteUrl': u'www.gha-associates.com'},
+   {u'name': u'Intelligent Decisions',
+    u'universalName': u'intelligent-decisions',
+    u'websiteUrl': u'http://www.intelligent.net'},
+   {u'name': u'Mindfire Solutions',
+    u'universalName': u'mindfire-solutions',
+    u'websiteUrl': u'www.mindfiresolutions.com'},
+   {u'name': u'Babel Media',
+    u'universalName': u'babel-media',
+    u'websiteUrl': u'http://www.babelmedia.com/'},
+   {u'name': u'Milestone Technologies',
+    u'universalName': u'milestone-technologies',
+    u'websiteUrl': u'www.milestonepowered.com'},
+   {u'name': u'Denali Advanced Integration',
+    u'universalName': u'denali-advanced-integration',
+    u'websiteUrl': u'www.denaliai.com'},
+   {u'name': u'MicroAge',
+    u'universalName': u'microage',
+    u'websiteUrl': u'www.microage.com'},
+   {u'name': u'TRUSTe',
+    u'universalName': u'truste',
+    u'websiteUrl': u'http://www.truste.com/'}]}}
+
+ +

The Job Search API enables search across LinkedIn's job postings. You can get more information from here.

+ +
application.search_job(selectors=[{'jobs': ['id', 'customer-job-code', 'posting-date']}], params={'title': 'python', 'count': 2})
+{u'jobs': {u'_count': 2,
+  u'_start': 0,
+  u'_total': 206747,
+  u'values': [{u'customerJobCode': u'0006YT23WQ',
+    u'id': 5174636,
+    u'postingDate': {u'day': 21, u'month': 3, u'year': 2013}},
+   {u'customerJobCode': u'00023CCVC2',
+    u'id': 5174634,
+    u'postingDate': {u'day': 21, u'month': 3, u'year': 2013}}]}}
+
+ +

Group API

+ +

The Groups API provides rich access to read and interact with LinkedIn’s groups functionality. You can get more information from here. By the help of the interface, you can fetch group details, get your group memberships as well as your posts for a specific group which you are a member of.

+ +
application.get_group(41001)
+{u'id': u'41001', u'name': u'Object Oriented Programming'}
+
+application.get_memberships(params={'count': 20})
+{u'_total': 1,
+ u'values': [{u'_key': u'25827',
+   u'group': {u'id': u'25827', u'name': u'Python Community'},
+   u'membershipState': {u'code': u'member'}}]}
+
+application.get_posts(41001)
+
+ +

You can also submit a new post into a specific group.

+ +
title = 'Scala for the Impatient'
+summary = 'A new book has been published'
+submitted_url = 'http://horstmann.com/scala/'
+submitted_image_url = 'http://horstmann.com/scala/images/cover.png'
+description = 'It is a great book for the keen beginners. Check it out!'
+
+application.submit_group_post(41001, title, summary, submitted_url, submitted_image_url, description)
+
+ +

Company API

+ +

The Company API:

+ +
    +
  • Retrieves and displays one or more company profiles based on the company ID or universal name.
  • +
  • Returns basic company profile data, such as name, website, and industry.
  • +
  • Returns handles to additional company content, such as RSS stream and Twitter feed.
  • +

You can query a company with either its ID or Universal Name. For more information, you can check out the documentation here.

+ +
application.get_companies(company_ids=[1035], universal_names=['apple'], selectors=['name'], params={'is-company-admin': 'true'})
+# 1035 is Microsoft
+# The URL is as follows: https://api.linkedin.com/v1/companies::(1035,universal-name=apple)?is-company-admin=true
+
+{u'_total': 2,
+ u'values': [{u'_key': u'1035', u'name': u'Microsoft'},
+  {u'_key': u'universal-name=apple', u'name': u'Apple'}]}
+
+# Get the latest updates about Microsoft
+application.get_company_updates(1035, params={'count': 2})
+{u'_count': 2,
+ u'_start': 0,
+ u'_total': 58,
+ u'values': [{u'isCommentable': True,
+   u'isLikable': True,
+   u'isLiked': False,
+   u'numLikes': 0,
+   u'timestamp': 1363855486620,
+   u'updateComments': {u'_total': 0},
+   u'updateContent': {u'company': {u'id': 1035, u'name': u'Microsoft'},
+    u'companyJobUpdate': {u'action': {u'code': u'created'},
+     u'job': {u'company': {u'id': 1035, u'name': u'Microsoft'},
+      u'description': u'Job Category: SalesLocation: Sacramento, CA, USJob ID: 812346-106756Division: Retail StoresStore...',
+      u'id': 5173319,
+      u'locationDescription': u'Sacramento, CA, US',
+      u'position': {u'title': u'Store Manager, Specialty Store'},
+      u'siteJobRequest': {u'url': u'http://www.linkedin.com/jobs?viewJob=&jobId=5173319'}}}},
+   u'updateKey': u'UNIU-c1035-5720424522989961216-FOLLOW_CMPY',
+   u'updateType': u'CMPY'},
+  {u'isCommentable': True,
+   u'isLikable': True,
+   u'isLiked': False,
+   u'numLikes': 0,
+   u'timestamp': 1363855486617,
+   u'updateComments': {u'_total': 0},
+   u'updateContent': {u'company': {u'id': 1035, u'name': u'Microsoft'},
+    u'companyJobUpdate': {u'action': {u'code': u'created'},
+     u'job': {u'company': {u'id': 1035, u'name': u'Microsoft'},
+      u'description': u'Job Category: Software Engineering: TestLocation: Redmond, WA, USJob ID: 794953-81760Division:...',
+      u'id': 5173313,
+      u'locationDescription': u'Redmond, WA, US',
+      u'position': {u'title': u'Software Development Engineer in Test, Senior-IEB-MSCIS (794953)'},
+      u'siteJobRequest': {u'url': u'http://www.linkedin.com/jobs?viewJob=&jobId=5173313'}}}},
+   u'updateKey': u'UNIU-c1035-5720424522977378304-FOLLOW_CMPY',
+   u'updateType': u'CMPY'}]}
+
+ +

You can follow or unfollow a specific company as well.

+ +
application.follow_company(1035)
+True
+
+application.unfollow_company(1035)
+True
+
+ +

Job API

+ +

The Jobs APIs provide access to view jobs and job data. You can get more information from its documentation.

+ +
application.get_job(job_id=5174636)
+{u'active': True,
+ u'company': {u'id': 2329, u'name': u'Schneider Electric'},
+ u'descriptionSnippet': u"The Industrial Accounts Sales Manager is a quota carrying senior sales position principally responsible for generating new sales and growing company's share of wallet within the industrial business, contracting business and consulting engineering business. The primary objective is to build and establish strong and lasting relationships with technical teams and at executive level within specific in",
+ u'id': 5174636,
+ u'position': {u'title': u'Industrial Accounts Sales Manager'},
+ u'postingTimestamp': 1363860033000}
+
+ +

You can also fetch you job bookmarks.

+ +
application.get_job_bookmarks()
+{u'_total': 0}
+
+ +

Share API

+ +

Network updates serve as one of the core experiences on LinkedIn, giving users the ability to share rich content to their professional network. You can get more information from here.

+ +
application.submit_share('Posting from the API using JSON', 'A title for your share', None, 'http://www.linkedin.com', 'http://d.pr/3OWS')
+{'updateKey': u'UNIU-8219502-5705061301949063168-SHARE'
+ 'updateURL': 'http://www.linkedin.com/updates?discuss=&scope=8219502&stype=M&topic=5705061301949063168&type=U&a=aovi'}
+
+ +

Network API

+ +

The Get Network Updates API returns the users network updates, which is the LinkedIn term for the user's feed. This call returns most of what shows up in the middle column of the LinkedIn.com home page, either for the member or the member's connections. You can get more information from here.

+ +

There are many network update types. You can look at them by importing NETWORK_UPDATES enumeration.

+ +
from linkedin.linkedin import NETWORK_UPDATES
+print NETWORK_UPDATES.enums
+{'APPLICATION': 'APPS',
+ 'CHANGED_PROFILE': 'PRFU',
+ 'COMPANY': 'CMPY',
+ 'CONNECTION': 'CONN',
+ 'EXTENDED_PROFILE': 'PRFX',
+ 'GROUP': 'JGRP',
+ 'JOB': 'JOBS',
+ 'PICTURE': 'PICT',
+ 'SHARED': 'SHAR',
+ 'VIRAL': 'VIRL'}
+
+update_types = (NETWORK_UPDATES.CONNECTION, NETWORK_UPDATES.PICTURE)
+application.get_network_updates(update_types)
+
+{u'_total': 1,
+ u'values': [{u'isCommentable': True,
+   u'isLikable': True,
+   u'isLiked': False,
+   u'numLikes': 0,
+   u'timestamp': 1363470126509,
+   u'updateComments': {u'_total': 0},
+   u'updateContent': {u'person': {u'apiStandardProfileRequest': {u'headers': {u'_total': 1,
+       u'values': [{u'name': u'x-li-auth-token', u'value': u'name:Egbj'}]},
+      u'url': u'http://api.linkedin.com/v1/people/COjFALsKDP'},
+     u'firstName': u'ozgur',
+     u'headline': u'This is my headline',
+     u'id': u'COjFALsKDP',
+     u'lastName': u'vatansever',
+     u'siteStandardProfileRequest': {u'url': u'http://www.linkedin.com/profile/view?id=46113651&authType=name&authToken=Egbj&trk=api*a101945*s101945*'}}},
+   u'updateKey': u'UNIU-46113651-5718808205493026816-SHARE',
+   u'updateType': u'SHAR'}]}
+
+ +

Invitation API

+ +

The Invitation API allows your users to invite people they find in your application to their LinkedIn network. You can get more information from here.

+ +
from linkedin.models import LinkedInRecipient, LinkedInInvitation
+recipient = LinkedInRecipient(None, 'john.doe@python.org', 'John', 'Doe')
+print recipient.json
+{'person': {'_path': '/people/email=john.doe@python.org',
+  'first-name': 'John',
+  'last-name': 'Doe'}}
+
+invitation = LinkedInInvitation('Hello John', "What's up? Can I add you as a friend?", (recipient,), 'friend')
+print invitation.json
+{'body': "What's up? Can I add you as a friend?",
+ 'item-content': {'invitation-request': {'connect-type': 'friend'}},
+ 'recipients': {'values': [{'person': {'_path': '/people/email=john.doe@python.org',
+     'first-name': 'John',
+     'last-name': 'Doe'}}]},
+ 'subject': 'Hello John'}
+
+application.send_invitation(invitation)
+True
+
+ +

Throttle Limits

+ +

LinkedIn API keys are throttled by default. You should take a look at the Throttle Limits Documentation to get more information about it.

+
+ + + + +
+
+ + \ No newline at end of file diff --git a/javascripts/main.js b/javascripts/main.js new file mode 100644 index 0000000..d8135d3 --- /dev/null +++ b/javascripts/main.js @@ -0,0 +1 @@ +console.log('This would be the main JS file.'); diff --git a/javascripts/scale.fix.js b/javascripts/scale.fix.js new file mode 100644 index 0000000..87a40ca --- /dev/null +++ b/javascripts/scale.fix.js @@ -0,0 +1,17 @@ +var metas = document.getElementsByTagName('meta'); +var i; +if (navigator.userAgent.match(/iPhone/i)) { + for (i=0; i 1: - params = urlparse.parse_qs(p[1], True, True) - app.authentication.authorization_code = params['code'][0] - app.authentication.get_access_token() - - server_address = ('', port) - httpd = BaseHTTPServer.HTTPServer(server_address, MyHandler) - httpd.handle_request() diff --git a/linkedin/utils.py b/linkedin/utils.py deleted file mode 100644 index 8b10aeb..0000000 --- a/linkedin/utils.py +++ /dev/null @@ -1,78 +0,0 @@ -# -*- coding: utf-8 -*- -import requests -from .exceptions import LinkedInError, get_exception_for_error_code -import sys -from io import StringIO - -try: - import simplejson as json -except ImportError: - try: - from django.utils import simplejson as json - except ImportError: - import json - - -if sys.version_info < (3,): - import __builtin__ - - def to_utf8(x): - return __builtin__.unicode(x) - - def to_string(x): - return str(x) -else: - def to_utf8(x): - return x - - def to_string(x): - return x - - -def enum(enum_type='enum', base_classes=None, methods=None, **attrs): - """ - Generates a enumeration with the given attributes. - """ - # Enumerations can not be initalized as a new instance - def __init__(instance, *args, **kwargs): - raise RuntimeError('%s types can not be initialized.' % enum_type) - - if base_classes is None: - base_classes = () - - if methods is None: - methods = {} - - base_classes = base_classes + (object,) - for k, v in methods.items(): - methods[k] = classmethod(v) - - attrs['enums'] = attrs.copy() - methods.update(attrs) - methods['__init__'] = __init__ - return type(to_string(enum_type), base_classes, methods) - -def raise_for_error(response): - try: - response.raise_for_status() - except (requests.HTTPError, requests.ConnectionError) as error: - try: - if len(response.content) == 0: - # There is nothing we can do here since LinkedIn has neither sent - # us a 2xx response nor a response content. - return - response = response.json() - if ('error' in response) or ('errorCode' in response): - message = '%s: %s' % (response.get('error', str(error)), - response.get('message', 'Unknown Error')) - error_code = response.get('status') - ex = get_exception_for_error_code(error_code) - raise ex(message) - else: - raise LinkedInError(error.message) - except (ValueError, TypeError): - raise LinkedInError(error.message) - - -HTTP_METHODS = enum('HTTPMethod', GET='GET', POST='POST', - PUT='PUT', DELETE='DELETE', PATCH='PATCH') diff --git a/params.json b/params.json new file mode 100644 index 0000000..530a2e3 --- /dev/null +++ b/params.json @@ -0,0 +1 @@ +{"name":"Python-linkedin","tagline":"Python interface to the LinkedIn API","body":"# Python LinkedIn\r\n\r\nPython interface to the LinkedIn API\r\n\r\n[![LinkedIn](http://developer.linkedin.com/sites/default/files/LinkedIn_Logo60px.png)](http://developer.linkedin.com)\r\n\r\nThis library provides a pure Python interface to the LinkedIn **Profile**, **Group**, **Company**, **Jobs**, **Search**, **Share**, **Network** and **Invitation** REST APIs.\r\n\r\n[LinkedIn](http://developer.linkedin.com) provides a service that lets people bring their LinkedIn profiles and networks with them to your site or application via their OAuth based API. This library provides a lightweight interface over a complicated LinkedIn OAuth based API to make it for python programmers easy to use.\r\n\r\n## Installation\r\n\r\n[![Build Status](https://travis-ci.org/ozgur/python-linkedin.png?branch=master)](https://travis-ci.org/ozgur/python-linkedin)\r\n\r\nYou can install **python-linkedin** library via pip:\r\n\r\n $ pip install python-linkedin\r\n\r\n## Authentication\r\n\r\nLinkedIn REST API uses **Oauth 2.0** protocol for authentication. In order to use the LinkedIn API, you have an **application key** and **application secret**. You can get more detail from [here](http://developers.linkedin.com/documents/authentication).\r\n\r\nFor debugging purposes you can use the credentials below. It belongs to my test application. Nothing's harmful.\r\n\r\n```python\r\nKEY = 'wFNJekVpDCJtRPFX812pQsJee-gt0zO4X5XmG6wcfSOSlLocxodAXNMbl0_hw3Vl'\r\nSECRET = 'daJDa6_8UcnGMw1yuq9TjoO_PMKukXMo8vEMo7Qv5J-G3SPgrAV0FqFCd0TNjQyG'\r\n```\r\nYou can also get those keys from [here](http://developer.linkedin.com/rest).\r\n\r\nLinkedIn redirects the user back to your website's URL after granting access (giving proper permissions) to your application. We call that url **RETURN URL**. Assuming your return url is **http://localhost:8000**, you can write something like this:\r\n\r\n```python\r\nfrom linkedin import linkedin\r\n\r\nAPI_KEY = 'wFNJekVpDCJtRPFX812pQsJee-gt0zO4X5XmG6wcfSOSlLocxodAXNMbl0_hw3Vl'\r\nAPI_SECRET = 'daJDa6_8UcnGMw1yuq9TjoO_PMKukXMo8vEMo7Qv5J-G3SPgrAV0FqFCd0TNjQyG'\r\nRETURN_URL = 'http://localhost:8000'\r\n\r\nauthentication = linkedin.LinkedInAuthentication(API_KEY, API_SECRET, RETURN_URL, linkedin.PERMISSIONS.enums.values())\r\nprint authentication.authorization_url # open this url on your browser\r\napplication = linkedin.LinkedInApplication(authentication)\r\n```\r\nWhen you grant access to the application, you will be redirected to the return url with the following query strings appended to your **RETURN_URL**:\r\n\r\n```python\r\n\"http://localhost:8000/?code=AQTXrv3Pe1iWS0EQvLg0NJA8ju_XuiadXACqHennhWih7iRyDSzAm5jaf3R7I8&state=ea34a04b91c72863c82878d2b8f1836c\"\r\n```\r\n\r\nThis means that the value of the **authorization_code** is **AQTXrv3Pe1iWS0EQvLg0NJA8ju_XuiadXACqHennhWih7iRyDSzAm5jaf3R7I8**. After setting it by hand, we can call the **.get_access_token()** to get the actual token.\r\n\r\n```python\r\nauthentication.authorization_code = 'AQTXrv3Pe1iWS0EQvLg0NJA8ju_XuiadXACqHennhWih7iRyDSzAm5jaf3R7I8'\r\nauthentication.get_access_token()\r\n```\r\n\r\n## Quick Usage From Python Interpreter\r\n\r\nFor testing the library using an interpreter, you can benefit from the test server.\r\n\r\n```python\r\nfrom linkedin import server\r\napplication = server.quick_api(KEY, SECRET)\r\n```\r\nThis will print the authorization url to the screen. Go into that URL using a browser to grant access to the application. After you do so, the method will return with an API object you can now use.\r\n\r\n## Profile API\r\nThe Profile API returns a member's LinkedIn profile. You can use this call to return one of two versions of a user's profile which are **public profile** and **standart profile**. For more information, check out the [documentation](http://developers.linkedin.com/documents/profile-api).\r\n\r\n```python\r\napplication.get_profile()\r\n{u'firstName': u'ozgur',\r\n u'headline': u'This is my headline',\r\n u'lastName': u'vatansever',\r\n u'siteStandardProfileRequest': {u'url': u'http://www.linkedin.com/profile/view?id=46113651&authType=name&authToken=Egbj&trk=api*a101945*s101945*'}}\r\n```\r\n\r\nThere are many **field selectors** that enable the client fetch more information from the API. All of them used by each API are listed [here](http://developers.linkedin.com/documents/field-selectors).\r\n\r\n```python\r\napplication.get_profile(selectors=['id', 'first-name', 'last-name', 'location', 'distance', 'num-connections', 'skills', 'educations'])\r\n{u'distance': 0,\r\n u'educations': {u'_total': 1,\r\n u'values': [{u'activities': u'This is my activity and society field',\r\n u'degree': u'graduate',\r\n u'endDate': {u'year': 2009},\r\n u'fieldOfStudy': u'computer science',\r\n u'id': 42611838,\r\n u'notes': u'This is my additional notes field',\r\n u'schoolName': u'\\u0130stanbul Bilgi \\xdcniversitesi',\r\n u'startDate': {u'year': 2004}}]},\r\n u'firstName': u'ozgur',\r\n u'id': u'COjFALsKDP',\r\n u'lastName': u'vatansever',\r\n u'location': {u'country': {u'code': u'tr'}, u'name': u'Istanbul, Turkey'},\r\n u'numConnections': 13}\r\n```\r\n\r\n## Connections API\r\nThe Connections API returns a list of **1st degree** connections for a user who has granted access to their account. For more information, you check out its [documentation](http://developers.linkedin.com/documents/connections-api).\r\n\r\nTo fetch your connections, you simply call **.get_connections()** method with proper GET querystring:\r\n\r\n```python\r\napplication.get_connections()\r\n{u'_total': 13,\r\n u'values': [{u'apiStandardProfileRequest': {u'headers': {u'_total': 1,\r\n u'values': [{u'name': u'x-li-auth-token', u'value': u'name:16V1033'}]},\r\n u'url': u'http://api.linkedin.com/v1/people/lddvGtD5xk'},\r\n u'firstName': u'John',\r\n u'headline': u'Ruby',\r\n u'id': u'2323SDFSsfd34',\r\n u'industry': u'Computer Software',\r\n u'lastName': u'DOE',\r\n u'location': {u'country': {u'code': u'tr'}, u'name': u'Istanbul, Turkey'},\r\n u'siteStandardProfileRequest': {u'url': u'http://www.linkedin.com/profile/view?id=049430532&authType=name&authToken=16V8&trk=api*a101945*s101945*'}},\r\n ....\r\n\r\napplication.get_connections(selectors=['headline', 'first-name', 'last-name'], params={'start':10, 'count':5})\r\n```\r\n\r\n## Search API\r\nThere are 3 types of Search APIs. One is the **People Search** API, second one is the **Company Search** API and the last one is **Jobs Search** API.\r\n\r\nThe People Search API returns information about people. It lets you implement most of what shows up when you do a search for \"People\" in the top right box on LinkedIn.com.\r\nYou can get more information from [here](http://developers.linkedin.com/documents/people-search-api).\r\n\r\n```python\r\napplication.search_profile(selectors=[{'people': ['first-name', 'last-name']}], params={'keywords': 'apple microsoft'})\r\n# Search URL is https://api.linkedin.com/v1/people-search:(people:(first-name,last-name))?keywords=apple%20microsoft\r\n\r\n{u'people': {u'_count': 10,\r\n u'_start': 0,\r\n u'_total': 2,\r\n u'values': [\r\n {u'firstName': u'John', u'lastName': 'Doe'},\r\n {u'firstName': u'Jane', u'lastName': u'Doe'}\r\n ]}}\r\n```\r\n\r\nThe Company Search API enables search across company pages. You can get more information from [here](http://developers.linkedin.com/documents/company-search).\r\n\r\n```python\r\napplication.search_company(selectors=[{'companies': ['name', 'universal-name', 'website-url']}], params={'keywords': 'apple microsoft'})\r\n# Search URL is https://api.linkedin.com/v1/company-search:(companies:(name,universal-name,website-url))?keywords=apple%20microsoft\r\n\r\n{u'companies': {u'_count': 10,\r\n u'_start': 0,\r\n u'_total': 1064,\r\n u'values': [{u'name': u'Netflix',\r\n u'universalName': u'netflix',\r\n u'websiteUrl': u'http://netflix.com'},\r\n {u'name': u'Alliance Data',\r\n u'universalName': u'alliance-data',\r\n u'websiteUrl': u'www.alliancedata.com'},\r\n {u'name': u'GHA Technologies',\r\n u'universalName': u'gha-technologies',\r\n u'websiteUrl': u'www.gha-associates.com'},\r\n {u'name': u'Intelligent Decisions',\r\n u'universalName': u'intelligent-decisions',\r\n u'websiteUrl': u'http://www.intelligent.net'},\r\n {u'name': u'Mindfire Solutions',\r\n u'universalName': u'mindfire-solutions',\r\n u'websiteUrl': u'www.mindfiresolutions.com'},\r\n {u'name': u'Babel Media',\r\n u'universalName': u'babel-media',\r\n u'websiteUrl': u'http://www.babelmedia.com/'},\r\n {u'name': u'Milestone Technologies',\r\n u'universalName': u'milestone-technologies',\r\n u'websiteUrl': u'www.milestonepowered.com'},\r\n {u'name': u'Denali Advanced Integration',\r\n u'universalName': u'denali-advanced-integration',\r\n u'websiteUrl': u'www.denaliai.com'},\r\n {u'name': u'MicroAge',\r\n u'universalName': u'microage',\r\n u'websiteUrl': u'www.microage.com'},\r\n {u'name': u'TRUSTe',\r\n u'universalName': u'truste',\r\n u'websiteUrl': u'http://www.truste.com/'}]}}\r\n```\r\n\r\nThe Job Search API enables search across LinkedIn's job postings. You can get more information from [here](http://developers.linkedin.com/documents/job-search-api).\r\n\r\n```python\r\napplication.search_job(selectors=[{'jobs': ['id', 'customer-job-code', 'posting-date']}], params={'title': 'python', 'count': 2})\r\n{u'jobs': {u'_count': 2,\r\n u'_start': 0,\r\n u'_total': 206747,\r\n u'values': [{u'customerJobCode': u'0006YT23WQ',\r\n u'id': 5174636,\r\n u'postingDate': {u'day': 21, u'month': 3, u'year': 2013}},\r\n {u'customerJobCode': u'00023CCVC2',\r\n u'id': 5174634,\r\n u'postingDate': {u'day': 21, u'month': 3, u'year': 2013}}]}}\r\n```\r\n\r\n## Group API\r\nThe Groups API provides rich access to read and interact with LinkedIn’s groups functionality. You can get more information from [here](http://developers.linkedin.com/documents/groups-api). By the help of the interface, you can fetch group details, get your group memberships as well as your posts for a specific group which you are a member of.\r\n\r\n```python\r\napplication.get_group(41001)\r\n{u'id': u'41001', u'name': u'Object Oriented Programming'}\r\n\r\napplication.get_memberships(params={'count': 20})\r\n{u'_total': 1,\r\n u'values': [{u'_key': u'25827',\r\n u'group': {u'id': u'25827', u'name': u'Python Community'},\r\n u'membershipState': {u'code': u'member'}}]}\r\n\r\napplication.get_posts(41001)\r\n```\r\n\r\nYou can also submit a new post into a specific group.\r\n\r\n```python\r\ntitle = 'Scala for the Impatient'\r\nsummary = 'A new book has been published'\r\nsubmitted_url = 'http://horstmann.com/scala/'\r\nsubmitted_image_url = 'http://horstmann.com/scala/images/cover.png'\r\ndescription = 'It is a great book for the keen beginners. Check it out!'\r\n\r\napplication.submit_group_post(41001, title, summary, submitted_url, submitted_image_url, description)\r\n```\r\n\r\n## Company API\r\nThe Company API:\r\n * Retrieves and displays one or more company profiles based on the company ID or universal name.\r\n * Returns basic company profile data, such as name, website, and industry.\r\n * Returns handles to additional company content, such as RSS stream and Twitter feed.\r\n\r\nYou can query a company with either its **ID** or **Universal Name**. For more information, you can check out the documentation [here](http://developers.linkedin.com/documents/company-lookup-api-and-fields).\r\n\r\n```python\r\napplication.get_companies(company_ids=[1035], universal_names=['apple'], selectors=['name'], params={'is-company-admin': 'true'})\r\n# 1035 is Microsoft\r\n# The URL is as follows: https://api.linkedin.com/v1/companies::(1035,universal-name=apple)?is-company-admin=true\r\n\r\n{u'_total': 2,\r\n u'values': [{u'_key': u'1035', u'name': u'Microsoft'},\r\n {u'_key': u'universal-name=apple', u'name': u'Apple'}]}\r\n\r\n# Get the latest updates about Microsoft\r\napplication.get_company_updates(1035, params={'count': 2})\r\n{u'_count': 2,\r\n u'_start': 0,\r\n u'_total': 58,\r\n u'values': [{u'isCommentable': True,\r\n u'isLikable': True,\r\n u'isLiked': False,\r\n u'numLikes': 0,\r\n u'timestamp': 1363855486620,\r\n u'updateComments': {u'_total': 0},\r\n u'updateContent': {u'company': {u'id': 1035, u'name': u'Microsoft'},\r\n u'companyJobUpdate': {u'action': {u'code': u'created'},\r\n u'job': {u'company': {u'id': 1035, u'name': u'Microsoft'},\r\n u'description': u'Job Category: SalesLocation: Sacramento, CA, USJob ID: 812346-106756Division: Retail StoresStore...',\r\n u'id': 5173319,\r\n u'locationDescription': u'Sacramento, CA, US',\r\n u'position': {u'title': u'Store Manager, Specialty Store'},\r\n u'siteJobRequest': {u'url': u'http://www.linkedin.com/jobs?viewJob=&jobId=5173319'}}}},\r\n u'updateKey': u'UNIU-c1035-5720424522989961216-FOLLOW_CMPY',\r\n u'updateType': u'CMPY'},\r\n {u'isCommentable': True,\r\n u'isLikable': True,\r\n u'isLiked': False,\r\n u'numLikes': 0,\r\n u'timestamp': 1363855486617,\r\n u'updateComments': {u'_total': 0},\r\n u'updateContent': {u'company': {u'id': 1035, u'name': u'Microsoft'},\r\n u'companyJobUpdate': {u'action': {u'code': u'created'},\r\n u'job': {u'company': {u'id': 1035, u'name': u'Microsoft'},\r\n u'description': u'Job Category: Software Engineering: TestLocation: Redmond, WA, USJob ID: 794953-81760Division:...',\r\n u'id': 5173313,\r\n u'locationDescription': u'Redmond, WA, US',\r\n u'position': {u'title': u'Software Development Engineer in Test, Senior-IEB-MSCIS (794953)'},\r\n u'siteJobRequest': {u'url': u'http://www.linkedin.com/jobs?viewJob=&jobId=5173313'}}}},\r\n u'updateKey': u'UNIU-c1035-5720424522977378304-FOLLOW_CMPY',\r\n u'updateType': u'CMPY'}]}\r\n```\r\n\r\nYou can follow or unfollow a specific company as well.\r\n\r\n```python\r\napplication.follow_company(1035)\r\nTrue\r\n\r\napplication.unfollow_company(1035)\r\nTrue\r\n```\r\n\r\n## Job API\r\nThe Jobs APIs provide access to view jobs and job data. You can get more information from its [documentation](http://developers.linkedin.com/documents/job-lookup-api-and-fields).\r\n\r\n```python\r\napplication.get_job(job_id=5174636)\r\n{u'active': True,\r\n u'company': {u'id': 2329, u'name': u'Schneider Electric'},\r\n u'descriptionSnippet': u\"The Industrial Accounts Sales Manager is a quota carrying senior sales position principally responsible for generating new sales and growing company's share of wallet within the industrial business, contracting business and consulting engineering business. The primary objective is to build and establish strong and lasting relationships with technical teams and at executive level within specific in\",\r\n u'id': 5174636,\r\n u'position': {u'title': u'Industrial Accounts Sales Manager'},\r\n u'postingTimestamp': 1363860033000}\r\n```\r\n\r\nYou can also fetch you job bookmarks.\r\n\r\n```python\r\napplication.get_job_bookmarks()\r\n{u'_total': 0}\r\n```\r\n\r\n## Share API\r\nNetwork updates serve as one of the core experiences on LinkedIn, giving users the ability to share rich content to their professional network. You can get more information from [here](http://developers.linkedin.com/documents/share-api).\r\n\r\n```\r\napplication.submit_share('Posting from the API using JSON', 'A title for your share', None, 'http://www.linkedin.com', 'http://d.pr/3OWS')\r\n{'updateKey': u'UNIU-8219502-5705061301949063168-SHARE'\r\n 'updateURL': 'http://www.linkedin.com/updates?discuss=&scope=8219502&stype=M&topic=5705061301949063168&type=U&a=aovi'}\r\n```\r\n\r\n## Network API\r\nThe Get Network Updates API returns the users network updates, which is the LinkedIn term for the user's feed. This call returns most of what shows up in the middle column of the LinkedIn.com home page, either for the member or the member's connections. You can get more information from [here](http://developers.linkedin.com/documents/get-network-updates-and-statistics-api).\r\n\r\nThere are many network update types. You can look at them by importing **NETWORK_UPDATES** enumeration.\r\n\r\n```python\r\nfrom linkedin.linkedin import NETWORK_UPDATES\r\nprint NETWORK_UPDATES.enums\r\n{'APPLICATION': 'APPS',\r\n 'CHANGED_PROFILE': 'PRFU',\r\n 'COMPANY': 'CMPY',\r\n 'CONNECTION': 'CONN',\r\n 'EXTENDED_PROFILE': 'PRFX',\r\n 'GROUP': 'JGRP',\r\n 'JOB': 'JOBS',\r\n 'PICTURE': 'PICT',\r\n 'SHARED': 'SHAR',\r\n 'VIRAL': 'VIRL'}\r\n\r\nupdate_types = (NETWORK_UPDATES.CONNECTION, NETWORK_UPDATES.PICTURE)\r\napplication.get_network_updates(update_types)\r\n\r\n{u'_total': 1,\r\n u'values': [{u'isCommentable': True,\r\n u'isLikable': True,\r\n u'isLiked': False,\r\n u'numLikes': 0,\r\n u'timestamp': 1363470126509,\r\n u'updateComments': {u'_total': 0},\r\n u'updateContent': {u'person': {u'apiStandardProfileRequest': {u'headers': {u'_total': 1,\r\n u'values': [{u'name': u'x-li-auth-token', u'value': u'name:Egbj'}]},\r\n u'url': u'http://api.linkedin.com/v1/people/COjFALsKDP'},\r\n u'firstName': u'ozgur',\r\n u'headline': u'This is my headline',\r\n u'id': u'COjFALsKDP',\r\n u'lastName': u'vatansever',\r\n u'siteStandardProfileRequest': {u'url': u'http://www.linkedin.com/profile/view?id=46113651&authType=name&authToken=Egbj&trk=api*a101945*s101945*'}}},\r\n u'updateKey': u'UNIU-46113651-5718808205493026816-SHARE',\r\n u'updateType': u'SHAR'}]}\r\n```\r\n\r\n## Invitation API\r\nThe Invitation API allows your users to invite people they find in your application to their LinkedIn network. You can get more information from [here](http://developers.linkedin.com/documents/invitation-api).\r\n\r\n```python\r\nfrom linkedin.models import LinkedInRecipient, LinkedInInvitation\r\nrecipient = LinkedInRecipient(None, 'john.doe@python.org', 'John', 'Doe')\r\nprint recipient.json\r\n{'person': {'_path': '/people/email=john.doe@python.org',\r\n 'first-name': 'John',\r\n 'last-name': 'Doe'}}\r\n\r\ninvitation = LinkedInInvitation('Hello John', \"What's up? Can I add you as a friend?\", (recipient,), 'friend')\r\nprint invitation.json\r\n{'body': \"What's up? Can I add you as a friend?\",\r\n 'item-content': {'invitation-request': {'connect-type': 'friend'}},\r\n 'recipients': {'values': [{'person': {'_path': '/people/email=john.doe@python.org',\r\n 'first-name': 'John',\r\n 'last-name': 'Doe'}}]},\r\n 'subject': 'Hello John'}\r\n\r\napplication.send_invitation(invitation)\r\nTrue\r\n```\r\n\r\n## Throttle Limits\r\n\r\nLinkedIn API keys are throttled by default. You should take a look at the [Throttle Limits Documentation](http://developer.linkedin.com/documents/throttle-limits) to get more information about it.","google":"","note":"Don't delete this file! It's used internally to help with page regeneration."} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 51a21b7..0000000 --- a/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -future==0.14.3 -requests -requests_oauthlib \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 5fa1bd9..0000000 --- a/setup.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -import os - -try: - from setuptools import setup -except ImportError: - from distutils.core import setup - -from linkedin import __version__ - - -with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme: - long_description = readme.read() - -setup(name='python-linkedin', - version=__version__, - description='Python Interface to the LinkedIn API', - long_description=long_description, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Natural Language :: English', - ], - keywords='linkedin python', - author='Ozgur Vatansever', - author_email='ozgurvt@gmail.com', - maintainer='Ozgur Vatansever', - maintainer_email='ozgurvt@gmail.com', - url='http://ozgur.github.com/python-linkedin/', - license='MIT', - packages=['linkedin'], - install_requires=['requests>=1.1.0', 'requests-oauthlib>=0.3'], - zip_safe=False -) diff --git a/stylesheets/print.css b/stylesheets/print.css new file mode 100644 index 0000000..541695b --- /dev/null +++ b/stylesheets/print.css @@ -0,0 +1,226 @@ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +body { + font-size: 13px; + line-height: 1.5; + font-family: 'Helvetica Neue', Helvetica, Arial, serif; + color: #000; +} + +a { + color: #d5000d; + font-weight: bold; +} + +header { + padding-top: 35px; + padding-bottom: 10px; +} + +header h1 { + font-weight: bold; + letter-spacing: -1px; + font-size: 48px; + color: #303030; + line-height: 1.2; +} + +header h2 { + letter-spacing: -1px; + font-size: 24px; + color: #aaa; + font-weight: normal; + line-height: 1.3; +} +#downloads { + display: none; +} +#main_content { + padding-top: 20px; +} + +code, pre { + font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal; + color: #222; + margin-bottom: 30px; + font-size: 12px; +} + +code { + padding: 0 3px; +} + +pre { + border: solid 1px #ddd; + padding: 20px; + overflow: auto; +} +pre code { + padding: 0; +} + +ul, ol, dl { + margin-bottom: 20px; +} + + +/* COMMON STYLES */ + +table { + width: 100%; + border: 1px solid #ebebeb; +} + +th { + font-weight: 500; +} + +td { + border: 1px solid #ebebeb; + text-align: center; + font-weight: 300; +} + +form { + background: #f2f2f2; + padding: 20px; + +} + + +/* GENERAL ELEMENT TYPE STYLES */ + +h1 { + font-size: 2.8em; +} + +h2 { + font-size: 22px; + font-weight: bold; + color: #303030; + margin-bottom: 8px; +} + +h3 { + color: #d5000d; + font-size: 18px; + font-weight: bold; + margin-bottom: 8px; +} + +h4 { + font-size: 16px; + color: #303030; + font-weight: bold; +} + +h5 { + font-size: 1em; + color: #303030; +} + +h6 { + font-size: .8em; + color: #303030; +} + +p { + font-weight: 300; + margin-bottom: 20px; +} + +a { + text-decoration: none; +} + +p a { + font-weight: 400; +} + +blockquote { + font-size: 1.6em; + border-left: 10px solid #e9e9e9; + margin-bottom: 20px; + padding: 0 0 0 30px; +} + +ul li { + list-style: disc inside; + padding-left: 20px; +} + +ol li { + list-style: decimal inside; + padding-left: 3px; +} + +dl dd { + font-style: italic; + font-weight: 100; +} + +footer { + margin-top: 40px; + padding-top: 20px; + padding-bottom: 30px; + font-size: 13px; + color: #aaa; +} + +footer a { + color: #666; +} + +/* MISC */ +.clearfix:after { + clear: both; + content: '.'; + display: block; + visibility: hidden; + height: 0; +} + +.clearfix {display: inline-block;} +* html .clearfix {height: 1%;} +.clearfix {display: block;} \ No newline at end of file diff --git a/stylesheets/pygment_trac.css b/stylesheets/pygment_trac.css new file mode 100644 index 0000000..c6a6452 --- /dev/null +++ b/stylesheets/pygment_trac.css @@ -0,0 +1,69 @@ +.highlight { background: #ffffff; } +.highlight .c { color: #999988; font-style: italic } /* Comment */ +.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.highlight .k { font-weight: bold } /* Keyword */ +.highlight .o { font-weight: bold } /* Operator */ +.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ +.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ +.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #aa0000 } /* Generic.Error */ +.highlight .gh { color: #999999 } /* Generic.Heading */ +.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ +.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #555555 } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold; } /* Generic.Subheading */ +.highlight .gt { color: #aa0000 } /* Generic.Traceback */ +.highlight .kc { font-weight: bold } /* Keyword.Constant */ +.highlight .kd { font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #009999 } /* Literal.Number */ +.highlight .s { color: #d14 } /* Literal.String */ +.highlight .na { color: #008080 } /* Name.Attribute */ +.highlight .nb { color: #0086B3 } /* Name.Builtin */ +.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ +.highlight .no { color: #008080 } /* Name.Constant */ +.highlight .ni { color: #800080 } /* Name.Entity */ +.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ +.highlight .nn { color: #555555 } /* Name.Namespace */ +.highlight .nt { color: #000080 } /* Name.Tag */ +.highlight .nv { color: #008080 } /* Name.Variable */ +.highlight .ow { font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #009999 } /* Literal.Number.Float */ +.highlight .mh { color: #009999 } /* Literal.Number.Hex */ +.highlight .mi { color: #009999 } /* Literal.Number.Integer */ +.highlight .mo { color: #009999 } /* Literal.Number.Oct */ +.highlight .sb { color: #d14 } /* Literal.String.Backtick */ +.highlight .sc { color: #d14 } /* Literal.String.Char */ +.highlight .sd { color: #d14 } /* Literal.String.Doc */ +.highlight .s2 { color: #d14 } /* Literal.String.Double */ +.highlight .se { color: #d14 } /* Literal.String.Escape */ +.highlight .sh { color: #d14 } /* Literal.String.Heredoc */ +.highlight .si { color: #d14 } /* Literal.String.Interpol */ +.highlight .sx { color: #d14 } /* Literal.String.Other */ +.highlight .sr { color: #009926 } /* Literal.String.Regex */ +.highlight .s1 { color: #d14 } /* Literal.String.Single */ +.highlight .ss { color: #990073 } /* Literal.String.Symbol */ +.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #008080 } /* Name.Variable.Class */ +.highlight .vg { color: #008080 } /* Name.Variable.Global */ +.highlight .vi { color: #008080 } /* Name.Variable.Instance */ +.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ + +.type-csharp .highlight .k { color: #0000FF } +.type-csharp .highlight .kt { color: #0000FF } +.type-csharp .highlight .nf { color: #000000; font-weight: normal } +.type-csharp .highlight .nc { color: #2B91AF } +.type-csharp .highlight .nn { color: #000000 } +.type-csharp .highlight .s { color: #A31515 } +.type-csharp .highlight .sc { color: #A31515 } diff --git a/stylesheets/styles.css b/stylesheets/styles.css new file mode 100644 index 0000000..dacf2e1 --- /dev/null +++ b/stylesheets/styles.css @@ -0,0 +1,255 @@ +@import url(https://fonts.googleapis.com/css?family=Lato:300italic,700italic,300,700); + +body { + padding:50px; + font:14px/1.5 Lato, "Helvetica Neue", Helvetica, Arial, sans-serif; + color:#777; + font-weight:300; +} + +h1, h2, h3, h4, h5, h6 { + color:#222; + margin:0 0 20px; +} + +p, ul, ol, table, pre, dl { + margin:0 0 20px; +} + +h1, h2, h3 { + line-height:1.1; +} + +h1 { + font-size:28px; +} + +h2 { + color:#393939; +} + +h3, h4, h5, h6 { + color:#494949; +} + +a { + color:#39c; + font-weight:400; + text-decoration:none; +} + +a small { + font-size:11px; + color:#777; + margin-top:-0.6em; + display:block; +} + +.wrapper { + width:860px; + margin:0 auto; +} + +blockquote { + border-left:1px solid #e5e5e5; + margin:0; + padding:0 0 0 20px; + font-style:italic; +} + +code, pre { + font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; + color:#333; + font-size:12px; +} + +pre { + padding:8px 15px; + background: #f8f8f8; + border-radius:5px; + border:1px solid #e5e5e5; + overflow-x: auto; +} + +table { + width:100%; + border-collapse:collapse; +} + +th, td { + text-align:left; + padding:5px 10px; + border-bottom:1px solid #e5e5e5; +} + +dt { + color:#444; + font-weight:700; +} + +th { + color:#444; +} + +img { + max-width:100%; +} + +header { + width:270px; + float:left; + position:fixed; +} + +header ul { + list-style:none; + height:40px; + + padding:0; + + background: #eee; + background: -moz-linear-gradient(top, #f8f8f8 0%, #dddddd 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd)); + background: -webkit-linear-gradient(top, #f8f8f8 0%,#dddddd 100%); + background: -o-linear-gradient(top, #f8f8f8 0%,#dddddd 100%); + background: -ms-linear-gradient(top, #f8f8f8 0%,#dddddd 100%); + background: linear-gradient(top, #f8f8f8 0%,#dddddd 100%); + + border-radius:5px; + border:1px solid #d2d2d2; + box-shadow:inset #fff 0 1px 0, inset rgba(0,0,0,0.03) 0 -1px 0; + width:270px; +} + +header li { + width:89px; + float:left; + border-right:1px solid #d2d2d2; + height:40px; +} + +header ul a { + line-height:1; + font-size:11px; + color:#999; + display:block; + text-align:center; + padding-top:6px; + height:40px; +} + +strong { + color:#222; + font-weight:700; +} + +header ul li + li { + width:88px; + border-left:1px solid #fff; +} + +header ul li + li + li { + border-right:none; + width:89px; +} + +header ul a strong { + font-size:14px; + display:block; + color:#222; +} + +section { + width:500px; + float:right; + padding-bottom:50px; +} + +small { + font-size:11px; +} + +hr { + border:0; + background:#e5e5e5; + height:1px; + margin:0 0 20px; +} + +footer { + width:270px; + float:left; + position:fixed; + bottom:50px; +} + +@media print, screen and (max-width: 960px) { + + div.wrapper { + width:auto; + margin:0; + } + + header, section, footer { + float:none; + position:static; + width:auto; + } + + header { + padding-right:320px; + } + + section { + border:1px solid #e5e5e5; + border-width:1px 0; + padding:20px 0; + margin:0 0 20px; + } + + header a small { + display:inline; + } + + header ul { + position:absolute; + right:50px; + top:52px; + } +} + +@media print, screen and (max-width: 720px) { + body { + word-wrap:break-word; + } + + header { + padding:0; + } + + header ul, header p.view { + position:static; + } + + pre, code { + word-wrap:normal; + } +} + +@media print, screen and (max-width: 480px) { + body { + padding:15px; + } + + header ul { + display:none; + } +} + +@media print { + body { + padding:0.4in; + font-size:12pt; + color:#444; + } +} diff --git a/stylesheets/stylesheet.css b/stylesheets/stylesheet.css new file mode 100644 index 0000000..020ad6d --- /dev/null +++ b/stylesheets/stylesheet.css @@ -0,0 +1,371 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} + +/* LAYOUT STYLES */ +body { + font-size: 1em; + line-height: 1.5; + background: #e7e7e7 url(../images/body-bg.png) 0 0 repeat; + font-family: 'Helvetica Neue', Helvetica, Arial, serif; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); + color: #6d6d6d; +} + +a { + color: #d5000d; +} +a:hover { + color: #c5000c; +} + +header { + padding-top: 35px; + padding-bottom: 25px; +} + +header h1 { + font-family: 'Chivo', 'Helvetica Neue', Helvetica, Arial, serif; font-weight: 900; + letter-spacing: -1px; + font-size: 48px; + color: #303030; + line-height: 1.2; +} + +header h2 { + letter-spacing: -1px; + font-size: 24px; + color: #aaa; + font-weight: normal; + line-height: 1.3; +} + +#container { + background: transparent url(../images/highlight-bg.jpg) 50% 0 no-repeat; + min-height: 595px; +} + +.inner { + width: 620px; + margin: 0 auto; +} + +#container .inner img { + max-width: 100%; +} + +#downloads { + margin-bottom: 40px; +} + +a.button { + -moz-border-radius: 30px; + -webkit-border-radius: 30px; + border-radius: 30px; + border-top: solid 1px #cbcbcb; + border-left: solid 1px #b7b7b7; + border-right: solid 1px #b7b7b7; + border-bottom: solid 1px #b3b3b3; + color: #303030; + line-height: 25px; + font-weight: bold; + font-size: 15px; + padding: 12px 8px 12px 8px; + display: block; + float: left; + width: 179px; + margin-right: 14px; + background: #fdfdfd; /* Old browsers */ + background: -moz-linear-gradient(top, #fdfdfd 0%, #f2f2f2 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#fdfdfd), color-stop(100%,#f2f2f2)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #fdfdfd 0%,#f2f2f2 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #fdfdfd 0%,#f2f2f2 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #fdfdfd 0%,#f2f2f2 100%); /* IE10+ */ + background: linear-gradient(top, #fdfdfd 0%,#f2f2f2 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fdfdfd', endColorstr='#f2f2f2',GradientType=0 ); /* IE6-9 */ + -webkit-box-shadow: 10px 10px 5px #888; + -moz-box-shadow: 10px 10px 5px #888; + box-shadow: 0px 1px 5px #e8e8e8; +} +a.button:hover { + border-top: solid 1px #b7b7b7; + border-left: solid 1px #b3b3b3; + border-right: solid 1px #b3b3b3; + border-bottom: solid 1px #b3b3b3; + background: #fafafa; /* Old browsers */ + background: -moz-linear-gradient(top, #fdfdfd 0%, #f6f6f6 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#fdfdfd), color-stop(100%,#f6f6f6)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #fdfdfd 0%,#f6f6f6 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #fdfdfd 0%,#f6f6f6 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #fdfdfd 0%,#f6f6f6 100%); /* IE10+ */ + background: linear-gradient(top, #fdfdfd 0%,#f6f6f6, 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fdfdfd', endColorstr='#f6f6f6',GradientType=0 ); /* IE6-9 */ +} + +a.button span { + padding-left: 50px; + display: block; + height: 23px; +} + +#download-zip span { + background: transparent url(../images/zip-icon.png) 12px 50% no-repeat; +} +#download-tar-gz span { + background: transparent url(../images/tar-gz-icon.png) 12px 50% no-repeat; +} +#view-on-github span { + background: transparent url(../images/octocat-icon.png) 12px 50% no-repeat; +} +#view-on-github { + margin-right: 0; +} + +code, pre { + font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal; + color: #222; + margin-bottom: 30px; + font-size: 14px; +} + +code { + background-color: #f2f2f2; + border: solid 1px #ddd; + padding: 0 3px; +} + +pre { + padding: 20px; + background: #303030; + color: #f2f2f2; + text-shadow: none; + overflow: auto; +} +pre code { + color: #f2f2f2; + background-color: #303030; + border: none; + padding: 0; +} + +ul, ol, dl { + margin-bottom: 20px; +} + + +/* COMMON STYLES */ + +hr { + height: 1px; + line-height: 1px; + margin-top: 1em; + padding-bottom: 1em; + border: none; + background: transparent url('../images/hr.png') 50% 0 no-repeat; +} + +strong { + font-weight: bold; +} + +em { + font-style: italic; +} + +table { + width: 100%; + border: 1px solid #ebebeb; +} + +th { + font-weight: 500; +} + +td { + border: 1px solid #ebebeb; + text-align: center; + font-weight: 300; +} + +form { + background: #f2f2f2; + padding: 20px; + +} + + +/* GENERAL ELEMENT TYPE STYLES */ + +h1 { + font-size: 32px; +} + +h2 { + font-size: 22px; + font-weight: bold; + color: #303030; + margin-bottom: 8px; +} + +h3 { + color: #d5000d; + font-size: 18px; + font-weight: bold; + margin-bottom: 8px; +} + +h4 { + font-size: 16px; + color: #303030; + font-weight: bold; +} + +h5 { + font-size: 1em; + color: #303030; +} + +h6 { + font-size: .8em; + color: #303030; +} + +p { + font-weight: 300; + margin-bottom: 20px; +} + +a { + text-decoration: none; +} + +p a { + font-weight: 400; +} + +blockquote { + font-size: 1.6em; + border-left: 10px solid #e9e9e9; + margin-bottom: 20px; + padding: 0 0 0 30px; +} + +ul li { + list-style: disc inside; + padding-left: 20px; +} + +ol li { + list-style: decimal inside; + padding-left: 3px; +} + +dl dt { + color: #303030; +} + +footer { + background: transparent url('../images/hr.png') 0 0 no-repeat; + margin-top: 40px; + padding-top: 20px; + padding-bottom: 30px; + font-size: 13px; + color: #aaa; +} + +footer a { + color: #666; +} +footer a:hover { + color: #444; +} + +/* MISC */ +.clearfix:after { + clear: both; + content: '.'; + display: block; + visibility: hidden; + height: 0; +} + +.clearfix {display: inline-block;} +* html .clearfix {height: 1%;} +.clearfix {display: block;} + +/* #Media Queries +================================================== */ + +/* Smaller than standard 960 (devices and browsers) */ +@media only screen and (max-width: 959px) {} + +/* Tablet Portrait size to standard 960 (devices and browsers) */ +@media only screen and (min-width: 768px) and (max-width: 959px) {} + +/* All Mobile Sizes (devices and browser) */ +@media only screen and (max-width: 767px) { + header { + padding-top: 10px; + padding-bottom: 10px; + } + #downloads { + margin-bottom: 25px; + } + #download-zip, #download-tar-gz { + display: none; + } + .inner { + width: 94%; + margin: 0 auto; + } +} + +/* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ +@media only screen and (min-width: 480px) and (max-width: 767px) {} + +/* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */ +@media only screen and (max-width: 479px) {}