-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdoffin.py
More file actions
148 lines (131 loc) · 4.58 KB
/
Copy pathdoffin.py
File metadata and controls
148 lines (131 loc) · 4.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
from collections import defaultdict
import dlt
import typer
from dlt.destinations.impl.filesystem.factory import filesystem
from dlt.sources.credentials import AwsCredentials
from dlt.sources.rest_api import rest_api_source
from dlt.sources.rest_api.config_setup import PageNumberPaginator
from .settings import log
app = typer.Typer(help="Export Doffin notices to Parquet in S3 bucket")
@app.command()
def run(
api_key: str = typer.Option(
"", envvar="DOFFIN_API_KEY", help="Doffin API subscription key"
),
base_url: str = typer.Option(
"https://api.doffin.no/public/v2/",
envvar="DOFFIN_BASE_URL",
help="Base URL for the Doffin API",
),
page_size: int = typer.Option(
100, envvar="DOFFIN_PAGE_SIZE", help="Number of hits per page"
),
access_key: str = typer.Option(
..., envvar="DOFFIN_AWS_ACCESS_KEY", help="AWS S3 access key"
),
secret_key: str = typer.Option(
..., envvar="DOFFIN_AWS_SECRET_KEY", help="AWS S3 secret key"
),
endpoint_url: str = typer.Option(
..., envvar="DOFFIN_AWS_ENDPOINT", help="AWS S3 endpoint URL"
),
bucket: str = typer.Option(
..., envvar="DOFFIN_AWS_BUCKET", help="AWS S3 bucket name"
),
prefix: str = typer.Option(
"doffin",
envvar="DOFFIN_AWS_PREFIX",
help="AWS S3 prefix (folder path) for storing data",
),
region: str = typer.Option(
"us-east-1", envvar="DOFFIN_S3_REGION", help="AWS S3 region"
),
params: list[str] | None = typer.Option( # noqa: B008
None,
"--param",
help="Additional API query parameters (format: key=value). "
"Supported: sortBy, searchString, type, status, cpvCode, location, "
"issueDateFrom, issueDateTo, estimatedValueFrom, estimatedValueTo. "
"Can specify multiple params.",
),
):
"""
Export Doffin notices to Parquet in S3 bucket.
Examples:
# Export all notices
datasync doffin run
# Filter by status and CPV code
datasync doffin run \\
--param status=active \\
--param cpvCode=45000000 \\
--param issueDateFrom=2024-01-01
"""
params_by_key = defaultdict(list)
params_by_key["numHitsPerPage"].append(str(page_size))
if params:
for param_str in params:
if "=" in param_str:
key, value = param_str.split("=", 1)
params_by_key[key.strip()].append(value.strip())
else:
log.warning(
f"Ignoring invalid param format (expected key=value): {param_str}"
)
source = rest_api_source(
{
"client": {
"base_url": base_url,
"headers": {"Ocp-Apim-Subscription-Key": api_key},
},
"resources": [
{
"name": "notices",
"endpoint": {
"path": "search",
"params": params_by_key,
"data_selector": "hits",
"paginator": PageNumberPaginator(
page_param="page",
base_page=1,
total_path=None,
# doffin api doesn't allow more than 1.000 rows
# being fetched
maximum_page=1000 // page_size,
),
},
}
],
}
)
credentials = AwsCredentials(
s3_url_style="path",
endpoint_url=endpoint_url,
aws_secret_access_key=secret_key,
aws_access_key_id=access_key,
region_name=region,
)
pipeline = dlt.pipeline(
pipeline_name="doffin",
destination=filesystem(
bucket_url=f"s3://{bucket}/{prefix}",
credentials=credentials,
layout="{table_name}.{ext}",
),
dataset_name="doffin",
)
load_info = pipeline.run(
source, loader_file_format="parquet", write_disposition="replace"
)
row_counts = {}
if pipeline.last_trace and pipeline.last_trace.last_normalize_info:
row_counts = pipeline.last_trace.last_normalize_info.row_counts or {}
notices_rows_loaded = row_counts.get("notices", 0)
log.info(
"doffin load complete",
path=f"s3://{bucket}/{prefix}/notices.parquet",
load_id=load_info.loads_ids[0] if load_info.loads_ids else None,
rows_loaded=notices_rows_loaded,
row_counts=row_counts,
)
if __name__ == "__main__":
app()