4646}
4747
4848
49+ def write_timestamp (
50+ con : duckdb .DuckDBPyConnection ,
51+ bucket : str ,
52+ prefix : str ,
53+ ) -> str :
54+ """
55+ TODO: This is also used in NVA script, move to a common utils
56+ Write last successful run timestamp to S3.
57+ """
58+ timestamp = datetime .now ().isoformat ()
59+ con .execute (f"""
60+ COPY (SELECT '{ timestamp } ' as last_successful_run)
61+ TO 's3://{ bucket } /{ prefix } /last_successful_run.parquet'
62+ (FORMAT PARQUET, COMPRESSION ZSTD)
63+ """ )
64+ log .info ("Last successful run timestamp written" , time = timestamp )
65+ return timestamp
66+
67+
68+ def setup_duckdb_s3_connection (
69+ endpoint_url : str ,
70+ access_key : str ,
71+ secret_key : str ,
72+ region : str ,
73+ ) -> duckdb .DuckDBPyConnection :
74+ """
75+ TODO: This is also used in NVA script, move to a common utils
76+ Set up DuckDB connection with S3 support
77+ """
78+ con = duckdb .connect ()
79+ con .execute ("INSTALL httpfs;" )
80+ con .execute ("LOAD httpfs;" )
81+
82+ # Clean endpoint URL
83+ clean_endpoint = endpoint_url .replace ("https://" , "" ).replace ("http://" , "" )
84+
85+ con .execute (f"""
86+ CREATE OR REPLACE SECRET (
87+ TYPE S3,
88+ KEY_ID '{ access_key } ',
89+ SECRET '{ secret_key } ',
90+ ENDPOINT '{ clean_endpoint } ',
91+ REGION '{ region } ',
92+ URL_STYLE 'path'
93+ );
94+ """ )
95+
96+ return con
97+
98+
4999def hex_to_decimal_tag (hex_tag ):
50100 """
51101 Convert hexadecimal PIT tag format to ISO decimal format.
@@ -104,7 +154,7 @@ def get_environmental_data(
104154 )
105155 yield from client .paginate (
106156 f"enviro/{ location_code } " ,
107- method = "get " ,
157+ method = "GET " ,
108158 params = {
109159 "begin_dt" : begin_date ,
110160 "end_dt" : end_date ,
@@ -121,7 +171,7 @@ def get_tags_data(
121171 log .debug ("Fetching tags data for location" , location_code = location_code )
122172 yield from client .paginate (
123173 f"tags/{ location_code } " ,
124- method = "get " ,
174+ method = "GET " ,
125175 params = {
126176 "begin_dt" : begin_date ,
127177 "end_dt" : end_date ,
@@ -141,7 +191,7 @@ def get_readers_voltage_data(
141191 )
142192 yield from client .paginate (
143193 f"reader/{ location_code } " ,
144- method = "get " ,
194+ method = "GET " ,
145195 params = {
146196 "begin_dt" : begin_date ,
147197 "end_dt" : end_date ,
@@ -405,7 +455,7 @@ def replicate(
405455 if tags and check_table_has_data (duckdb_path , "tags" , dataset_name ):
406456 stream_configs ["tags" ] = {
407457 "table_name" : f"{ dataset_name } .tags" ,
408- "primary_key" : ["detected_at" ],
458+ "primary_key" : ["detected_at" , "tag" ],
409459 "update_key" : "detected_at" ,
410460 "time_column" : "detected_at" ,
411461 "location" : "antenna__reader__site__slug" ,
@@ -456,6 +506,15 @@ def replicate(
456506 debug = True ,
457507 ).run ()
458508
509+ con = setup_duckdb_s3_connection (
510+ endpoint_url = endpoint_url ,
511+ access_key = access_key ,
512+ secret_key = secret_key ,
513+ region = region ,
514+ )
515+
516+ write_timestamp (con , bucket , "tables" )
517+
459518
460519if __name__ == "__main__" :
461520 app ()
0 commit comments