From b9a698e4eeaef5ec767a28dad9850b4204752ec5 Mon Sep 17 00:00:00 2001 From: Neveu Date: Fri, 10 Apr 2026 16:27:48 -0700 Subject: [PATCH 1/2] working on grabbing lcls elements from oracle. --- slac_db/create/lcls_elements.py | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/slac_db/create/lcls_elements.py b/slac_db/create/lcls_elements.py index 988bbae..b4753c9 100644 --- a/slac_db/create/lcls_elements.py +++ b/slac_db/create/lcls_elements.py @@ -1,7 +1,36 @@ import csv +from sqlalchemy import create_engine, text import slac_db.config import slac_db.oracle +_ORACLE_TNS = 'slacprod' # name of Oracle DB on prod +_ORACLE_USERNAME = 'lcls_read' + +def get_lcls_elements_csv(csv_output='lcls_elements.csv'): + """Get the lcls_elements.csv file from Oracle. + This function only works on production. + + Args: + csv_output: Name of the output csv file. + """ + password = _get_oracle_pw(_ORACLE_USERNAME) + connection_string = f'oracle+cx_oracle://{_ORACLE_USERNAME}:{password}@{_ORACLE_TNS}' + engine = create_engine(connection_string) + sql_query = text("select * from lcls_infrastructure.V_LCLS_ELEMENTS_DIAG") + + try: + with engine.connect() as connection: + import pandas as pd + df = pd.read_sql(sql_query, connection) + df.to_csv(csv_output, index=False) + + except Exception as e: + print(f"An error occurred: {e}") + + engine.dispose() + return None + + def to_oracle_db(csv_source=None): """ Build oracle DB with SQLAlchemy. @@ -31,3 +60,16 @@ def _parse_csv(self, reader): values = [None if v == '' else v for v in row] self.rows[i] = dict(zip(names, values)) i += 1 + +def _get_oracle_pw(username=_ORACLE_USERNAME): + """Get Oracle password. This only works on production. + """ + try: + import subprocess + cmd = subprocess.run(['getPwd', username], capture_output=True, text=True, check=True) + password = cmd.stdout.strip() + return password + except Exception as e: + print(f"Could not get Oracle password: {e}") + return None + From bf8f9fa2e230cdee305dc3052b164408f89894d7 Mon Sep 17 00:00:00 2001 From: Neveu Date: Mon, 20 Apr 2026 23:25:35 -0700 Subject: [PATCH 2/2] working on remote oracle session --- slac_db/create/lcls_elements.py | 20 +----- slac_db/oracle_remote.py | 105 ++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 17 deletions(-) create mode 100644 slac_db/oracle_remote.py diff --git a/slac_db/create/lcls_elements.py b/slac_db/create/lcls_elements.py index b4753c9..8b91f77 100644 --- a/slac_db/create/lcls_elements.py +++ b/slac_db/create/lcls_elements.py @@ -2,9 +2,7 @@ from sqlalchemy import create_engine, text import slac_db.config import slac_db.oracle - -_ORACLE_TNS = 'slacprod' # name of Oracle DB on prod -_ORACLE_USERNAME = 'lcls_read' +from slac_db.oracle_remote import get_connection def get_lcls_elements_csv(csv_output='lcls_elements.csv'): """Get the lcls_elements.csv file from Oracle. @@ -13,9 +11,7 @@ def get_lcls_elements_csv(csv_output='lcls_elements.csv'): Args: csv_output: Name of the output csv file. """ - password = _get_oracle_pw(_ORACLE_USERNAME) - connection_string = f'oracle+cx_oracle://{_ORACLE_USERNAME}:{password}@{_ORACLE_TNS}' - engine = create_engine(connection_string) + engine = get_connection() sql_query = text("select * from lcls_infrastructure.V_LCLS_ELEMENTS_DIAG") try: @@ -61,15 +57,5 @@ def _parse_csv(self, reader): self.rows[i] = dict(zip(names, values)) i += 1 -def _get_oracle_pw(username=_ORACLE_USERNAME): - """Get Oracle password. This only works on production. - """ - try: - import subprocess - cmd = subprocess.run(['getPwd', username], capture_output=True, text=True, check=True) - password = cmd.stdout.strip() - return password - except Exception as e: - print(f"Could not get Oracle password: {e}") - return None + diff --git a/slac_db/oracle_remote.py b/slac_db/oracle_remote.py new file mode 100644 index 0000000..a239ae4 --- /dev/null +++ b/slac_db/oracle_remote.py @@ -0,0 +1,105 @@ +import slac_db.config +import sqlalchemy +import pykern.sql_db +import os.path +import os +from oracle import get_address_header, get_devices, get_all_rows, get_device_row, get_beampaths, get_areas, recreate + +_ORACLE_TNS = 'slacprod' # name/connection of Oracle DB on prod +_ORACLE_USERNAME = 'lcls_read' + +_meta = None + +def get_address_header(device=None): + """Get address header of a device. + + Args: + device (str): MAD name of the device as found in Oracle. + + Returns: + tuple: The address header. + """ + with _session() as s: + return s.select_one( + sqlalchemy.select( + s.t.elements.c["control system name"] + ).where( + s.t.elements.c["element"] == device + ) + )["control system name"] + + +def get_beampaths(): + """Get all beampaths from Oracle. + + Returns: + List of beampaths sorted alphabetically. + """ + beampaths = set() + def parse_beampaths(beampath_csv): + if beampath_csv is None: + return + c = beampath_csv.replace(' ', '').split(',') + c = filter(None, c) + beampaths.update(c) + + with _session() as s: + query = sqlalchemy.select(s.t.elements.c.beampath).distinct() + for r in s.select(query): + parse_beampaths(r.beampath) + return sorted(list(beampaths)) + +def get_connection(): + """Start and return connection to Oracle. This only works on production.""" + password = _get_oracle_pw(_ORACLE_USERNAME) + connection_string = _get_remote_uri() + engine = sqlalchemy.create_engine(connection_string) + return engine.connect() # TODO: Do I need to watch out how this is closed if I pass this way? + +def _get_oracle_pw(username=_ORACLE_USERNAME): + """Get Oracle password. This only works on production. + """ + try: + import subprocess + cmd = subprocess.run(['getPwd', username], capture_output=True, text=True, check=True) + password = cmd.stdout.strip() + return password + except Exception as e: + print(f"Could not get Oracle password: {e}") + return None + +def _get_remote_uri(): + """Get string needed to connect to Oracle remotely. + """ + password = _get_oracle_pw(_ORACLE_USERNAME) + connection_string = f'oracle+cx_oracle://{_ORACLE_USERNAME}:{password}@{_ORACLE_TNS}' + return connection_string + + +def _init_remote_db(): + """Assumes remote oracle DB. TODO: Make this more general? + + _meta: wrapper that holds sqlalchemy metadata. + """ + global _meta + connection = get_connection() + #TODO: grab schema from Oracle? + schema = None + #TODO: is uri just the connection string? + uri = _get_remote_uri() + _meta = pykern.sql_db.Meta( + uri=uri, + schema=None + ) + return + +def _oracle_location(): + loc = ( + slac_db.config.package_data() / 'lcls_elements.sqlite3' + ) + return str(loc) + +def _session(): + if _meta is None: + _init_remote_db() + return _meta.session()