def test_perform_actual_upgrade_5_to_6(): fixpath = os.path.join(fixturepath, 'db_files', 'version5') db_file = 'empty.db' dbname_old = os.path.join(fixpath, db_file) if not os.path.exists(dbname_old): pytest.skip("No db-file fixtures found. You can generate test db-files" " using the scripts in the " "https://github.com/QCoDeS/qcodes_generate_test_db/ repo") with temporarily_copied_DB(dbname_old, debug=False, version=5) as conn: perform_db_upgrade_5_to_6(conn) assert get_user_version(conn) == 6 db_file = 'some_runs.db' dbname_old = os.path.join(fixpath, db_file) with temporarily_copied_DB(dbname_old, debug=False, version=5) as conn: perform_db_upgrade_5_to_6(conn) assert get_user_version(conn) == 6 no_of_runs_query = "SELECT max(run_id) FROM runs" no_of_runs = one( atomic_transaction(conn, no_of_runs_query), 'max(run_id)') assert no_of_runs == 10 for run_id in range(1, no_of_runs + 1): json_str = get_run_description(conn, run_id) deser = json.loads(json_str) assert deser['version'] == 0 desc = serial.from_json_to_current(json_str) assert desc._version == 1
def test_version_4a_bugfix(): v4fixpath = os.path.join(fixturepath, 'db_files', 'version4a') dbname_old = os.path.join(v4fixpath, 'some_runs.db') if not os.path.exists(dbname_old): pytest.skip("No db-file fixtures found. You can generate test db-files" " using the scripts in the legacy_DB_generation folder") with temporarily_copied_DB(dbname_old, debug=False, version=4) as conn: dd = fix_version_4a_run_description_bug(conn) assert dd['runs_inspected'] == 10 assert dd['runs_fixed'] == 10 # Ensure the structure of the run_description JSON after applying # the fix function for run_id in range(1, 10 + 1): rd_str = get_run_description(conn, run_id) rd_dict = json.loads(rd_str) assert list(rd_dict.keys()) == ['interdependencies'] assert list(rd_dict['interdependencies'].keys()) == ['paramspecs'] dd = fix_version_4a_run_description_bug(conn) assert dd['runs_inspected'] == 10 assert dd['runs_fixed'] == 0
def fix_version_4a_run_description_bug(conn: ConnectionPlus) -> Dict[str, int]: """ Fix function to fix a bug where the RunDescriber accidentally wrote itself to string using the (new) InterDependencies_ object instead of the (old) InterDependencies object. After the first call, this function should be idempotent. Args: conn: the connection to the database Returns: A dict with the fix results ('runs_inspected', 'runs_fixed') """ user_version = get_user_version(conn) if not user_version == 4: raise RuntimeError('Database of wrong version. Will not apply fix. ' 'Expected version 4, found version {user_version}') no_of_runs_query = "SELECT max(run_id) FROM runs" no_of_runs = one(atomic_transaction(conn, no_of_runs_query), 'max(run_id)') no_of_runs = no_of_runs or 0 with atomic(conn) as conn: pbar = tqdm(range(1, no_of_runs+1)) pbar.set_description("Fixing database") # collect some metrics runs_inspected = 0 runs_fixed = 0 old_style_keys = ['paramspecs'] new_style_keys = ['parameters', 'dependencies', 'inferences', 'standalones'] for run_id in pbar: desc_str = get_run_description(conn, run_id) desc_ser = json.loads(desc_str) idps_ser = desc_ser['interdependencies'] if list(idps_ser.keys()) == old_style_keys: pass elif list(idps_ser.keys()) == new_style_keys: old_desc_ser = \ _convert_run_describer_v1_like_dict_to_v0_like_dict( desc_ser) json_str = json.dumps(old_desc_ser) _update_run_description(conn, run_id, json_str) runs_fixed += 1 else: raise RuntimeError(f'Invalid runs_description for run_id: ' f'{run_id}') runs_inspected += 1 return {'runs_inspected': runs_inspected, 'runs_fixed': runs_fixed}
def upgrade_5_to_6(conn: ConnectionPlus) -> None: """ Perform the upgrade from version 5 to version 6. The upgrade ensures that the runs_description has a top-level entry called 'version'. Note that version changes of the runs_description will not be tracked as schema upgrades. """ no_of_runs_query = "SELECT max(run_id) FROM runs" no_of_runs = one(atomic_transaction(conn, no_of_runs_query), 'max(run_id)') no_of_runs = no_of_runs or 0 # If one run fails, we want the whole upgrade to roll back, hence the # entire upgrade is one atomic transaction with atomic(conn) as conn: pbar = tqdm(range(1, no_of_runs + 1)) pbar.set_description("Upgrading database, version 5 -> 6") empty_idps_ser = InterDependencies()._to_dict() for run_id in pbar: json_str = get_run_description(conn, run_id) if json_str is None: new_json = json.dumps({ 'version': 0, 'interdependencies': empty_idps_ser }) else: ser = json.loads(json_str) new_ser = {'version': 0} # let 'version' be the first entry new_ser['interdependencies'] = ser['interdependencies'] new_json = json.dumps(new_ser) update_run_description(conn, run_id, new_json)
def test_fix_wrong_run_descriptions(): v3fixpath = os.path.join(fixturepath, 'db_files', 'version3') dbname_old = os.path.join(v3fixpath, 'some_runs_without_run_description.db') if not os.path.exists(dbname_old): pytest.skip("No db-file fixtures found. You can generate test db-files" " using the scripts in the legacy_DB_generation folder") def make_ps(n): ps = ParamSpec(f'p{n}', label=f'Parameter {n}', unit=f'unit {n}', paramtype='numeric') return ps paramspecs = [make_ps(n) for n in range(6)] paramspecs[2]._inferred_from = ['p0'] paramspecs[3]._inferred_from = ['p1', 'p0'] paramspecs[4]._depends_on = ['p2', 'p3'] paramspecs[5]._inferred_from = ['p0'] with temporarily_copied_DB(dbname_old, debug=False, version=3) as conn: assert get_user_version(conn) == 3 expected_description = v0.RunDescriber( v0.InterDependencies(*paramspecs)) empty_description = v0.RunDescriber(v0.InterDependencies()) fix_wrong_run_descriptions(conn, [1, 2, 3, 4]) for run_id in [1, 2, 3]: desc_str = get_run_description(conn, run_id) desc = serial.from_json_to_native(desc_str) assert desc == expected_description desc_str = get_run_description(conn, run_id=4) desc = serial.from_json_to_native(desc_str) assert desc == empty_description
def fix_version_4a_run_description_bug(conn: ConnectionPlus) -> Dict[str, int]: """ Fix function to fix a bug where the RunDescriber accidentally wrote itself to string using the (new) InterDependencies_ object instead of the (old) InterDependencies object. After the first run, this function should be idempotent. Args: conn: the connection to the database Returns: A dict with the fix results ('runs_inspected', 'runs_fixed') """ user_version = get_user_version(conn) if not user_version == 4: raise RuntimeError('Database of wrong version. Will not apply fix. ' 'Expected version 4, found version {user_version}') no_of_runs_query = "SELECT max(run_id) FROM runs" no_of_runs = one(atomic_transaction(conn, no_of_runs_query), 'max(run_id)') no_of_runs = no_of_runs or 0 with atomic(conn) as conn: pbar = tqdm(range(1, no_of_runs + 1)) pbar.set_description("Fixing database") # collect some metrics runs_inspected = 0 runs_fixed = 0 for run_id in pbar: desc_str = get_run_description(conn, run_id) desc_ser = json.loads(desc_str) idps_ser = desc_ser['interdependencies'] if RunDescriber._is_description_old_style(idps_ser): pass else: new_desc = RunDescriber.from_json(desc_str) update_run_description(conn, run_id, new_desc.to_json()) runs_fixed += 1 runs_inspected += 1 return {'runs_inspected': runs_inspected, 'runs_fixed': runs_fixed}
def _get_run_description_from_db(self) -> RunDescriber: """ Look up the run_description from the database """ desc_str = get_run_description(self.conn, self.run_id) return RunDescriber.from_json(desc_str)