11import logging
22import argparse
3+ from datetime import datetime
34from sqlalchemy import create_engine
45from modules .DataLoadManager import DataLoadManager
56from modules .shared import Constants
89from sqlalchemy .orm import sessionmaker
910
1011_LOG_LEVEL_STRINGS = ['CRITICAL' , 'ERROR' , 'WARNING' , 'INFO' , 'DEBUG' ]
12+ _AUDIT_FUNCTION_OPTIONS = {
13+ 'FULL' : DataLoadTrackerRepository .get_full_refresh_since ,
14+ 'INCR' : DataLoadTrackerRepository .get_only_incremental_since ,
15+ }
1116
1217
1318class RelationalDataLoader :
@@ -16,19 +21,33 @@ def __init__(self, logger=None):
1621 self .data_source_factory = DataSourceFactory ()
1722
1823 def main (self ):
19- args = self .get_arguments ()
24+ self . args = self .get_arguments ()
2025
21- self .configure_root_logger (args .log_level )
26+ self .configure_root_logger (self . args .log_level )
2227
23- source_db = self .data_source_factory . create_source ( args .source_connection_string )
28+ self .args .func ( )
2429
25- destination_db = create_engine (args .destination_connection_string )
30+ def execute_process_command (self ):
31+ source_db = self .data_source_factory .create_source (self .args .source_connection_string )
32+
33+ destination_db = create_engine (self .args .destination_connection_string )
2634 session_maker = sessionmaker (bind = destination_db )
2735 repository = DataLoadTrackerRepository (session_maker )
2836 repository .ensure_schema_exists (destination_db )
2937
30- data_load_manager = DataLoadManager (args .configuration_folder , source_db , destination_db , repository )
31- data_load_manager .start_imports (args .force_full_refresh_models )
38+ data_load_manager = DataLoadManager (self .args .configuration_folder , source_db , destination_db , repository )
39+ data_load_manager .start_imports (self .args .force_full_refresh_models )
40+
41+ def execute_audit_command (self ):
42+ destination_db = create_engine (self .args .destination_connection_string )
43+ session_maker = sessionmaker (bind = destination_db )
44+ data_load_tracker_repository = DataLoadTrackerRepository (session_maker )
45+
46+ last_successful_timestamp = datetime .fromisoformat (self .args .timestamp )
47+
48+ results = _AUDIT_FUNCTION_OPTIONS [self .args .model_type ](data_load_tracker_repository , last_successful_timestamp )
49+
50+ print (results .join (" " ))
3251
3352 def configure_root_logger (self , log_level ):
3453 # get the root logger
@@ -67,39 +86,81 @@ def raw_connection_string_to_valid_source_connection_string(self, connection_str
6786 def get_arguments (self ):
6887 parser = argparse .ArgumentParser (description = Constants .APP_NAME )
6988
70- parser .add_argument (
89+ subparsers = parser .add_subparsers (title = 'commands' , metavar = '' , dest = 'command' )
90+
91+ process_command_parser = subparsers .add_parser ('process' , help = 'processes load models' )
92+ process_command_parser .set_defaults (func = self .execute_process_command )
93+
94+ process_command_parser .add_argument (
7195 'source_connection_string' ,
7296 metavar = 'source-connection-string' ,
7397 type = self .raw_connection_string_to_valid_source_connection_string ,
7498 help = 'The source connections string as a 64bit ODBC system dsn. Eg: mssql+pyodbc://dwsource or '
7599 'csv://c://some//Path//To//Csv//Files//' )
76100
77- parser .add_argument ('destination_connection_string' ,
78- metavar = 'destination-connection-string' ,
79- help = 'The destination database connection string. Provide in PostgreSQL + Psycopg format. '
80- 'Eg: \' postgresql+psycopg2://username:password@host:port/dbname\' ' )
81-
82- parser .add_argument ('configuration_folder' ,
83- metavar = 'configuration-folder' ,
84- help = 'Absolute or relative path to the models. '
85- 'Eg \' ./models\' , \' C:/path/to/models\' ' )
86-
87- parser .add_argument ('-f' ,
88- '--force-full-refresh-models' ,
89- nargs = '?' ,
90- const = '*' ,
91- help = 'Comma separated model names in the configuration folder. These models would be '
92- 'forcefully refreshed dropping and recreating the destination tables. All others '
93- 'models would only be refreshed if required as per the state of the source and '
94- 'destination tables. '
95- 'Eg \' CompoundPkTest,LargeTableTest\' . '
96- 'Leave blank or use glob (*) to force full refresh of all models.' )
97-
98- parser .add_argument ('-l' , '--log-level' ,
99- default = 'INFO' ,
100- type = self .log_level_string_to_int ,
101- nargs = '?' ,
102- help = f'Set the logging output level. { _LOG_LEVEL_STRINGS } ' )
101+ process_command_parser .add_argument (
102+ 'destination_connection_string' ,
103+ metavar = 'destination-connection-string' ,
104+ help = 'The destination database connection string. Provide in PostgreSQL'
105+ ' + Psycopg format. '
106+ 'Eg: \' postgresql+psycopg2://username:password@host:port/dbname\' ' )
107+
108+ process_command_parser .add_argument (
109+ 'configuration_folder' ,
110+ metavar = 'configuration-folder' ,
111+ help = 'Absolute or relative path to the models. '
112+ 'Eg \' ./models\' , \' C:/path/to/models\' ' )
113+
114+ process_command_parser .add_argument (
115+ '-f' ,
116+ '--force-full-refresh-models' ,
117+ nargs = '?' ,
118+ const = '*' ,
119+ help = 'Comma separated model names in the configuration folder. '
120+ 'These models would be forcefully refreshed dropping and recreating the '
121+ 'destination tables. All others models would only be refreshed if required '
122+ 'as per the state of the source and destination tables. '
123+ 'Eg \' CompoundPkTest,LargeTableTest\' . '
124+ 'Leave blank or use glob (*) to force full refresh of all models.' )
125+
126+ process_command_parser .add_argument (
127+ '-l' , '--log-level' ,
128+ default = 'INFO' ,
129+ type = self .log_level_string_to_int ,
130+ nargs = '?' ,
131+ help = f'Set the logging output level. { _LOG_LEVEL_STRINGS } ' )
132+
133+ audit_command_parser = subparsers .add_parser ('audit' ,
134+ help = 'provides list of processed models since a given timestamp' )
135+ audit_command_parser .set_defaults (func = self .execute_audit_command )
136+
137+ audit_command_parser .add_argument (
138+ 'destination_connection_string' ,
139+ metavar = 'destination-connection-string' ,
140+ help = 'The destination database connection string. Provide in PostgreSQL'
141+ ' + Psycopg format. '
142+ 'Eg: \' postgresql+psycopg2://username:password@host:port/dbname\' ' )
143+
144+ audit_command_parser .add_argument (
145+ 'model_type' ,
146+ metavar = 'model-type' ,
147+ choices = _AUDIT_FUNCTION_OPTIONS .keys (),
148+ help = 'Use the command FULL to return full refresh models or the '
149+ 'command INCR to return only the incremental models since the timestamp' )
150+
151+ audit_command_parser .add_argument (
152+ 'timestamp' ,
153+ metavar = 'timestamp' ,
154+ help = 'ISO 8601 datetime with timezone (yyyy-mm-ddThh:mm:ss.nnnnnn+|-hh:mm) used to provide information '
155+ 'on all actions since the specified date. '
156+ 'Eg \' 2019-02-14T01:55:54.123456+00:00\' . ' )
157+
158+ audit_command_parser .add_argument (
159+ '-l' , '--log-level' ,
160+ default = 'INFO' ,
161+ type = self .log_level_string_to_int ,
162+ nargs = '?' ,
163+ help = f'Set the logging output level. { _LOG_LEVEL_STRINGS } ' )
103164
104165 return parser .parse_args ()
105166
0 commit comments