Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Importation des statistiques sur les demandeurs d'emploi de l'API France Travail #409

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

calummackervoy
Copy link
Contributor

**Carte Notion : ** https://www.notion.so/gip-inclusion/API-FT-Se-brancher-l-API-demandeurs-d-emploi-1765f321b60480e68694edeee2d710f3

Pourquoi ?

Hebdomanaire, on recherche des nouvelles depuis l'API FT et on stocke les nouvelles données disponibles dans un table SQL, organisé par caracteristique, activité, période et territoire.

Checks

  • J'ai lancé le modèle ou seed sur un dump local (si pertinent)
  • J'ai ajouté des tests à mon code Python, ou des assertions DBT sur le modèle SQL
  • J'ai documenté ce modèle voire certains de ses champs (usage métier, tableau de bord, etc)

The data is organized into one table, by period, territory, activity and charactertistic
Comment on lines +81 to +106
# Table configuration
# Columns defined in the main body of the request
shared_columns = [
"codeTypeTerritoire",
"codeTerritoire",
"codePeriode",
"libTerritoire",
"codeTypeActivite",
"codeActivite",
"libActivite",
"codeNomenclature",
"libNomenclature",
"codeTypePeriode",
"libPeriode",
"datMaj",
]
# Columns defined on each characteristic (row) of the table
characteristic_columns = [
# Part of the composite primary key
"codeCaract",
"codeTypeCaract",
# Other fields
"libCaract",
"nombre",
"pourcentage",
]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je pense qu'il est au bon endroit, car c'est la configuration par rapport au structure du API

Du coup on reçoit les données sous la forme :

{
    //  Columns defined in the main body of the request
    "codeTypeTerritoire": "DEP",
    // Columns defined on each characteristic (row) of the table
    "listeValeursParPeriode": [
         {
             "codeCaract": "AGE1",
             ...
         },
         {
             "codeCaract": "AGE1",
             ...
         }
}

Devrais-je changer la structure ici, ou eteindre le commentaire pour que c'est plus maintainable ?

# If a log is present, we make the assumptions that
# - the DAG has run successfully since the last quarter
# - the data we have for previous quarters don't need to be updated
# - the most recently updated quarter is the only one we are missing
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ça vaut le coup d’ajouter une SELECT DISTINCT “codePeriode” FROM france_travail.demandeurs_a; afin de signaler s’il y a un trou ? (par example si un jour FT téléverse plusieurs trimestres au même temps)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oui, car ça permet aussi de gérer le cas où "the DAG has run successfully since the last quarter" est faux :).

Comment on lines +132 to +134
# We log which quarters have already been accessed by previous executions of this task
# If this cache is empty, we'll pull everything available from the API
logged_sessions_by_territory = json.loads(Variable.get("FT_INFORMATION_TERRITOIRE_PERIOD_LOG", "{}"))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2025-02-20 at 16 36 07

Ça fait une différence 😁

Comment on lines +68 to +76
- name: france_travail_marche_travail
schema: france_travail
description: >
Nombre et % de demandeurs par catégories x caractéristiques, % par activités (Rome, Compétence), par territoire.
tables:
- name: job_seeker_stats
description: >
Table qui stocke des statistiques sur les chercheurs d'emploi du API France Travail. Les statistiques sont organisées
par territoire, characteristique, activité et période, selon le schema du API.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ces informations sont utiles dans le fichier dbt/models/_sources.yml?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

De mémoire c'est pour dbt doc, donc oui c'est utile mais après ce n'est peut-être pas utilisé 😁

@calummackervoy calummackervoy self-assigned this Feb 20, 2025
Copy link
Contributor

@rsebille rsebille left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je n'ai pas lancé le DAG pour le moment puisque on n'a pas de credentials partagés, mais j'ai bien vu la note dans le ticket notion donc je pense qu'on va créer un tech.pilotage@, ça devrais suffire ou il faut autre chose à ton avis ?

FT_API_BASE_URL = "https://api.francetravail.io/partenaire/stats-offres-demandes-emploi/v1"


Territory = namedtuple("Territory", ["type", "code"])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je pense qu'ici tu pourrais faire une dataclass car automatiquement tu auras quelque chose d'utilisable pour les log sans avoir à le faire toi même avec %s (%s) :

In [17]: @dataclasses.dataclass
    ...: class Territory:
    ...:     type: str
    ...:     code: str
    ...: 

In [18]: str(Territory("DEP", "75"))
Out[18]: "Territory(type='DEP', code='75')"

Comment on lines +24 to +29
codeCaract = Column(String, primary_key=True)
codeNomenclature = Column(String, primary_key=True)
codePeriode = Column(String, primary_key=True)
codeTerritoire = Column(String, primary_key=True)
codeTypeCaract = Column(String, primary_key=True)
codeTypeTerritoire = Column(String, primary_key=True)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sauf si il y a une raison précise c'est mieux de garder les champs associés côte à côte :

Suggested change
codeCaract = Column(String, primary_key=True)
codeNomenclature = Column(String, primary_key=True)
codePeriode = Column(String, primary_key=True)
codeTerritoire = Column(String, primary_key=True)
codeTypeCaract = Column(String, primary_key=True)
codeTypeTerritoire = Column(String, primary_key=True)
codeCaract = Column(String, primary_key=True)
codeTypeCaract = Column(String, primary_key=True)
codeNomenclature = Column(String, primary_key=True)
codePeriode = Column(String, primary_key=True)
codeTerritoire = Column(String, primary_key=True)
codeTypeTerritoire = Column(String, primary_key=True)

Et pour tout ce qui est clés primaire, clés unique, ou indexe c'est toujours bien de lister les champs dans l'ordre où on va drill down [1], ce qui si j'ai bien compris devrais donner quelque chose comme ceci :

Suggested change
codeCaract = Column(String, primary_key=True)
codeNomenclature = Column(String, primary_key=True)
codePeriode = Column(String, primary_key=True)
codeTerritoire = Column(String, primary_key=True)
codeTypeCaract = Column(String, primary_key=True)
codeTypeTerritoire = Column(String, primary_key=True)
codeNomenclature = Column(String, primary_key=True)
codeTypeCaract = Column(String, primary_key=True)
codeCaract = Column(String, primary_key=True)
codeTypeTerritoire = Column(String, primary_key=True)
codeTerritoire = Column(String, primary_key=True)
codePeriode = Column(String, primary_key=True)

[1] https://use-the-index-luke.com/sql/where-clause/the-equals-operator/concatenated-keys

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C'est une bonne astuce, merci pour le lien

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Côté performance je note que chaque fois on lance le DAG, chaque règle va normalement avoir des conflits avec un autre règle 5/6 des colonnes principales. C'est donc garanti qu'on va traverser l'indice entière

L'alternatif pourrait être de créer un nouveau colonne, id non-composite, qui concat ces champs dans un valuer

Comment on lines +68 to +76
- name: france_travail_marche_travail
schema: france_travail
description: >
Nombre et % de demandeurs par catégories x caractéristiques, % par activités (Rome, Compétence), par territoire.
tables:
- name: job_seeker_stats
description: >
Table qui stocke des statistiques sur les chercheurs d'emploi du API France Travail. Les statistiques sont organisées
par territoire, characteristique, activité et période, selon le schema du API.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

De mémoire c'est pour dbt doc, donc oui c'est utile mais après ce n'est peut-être pas utilisé 😁

@calummackervoy
Copy link
Contributor Author

calummackervoy commented Mar 4, 2025

@rsebille tu peux trouver les creds ici : https://github.com/gip-inclusion/itou-secrets/pull/127. Je vais créer un compte partagé et les remplacer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants