44
55import os
66from datetime import datetime , timedelta
7+ from pathlib import Path
78
89import dlt
10+ import duckdb
911import requests
1012import typer
1113from dlt .sources .helpers .rest_client import RESTClient
2729BIOMARK_REGION = env ("BIOMARK_REGION" , default = "us-east-1" )
2830BIOMARK_ACCESS_KEY = env ("BIOMARK_ACCESS_KEY" , default = "" )
2931BIOMARK_SECRET_KEY = env ("BIOMARK_SECRET_KEY" , default = "" )
32+ BIOMARK_DUCKDB_PATH = env ("BIOMARK_DUCKDB_PATH" , default = "" )
3033
3134app = typer .Typer ()
3235
@@ -299,12 +302,54 @@ def create_stream_config(config: dict, location: str) -> dict:
299302 }
300303
301304
305+ def check_table_has_data (db_path : str , table_name : str ) -> bool :
306+ """
307+ Check if a table exists and has data in the DuckDB database.
308+
309+ Args:
310+ db_path: Path to the DuckDB database file
311+ table_name: Name of the table to check
312+
313+ Returns:
314+ bool: True if table exists and has at least one row, False otherwise
315+ """
316+ try :
317+ conn = duckdb .connect (db_path , read_only = True )
318+ try :
319+ # check if table exists
320+ result = conn .execute (
321+ "SELECT table_name FROM information_schema.tables WHERE table_name = ?" ,
322+ [table_name ],
323+ ).fetchone ()
324+
325+ if result is None :
326+ log .info (f"Table { table_name } does not exist" )
327+ return False
328+
329+ # check if table has data
330+ count_result = conn .execute (
331+ "SELECT COUNT(*) FROM ?" , [table_name ]
332+ ).fetchone ()
333+ row_count = count_result [0 ] if count_result else 0
334+
335+ log .info (f"Table { table_name } has { row_count } rows" )
336+ return row_count > 0
337+
338+ finally :
339+ conn .close ()
340+
341+ except Exception as e :
342+ log .error (f"Error checking table { table_name } : { e } " )
343+ return False
344+
345+
302346@app .command ()
303347def replicate (
304348 bucket : str = BIOMARK_BUCKET ,
305349 endpoint_url : str = BIOMARK_AWS_ENDPOINT ,
306350 access_key : str = BIOMARK_ACCESS_KEY ,
307351 secret_key : str = BIOMARK_SECRET_KEY ,
352+ duckdb_path : str = BIOMARK_DUCKDB_PATH ,
308353 region : str = BIOMARK_REGION ,
309354 tags : bool = typer .Option (False , help = "Add tags data to S3" ),
310355 readers : bool = typer .Option (False , help = "Add readers voltage data to S3" ),
@@ -316,46 +361,61 @@ def replicate(
316361 os .environ ["AWS_SECRET_ACCESS_KEY" ] = secret_key
317362 os .environ ["AWS_REGION" ] = region
318363 os .environ ["AWS_ENDPOINT" ] = endpoint_url
319- os .environ ["DUCKDB" ] = (
320- "{type: duckdb, instance: biomark_pit_registering_salmon_v1.duckdb}"
321- )
364+ os .environ ["DUCKDB" ] = f"{{type: duckdb, instance: { duckdb_path } }}"
322365
323366 if not any ([readers , tags , environment ]):
324- typer .echo (
367+ typer .BadParameter (
325368 "Error: At least one data type must be selected "
326369 "(--readers, --tags, or --environment)"
327370 )
328371 raise typer .Exit (1 )
329372
330- # Only include configs for enabled data types
373+ if not Path (duckdb_path ).exists ():
374+ typer .BadParameter (f"Error: DuckDB file not found at { duckdb_path } " )
375+ raise typer .Exit (1 )
376+
377+ # only include configs for enabled data types that have data
331378 stream_configs = {}
332379
333- if readers :
380+ if readers and check_table_has_data ( duckdb_path , "readers_voltage" ) :
334381 stream_configs ["readers" ] = {
335382 "table_name" : "readers_voltage" ,
336383 "primary_key" : ["read_at" ],
337384 "update_key" : "read_at" ,
338385 "time_column" : "read_at" ,
339386 "location" : "reader__site__slug" ,
340387 }
388+ log .info ("Added readers_voltage to stream config (has data)" )
389+ elif readers :
390+ log .warning ("--readers_voltage flag is True but table has no data, skipping" )
341391
342- if tags :
392+ if tags and check_table_has_data ( duckdb_path , "tags" ) :
343393 stream_configs ["tags" ] = {
344394 "table_name" : "tags" ,
345395 "primary_key" : ["detected_at" ],
346396 "update_key" : "detected_at" ,
347397 "time_column" : "detected_at" ,
348398 "location" : "antenna__reader__site__slug" ,
349399 }
400+ log .info ("Added tags to stream config (has data)" )
401+ elif tags :
402+ log .warning ("--tags flag is set but table has no data, skipping" )
350403
351- if environment :
404+ if environment and check_table_has_data ( duckdb_path , "environment_data" ) :
352405 stream_configs ["environment" ] = {
353406 "table_name" : "environment_data" ,
354407 "primary_key" : ["read_at" ],
355408 "update_key" : "read_at" ,
356409 "time_column" : "read_at" ,
357410 "location" : "reader__site__slug" ,
358411 }
412+ log .info ("Added environment_data to stream config (has data)" )
413+ elif environment :
414+ log .warning ("--environment flag is set, but table has no data, skipping" )
415+
416+ if not stream_configs :
417+ log .error ("No tables with data found to replicate" )
418+ raise typer .Exit (1 )
359419
360420 streams = {}
361421 for data_type , config in stream_configs .items ():
0 commit comments