-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
first commit with .py, licence, img and setup.py
- Loading branch information
0 parents
commit d210bc4
Showing
8 changed files
with
568 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2020 KINTZLER Etienne | ||
|
||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
# api-offres-emploi | ||
A python wrapper for [API Offres d'emploi v2](https://www.emploi-store-dev.fr/portail-developpeur-cms/home/catalogue-des-api/documentation-des-api/api/api-offres-demploi-v2.html) available on [Emploi Store](https://www.emploi-store.fr/portail/accueil) (Pole Emploi). | ||
|
||
### Table of content | ||
|
||
- [1. Setting up](#1-setting-up) | ||
- [2. Usage](#2-usage) | ||
- [Installation](#21-installation) | ||
- [Authentification](#22-authentification) | ||
- [3. Examples](#3-examples) | ||
- [3.1. search](#31-search) | ||
- [3.2. referentiel](#32-referentiel) | ||
- [4. Analysis of the search output](#4-analysis-of-the-search-output) | ||
- [4.1. Aggregate view of job offers](#41-aggregate-view-of-job-offers) | ||
- [4.2. Detailed view of job offers](#42-detailed-view-of-job-offers) | ||
|
||
## 1. Setting up | ||
To use the API, you need to subscribe to the _Api Offres d'emploi v2_. Here are the steps: | ||
- Create an account on [Emploi store](https://www.emploi-store-dev.fr/) | ||
- Go to the dashboard (*Tableau de bord*) and create an application. You should then have the client ID (_Identifiant_) and client secret key (_Clé secrète_). | ||
- Go to [catalogue](https://www.emploi-store-dev.fr/portail-developpeur/catalogueapi) and subscribe to _Api Offres d'emploi v2_ | ||
|
||
## 2. Usage | ||
|
||
### 2.1. Installation | ||
The package can be installed via pip: | ||
```python | ||
pip install api-offres-emploi | ||
``` | ||
|
||
### 2.2. Authentification | ||
To authentificate, create an instance of `Api` with your client id and secrets (you might want to access these two variables through environment variables instead of hardcoding them): | ||
|
||
```python | ||
from offres_emploi import Api | ||
client = Api(client_id="<your client id>", | ||
client_secret="<your client secret>") | ||
``` | ||
|
||
### 2.3. Methods exposed by the API 'Offres d'emploi v2' | ||
The API _Offres d'emploi v2_ has two methods : | ||
- `search` : return aggregate count (named _filtresPossibles_) of the job offers along with the detailed list of these job offers (named _resultats_). A lot of parameters are available : date, keywords, localization, etc. Extensive information on the website page [Rechercher par critères](https://www.emploi-store-dev.fr/portail-developpeur-cms/home/catalogue-des-api/documentation-des-api/api/api-offres-demploi-v2/rechercher-par-criteres.html). | ||
- `referentiel` : return reference source (more information on the website page [Référentiels](https://www.emploi-store-dev.fr/portail-developpeur-cms/home/catalogue-des-api/documentation-des-api/api/api-offres-demploi-v2/referentiels.html)). | ||
|
||
**About range and pagination**: In the method `search`, only the first 150 job offers are returned by default by the API (since the default `range` value is `0-149`). There are three constraints regarding the `range` parameter ([see the reference for the parameter 'range'](https://www.emploi-store-dev.fr/portail-developpeur-cms/home/catalogue-des-api/documentation-des-api/api/api-offres-demploi-v2/rechercher-par-criteres.html)): | ||
- the maximal value for the first element of range is 1000 | ||
- the maximal value for the second element of range is 1149 | ||
- the number of result for a given requests must be inferior or equals to 150 | ||
|
||
Hence for a given search, it is possible to get up to 1150 job offers, by modifying the value of the parameter `range` for each request (`0-149` then `150-299`, `300-449`, etc.). However some `search` might have much more than 1150 job offers. In this case one has to narrow down the search using additional parameters (for instance: by playing with the parameters `minCreationDate` and `maxCreationDate` of the request). | ||
|
||
**Limit rate** : The API limit rate is set to 3 requests per second. | ||
|
||
**Equivalence between the *API Offres d'emploi v2* methods and this module methods**: The 2 main methods from *Api Offres d'emploi v2* have the same name in the python module (`search` and `referentiel`) and are methods of the module class `Api`. One notable difference is that the output of `search` of this module has an additional `Content-Range` entry (describe hereafter). | ||
|
||
## 3. Examples | ||
### 3.1. Search | ||
#### Minimal example of search | ||
The _API Offres d'emploi v2_ has default parameters, so we can make a requests without parameters (will return the latest job offers) : | ||
```python | ||
basic_search = client.search() | ||
``` | ||
The output of the module's `Api.search` method is the same as the _Api Offres d'emploi v2_ (dictionnary with entries `filtresPossibles` and `resultats`) with an additional entry, `Content-Range`, that gives the first and last index used for the `range` parameter along with the total number of available job offers for this request. For instance : | ||
```python | ||
basic_search['Content-Range'] | ||
``` | ||
will output : | ||
```python | ||
{'first_index': '0', 'last_index': '149', 'max_results': '300749'} | ||
``` | ||
The other fields (`filtresPossibles` and `resultats`) are explored further the [section 4](#4-analysis-of-the-search-output). | ||
|
||
#### A more complex search | ||
A more sensible approach would be to search for special keyword over a certain period, for instance the job offers with that include the keyword _big data_ since 2020-03-01 at 12h30. | ||
```python | ||
from offres_emploi.utils import dt_to_str_iso | ||
import datetime | ||
|
||
start_dt = datetime.datetime(2020, 3, 1, 12, 30) | ||
end_dt = datetime.datetime.today() | ||
params = { | ||
"motsCles": "data science", | ||
'minCreationDate': dt_to_str_iso(start_dt), | ||
'maxCreationDate': dt_to_str_iso(end_dt) | ||
} | ||
search_on_big_data = client.search(params=params) | ||
``` | ||
|
||
The full list of parameters for the `search` request are available in the page [Rechercher par critères](https://www.emploi-store-dev.fr/portail-developpeur-cms/home/catalogue-des-api/documentation-des-api/api/api-offres-demploi-v2/rechercher-par-criteres.html). These parameters are passed as dictionary to the parameter `params`. Note that the keys of this dictionnary are in camelCase, as in the _API Offres d'emploi v2_ specification. Also, we feed a `datetime` object to the helper function `dt_to_str_iso` to convert it to a string with the appropriate [ISO-8601](https://www.w3.org/TR/NOTE-datetime) format required by the API. | ||
|
||
### 3.2. Referentiel | ||
Getting reference source (_referentiel_ in french) is more straightforward since it does not need any parameter ; one just need to specify the desired reference source: | ||
```python | ||
referentiel_metiers = client.referentiel('metiers') | ||
``` | ||
It will output the following list of dictionnaries: | ||
```python | ||
[{'code': '1', | ||
'libelle': "Métiers de l'environnement et du développement durable"}, | ||
{'code': '2', | ||
'libelle': 'Métiers de la défense et de la sécurité publique (hors logistique, santé, tertiaire, restauration)'}, | ||
{'code': '3', | ||
'libelle': "Métiers du patrimoine et de la restauration d'oeuvres d'art"}, | ||
{'code': '4', 'libelle': "Métiers de l'intelligence économique"}, | ||
{'code': '5', 'libelle': 'Métiers de la recherche'}, | ||
... | ||
] | ||
``` | ||
|
||
The full list of *referentiel* is available in the docstring of the method or in the [Référentiel page](https://www.emploi-store-dev.fr/portail-developpeur-cms/home/catalogue-des-api/documentation-des-api/api/api-offres-demploi-v2/referentiels.html) of the API. | ||
|
||
## 4. Analysis of the search output | ||
The output of the `Api.search` is a dictionary with three entries: | ||
|
||
- *filtresPossibles* | ||
- *resultats* | ||
- *Content-Range* | ||
|
||
The breakdown of the available job offers into different categories (type of contract, experience, qualification, nature of contract) are available in the _filtresPossibles_ field and the detailed view of the job offers in _resultats_ field. | ||
|
||
```python | ||
filters = search_on_big_data['filtresPossibles'] | ||
results = search_on_big_data['resultats'] | ||
content_range = search_on_big_data['Content-Range'] | ||
``` | ||
|
||
The number of job offers available at this point in time for this search is given by: | ||
|
||
```python | ||
content_range['max_results'] | ||
``` | ||
|
||
### 4.1. Aggregate view of job offers | ||
The helper function `filters_to_df` is used to convert the field `filtresPossibles` in a suitable format: | ||
|
||
```python | ||
from offres_emploi.utils import filters_to_df | ||
filters_df = filters_to_df(filters) | ||
``` | ||
It will output: | ||
```python | ||
filtre valeur_possible nb_resultats | ||
0 typeContrat CDD 3 | ||
1 typeContrat CDI 138 | ||
2 typeContrat LIB 37 | ||
3 experience 0 15 | ||
4 experience 1 96 | ||
5 experience 2 47 | ||
6 experience 3 20 | ||
7 qualification 0 4 | ||
8 qualification 9 76 | ||
9 qualification X 98 | ||
10 natureContrat E1 141 | ||
11 natureContrat NS 37 | ||
``` | ||
|
||
It is then straightforward to plot these figures using the data visualization library [seaborn](https://seaborn.pydata.org/): | ||
|
||
```python | ||
import seaborn as sns | ||
g = sns.FacetGrid(filters_df, col="filtre", sharex=False, sharey=False) | ||
g = g.map(data=sns.barplot, row="valeur_possible", col="nb_resultats") | ||
``` | ||
|
||
![barplot of the breakdown](/img/filters.png) | ||
|
||
|
||
### 4.2 Detailed view of job offers | ||
The detailed view _resultats_ has a more friendly structure and can be pass directly to a `pandas.DataFrame` constructor. For example, to know the salary offered by the enterprises for this search: | ||
|
||
```python | ||
import pandas as pd | ||
results_df = pd.DataFrame(results) | ||
salary_by_enterprise = ( | ||
results_df[['entreprise', 'salaire']] | ||
.dropna() | ||
.agg(dict(entreprise=lambda x: x.get('nom'), | ||
salaire=lambda x: x.get('commentaire'))) | ||
.dropna(subset=["salaire"]) | ||
.loc[lambda df: df.salaire.str.contains("\d+")] | ||
.sort_values("salaire") | ||
) | ||
``` | ||
|
||
It will output: | ||
|
||
```python | ||
entreprise salaire | ||
73 CLEEVEN SE 25 - 45 k€ brut annuel | ||
112 ASTEK 30 - 40 k€ brut annuel | ||
113 ADSERVIO 30 - 50 k€ brut annuel | ||
15 REDLEAN 30 - 50 k€ brut annuel | ||
29 EOLE CONSULTING 30 - 50 k€ brut annuel | ||
66 PHOENIX ISI 32 - 35 k€ brut annuel | ||
12 DGA DRH CPP SDCO 32 - 52 k€ brut annuel | ||
109 EXPERTEAM 35 - 45 k€ brut annuel | ||
47 GROUPE ASTEN 35 - 48 k€ brut annuel | ||
16 ENERGISME 35 - 60 k€ brut annuel | ||
90 VISEO 36 - 50 k€ brut annuel | ||
80 SOCIETE ALTRAN, Bat Teck - E. Golf Park 38 - 42 k€ brut annuel | ||
14 AINABL TECHNOLOGIES FRANCE 38 - 50 k€ brut annuel | ||
68 SOCIETE ALTRAN, Bat Teck - E. Golf Park 40 - 45 k€ brut annuel | ||
28 Katchme 40 - 50 k€ brut annuel | ||
124 HUMAINEA 42 - 45 k€ brut annuel | ||
79 SOCIETE ALTRAN, Bat Teck - E. Golf Park 45 - 48 k€ brut annuel | ||
86 DGA DRH CPP FDCO A partir de 34 k€ brut annuel | ||
114 SILICOM REGION OUEST A partir de 35 k€ brut annuel | ||
17 SNEF A partir de 40 k€ brut annuel | ||
96 NODYA GROUP A partir de 40 k€ brut annuel | ||
89 QUADRA INFORMATIQUE A partir de 45 k€ brut annuel | ||
35 Hunteed A partir de 49 k€ brut annuel | ||
``` | ||
Now you know where to apply. | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .api import Api |
Oops, something went wrong.