claudient 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +42 -0
- package/CONTEXT.md +58 -0
- package/README.md +165 -0
- package/agents/build-resolvers/de/python-resolver.md +64 -0
- package/agents/build-resolvers/de/typescript-resolver.md +65 -0
- package/agents/build-resolvers/es/python-resolver.md +64 -0
- package/agents/build-resolvers/es/typescript-resolver.md +65 -0
- package/agents/build-resolvers/fr/python-resolver.md +64 -0
- package/agents/build-resolvers/fr/typescript-resolver.md +65 -0
- package/agents/build-resolvers/nl/python-resolver.md +64 -0
- package/agents/build-resolvers/nl/typescript-resolver.md +65 -0
- package/agents/build-resolvers/python-resolver.md +62 -0
- package/agents/build-resolvers/typescript-resolver.md +63 -0
- package/agents/core/architect.md +64 -0
- package/agents/core/code-reviewer.md +78 -0
- package/agents/core/de/architect.md +66 -0
- package/agents/core/de/code-reviewer.md +80 -0
- package/agents/core/de/planner.md +63 -0
- package/agents/core/de/security-reviewer.md +93 -0
- package/agents/core/es/architect.md +66 -0
- package/agents/core/es/code-reviewer.md +80 -0
- package/agents/core/es/planner.md +63 -0
- package/agents/core/es/security-reviewer.md +93 -0
- package/agents/core/fr/architect.md +66 -0
- package/agents/core/fr/code-reviewer.md +80 -0
- package/agents/core/fr/planner.md +63 -0
- package/agents/core/fr/security-reviewer.md +93 -0
- package/agents/core/nl/architect.md +66 -0
- package/agents/core/nl/code-reviewer.md +80 -0
- package/agents/core/nl/planner.md +63 -0
- package/agents/core/nl/security-reviewer.md +93 -0
- package/agents/core/planner.md +61 -0
- package/agents/core/security-reviewer.md +91 -0
- package/guides/agent-orchestration.md +231 -0
- package/guides/de/agent-orchestration.md +174 -0
- package/guides/de/getting-started.md +164 -0
- package/guides/de/hooks-cookbook.md +160 -0
- package/guides/de/memory-management.md +153 -0
- package/guides/de/security.md +180 -0
- package/guides/de/skill-authoring.md +214 -0
- package/guides/de/token-optimization.md +156 -0
- package/guides/es/agent-orchestration.md +174 -0
- package/guides/es/getting-started.md +164 -0
- package/guides/es/hooks-cookbook.md +160 -0
- package/guides/es/memory-management.md +153 -0
- package/guides/es/security.md +180 -0
- package/guides/es/skill-authoring.md +214 -0
- package/guides/es/token-optimization.md +156 -0
- package/guides/fr/agent-orchestration.md +174 -0
- package/guides/fr/getting-started.md +164 -0
- package/guides/fr/hooks-cookbook.md +227 -0
- package/guides/fr/memory-management.md +169 -0
- package/guides/fr/security.md +180 -0
- package/guides/fr/skill-authoring.md +214 -0
- package/guides/fr/token-optimization.md +158 -0
- package/guides/getting-started.md +164 -0
- package/guides/hooks-cookbook.md +423 -0
- package/guides/memory-management.md +192 -0
- package/guides/nl/agent-orchestration.md +174 -0
- package/guides/nl/getting-started.md +164 -0
- package/guides/nl/hooks-cookbook.md +160 -0
- package/guides/nl/memory-management.md +153 -0
- package/guides/nl/security.md +180 -0
- package/guides/nl/skill-authoring.md +214 -0
- package/guides/nl/token-optimization.md +156 -0
- package/guides/security.md +229 -0
- package/guides/skill-authoring.md +226 -0
- package/guides/token-optimization.md +169 -0
- package/hooks/lifecycle/cost-tracker.md +49 -0
- package/hooks/lifecycle/cost-tracker.sh +59 -0
- package/hooks/lifecycle/pre-compact-save.md +56 -0
- package/hooks/lifecycle/pre-compact-save.sh +37 -0
- package/hooks/lifecycle/session-start.md +50 -0
- package/hooks/lifecycle/session-start.sh +47 -0
- package/hooks/post-tool-use/audit-log.md +53 -0
- package/hooks/post-tool-use/audit-log.sh +53 -0
- package/hooks/post-tool-use/prettier.md +53 -0
- package/hooks/post-tool-use/prettier.sh +49 -0
- package/hooks/pre-tool-use/block-dangerous.md +48 -0
- package/hooks/pre-tool-use/block-dangerous.sh +76 -0
- package/hooks/pre-tool-use/git-push-confirm.md +46 -0
- package/hooks/pre-tool-use/git-push-confirm.sh +36 -0
- package/mcp/configs/github.json +11 -0
- package/mcp/configs/postgres.json +11 -0
- package/mcp/de/recommended-servers.md +170 -0
- package/mcp/es/recommended-servers.md +170 -0
- package/mcp/fr/recommended-servers.md +170 -0
- package/mcp/nl/recommended-servers.md +170 -0
- package/mcp/recommended-servers.md +168 -0
- package/package.json +45 -0
- package/prompts/project-starters/de/fastapi-project.md +62 -0
- package/prompts/project-starters/de/nextjs-project.md +82 -0
- package/prompts/project-starters/es/fastapi-project.md +62 -0
- package/prompts/project-starters/es/nextjs-project.md +82 -0
- package/prompts/project-starters/fastapi-project.md +60 -0
- package/prompts/project-starters/fr/fastapi-project.md +62 -0
- package/prompts/project-starters/fr/nextjs-project.md +82 -0
- package/prompts/project-starters/nextjs-project.md +80 -0
- package/prompts/project-starters/nl/fastapi-project.md +62 -0
- package/prompts/project-starters/nl/nextjs-project.md +82 -0
- package/prompts/system-prompts/ai-product.md +80 -0
- package/prompts/system-prompts/data-pipeline.md +76 -0
- package/prompts/system-prompts/de/ai-product.md +82 -0
- package/prompts/system-prompts/de/data-pipeline.md +78 -0
- package/prompts/system-prompts/de/saas-backend.md +71 -0
- package/prompts/system-prompts/es/ai-product.md +82 -0
- package/prompts/system-prompts/es/data-pipeline.md +78 -0
- package/prompts/system-prompts/es/saas-backend.md +71 -0
- package/prompts/system-prompts/fr/ai-product.md +82 -0
- package/prompts/system-prompts/fr/data-pipeline.md +78 -0
- package/prompts/system-prompts/fr/saas-backend.md +71 -0
- package/prompts/system-prompts/nl/ai-product.md +82 -0
- package/prompts/system-prompts/nl/data-pipeline.md +78 -0
- package/prompts/system-prompts/nl/saas-backend.md +71 -0
- package/prompts/system-prompts/saas-backend.md +69 -0
- package/prompts/task-specific/changelog.md +81 -0
- package/prompts/task-specific/de/changelog.md +83 -0
- package/prompts/task-specific/de/debugging.md +78 -0
- package/prompts/task-specific/de/pr-description.md +69 -0
- package/prompts/task-specific/debugging.md +76 -0
- package/prompts/task-specific/es/changelog.md +83 -0
- package/prompts/task-specific/es/debugging.md +78 -0
- package/prompts/task-specific/es/pr-description.md +69 -0
- package/prompts/task-specific/fr/changelog.md +83 -0
- package/prompts/task-specific/fr/debugging.md +78 -0
- package/prompts/task-specific/fr/pr-description.md +69 -0
- package/prompts/task-specific/nl/changelog.md +83 -0
- package/prompts/task-specific/nl/debugging.md +78 -0
- package/prompts/task-specific/nl/pr-description.md +69 -0
- package/prompts/task-specific/pr-description.md +67 -0
- package/rules/common/coding-style.md +45 -0
- package/rules/common/de/coding-style.md +47 -0
- package/rules/common/de/git.md +48 -0
- package/rules/common/de/performance.md +40 -0
- package/rules/common/de/security.md +45 -0
- package/rules/common/de/testing.md +45 -0
- package/rules/common/es/coding-style.md +47 -0
- package/rules/common/es/git.md +48 -0
- package/rules/common/es/performance.md +40 -0
- package/rules/common/es/security.md +45 -0
- package/rules/common/es/testing.md +45 -0
- package/rules/common/fr/coding-style.md +47 -0
- package/rules/common/fr/git.md +48 -0
- package/rules/common/fr/performance.md +40 -0
- package/rules/common/fr/security.md +45 -0
- package/rules/common/fr/testing.md +45 -0
- package/rules/common/git.md +46 -0
- package/rules/common/nl/coding-style.md +47 -0
- package/rules/common/nl/git.md +48 -0
- package/rules/common/nl/performance.md +40 -0
- package/rules/common/nl/security.md +45 -0
- package/rules/common/nl/testing.md +45 -0
- package/rules/common/performance.md +38 -0
- package/rules/common/security.md +43 -0
- package/rules/common/testing.md +43 -0
- package/rules/language-specific/de/go.md +48 -0
- package/rules/language-specific/de/python.md +38 -0
- package/rules/language-specific/de/typescript.md +51 -0
- package/rules/language-specific/es/go.md +48 -0
- package/rules/language-specific/es/python.md +38 -0
- package/rules/language-specific/es/typescript.md +51 -0
- package/rules/language-specific/fr/go.md +48 -0
- package/rules/language-specific/fr/python.md +38 -0
- package/rules/language-specific/fr/typescript.md +51 -0
- package/rules/language-specific/go.md +46 -0
- package/rules/language-specific/nl/go.md +48 -0
- package/rules/language-specific/nl/python.md +38 -0
- package/rules/language-specific/nl/typescript.md +51 -0
- package/rules/language-specific/python.md +36 -0
- package/rules/language-specific/typescript.md +49 -0
- package/scripts/cli.js +161 -0
- package/scripts/link-skills.sh +35 -0
- package/scripts/list-skills.sh +34 -0
- package/skills/ai-engineering/agent-construction.md +285 -0
- package/skills/ai-engineering/claude-api.md +248 -0
- package/skills/ai-engineering/de/agent-construction.md +287 -0
- package/skills/ai-engineering/de/claude-api.md +250 -0
- package/skills/ai-engineering/es/agent-construction.md +287 -0
- package/skills/ai-engineering/es/claude-api.md +250 -0
- package/skills/ai-engineering/fr/agent-construction.md +287 -0
- package/skills/ai-engineering/fr/claude-api.md +250 -0
- package/skills/ai-engineering/nl/agent-construction.md +287 -0
- package/skills/ai-engineering/nl/claude-api.md +250 -0
- package/skills/backend/dotnet/csharp.md +304 -0
- package/skills/backend/dotnet/de/csharp.md +306 -0
- package/skills/backend/dotnet/es/csharp.md +306 -0
- package/skills/backend/dotnet/fr/csharp.md +306 -0
- package/skills/backend/dotnet/nl/csharp.md +306 -0
- package/skills/backend/go/de/go.md +307 -0
- package/skills/backend/go/es/go.md +307 -0
- package/skills/backend/go/fr/go.md +307 -0
- package/skills/backend/go/go.md +305 -0
- package/skills/backend/go/nl/go.md +307 -0
- package/skills/backend/nodejs/de/nestjs.md +274 -0
- package/skills/backend/nodejs/de/nextjs.md +222 -0
- package/skills/backend/nodejs/es/nestjs.md +274 -0
- package/skills/backend/nodejs/es/nextjs.md +222 -0
- package/skills/backend/nodejs/fr/nestjs.md +274 -0
- package/skills/backend/nodejs/fr/nextjs.md +222 -0
- package/skills/backend/nodejs/nestjs.md +272 -0
- package/skills/backend/nodejs/nextjs.md +220 -0
- package/skills/backend/nodejs/nl/nestjs.md +274 -0
- package/skills/backend/nodejs/nl/nextjs.md +222 -0
- package/skills/backend/python/de/django.md +285 -0
- package/skills/backend/python/de/fastapi.md +244 -0
- package/skills/backend/python/django.md +283 -0
- package/skills/backend/python/es/django.md +285 -0
- package/skills/backend/python/es/fastapi.md +244 -0
- package/skills/backend/python/fastapi.md +242 -0
- package/skills/backend/python/fr/django.md +285 -0
- package/skills/backend/python/fr/fastapi.md +244 -0
- package/skills/backend/python/nl/django.md +285 -0
- package/skills/backend/python/nl/fastapi.md +244 -0
- package/skills/data-ml/dbt-data-pipelines.md +155 -0
- package/skills/data-ml/de/dbt-data-pipelines.md +157 -0
- package/skills/data-ml/de/pandas-polars.md +147 -0
- package/skills/data-ml/de/pytorch-tensorflow.md +171 -0
- package/skills/data-ml/es/dbt-data-pipelines.md +157 -0
- package/skills/data-ml/es/pandas-polars.md +147 -0
- package/skills/data-ml/es/pytorch-tensorflow.md +171 -0
- package/skills/data-ml/fr/dbt-data-pipelines.md +157 -0
- package/skills/data-ml/fr/pandas-polars.md +147 -0
- package/skills/data-ml/fr/pytorch-tensorflow.md +171 -0
- package/skills/data-ml/nl/dbt-data-pipelines.md +157 -0
- package/skills/data-ml/nl/pandas-polars.md +147 -0
- package/skills/data-ml/nl/pytorch-tensorflow.md +171 -0
- package/skills/data-ml/pandas-polars.md +145 -0
- package/skills/data-ml/pytorch-tensorflow.md +169 -0
- package/skills/database/de/graphql.md +181 -0
- package/skills/database/es/graphql.md +181 -0
- package/skills/database/fr/graphql.md +181 -0
- package/skills/database/graphql.md +179 -0
- package/skills/database/nl/graphql.md +181 -0
- package/skills/devops-infra/de/docker.md +133 -0
- package/skills/devops-infra/de/github-actions.md +179 -0
- package/skills/devops-infra/de/kubernetes.md +129 -0
- package/skills/devops-infra/de/terraform.md +130 -0
- package/skills/devops-infra/docker.md +131 -0
- package/skills/devops-infra/es/docker.md +133 -0
- package/skills/devops-infra/es/github-actions.md +179 -0
- package/skills/devops-infra/es/kubernetes.md +129 -0
- package/skills/devops-infra/es/terraform.md +130 -0
- package/skills/devops-infra/fr/docker.md +133 -0
- package/skills/devops-infra/fr/github-actions.md +179 -0
- package/skills/devops-infra/fr/kubernetes.md +129 -0
- package/skills/devops-infra/fr/terraform.md +130 -0
- package/skills/devops-infra/github-actions.md +177 -0
- package/skills/devops-infra/kubernetes.md +127 -0
- package/skills/devops-infra/nl/docker.md +133 -0
- package/skills/devops-infra/nl/github-actions.md +179 -0
- package/skills/devops-infra/nl/kubernetes.md +129 -0
- package/skills/devops-infra/nl/terraform.md +130 -0
- package/skills/devops-infra/terraform.md +128 -0
- package/skills/finance-payments/de/stripe.md +187 -0
- package/skills/finance-payments/es/stripe.md +187 -0
- package/skills/finance-payments/fr/stripe.md +187 -0
- package/skills/finance-payments/nl/stripe.md +187 -0
- package/skills/finance-payments/stripe.md +185 -0
- package/workflows/code-review.md +151 -0
- package/workflows/de/code-review.md +153 -0
- package/workflows/de/debugging-session.md +146 -0
- package/workflows/de/feature-development.md +155 -0
- package/workflows/de/new-project-bootstrap.md +175 -0
- package/workflows/de/refactor-safely.md +150 -0
- package/workflows/debugging-session.md +144 -0
- package/workflows/es/code-review.md +153 -0
- package/workflows/es/debugging-session.md +146 -0
- package/workflows/es/feature-development.md +155 -0
- package/workflows/es/new-project-bootstrap.md +175 -0
- package/workflows/es/refactor-safely.md +150 -0
- package/workflows/feature-development.md +153 -0
- package/workflows/fr/code-review.md +153 -0
- package/workflows/fr/debugging-session.md +146 -0
- package/workflows/fr/feature-development.md +155 -0
- package/workflows/fr/new-project-bootstrap.md +175 -0
- package/workflows/fr/refactor-safely.md +150 -0
- package/workflows/new-project-bootstrap.md +173 -0
- package/workflows/nl/code-review.md +153 -0
- package/workflows/nl/debugging-session.md +146 -0
- package/workflows/nl/feature-development.md +155 -0
- package/workflows/nl/new-project-bootstrap.md +175 -0
- package/workflows/nl/refactor-safely.md +150 -0
- package/workflows/refactor-safely.md +148 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
> 🇫🇷 This is the French translation. [English version](../dbt-data-pipelines.md).
|
|
2
|
+
|
|
3
|
+
# Compétence dbt Data Pipelines
|
|
4
|
+
|
|
5
|
+
## Quand activer
|
|
6
|
+
- Rédiger des modèles dbt (couches staging, intermediate, mart)
|
|
7
|
+
- Configurer les sources, refs et dépendances dbt
|
|
8
|
+
- Rédiger des tests dbt (tests de schéma, tests singuliers, tests génériques personnalisés)
|
|
9
|
+
- Configurer la structure d'un projet dbt pour un nouvel entrepôt de données
|
|
10
|
+
- Rédiger des macros dbt pour de la logique SQL réutilisable
|
|
11
|
+
- Configurer la documentation et les contrôles de fraîcheur dbt
|
|
12
|
+
- Déboguer des erreurs de compilation dbt ou des runs de modèles en échec
|
|
13
|
+
- Configurer dbt avec BigQuery, Snowflake, Redshift, ou DuckDB
|
|
14
|
+
|
|
15
|
+
## Quand NE PAS utiliser
|
|
16
|
+
- Pipelines ETL bruts sans entrepôt (utiliser Airflow, Prefect, ou Dagster à la place)
|
|
17
|
+
- Données en streaming temps réel (dbt est uniquement batch)
|
|
18
|
+
- Transformations en mémoire Pandas/Polars (utiliser la compétence pandas-polars)
|
|
19
|
+
- Ingestion de données (dbt transforme, il n'ingère pas)
|
|
20
|
+
|
|
21
|
+
## Instructions
|
|
22
|
+
|
|
23
|
+
### Architecture en couches du projet
|
|
24
|
+
Toujours séparer les modèles en trois couches :
|
|
25
|
+
```
|
|
26
|
+
models/
|
|
27
|
+
├── staging/ ← 1:1 avec les tables sources. Nettoyage léger seulement. Pas de jointures.
|
|
28
|
+
│ ├── stg_orders.sql
|
|
29
|
+
│ └── stg_customers.sql
|
|
30
|
+
├── intermediate/ ← Logique métier. Jointures autorisées. Pas exposé aux outils BI.
|
|
31
|
+
│ └── int_orders_with_customers.sql
|
|
32
|
+
└── marts/ ← Entités métier finales. Exposées au BI. Les agrégations sont ici.
|
|
33
|
+
├── finance/
|
|
34
|
+
│ └── fct_revenue.sql
|
|
35
|
+
└── marketing/
|
|
36
|
+
└── dim_customers.sql
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Règles pour le staging :**
|
|
40
|
+
- Renommer les colonnes selon les conventions du projet (snake_case)
|
|
41
|
+
- Caster les types explicitement
|
|
42
|
+
- Pas de logique métier — pas de jointures, pas d'agrégations
|
|
43
|
+
- Préfixer avec `stg_`
|
|
44
|
+
|
|
45
|
+
**Règles pour les marts :**
|
|
46
|
+
- Préfixe `fct_` pour les tables de faits (événements, transactions)
|
|
47
|
+
- Préfixe `dim_` pour les tables de dimensions (clients, produits)
|
|
48
|
+
- Toujours documenter dans schema.yml
|
|
49
|
+
|
|
50
|
+
### Configuration des modèles
|
|
51
|
+
```sql
|
|
52
|
+
-- models/marts/finance/fct_revenue.sql
|
|
53
|
+
{{
|
|
54
|
+
config(
|
|
55
|
+
materialized='incremental',
|
|
56
|
+
unique_key='order_id',
|
|
57
|
+
on_schema_change='fail'
|
|
58
|
+
)
|
|
59
|
+
}}
|
|
60
|
+
|
|
61
|
+
with orders as (
|
|
62
|
+
select * from {{ ref('int_orders_with_customers') }}
|
|
63
|
+
{% if is_incremental() %}
|
|
64
|
+
where created_at > (select max(created_at) from {{ this }})
|
|
65
|
+
{% endif %}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
select
|
|
69
|
+
order_id,
|
|
70
|
+
customer_id,
|
|
71
|
+
amount,
|
|
72
|
+
created_at
|
|
73
|
+
from orders
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Choix de matérialisation :**
|
|
77
|
+
- `view` : par défaut — bon pour les modèles staging et intermediate
|
|
78
|
+
- `table` : pour les requêtes coûteuses interrogées fréquemment
|
|
79
|
+
- `incremental` : pour les grandes tables de faits qui croissent au fil du temps
|
|
80
|
+
- `ephemeral` : CTEs, pas matérialisés — à utiliser pour les transformations simples appelées une seule fois
|
|
81
|
+
|
|
82
|
+
### Tests — requis sur chaque modèle mart
|
|
83
|
+
```yaml
|
|
84
|
+
# models/marts/finance/schema.yml
|
|
85
|
+
version: 2
|
|
86
|
+
|
|
87
|
+
models:
|
|
88
|
+
- name: fct_revenue
|
|
89
|
+
description: "Une ligne par commande complétée"
|
|
90
|
+
columns:
|
|
91
|
+
- name: order_id
|
|
92
|
+
description: "Clé primaire"
|
|
93
|
+
tests:
|
|
94
|
+
- unique
|
|
95
|
+
- not_null
|
|
96
|
+
- name: customer_id
|
|
97
|
+
tests:
|
|
98
|
+
- not_null
|
|
99
|
+
- relationships:
|
|
100
|
+
to: ref('dim_customers')
|
|
101
|
+
field: customer_id
|
|
102
|
+
- name: amount
|
|
103
|
+
tests:
|
|
104
|
+
- not_null
|
|
105
|
+
- dbt_utils.accepted_range:
|
|
106
|
+
min_value: 0
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Tests minimum sur chaque modèle mart : `unique` + `not_null` sur la clé primaire, `not_null` sur les clés étrangères critiques.
|
|
110
|
+
|
|
111
|
+
### Configuration des sources
|
|
112
|
+
```yaml
|
|
113
|
+
# models/staging/sources.yml
|
|
114
|
+
version: 2
|
|
115
|
+
|
|
116
|
+
sources:
|
|
117
|
+
- name: raw_stripe
|
|
118
|
+
database: raw
|
|
119
|
+
schema: stripe
|
|
120
|
+
freshness:
|
|
121
|
+
warn_after: {count: 12, period: hour}
|
|
122
|
+
error_after: {count: 24, period: hour}
|
|
123
|
+
loaded_at_field: _ingested_at
|
|
124
|
+
tables:
|
|
125
|
+
- name: charges
|
|
126
|
+
description: "Charges Stripe brutes depuis Fivetran"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Toujours définir `freshness` sur les sources — les données sources périmées sont un échec silencieux.
|
|
130
|
+
|
|
131
|
+
### Macros pour la logique réutilisable
|
|
132
|
+
```sql
|
|
133
|
+
-- macros/cents_to_dollars.sql
|
|
134
|
+
{% macro cents_to_dollars(column_name) %}
|
|
135
|
+
({{ column_name }} / 100.0)::numeric(10, 2)
|
|
136
|
+
{% endmacro %}
|
|
137
|
+
|
|
138
|
+
-- Utilisation dans un modèle
|
|
139
|
+
select
|
|
140
|
+
{{ cents_to_dollars('amount_cents') }} as amount_dollars
|
|
141
|
+
from orders
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Exemple
|
|
145
|
+
|
|
146
|
+
**Utilisateur :** Créer des modèles staging et mart pour les données de paiements Stripe (charges, remboursements) avec tests et contrôles de fraîcheur.
|
|
147
|
+
|
|
148
|
+
**Sortie attendue :**
|
|
149
|
+
- `models/staging/stripe/sources.yml` — source avec contrôle de fraîcheur sur `_ingested_at`
|
|
150
|
+
- `models/staging/stripe/stg_stripe_charges.sql` — renommer, caster, pas de jointures
|
|
151
|
+
- `models/staging/stripe/stg_stripe_refunds.sql` — même pattern
|
|
152
|
+
- `models/marts/finance/fct_payments.sql` — joindre charges + remboursements, montant net, matérialisation incrémentale
|
|
153
|
+
- `models/marts/finance/schema.yml` — `unique` + `not_null` sur `charge_id`, test de relation sur `customer_id`
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
> **Travaillez avec nous :** Claudient est soutenu par [Uitbreiden](https://uitbreiden.com/) — nous construisons des produits IA et des solutions B2B avec des communautés de développeurs. Vous construisez des pipelines de données pour des produits IA ou analytiques ? [uitbreiden.com](https://uitbreiden.com/)
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
> 🇫🇷 This is the French translation. [English version](../pandas-polars.md).
|
|
2
|
+
|
|
3
|
+
# Compétence Pandas / Polars
|
|
4
|
+
|
|
5
|
+
## Quand activer
|
|
6
|
+
- Nettoyer, transformer ou agréger des données tabulaires en Python
|
|
7
|
+
- Fusionner, joindre ou remodeler des DataFrames
|
|
8
|
+
- Rédiger des validations ou des contrôles de qualité de données
|
|
9
|
+
- Convertir entre des formats (CSV, Parquet, JSON, Excel)
|
|
10
|
+
- Profiler ou explorer un nouveau jeu de données
|
|
11
|
+
- Optimiser du code Pandas lent pour de grands jeux de données
|
|
12
|
+
- Migrer du code Pandas vers Polars pour les performances
|
|
13
|
+
|
|
14
|
+
## Quand NE PAS utiliser
|
|
15
|
+
- SQL dans une base de données (pousser les transformations vers la base quand les données y sont déjà)
|
|
16
|
+
- Spark/calcul distribué (utiliser la compétence PySpark pour les jeux de données > RAM disponible)
|
|
17
|
+
- Modèles dbt (transformations SQL dans un entrepôt de données)
|
|
18
|
+
- Opérations sur des tableaux NumPy pour des données non tabulaires
|
|
19
|
+
|
|
20
|
+
## Instructions
|
|
21
|
+
|
|
22
|
+
### Pandas — règles de performance
|
|
23
|
+
```python
|
|
24
|
+
import pandas as pd
|
|
25
|
+
import numpy as np
|
|
26
|
+
|
|
27
|
+
# Ne jamais utiliser iterrows() — vectoriser à la place
|
|
28
|
+
# Mauvais :
|
|
29
|
+
for idx, row in df.iterrows():
|
|
30
|
+
df.at[idx, 'tax'] = row['price'] * 0.2
|
|
31
|
+
|
|
32
|
+
# Bon :
|
|
33
|
+
df['tax'] = df['price'] * 0.2
|
|
34
|
+
|
|
35
|
+
# Utiliser .loc pour l'accès par label, .iloc pour l'accès par position
|
|
36
|
+
# Ne jamais chaîner sans assignation — cause SettingWithCopyWarning
|
|
37
|
+
df.loc[df['status'] == 'active', 'flag'] = True
|
|
38
|
+
|
|
39
|
+
# Type catégoriel pour les colonnes de chaînes à faible cardinalité (énorme économie mémoire)
|
|
40
|
+
df['country'] = df['country'].astype('category')
|
|
41
|
+
|
|
42
|
+
# Réduire les types numériques pour économiser la mémoire
|
|
43
|
+
df['quantity'] = pd.to_numeric(df['quantity'], downcast='integer')
|
|
44
|
+
df['price'] = pd.to_numeric(df['price'], downcast='float')
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Pandas — agrégation et groupby
|
|
48
|
+
```python
|
|
49
|
+
# Groupby avec plusieurs agrégations
|
|
50
|
+
summary = (
|
|
51
|
+
df.groupby(['region', 'category'])
|
|
52
|
+
.agg(
|
|
53
|
+
total_revenue=('revenue', 'sum'),
|
|
54
|
+
order_count=('order_id', 'nunique'),
|
|
55
|
+
avg_order_value=('revenue', 'mean'),
|
|
56
|
+
)
|
|
57
|
+
.reset_index()
|
|
58
|
+
.sort_values('total_revenue', ascending=False)
|
|
59
|
+
)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Pandas — fusion
|
|
63
|
+
```python
|
|
64
|
+
# Toujours spécifier how= explicitement — ne jamais se fier au défaut (inner)
|
|
65
|
+
result = pd.merge(
|
|
66
|
+
orders,
|
|
67
|
+
customers,
|
|
68
|
+
on='customer_id',
|
|
69
|
+
how='left', # explicite
|
|
70
|
+
validate='m:1', # valide la cardinalité — lève une exception si violée
|
|
71
|
+
suffixes=('_order', '_customer')
|
|
72
|
+
)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Polars — quand l'utiliser à la place de Pandas
|
|
76
|
+
Utiliser Polars quand :
|
|
77
|
+
- Jeu de données > 1M lignes (Polars est 5–100x plus rapide pour de nombreuses opérations)
|
|
78
|
+
- Vous avez besoin d'évaluation paresseuse (optimisation des requêtes avant exécution)
|
|
79
|
+
- Le parallélisme est important (Polars utilise tous les cœurs CPU par défaut)
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
import polars as pl
|
|
83
|
+
|
|
84
|
+
# API Lazy — les requêtes sont optimisées avant exécution
|
|
85
|
+
result = (
|
|
86
|
+
pl.scan_parquet("orders.parquet") # Scan paresseux — aucune donnée chargée encore
|
|
87
|
+
.filter(pl.col("status") == "completed")
|
|
88
|
+
.group_by(["region", "category"])
|
|
89
|
+
.agg([
|
|
90
|
+
pl.col("revenue").sum().alias("total_revenue"),
|
|
91
|
+
pl.col("order_id").n_unique().alias("order_count"),
|
|
92
|
+
pl.col("revenue").mean().alias("avg_order_value"),
|
|
93
|
+
])
|
|
94
|
+
.sort("total_revenue", descending=True)
|
|
95
|
+
.collect() # Exécuter maintenant
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Polars — expressions (pas d'indexation chaînée)
|
|
100
|
+
```python
|
|
101
|
+
# Polars : pas de SettingWithCopyWarning, pas d'indexation chaînée
|
|
102
|
+
df = df.with_columns([
|
|
103
|
+
(pl.col("price") * 0.2).alias("tax"),
|
|
104
|
+
pl.col("name").str.to_uppercase().alias("name_upper"),
|
|
105
|
+
pl.when(pl.col("quantity") > 10)
|
|
106
|
+
.then(pl.lit("bulk"))
|
|
107
|
+
.otherwise(pl.lit("standard"))
|
|
108
|
+
.alias("order_type"),
|
|
109
|
+
])
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Pattern de validation des données
|
|
113
|
+
```python
|
|
114
|
+
def validate_orders(df: pd.DataFrame) -> None:
|
|
115
|
+
assert df['order_id'].notna().all(), "order_id has nulls"
|
|
116
|
+
assert df['order_id'].is_unique, "order_id has duplicates"
|
|
117
|
+
assert (df['amount'] >= 0).all(), "amount has negative values"
|
|
118
|
+
assert df['status'].isin(['pending', 'completed', 'cancelled']).all(), "invalid status values"
|
|
119
|
+
assert pd.to_datetime(df['created_at'], errors='coerce').notna().all(), "created_at has invalid dates"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Conversion de format
|
|
123
|
+
```python
|
|
124
|
+
# Lecture
|
|
125
|
+
df = pd.read_parquet("data.parquet", columns=['id', 'name', 'amount']) # Sélection de colonnes à la lecture
|
|
126
|
+
df = pd.read_csv("data.csv", dtype={'id': str}, parse_dates=['created_at'])
|
|
127
|
+
|
|
128
|
+
# Écriture — toujours utiliser Parquet plutôt que CSV pour les grands jeux de données
|
|
129
|
+
df.to_parquet("output.parquet", index=False, compression='snappy')
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Exemple
|
|
133
|
+
|
|
134
|
+
**Utilisateur :** Nettoyer un CSV de commandes brutes : corriger les types, supprimer les doublons, gérer les valeurs nulles, ajouter des colonnes dérivées (revenue_after_tax, order_size_bucket) et produire un fichier Parquet validé.
|
|
135
|
+
|
|
136
|
+
**Sortie attendue :**
|
|
137
|
+
- Lecture avec `dtype=` et `parse_dates=` explicites
|
|
138
|
+
- Supprimer les lignes `order_id` en doublon (garder la dernière)
|
|
139
|
+
- Remplir les nulls : `quantity` → 0, `discount` → 0.0, supprimer les lignes où `customer_id` est null
|
|
140
|
+
- Dériver : `revenue_after_tax = price * quantity * (1 - discount) * 0.8`
|
|
141
|
+
- Bucket : `order_size_bucket` = 'small'/<100, 'medium'/100–1000, 'large'/>1000
|
|
142
|
+
- Valider avec des assertions avant l'écriture
|
|
143
|
+
- Écrire en Parquet avec compression snappy
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
> **Travaillez avec nous :** Claudient est soutenu par [Uitbreiden](https://uitbreiden.com/) — nous construisons des produits IA et des solutions B2B avec des communautés de développeurs. Vous construisez des pipelines de données ou des produits IA basés sur des données ? [uitbreiden.com](https://uitbreiden.com/)
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
> 🇫🇷 This is the French translation. [English version](../pytorch-tensorflow.md).
|
|
2
|
+
|
|
3
|
+
# Compétence PyTorch / TensorFlow
|
|
4
|
+
|
|
5
|
+
## Quand activer
|
|
6
|
+
- Rédiger des boucles d'entraînement de réseaux de neurones avec PyTorch
|
|
7
|
+
- Construire et entraîner des modèles Keras/TensorFlow
|
|
8
|
+
- Implémenter des fonctions de perte personnalisées ou des architectures de modèles
|
|
9
|
+
- Configurer l'entraînement GPU avec la gestion des devices
|
|
10
|
+
- Rédiger des data loaders et des pipelines de prétraitement pour l'entraînement de modèles
|
|
11
|
+
- Implémenter l'évaluation de modèles, le checkpointing et l'arrêt anticipé
|
|
12
|
+
- Déboguer des pertes NaN, des gradients explosifs ou une instabilité d'entraînement
|
|
13
|
+
- Porter des modèles entre PyTorch et TensorFlow
|
|
14
|
+
|
|
15
|
+
## Quand NE PAS utiliser
|
|
16
|
+
- Tâches scikit-learn (classification, régression, clustering sur des données tabulaires) — pas du deep learning
|
|
17
|
+
- Manipulation de données Pandas/Polars avant l'étape de modélisation
|
|
18
|
+
- Fine-tuning Hugging Face avec l'API trainer (workflow différent)
|
|
19
|
+
- Déploiements d'inférence uniquement sans code d'entraînement
|
|
20
|
+
|
|
21
|
+
## Instructions
|
|
22
|
+
|
|
23
|
+
### Boucle d'entraînement PyTorch — structure standard
|
|
24
|
+
```python
|
|
25
|
+
import torch
|
|
26
|
+
import torch.nn as nn
|
|
27
|
+
from torch.utils.data import DataLoader
|
|
28
|
+
|
|
29
|
+
def train(model, train_loader, val_loader, epochs, lr, device):
|
|
30
|
+
optimizer = torch.optim.AdamW(model.parameters(), lr=lr, weight_decay=1e-2)
|
|
31
|
+
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=epochs)
|
|
32
|
+
criterion = nn.CrossEntropyLoss()
|
|
33
|
+
|
|
34
|
+
best_val_loss = float('inf')
|
|
35
|
+
|
|
36
|
+
for epoch in range(epochs):
|
|
37
|
+
# Entraînement
|
|
38
|
+
model.train()
|
|
39
|
+
train_loss = 0.0
|
|
40
|
+
for batch in train_loader:
|
|
41
|
+
inputs, targets = batch
|
|
42
|
+
inputs, targets = inputs.to(device), targets.to(device)
|
|
43
|
+
|
|
44
|
+
optimizer.zero_grad()
|
|
45
|
+
outputs = model(inputs)
|
|
46
|
+
loss = criterion(outputs, targets)
|
|
47
|
+
loss.backward()
|
|
48
|
+
|
|
49
|
+
# Écrêtage des gradients — toujours pour la stabilité
|
|
50
|
+
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
|
|
51
|
+
|
|
52
|
+
optimizer.step()
|
|
53
|
+
train_loss += loss.item()
|
|
54
|
+
|
|
55
|
+
# Validation
|
|
56
|
+
model.eval()
|
|
57
|
+
val_loss = 0.0
|
|
58
|
+
with torch.no_grad():
|
|
59
|
+
for batch in val_loader:
|
|
60
|
+
inputs, targets = batch
|
|
61
|
+
inputs, targets = inputs.to(device), targets.to(device)
|
|
62
|
+
outputs = model(inputs)
|
|
63
|
+
val_loss += criterion(outputs, targets).item()
|
|
64
|
+
|
|
65
|
+
scheduler.step()
|
|
66
|
+
|
|
67
|
+
# Sauvegarder le meilleur modèle
|
|
68
|
+
if val_loss < best_val_loss:
|
|
69
|
+
best_val_loss = val_loss
|
|
70
|
+
torch.save(model.state_dict(), 'best_model.pt')
|
|
71
|
+
|
|
72
|
+
print(f"Epoch {epoch+1}/{epochs} | Train: {train_loss/len(train_loader):.4f} | Val: {val_loss/len(val_loader):.4f}")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Gestion des devices
|
|
76
|
+
```python
|
|
77
|
+
# Toujours sélectionner le device explicitement
|
|
78
|
+
device = torch.device('cuda' if torch.cuda.is_available() else
|
|
79
|
+
'mps' if torch.backends.mps.is_available() else
|
|
80
|
+
'cpu')
|
|
81
|
+
model = model.to(device)
|
|
82
|
+
```
|
|
83
|
+
Ne jamais coder en dur `'cuda'` — toujours vérifier la disponibilité.
|
|
84
|
+
|
|
85
|
+
### Structure de modèle personnalisé
|
|
86
|
+
```python
|
|
87
|
+
class MyModel(nn.Module):
|
|
88
|
+
def __init__(self, input_dim, hidden_dim, output_dim, dropout=0.3):
|
|
89
|
+
super().__init__()
|
|
90
|
+
self.network = nn.Sequential(
|
|
91
|
+
nn.Linear(input_dim, hidden_dim),
|
|
92
|
+
nn.LayerNorm(hidden_dim),
|
|
93
|
+
nn.GELU(),
|
|
94
|
+
nn.Dropout(dropout),
|
|
95
|
+
nn.Linear(hidden_dim, output_dim)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def forward(self, x):
|
|
99
|
+
return self.network(x)
|
|
100
|
+
```
|
|
101
|
+
Préférer `nn.Sequential` pour les réseaux feedforward simples ; utiliser la surcharge de `forward()` pour les branchements complexes.
|
|
102
|
+
|
|
103
|
+
### Débogage de l'instabilité d'entraînement
|
|
104
|
+
1. **Perte NaN** → vérifier log(0) dans la perte, des entrées explosives, ou une division par zéro dans le prétraitement
|
|
105
|
+
2. **Gradients explosifs** → ajouter `clip_grad_norm_` (déjà dans le template ci-dessus)
|
|
106
|
+
3. **Gradients qui disparaissent** → vérifier les fonctions d'activation (éviter sigmoid/tanh dans les réseaux profonds), utiliser des connexions résiduelles
|
|
107
|
+
4. **Perte qui ne diminue pas** → réduire le LR de 10x, vérifier le mélange du data loader, vérifier que les labels sont corrects
|
|
108
|
+
5. **GPU OOM** → réduire la taille de batch, utiliser le gradient checkpointing, utiliser la précision mixte
|
|
109
|
+
|
|
110
|
+
### Entraînement en précision mixte (PyTorch)
|
|
111
|
+
```python
|
|
112
|
+
from torch.cuda.amp import autocast, GradScaler
|
|
113
|
+
|
|
114
|
+
scaler = GradScaler()
|
|
115
|
+
|
|
116
|
+
for batch in train_loader:
|
|
117
|
+
optimizer.zero_grad()
|
|
118
|
+
with autocast():
|
|
119
|
+
outputs = model(inputs)
|
|
120
|
+
loss = criterion(outputs, targets)
|
|
121
|
+
scaler.scale(loss).backward()
|
|
122
|
+
scaler.unscale_(optimizer)
|
|
123
|
+
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
|
|
124
|
+
scaler.step(optimizer)
|
|
125
|
+
scaler.update()
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### TensorFlow/Keras — structure standard
|
|
129
|
+
```python
|
|
130
|
+
import tensorflow as tf
|
|
131
|
+
|
|
132
|
+
model = tf.keras.Sequential([
|
|
133
|
+
tf.keras.layers.Dense(256, activation='relu'),
|
|
134
|
+
tf.keras.layers.Dropout(0.3),
|
|
135
|
+
tf.keras.layers.Dense(10, activation='softmax')
|
|
136
|
+
])
|
|
137
|
+
|
|
138
|
+
model.compile(
|
|
139
|
+
optimizer=tf.keras.optimizers.AdamW(learning_rate=1e-3, weight_decay=1e-2),
|
|
140
|
+
loss='sparse_categorical_crossentropy',
|
|
141
|
+
metrics=['accuracy']
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
callbacks = [
|
|
145
|
+
tf.keras.callbacks.EarlyStopping(patience=5, restore_best_weights=True),
|
|
146
|
+
tf.keras.callbacks.ModelCheckpoint('best_model.keras', save_best_only=True),
|
|
147
|
+
tf.keras.callbacks.ReduceLROnPlateau(patience=3, factor=0.5)
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
history = model.fit(
|
|
151
|
+
train_dataset,
|
|
152
|
+
validation_data=val_dataset,
|
|
153
|
+
epochs=100,
|
|
154
|
+
callbacks=callbacks
|
|
155
|
+
)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Exemple
|
|
159
|
+
|
|
160
|
+
**Utilisateur :** Construire un classificateur de texte PyTorch pour l'analyse de sentiment (binaire) avec embedding, LSTM et dropout.
|
|
161
|
+
|
|
162
|
+
**Sortie attendue :**
|
|
163
|
+
- `SentimentLSTM(nn.Module)` — couche d'embedding, LSTM, dropout, tête linéaire
|
|
164
|
+
- `forward()` — gère les séquences packed ou les entrées paddées
|
|
165
|
+
- Boucle d'entraînement avec écrêtage des gradients, validation par epoch, checkpoint du meilleur modèle
|
|
166
|
+
- `device` auto-détecté (CUDA/MPS/CPU)
|
|
167
|
+
- Séparation train/val via `DataLoader` avec mélange uniquement sur train
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
> **Travaillez avec nous :** Claudient est soutenu par [Uitbreiden](https://uitbreiden.com/) — nous construisons des produits IA et des solutions B2B avec des communautés de développeurs. Vous construisez des modèles ML ou des produits alimentés par l'IA ? [uitbreiden.com](https://uitbreiden.com/)
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
> 🇳🇱 Dit is de Nederlandse vertaling. [Engelse versie](../dbt-data-pipelines.md).
|
|
2
|
+
|
|
3
|
+
# dbt Data Pipelines Skill
|
|
4
|
+
|
|
5
|
+
## Wanneer te activeren
|
|
6
|
+
- dbt-modellen schrijven (staging-, intermediate-, mart-lagen)
|
|
7
|
+
- dbt-sources, refs en afhankelijkheden configureren
|
|
8
|
+
- dbt-tests schrijven (schema-tests, enkelvoudige tests, aangepaste generieke tests)
|
|
9
|
+
- dbt-projectstructuur instellen voor een nieuw data warehouse
|
|
10
|
+
- dbt-macro's schrijven voor herbruikbare SQL-logica
|
|
11
|
+
- dbt-documentatie en versheidscontroles configureren
|
|
12
|
+
- dbt-compilatiefouten of mislukte modeldraaiingen debuggen
|
|
13
|
+
- dbt instellen met BigQuery, Snowflake, Redshift, of DuckDB
|
|
14
|
+
|
|
15
|
+
## Wanneer NIET te gebruiken
|
|
16
|
+
- Ruwe ETL-pipelines zonder een warehouse (gebruik in plaats daarvan Airflow, Prefect of Dagster)
|
|
17
|
+
- Real-time streamingdata (dbt is alleen batch)
|
|
18
|
+
- Pandas/Polars in-memory-transformaties (gebruik de pandas-polars skill)
|
|
19
|
+
- Data-ingestie (dbt transformeert, het ingesteert niet)
|
|
20
|
+
|
|
21
|
+
## Instructies
|
|
22
|
+
|
|
23
|
+
### Projectlaagarchitectuur
|
|
24
|
+
Scheid modellen altijd in drie lagen:
|
|
25
|
+
```
|
|
26
|
+
models/
|
|
27
|
+
├── staging/ ← 1:1 met brontabellen. Alleen lichte opschoning. Geen joins.
|
|
28
|
+
│ ├── stg_orders.sql
|
|
29
|
+
│ └── stg_customers.sql
|
|
30
|
+
├── intermediate/ ← Bedrijfslogica. Joins toegestaan. Niet blootgesteld aan BI-tools.
|
|
31
|
+
│ └── int_orders_with_customers.sql
|
|
32
|
+
└── marts/ ← Definitieve bedrijfsentiteiten. Blootgesteld aan BI. Aggregaties hier.
|
|
33
|
+
├── finance/
|
|
34
|
+
│ └── fct_revenue.sql
|
|
35
|
+
└── marketing/
|
|
36
|
+
└── dim_customers.sql
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Staging-regels:**
|
|
40
|
+
- Kolommen hernoemen naar projectconventies (snake_case)
|
|
41
|
+
- Typen expliciet casten
|
|
42
|
+
- Geen bedrijfslogica — geen joins, geen aggregaties
|
|
43
|
+
- Prefix met `stg_`
|
|
44
|
+
|
|
45
|
+
**Mart-regels:**
|
|
46
|
+
- `fct_`-prefix voor facttabellen (gebeurtenissen, transacties)
|
|
47
|
+
- `dim_`-prefix voor dimensietabellen (klanten, producten)
|
|
48
|
+
- Altijd documenteren in schema.yml
|
|
49
|
+
|
|
50
|
+
### Modelconfiguratie
|
|
51
|
+
```sql
|
|
52
|
+
-- models/marts/finance/fct_revenue.sql
|
|
53
|
+
{{
|
|
54
|
+
config(
|
|
55
|
+
materialized='incremental',
|
|
56
|
+
unique_key='order_id',
|
|
57
|
+
on_schema_change='fail'
|
|
58
|
+
)
|
|
59
|
+
}}
|
|
60
|
+
|
|
61
|
+
with orders as (
|
|
62
|
+
select * from {{ ref('int_orders_with_customers') }}
|
|
63
|
+
{% if is_incremental() %}
|
|
64
|
+
where created_at > (select max(created_at) from {{ this }})
|
|
65
|
+
{% endif %}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
select
|
|
69
|
+
order_id,
|
|
70
|
+
customer_id,
|
|
71
|
+
amount,
|
|
72
|
+
created_at
|
|
73
|
+
from orders
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Materialisatiekeuzes:**
|
|
77
|
+
- `view`: standaard — goed voor staging- en intermediate-modellen
|
|
78
|
+
- `table`: voor dure queries die vaak worden bevraagd
|
|
79
|
+
- `incremental`: voor grote facttabellen die in de loop der tijd groeien
|
|
80
|
+
- `ephemeral`: CTE's, niet gematerialiseerd — gebruik voor eenvoudige transformaties die eenmaal worden aangeroepen
|
|
81
|
+
|
|
82
|
+
### Testen — vereist op elk mart-model
|
|
83
|
+
```yaml
|
|
84
|
+
# models/marts/finance/schema.yml
|
|
85
|
+
version: 2
|
|
86
|
+
|
|
87
|
+
models:
|
|
88
|
+
- name: fct_revenue
|
|
89
|
+
description: "Eén rij per voltooide bestelling"
|
|
90
|
+
columns:
|
|
91
|
+
- name: order_id
|
|
92
|
+
description: "Primaire sleutel"
|
|
93
|
+
tests:
|
|
94
|
+
- unique
|
|
95
|
+
- not_null
|
|
96
|
+
- name: customer_id
|
|
97
|
+
tests:
|
|
98
|
+
- not_null
|
|
99
|
+
- relationships:
|
|
100
|
+
to: ref('dim_customers')
|
|
101
|
+
field: customer_id
|
|
102
|
+
- name: amount
|
|
103
|
+
tests:
|
|
104
|
+
- not_null
|
|
105
|
+
- dbt_utils.accepted_range:
|
|
106
|
+
min_value: 0
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Minimale tests op elk mart-model: `unique` + `not_null` op primaire sleutel, `not_null` op kritieke externe sleutels.
|
|
110
|
+
|
|
111
|
+
### Bronconfiguratie
|
|
112
|
+
```yaml
|
|
113
|
+
# models/staging/sources.yml
|
|
114
|
+
version: 2
|
|
115
|
+
|
|
116
|
+
sources:
|
|
117
|
+
- name: raw_stripe
|
|
118
|
+
database: raw
|
|
119
|
+
schema: stripe
|
|
120
|
+
freshness:
|
|
121
|
+
warn_after: {count: 12, period: hour}
|
|
122
|
+
error_after: {count: 24, period: hour}
|
|
123
|
+
loaded_at_field: _ingested_at
|
|
124
|
+
tables:
|
|
125
|
+
- name: charges
|
|
126
|
+
description: "Ruwe Stripe-charges van Fivetran"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Stel altijd `freshness` in op bronnen — verouderde brondata is een stille fout.
|
|
130
|
+
|
|
131
|
+
### Macro's voor herbruikbare logica
|
|
132
|
+
```sql
|
|
133
|
+
-- macros/cents_to_dollars.sql
|
|
134
|
+
{% macro cents_to_dollars(column_name) %}
|
|
135
|
+
({{ column_name }} / 100.0)::numeric(10, 2)
|
|
136
|
+
{% endmacro %}
|
|
137
|
+
|
|
138
|
+
-- Gebruik in model
|
|
139
|
+
select
|
|
140
|
+
{{ cents_to_dollars('amount_cents') }} as amount_dollars
|
|
141
|
+
from orders
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Voorbeeld
|
|
145
|
+
|
|
146
|
+
**Gebruiker:** Maak staging- en mart-modellen voor Stripe-betalingsdata (charges, refunds) met tests en versheidscontroles.
|
|
147
|
+
|
|
148
|
+
**Verwachte output:**
|
|
149
|
+
- `models/staging/stripe/sources.yml` — bron met versheidscontrole op `_ingested_at`
|
|
150
|
+
- `models/staging/stripe/stg_stripe_charges.sql` — hernoemen, casten, geen joins
|
|
151
|
+
- `models/staging/stripe/stg_stripe_refunds.sql` — zelfde patroon
|
|
152
|
+
- `models/marts/finance/fct_payments.sql` — join charges + refunds, nettobedrag, incrementele materialisatie
|
|
153
|
+
- `models/marts/finance/schema.yml` — `unique` + `not_null` op `charge_id`, relatietest op `customer_id`
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
> **Werk met ons:** Claudient wordt ondersteund door [Uitbreiden](https://uitbreiden.com/) — we bouwen AI-producten en B2B-oplossingen met ontwikkelaarsgemeenschappen. Datapipelines bouwen voor AI- of analyseproducten? [uitbreiden.com](https://uitbreiden.com/)
|