Import
Objectif
Le module import introduit des données externes dans outsend en tant que source de pipeline. Il est interne au pipeline — voir /docs/fr/concepts/pipeline-orchestration — et produit une liste de POI normalisée que les nœuds d'enrichissement, de vérification ou de traitement en aval peuvent consommer. Contrairement à scrap, import ne consomme aucun quota d'extraction (coût EF nul).
Entrées
La configuration du nœud expose un seul discriminateur, source, avec trois modes mutuellement exclusifs.
| Champ | Type | Requis | Description |
|---|---|---|---|
source |
"paste" | "url" | "from_job" |
oui | Sélectionne lequel des trois canaux d'entrée ci-dessous s'applique. Vaut paste par défaut si omis. |
text |
chaîne | si source = paste |
Contenu CSV brut. Lu uniquement en mode paste. |
url |
chaîne | si source = url |
URL publique de tableur. Lue uniquement en mode url. |
from_job_id |
chaîne | si source = from_job |
UUID d'un job de scrap existant appartenant à l'appelant. Lu uniquement en mode from_job. |
paste — CSV en ligne
La charge utile text est parsée comme CSV par la couche de résolution partagée (app/column_map.py). Le délimiteur est auto-détecté (virgule, point-virgule ou tabulation) ; UTF-8 est attendu, avec UTF-8 BOM et Latin-1 / cp1252 acceptés en repli. Les en-têtes ne sont pas obligatoires : les noms de colonnes sont reconnus de façon souple via des alias acceptés (un en-tête Website, url, e-mail ou raison sociale est mappé vers la bonne colonne canonique), et un fichier sans en-tête est auto-détecté — ses colonnes sont alors déduites de leur contenu. Dans tous les cas l'import émet une notice (bannière d'info sur la page du job, ⓘ au dashboard) indiquant ce qui a été auto-mappé, déduit ou ignoré, pour que le mapping ne soit jamais silencieux.
url — tableur public
La charge utile url pointe vers un tableur lisible publiquement (forme typique : https://docs.google.com/spreadsheets/d/.../edit#gid=0). La feuille doit être partagée en « toute personne ayant le lien peut consulter » — outsend ne s'authentifie pas auprès de fournisseurs tiers. Le contenu récupéré est parsé selon les mêmes règles CSV que paste.
from_job — réutilisation d'un scrap récent
La charge utile from_job_id référence un job scrap antérieur. La référence est validée côté serveur à la création du job :
| Contrainte | Règle |
|---|---|
| Existence | L'ID du job doit correspondre à un job existant. |
| Propriété | L'appelant doit être propriétaire du job source. |
| Type de job | Doit être scrap. Les autres types ne peuvent pas être réimportés par ce canal. |
| Disponibilité | Le CSV source doit encore être téléchargeable (is_download_available). |
| Fraîcheur | Le job source doit avoir moins de 7 jours. |
Lorsque la validation passe, l'import résultant hérite de toutes les colonnes produites par le scrap source.
Sorties
import produit une liste de POI normalisée, déclarée dans le registre du pipeline avec output: "pois" — la même forme que celle émise par scrap. Les nœuds en aval qui acceptent pois_any (reviews, emails, socials, dead-check, techstack, ads-intelligence, brand-assets) s'enchaînent directement. Les nœuds qui exigent pois_email (verify) ne s'enchaînent que si le CSV importé porte déjà une colonne email.
L'ensemble de colonnes est dynamique : il reflète ce que fournit la source. Le registre déclare needs: [] et produces: [] pour cette raison — le module est permissif en entrée et propage le schéma d'entrée en sortie.
Cycle de vie
Cycle de vie de job standard — voir /docs/fr/concepts/jobs-lifecycle. Le job est rattaché à son pipeline via pipeline_id et pipeline_node_id et s'exécute dès que le pipeline passe à running.
Pipeline
import est un nœud racine. Il n'accepte aucun arc amont. Tout nœud dont l'input est pois_any, any_pois ou pois_email (quand le CSV porte des emails) peut être câblé en aval.
| Direction | Types compatibles |
|---|---|
| Amont | aucun — import est un ROOT_TYPE aux côtés de scrap |
| Aval | reviews, emails, verify (avec colonne email), socials, dead_check, techstack, ads_intelligence, brand_assets, filter, sort |
Registre : needs: [], produces: [].
Endpoints
Le module import n'est pas exposé comme endpoint de job autonome — il est interne au pipeline (voir /docs/fr/concepts/pipeline-orchestration) et créé uniquement comme racine de pipeline. Deux endpoints adjacents sont utiles lors de l'assemblage d'un import :
| Méthode | Chemin | Rôle |
|---|---|---|
POST |
/api/jobs/parse-list |
Valide l'entrée CSV avant soumission. Accepte soit un JSON {"text": "..."}, soit un envoi multipart/form-data avec un champ file. Renvoie {count, with_lien_google_maps, with_site_web, sample, items, delimiter}. |
GET |
/api/jobs/{job_id}/items |
Renvoie les lignes CSV d'un job scrap terminé dans une structure adaptée à la réutilisation from_job. |
La charge utile du nœud de pipeline suit cette forme :
{
"type": "import",
"config": {
"source": "paste",
"text": "nom,site_web\n...",
"url": "",
"from_job_id": ""
}
}
Exactement un parmi text, url, from_job_id est lu, déterminé par source. Les champs inutilisés sont persistés sous forme de chaînes vides.
Limites
Limites globales — voir /docs/fr/concepts/limits. Spécifiques au module :
| Limite | Valeur |
|---|---|
Fraîcheur from_job |
7 jours. Le job source est rejeté au-delà. |
Type source from_job |
scrap uniquement. |
| Formats pris en charge | CSV avec délimiteur virgule, point-virgule ou tabulation. Encodages : UTF-8 (préféré), UTF-8 avec BOM, Latin-1 / cp1252 (repli). En-têtes facultatifs — un fichier sans en-tête est auto-détecté et ses colonnes déduites du contenu. |
Erreurs
| Condition | Surface | Forme du message |
|---|---|---|
source hors de {paste, url, from_job} |
Création du pipeline | Source d'import invalide : <value> (attendu: paste \| url \| from_job) |
from_job sans from_job_id |
Création du pipeline | Source 'from_job' : aucun job sélectionné |
from_job_id inconnu |
Création du pipeline | Job source introuvable : <id> |
Source from_job non détenue par l'appelant |
Création du pipeline | Job source non autorisé pour cet utilisateur |
Source from_job n'étant pas un scrap |
Création du pipeline | Seuls les scraps Gmaps peuvent être importés via 'from_job' |
CSV source from_job indisponible |
Création du pipeline | Le CSV du job source n'est pas (ou plus) disponible |
Source from_job de plus de 7 jours |
Création du pipeline | Le job source a plus de 7 jours — relancez un scrap ou collez le CSV. |
| Charge utile paste vide | parse-list |
HTTP 400, Aucun texte fourni |
| Échec de parsing CSV | parse-list |
HTTP 400, CSV invalide: <detail> |
| Zéro ligne parsée | parse-list |
HTTP 400, Aucune ligne lue dans le CSV |
| Envoi multipart sans fichier | parse-list |
HTTP 400, Aucun fichier fourni |
| URL injoignable ou réponse non CSV | Exécution du pipeline | Le job d'import passe à failed ; le message nomme la source injoignable. |
| Tableur privé (page de connexion renvoyée au lieu du CSV) | Exécution du pipeline | L'import échoue franchement avec une explication au lieu de réussir silencieusement — le contenu était du HTML (page de connexion), pas du CSV. Partagez la feuille en « toute personne ayant le lien peut consulter ». |
| Vide, en-tête seul, ou rien d'exploitable | Exécution du pipeline / parse-list |
L'import échoue avec une explication (aucune ligne exploitable) plutôt que d'annoncer un faux succès. |
Et après
| Module | Usage |
|---|---|
| filter | Restreindre la liste importée par des prédicats de colonnes avant de payer l'enrichissement en aval. |
| sort | Ordonner la liste importée — utile combiné à des limites de lignes dans les étapes ultérieures. |