From 0fafba60b5c12ed1ebbc90072415a2e2859c5cb3 Mon Sep 17 00:00:00 2001 From: bikegeek <3753118+bikegeek@users.noreply.github.com> Date: Wed, 26 Jun 2024 11:13:07 -0600 Subject: [PATCH] Feature 307 schema total dir (#311) * Issue #307 TOTAL_DIR for VL1L2, VAL1L2, and VCNT linetypes * Issue #307 added TOTAL_WIND to VL1L2, VAL1L2, and VCNT * Issue #307 initial commit of test for VL1L2, VAL1L2, and VCNT for the TOTAL_DIR column * issue #307 config file for tests * issue #307 modify setup_db to return the connection object instead of the database settings * modify setup to return only the database data class * updated to separate the hostname from the port in the config file and return the connection object in the setup code * update to separate the host and port and simplify test setup code * fix indentation and complete the try-finally block * add test to check if db is created and fix test for tables test * added test for presence of the mv_load_test DB * Added test to check for the total_dir column in the VL1L2, VAL1L2, and VCNT linetype tables * Clean up extraneous blank lines * Sample data for testing --------- Co-authored-by: Minna Win --- METdbLoad/sql/mv_mysql.sql | 5 +- .../sql/updates/update_for_6_0_beta5.sql | 13 ++ .../update_schema_6.0_beta4/test_loading.py | 99 +++-------- .../update_schema_6.0_beta4/test_loading.yaml | 3 +- .../Data/total_dir.tar | Bin 0 -> 81920 bytes .../update_schema_6.0_beta5/test_loading.yaml | 7 + .../update_schema_6.0_beta5/test_schema.py | 154 ++++++++++++++++++ 7 files changed, 205 insertions(+), 76 deletions(-) create mode 100644 METdbLoad/sql/updates/update_for_6_0_beta5.sql create mode 100644 METdbLoad/tests/update_schema_6.0_beta5/Data/total_dir.tar create mode 100644 METdbLoad/tests/update_schema_6.0_beta5/test_loading.yaml create mode 100644 METdbLoad/tests/update_schema_6.0_beta5/test_schema.py diff --git a/METdbLoad/sql/mv_mysql.sql b/METdbLoad/sql/mv_mysql.sql index ddb990ab..05d4f04a 100644 --- a/METdbLoad/sql/mv_mysql.sql +++ b/METdbLoad/sql/mv_mysql.sql @@ -912,6 +912,7 @@ CREATE TABLE line_data_vl1l2 uvoobar DOUBLE, f_speed_bar DOUBLE DEFAULT -9999, o_speed_bar DOUBLE DEFAULT -9999, + total_dir INT UNSIGNED, dir_me DOUBLE, dir_mae DOUBLE, dir_mse DOUBLE, @@ -953,6 +954,7 @@ CREATE TABLE line_data_val1l2 uvooabar DOUBLE, fa_speed_bar DOUBLE DEFAULT -9999, oa_speed_bar DOUBLE DEFAULT -9999, + total_dir INT UNSIGNED, dira_me DOUBLE, dira_mae DOUBLE, dira_mse DOUBLE, @@ -1590,7 +1592,6 @@ CREATE TABLE line_data_ssvar obs_valid_end DATETIME, alpha DOUBLE, total INT UNSIGNED, - n_bin INT UNSIGNED, bin_i INT UNSIGNED, bin_n INT UNSIGNED, @@ -1602,7 +1603,6 @@ CREATE TABLE line_data_ssvar fobar DOUBLE, ffbar DOUBLE, oobar DOUBLE, - fbar_ncl DOUBLE, fbar_ncu DOUBLE, fstdev DOUBLE, @@ -1714,6 +1714,7 @@ CREATE TABLE line_data_vcnt anom_corr_uncntr DOUBLE DEFAULT -9999, anom_corr_uncntr_bcl DOUBLE DEFAULT -9999, anom_corr_uncntr_bcu DOUBLE DEFAULT -9999, + total_dir INT UNSIGNED, dir_me DOUBLE, dir_me_bcl DOUBLE, dir_me_bcu DOUBLE, diff --git a/METdbLoad/sql/updates/update_for_6_0_beta5.sql b/METdbLoad/sql/updates/update_for_6_0_beta5.sql new file mode 100644 index 00000000..e51cfb0a --- /dev/null +++ b/METdbLoad/sql/updates/update_for_6_0_beta5.sql @@ -0,0 +1,13 @@ +DELIMITER | + + +ALTER TABLE line_data_vcnt + ADD COLUMN total_dir DOUBLE | + +ALTER TABLE line_data_val1l2 + ADD COLUMN total_dir DOUBLE | + +ALTER TABLE line_data_vl1l2 + ADD COLUMN total_dir DOUBLE | + +DELIMITER ; diff --git a/METdbLoad/tests/update_schema_6.0_beta4/test_loading.py b/METdbLoad/tests/update_schema_6.0_beta4/test_loading.py index 192abf11..4d5978db 100644 --- a/METdbLoad/tests/update_schema_6.0_beta4/test_loading.py +++ b/METdbLoad/tests/update_schema_6.0_beta4/test_loading.py @@ -29,13 +29,20 @@ def setup_db(): print(exc) # Create a dataclass of the database information - #TCDiag = make_dataclass("TCDiag", ["total", "index", "diag_src", "diag_val"], frozen=True) - #orig = TCDiag(orig_total, orig_index, orig_diag_src, orig_diag_val) - DBS = make_dataclass("DBS", ["hostname", "username", "password", "dbname"]) - db_settings = DBS(parms['hostname'], parms['username'], parms['password'], parms['dbname']) + DBS = make_dataclass("DBS", ["hostname", "port", "username", "password", "dbname"]) + db_settings = DBS(parms['hostname'], parms['port'], parms['username'], parms['password'], parms['dbname']) # Return the db settings (hostname, username, etc.) - yield db_settings + conn = pymysql.connect( + host=db_settings.hostname, + port=db_settings.port, + user=db_settings.username, + password=db_settings.password, + db=db_settings.dbname, + charset='utf8mb4' + ) + + yield conn def test_ecnt_db_created(setup_db): @@ -45,16 +52,8 @@ def test_ecnt_db_created(setup_db): # found. - conn = pymysql.connect( - host=setup_db.hostname, - user=setup_db.username, - password=setup_db.password, - db=setup_db.dbname, - charset='utf8mb4' - ) - try: - with conn.cursor() as cursor: + with setup_db.cursor() as cursor: # Check that the mv_load_test database was created check_db_exists_query = "show databases;" cursor.execute(check_db_exists_query) @@ -69,23 +68,14 @@ def test_ecnt_db_created(setup_db): finally: - conn.close() + setup_db.close() def test_tables_created(setup_db): # log into the database and verify the ECNT, VCNT, VL1L2, and VAL1L2 tables exist - - conn = pymysql.connect( - host=setup_db.hostname, - user=setup_db.username, - password=setup_db.password, - db=setup_db.dbname, - charset='utf8mb4' - ) - try: - with conn.cursor() as cursor: + with setup_db.cursor() as cursor: # Check that the line_data_ecnt, line_data_vcnt, line_data_vl1l2, and # line_data_val1l2 tables were created cursor.execute(CONST_LOAD_DB_CMD) @@ -102,23 +92,15 @@ def test_tables_created(setup_db): assert 'line_data_val1l2' in list_of_rows finally: - conn.close() + setup_db.close() def test_ecnt_columns(setup_db): # log into the database and verify the ign_conv_oerr and ign_corr_oerr columns are in the # list_data_ecnt database table. - conn = pymysql.connect( - host=setup_db.hostname, - user=setup_db.username, - password=setup_db.password, - db=setup_db.dbname, - charset='utf8mb4' - ) - try: - with conn.cursor() as cursor: + with setup_db.cursor() as cursor: # Check that the line_data_ecnt, line_data_vcnt, line_data_vl1l2, and # line_data_val1l2 tables were created cursor.execute(CONST_LOAD_DB_CMD) @@ -134,7 +116,7 @@ def test_ecnt_columns(setup_db): assert 'ign_corr_oerr' in list_of_rows finally: - conn.close() + setup_db.close() def test_vcnt_columns(setup_db): # log into the database and verify the dir_me, dir_me_bcl, dir_me_bcu, ..., etc. columns are in the @@ -145,16 +127,9 @@ def test_vcnt_columns(setup_db): 'dir_mse', 'dir_mse_bcl', 'dir_mse_bcu', 'dir_rmse', 'dir_rmse_bcl', 'dir_rmse_bcu' ] - conn = pymysql.connect( - host=setup_db.hostname, - user=setup_db.username, - password=setup_db.password, - db=setup_db.dbname, - charset='utf8mb4' - ) try: - with conn.cursor() as cursor: + with setup_db.cursor() as cursor: # Check that the line_data_vcnt expected columns were created cursor.execute(CONST_LOAD_DB_CMD) @@ -169,23 +144,16 @@ def test_vcnt_columns(setup_db): assert expected in list_of_rows finally: - conn.close() + setup_db.close() def test_vl1l2_columns(setup_db): # log into the database and verify the dir_me, dir_mae, and dir_mse columns are in the # list_data_vl1l2 database table. expected_cols = ['dir_me', 'dir_mae', 'dir_mse'] - conn = pymysql.connect( - host=setup_db.hostname, - user=setup_db.username, - password=setup_db.password, - db=setup_db.dbname, - charset='utf8mb4' - ) try: - with conn.cursor() as cursor: + with setup_db.cursor() as cursor: # Check that the line_data_vl1l2 table has the expected columns cursor.execute(CONST_LOAD_DB_CMD) @@ -200,23 +168,16 @@ def test_vl1l2_columns(setup_db): assert expected in list_of_rows finally: - conn.close() + setup_db.close() def test_val1l2_columns(setup_db): # log into the database and verify the dira_me, dira_mae, and dira_mse columns are in the # list_data_val1l2 database table. expected_cols = ['dira_me', 'dira_mae', 'dira_mse'] - conn = pymysql.connect( - host=setup_db.hostname, - user=setup_db.username, - password=setup_db.password, - db=setup_db.dbname, - charset='utf8mb4' - ) try: - with conn.cursor() as cursor: + with setup_db.cursor() as cursor: # Check that the line_data_vl1l2 table has the expected columns cursor.execute(CONST_LOAD_DB_CMD) @@ -231,7 +192,7 @@ def test_val1l2_columns(setup_db): assert expected in list_of_rows finally: - conn.close() + setup_db.close() def test_ecnt_vals(setup_db): @@ -242,16 +203,8 @@ def test_ecnt_vals(setup_db): ign_conv_oerr = "33.41424" ign_corr_oerr = "440.06905" - conn = pymysql.connect( - host=setup_db.hostname, - user=setup_db.username, - password=setup_db.password, - db=setup_db.dbname, - charset='utf8mb4' - ) - try: - with conn.cursor() as cursor: + with setup_db.cursor() as cursor: # Check that the line_data_vl1l2 table has the expected columns cursor.execute(CONST_LOAD_DB_CMD) @@ -266,6 +219,6 @@ def test_ecnt_vals(setup_db): finally: - conn.close() + setup_db.close() diff --git a/METdbLoad/tests/update_schema_6.0_beta4/test_loading.yaml b/METdbLoad/tests/update_schema_6.0_beta4/test_loading.yaml index 532eb3d8..7172865a 100644 --- a/METdbLoad/tests/update_schema_6.0_beta4/test_loading.yaml +++ b/METdbLoad/tests/update_schema_6.0_beta4/test_loading.yaml @@ -1,4 +1,5 @@ -hostname: 'localhost:3306' +hostname: 'localhost' +port: 3306 username: 'mvadmin' password: 'passwordgoeshere' dbname: 'mv_load_test' diff --git a/METdbLoad/tests/update_schema_6.0_beta5/Data/total_dir.tar b/METdbLoad/tests/update_schema_6.0_beta5/Data/total_dir.tar new file mode 100644 index 0000000000000000000000000000000000000000..30528723ea737e83d22f3689e4a0019dab72176f GIT binary patch literal 81920 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 Qxc0!c2d+Kv