22
33"""Biomark PIT registering salmon data synchronization."""
44
5+ import os
56from datetime import datetime , timedelta
67
78import dlt
89import requests
910import typer
10- from dlt .destinations .impl .filesystem .factory import filesystem
11- from dlt .sources .credentials import AwsCredentials
1211from dlt .sources .helpers .rest_client import RESTClient
1312from dlt .sources .helpers .rest_client .auth import BearerTokenAuth
13+ from dlt .sources .helpers .rest_client .paginators import SinglePagePaginator
14+ from sling import Replication
1415
1516from .settings import (
1617 BIOMARK_ACCESS_KEY ,
1920 BIOMARK_AWS_ENDPOINT ,
2021 BIOMARK_BASE_URL ,
2122 BIOMARK_BUCKET ,
22- BIOMARK_PREFIX ,
2323 BIOMARK_REGION ,
2424 BIOMARK_SECRET_KEY ,
2525 log ,
@@ -84,48 +84,59 @@ def get_bearer_token():
8484
8585
8686def get_environmental_data (
87- client : RESTClient , location_code : str , begin_date : str , end_date : str
87+ client : RESTClient , locations : list [ str ] , begin_date : str , end_date : str
8888):
8989 """Fetch environmental data from Biomark API."""
90- log .debug ("Fetching environmental data for location" , location_code = location_code )
91- yield from client .paginate (
92- f"enviro/{ location_code } " ,
93- method = "get" ,
94- params = {
95- "begin_dt" : begin_date ,
96- "end_dt" : end_date ,
97- },
98- )
90+ for location_name in locations :
91+ location_code = SITES .get (location_name )
92+ log .debug (
93+ "Fetching environmental data for location" , location_code = location_code
94+ )
95+ yield from client .paginate (
96+ f"enviro/{ location_code } " ,
97+ method = "get" ,
98+ params = {
99+ "begin_dt" : begin_date ,
100+ "end_dt" : end_date ,
101+ },
102+ )
99103
100104
101105def get_tags_data (
102- client : RESTClient , location_code : str , begin_date : str , end_date : str
106+ client : RESTClient , locations : list [ str ] , begin_date : str , end_date : str
103107):
104108 """Fetch tags data from Biomark API."""
105- log .debug ("Fetching tags data for location" , location_code = location_code )
106- yield from client .paginate (
107- f"tags/{ location_code } " ,
108- method = "get" ,
109- params = {
110- "begin_dt" : begin_date ,
111- "end_dt" : end_date ,
112- },
113- )
109+ for location_name in locations :
110+ location_code = SITES .get (location_name )
111+ log .debug ("Fetching tags data for location" , location_code = location_code )
112+ yield from client .paginate (
113+ f"tags/{ location_code } " ,
114+ method = "get" ,
115+ params = {
116+ "begin_dt" : begin_date ,
117+ "end_dt" : end_date ,
118+ },
119+ )
114120
115121
116122def get_readers_voltage_data (
117- client : RESTClient , location_code : str , begin_date : str , end_date : str
123+ client : RESTClient , locations : list [ str ] , begin_date : str , end_date : str
118124):
119125 """Fetch readers voltage data from Biomark API."""
120- log .debug ("Fetching readers voltage data for location" , location_code = location_code )
121- yield from client .paginate (
122- f"reader/{ location_code } " ,
123- method = "get" ,
124- params = {
125- "begin_dt" : begin_date ,
126- "end_dt" : end_date ,
127- },
128- )
126+
127+ for location_name in locations :
128+ location_code = SITES .get (location_name )
129+ log .debug (
130+ "Fetching readers voltage data for location" , location_code = location_code
131+ )
132+ yield from client .paginate (
133+ f"reader/{ location_code } " ,
134+ method = "get" ,
135+ params = {
136+ "begin_dt" : begin_date ,
137+ "end_dt" : end_date ,
138+ },
139+ )
129140
130141
131142@dlt .transformer (primary_key = ["tag" , "detected_at" ])
@@ -156,6 +167,7 @@ def biomark_pit_salmon(
156167 client = RESTClient (
157168 base_url = base_url ,
158169 auth = BearerTokenAuth (token ),
170+ paginator = SinglePagePaginator (),
159171 )
160172
161173 # set default dates if not provided
@@ -164,46 +176,36 @@ def biomark_pit_salmon(
164176 begin_date = (datetime .today () - timedelta (days = 1 )).strftime ("%Y-%m-%d" )
165177 end_date = datetime .today ().strftime ("%Y-%m-%d" )
166178
167- for location_name in locations :
168- location_code = SITES .get (location_name )
169- if not location_code :
170- log .warning (f"Unknown location: { location_name } " )
171- continue
172-
173- try :
174- if tags :
175- tags_resource = dlt .resource (
176- get_tags_data (client , location_code , begin_date , end_date ),
177- name = f"tags_{ location_name } " ,
178- primary_key = ["tag" , "detected_at" ],
179- write_disposition = "append" ,
180- )
181- # Apply decimal tag transformation
182- yield tags_resource | add_decimal_tags .with_name (
183- f"tags_{ location_name } "
184- )
185-
186- if readers :
187- yield dlt .resource (
188- get_readers_voltage_data (
189- client , location_code , begin_date , end_date
190- ),
191- name = f"readers_voltage_{ location_name } " ,
192- primary_key = ["read_at" ],
193- write_disposition = "append" ,
194- )
195-
196- if environment :
197- yield dlt .resource (
198- get_environmental_data (client , location_code , begin_date , end_date ),
199- name = f"environment_data_{ location_name } " ,
200- primary_key = ["read_at" ],
201- write_disposition = "append" ,
202- )
203-
204- except Exception as e :
205- log .error (f"Error processing { location_name } : { e } " )
206- # continue with next location
179+ try :
180+ if tags :
181+ tags_resource = dlt .resource (
182+ get_tags_data (client , locations , begin_date , end_date ),
183+ name = "tags" ,
184+ primary_key = ["tag" , "detected_at" ],
185+ )
186+ yield tags_resource | add_decimal_tags .with_name ("tags" )
187+
188+ if readers :
189+ readers_resource = dlt .resource (
190+ get_readers_voltage_data (client , locations , begin_date , end_date ),
191+ name = "readers_voltage" ,
192+ primary_key = ["reader__site__slug" , "read_at" ],
193+ )
194+ yield readers_resource
195+
196+ if environment :
197+ env_resource = dlt .resource (
198+ get_environmental_data (client , locations , begin_date , end_date ),
199+ name = "environment_data" ,
200+ primary_key = ["read_at" ],
201+ write_disposition = "append" ,
202+ )
203+ env_resource .apply_hints (incremental = dlt .sources .incremental ("read_at" ))
204+ yield env_resource
205+
206+ except Exception as e :
207+ log .error (f"Error processing locations: { e } " )
208+ # continue with next location
207209
208210
209211@app .command ()
@@ -224,12 +226,6 @@ def run(
224226 False , help = "Download data from all accessible locations"
225227 ),
226228 base_url : str = BIOMARK_BASE_URL ,
227- bucket : str = BIOMARK_BUCKET ,
228- prefix : str = BIOMARK_PREFIX ,
229- endpoint_url : str = BIOMARK_AWS_ENDPOINT ,
230- access_key : str = BIOMARK_ACCESS_KEY ,
231- secret_key : str = BIOMARK_SECRET_KEY ,
232- region : str = BIOMARK_REGION ,
233229):
234230 """Biomark PIT registering salmon data synchronization."""
235231
@@ -255,22 +251,10 @@ def run(
255251 locations = [place ]
256252 log .info ("Processing single location" , location = place )
257253
258- credentials = AwsCredentials (
259- s3_url_style = "path" ,
260- endpoint_url = endpoint_url ,
261- aws_secret_access_key = secret_key ,
262- aws_access_key_id = access_key ,
263- region_name = region ,
264- )
265-
266254 # Set up DLT pipeline
267255 pipeline = dlt .pipeline (
268- pipeline_name = "biomark_pit_registering_salmon" ,
269- destination = filesystem (
270- bucket_url = f"s3://{ bucket } /" + prefix ,
271- credentials = credentials ,
272- layout = "{table_name}.{ext}" ,
273- ),
256+ pipeline_name = "biomark_pit" ,
257+ destination = "duckdb" ,
274258 dataset_name = "main" ,
275259 progress = "log" ,
276260 )
@@ -287,11 +271,46 @@ def run(
287271 readers = readers ,
288272 environment = environment ,
289273 ),
290- write_disposition = "append" ,
291- loader_file_format = "parquet" ,
292274 )
293275 )
294276
295277
278+ @app .command ()
279+ def replicate (
280+ bucket : str = BIOMARK_BUCKET ,
281+ endpoint_url : str = BIOMARK_AWS_ENDPOINT ,
282+ access_key : str = BIOMARK_ACCESS_KEY ,
283+ secret_key : str = BIOMARK_SECRET_KEY ,
284+ region : str = BIOMARK_REGION ,
285+ ):
286+ os .environ ["NINAS3" ] = f"{{type: s3, bucket: { bucket } , use_environment: true }}"
287+ os .environ ["AWS_ACCESS_KEY_ID" ] = access_key
288+ os .environ ["AWS_SECRET_ACCESS_KEY" ] = secret_key
289+ os .environ ["AWS_REGION" ] = region
290+ os .environ ["AWS_ENDPOINT" ] = endpoint_url
291+ os .environ ["DUCKDB" ] = "{type: duckdb, instance: biomark_pit.duckdb}"
292+
293+ Replication (
294+ source = "DUCKDB" ,
295+ target = "NINAS3" ,
296+ defaults = {
297+ "object" : "tables/{stream_name}/{part_year}/{part_month}/{part_day}" ,
298+ "mode" : "incremental" ,
299+ "target_options" : {"format" : "parquet" },
300+ },
301+ streams = {
302+ "readers_voltage" : {
303+ "primary_key" : ["location_name" , "read_at" ],
304+ "update_key" : "read_at" ,
305+ "object" : "tables/{stream_name}/{part_year}/{part_month}/{part_day}" ,
306+ "mode" : "incremental" ,
307+ "target_options" : {"format" : "parquet" },
308+ }
309+ },
310+ env = {"SLING_STATE" : "NINAS3/data/sling" },
311+ debug = True ,
312+ ).run ()
313+
314+
296315if __name__ == "__main__" :
297316 app ()
0 commit comments