1111
1212import boto3
1313import knackpy
14+ from tqdm import tqdm
1415
1516from config import FIELD_MAPS
1617
@@ -35,6 +36,17 @@ def cli_args():
3536 choices = ["data-tracker" , "finance-purchasing" ],
3637 help = "The name of the destination Knack app" ,
3738 )
39+ parser .add_argument (
40+ "--progress-bar" ,
41+ "-p" ,
42+ action = "store_true" ,
43+ help = (
44+ "Display a tqdm progress bar instead of the default periodic log "
45+ "messages. Not recommended when running under Airflow, since the "
46+ "bar's carriage-return redraws don't render cleanly in Airflow's "
47+ "log viewer."
48+ ),
49+ )
3850 return parser .parse_args ()
3951
4052
@@ -182,10 +194,10 @@ def coalesce_records(records_current, coalesce_fields, current_pk, separator=",\
182194 return list (index .values ())
183195
184196
185- def process_record (record , knack_obj , app , count ):
197+ def process_record (record , knack_obj , app , count = None ):
186198 method = "create" if not record .get ("id" ) else "update"
187199 app .record (data = record , method = method , obj = knack_obj )
188- if count % 10 == 0 :
200+ if count is not None and count % 10 == 0 :
189201 logging .info (f"{ count } records processed so far..." )
190202
191203
@@ -234,17 +246,31 @@ def main():
234246 logging .info (f"{ len (todos )} records to process." )
235247
236248 # Process todos with 10 workers
237- count = 0
238249 with ThreadPoolExecutor (max_workers = 10 ) as executor :
239- futures = []
240- for record in todos :
241- futures .append (
242- executor .submit (process_record , record , knack_obj , app , count )
243- )
244- count += 1
245-
246- for future in futures :
247- future .result () # This will re-raise any exceptions encountered in the threads
250+ if args .progress_bar :
251+ # tqdm progress bar (best for interactive/terminal use; the
252+ # carriage-return redraws don't render cleanly in Airflow logs)
253+ futures = [
254+ executor .submit (process_record , record , knack_obj , app )
255+ for record in todos
256+ ]
257+ for future in tqdm (
258+ futures , total = len (futures ), desc = "Processing records" , unit = "rec"
259+ ):
260+ future .result () # This will re-raise any exceptions encountered in the threads
261+ else :
262+ # Default: periodic log messages, which render cleanly in Airflow's
263+ # log viewer
264+ futures = []
265+ count = 0
266+ for record in todos :
267+ futures .append (
268+ executor .submit (process_record , record , knack_obj , app , count )
269+ )
270+ count += 1
271+
272+ for future in futures :
273+ future .result () # This will re-raise any exceptions encountered in the threads
248274
249275
250276if __name__ == "__main__" :
0 commit comments