Skip to content

Commit

Permalink
Add host, port optional arguments
Browse files Browse the repository at this point in the history
Add host port arguments
  • Loading branch information
einarsi authored Sep 11, 2020
2 parents 76a9b3c + e3555e6 commit 7e17d36
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 46 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ extratests
extratests/*

# tagreader cache file
.h5
*.h5

# vscode
.vscode/
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Changelog

This changelog is deprecated. All changes are documented under [releases](https://github.com/equinor/tagreader-python/releases).

## [1.1.0] - 2020-06-18
Improved handling of tags with maps for Aspen IP.21.

Expand Down
11 changes: 11 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,24 @@ steps:

- script: |
coverage erase
del /f coverage.xml
coverage run -m pytest tests extratests
coverage xml --include='tagreader/*'
condition: and(succeeded(), eq(variables['python.version'], '3.6'))
displayName: 'Run tests with coverage'
env:
HTTPS_PROXY: $(var_http_proxy)

- script: |
echo $(python.version)
pytest tests extratests
condition: and(succeeded(), not(eq(variables['python.version'], '3.6')))
displayName: 'Run tests'
env:
HTTPS_PROXY: $(var_http_proxy)

- task: PublishCodeCoverageResults@1
condition: and(succeeded(), eq(variables['python.version'], '3.6'))
inputs:
codeCoverageTool: Cobertura
summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml'
10 changes: 8 additions & 2 deletions docs/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ The following input arguments can be used when connecting to either `piwebapi` o
* `verifySSL` (optional): Whether to verify SSL certificate sent from server. **Default**: True.
* `auth` (optional): Auth object to pass to the server for authentication. **Default**: Kerberos-based auth object that works with Equinor servers. If not connecting to an Equinor server, you need to create your own auth object.


If `imstype` is an ODBC type, i.e. `pi` or `ip21`, the host and port to connect to will by default be found by performing a search in Windows registry. For some systems this may not work. In those cases the user can explicitly specify the following optional parameters:

* `host` (optional): Overrides mapping of datasource to hostname via Windows registry.
* `port` (optional): Overrides mapping of datasource to port number via Windows registry. **Default**: 10014 for `ip21` and 5450 for `pi`.

## Connecting to data source

After creating the client as described above, connect to the server with the `connect()` method.
Expand Down Expand Up @@ -245,14 +251,14 @@ Read interpolated data for the provided tag with 3-minute intervals between the
``` python
c = tagreader.IMSClient("PINO", "pi")
c.connect()
df = c.read(['BA: ACTIVE.1'], '05-Jan-2020 08:00:00', '05/01/20 11:30am', 180)
df = c.read(['BA:ACTIVE.1'], '05-Jan-2020 08:00:00', '05/01/20 11:30am', 180)

```

Read the average value for the two provided tags within each 3-minute interval between the two time stamps:

``` python
df = c.read(['BA: CONC.1'], '05-Jan-2020 08:00:00', '05/01/20 11:30am', 180, read_type=tagreader.ReaderType.AVG)
df = c.read(['BA:CONC.1'], '05-Jan-2020 08:00:00', '05/01/20 11:30am', 180, read_type=tagreader.ReaderType.AVG)
```

## Caching results
Expand Down
57 changes: 38 additions & 19 deletions tagreader/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,25 +162,37 @@ def get_server_address_pi(datasource):
return None


def get_handler(imstype, datasource, url=None, options={}, verifySSL=None, auth=None):
accepted_values = ["pi", "aspen", "ip21", "piwebapi", "aspenone"]

if not imstype or imstype.lower() not in accepted_values:
raise ValueError(f"`imstype` must be one of {accepted_values}")
def get_handler(
imstype,
datasource,
url=None,
host=None,
port=None,
options={},
verifySSL=None,
auth=None,
):
accepted_imstypes = ["pi", "aspen", "ip21", "piwebapi", "aspenone"]

if not imstype or imstype.lower() not in accepted_imstypes:
raise ValueError(f"`imstype` must be one of {accepted_imstypes}")

if imstype.lower() == "pi":
if "PI ODBC Driver" not in pyodbc.drivers():
raise RuntimeError(
"No PI ODBC driver detected. "
"Either switch to Web API ('piweb') or install appropriate driver."
)
hostport = get_server_address_pi(datasource)
if not hostport:
raise ValueError(
f"Unable to locate data source '{datasource}'."
"Do you have correct permissions?"
)
host, port = hostport
if host is None:
hostport = get_server_address_pi(datasource)
if not hostport:
raise ValueError(
f"Unable to locate data source '{datasource}'."
"Do you have correct permissions?"
)
host, port = hostport
if port is None:
port = 5450
return PIHandlerODBC(host=host, port=port, options=options)

if imstype.lower() in ["aspen", "ip21"]:
Expand All @@ -189,13 +201,16 @@ def get_handler(imstype, datasource, url=None, options={}, verifySSL=None, auth=
"No Aspen SQLplus ODBC driver detected. Either switch to Web API "
"('aspenweb') or install appropriate driver."
)
hostport = get_server_address_aspen(datasource)
if not hostport:
raise ValueError(
f"Unable to locate data source '{datasource}'."
"Do you have correct permissions?"
)
host, port = hostport
if host is None:
hostport = get_server_address_aspen(datasource)
if not hostport:
raise ValueError(
f"Unable to locate data source '{datasource}'."
"Do you have correct permissions?"
)
host, port = hostport
if port is None:
port = 10014
return AspenHandlerODBC(host=host, port=port, options=options)

if imstype.lower() == "piwebapi":
Expand Down Expand Up @@ -224,6 +239,8 @@ def __init__(
imstype=None,
tz="Europe/Oslo",
url=None,
host=None,
port=None,
handler_options={},
verifySSL=None,
auth=None,
Expand All @@ -235,6 +252,8 @@ def __init__(
imstype,
datasource,
url=url,
host=host,
port=port,
options=handler_options,
verifySSL=verifySSL,
auth=auth,
Expand Down
36 changes: 19 additions & 17 deletions tagreader/odbc_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,13 @@ def __init__(self, host=None, port=None, options={}):
self.conn = None
self.cursor = None
self._max_rows = options.get("max_rows", 100000)
self._connection_string = options.get("connection_string", None)

@staticmethod
def generate_connection_string(host, port, max_rows=100000):
return f"DRIVER={{AspenTech SQLPlus}};HOST={host};PORT={port};READONLY=Y;MAXROWS={max_rows}" # noqa: E501
def generate_connection_string(self):
if self._connection_string is None:
return f"DRIVER={{AspenTech SQLPlus}};HOST={self.host};PORT={self.port};READONLY=Y;MAXROWS={self._max_rows}" # noqa E501
else:
return self._connection_string

@staticmethod
def generate_read_query(tag, mapdef, start_time, stop_time, sample_time, read_type):
Expand Down Expand Up @@ -171,9 +174,7 @@ def set_options(self, options):
pass

def connect(self):
connection_string = self.generate_connection_string(
self.host, self.port, max_rows=self._max_rows
)
connection_string = self.generate_connection_string()
# The default autocommit=False is not supported by PI odbc driver.
self.conn = pyodbc.connect(connection_string, autocommit=True)
self.cursor = self.conn.cursor()
Expand Down Expand Up @@ -354,17 +355,20 @@ def __init__(self, host=None, port=None, options={}):
# It seems that is actually not possible anymore.
# ws3099.statoil.net
self._das_server = options.get("das_server", "piwebapi.equinor.com")
self._connection_string = options.get("connection_string", None)

# print(self._das_server)

@staticmethod
def generate_connection_string(host, port, das_server):
return (
f"DRIVER={{PI ODBC Driver}};Server={das_server};"
"Trusted_Connection=Yes;Command Timeout=1800;Provider Type=PIOLEDB;"
f'Provider String={{Data source={host.replace(".statoil.net", "")};'
"Integrated_Security=SSPI;Time Zone=UTC};"
)
def generate_connection_string(self):
if self._connection_string is None:
return (
f"DRIVER={{PI ODBC Driver}};Server={self._das_server};"
"Trusted_Connection=Yes;Command Timeout=1800;Provider Type=PIOLEDB;"
f'Provider String={{Data source={self.host.replace(".statoil.net", "")};' # noqa: E501
"Integrated_Security=SSPI;Time Zone=UTC};"
)
else:
return self._connection_string

@staticmethod
def generate_search_query(tag=None, desc=None):
Expand Down Expand Up @@ -474,9 +478,7 @@ def set_options(self, options):
pass

def connect(self):
connection_string = self.generate_connection_string(
self.host, self.port, self._das_server
)
connection_string = self.generate_connection_string()
# The default autocommit=False is not supported by PI odbc driver.
self.conn = pyodbc.connect(connection_string, autocommit=True)
self.cursor = self.conn.cursor()
Expand Down
13 changes: 11 additions & 2 deletions tests/test_AspenHandlerODBC.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,17 @@
SAMPLE_TIME = 60


def test_generate_connection_string():
res = AspenHandlerODBC.generate_connection_string("thehostname", 1234, 567890)
@pytest.fixture(scope="module")
def AspenHandler():
from tagreader.odbc_handlers import AspenHandlerODBC

yield AspenHandlerODBC(
"thehostname", 1234, options={"max_rows": 567890}
)


def test_generate_connection_string(AspenHandler):
res = AspenHandler.generate_connection_string()
expected = (
"DRIVER={AspenTech SQLPlus};HOST=thehostname;PORT=1234;"
"READONLY=Y;MAXROWS=567890"
Expand Down
10 changes: 5 additions & 5 deletions tests/test_PIHandlerODBC.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
def PIHandler():
from tagreader.odbc_handlers import PIHandlerODBC

yield PIHandlerODBC("thehostname.statoil.net", 1234)
yield PIHandlerODBC(
"thehostname.statoil.net", 1234, options={"das_server": "the_das_server"}
)


def test_generate_connection_string(PIHandler):
res = PIHandler.generate_connection_string(
PIHandler.host, PIHandler.port, "das_server"
)
res = PIHandler.generate_connection_string()
expected = (
"DRIVER={PI ODBC Driver};Server=das_server;Trusted_Connection=Yes;"
"DRIVER={PI ODBC Driver};Server=the_das_server;Trusted_Connection=Yes;"
"Command Timeout=1800;Provider Type=PIOLEDB;"
"Provider String={Data source=thehostname;Integrated_Security=SSPI;"
"Time Zone=UTC};"
Expand Down
56 changes: 56 additions & 0 deletions tests/test_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,62 @@ def test_get_missing_intervals():
)


@pytest.mark.skipif(
is_GITHUBACTION, reason="ODBC drivers unavailable in GitHub Actions"
)
def test_PI_init_odbc_client_with_host_port():
host = "thehostname"
port = 999
c = IMSClient(datasource="whatever", imstype="pi", host=host)
assert c.handler.host == host
assert c.handler.port == 5450
c = IMSClient(datasource="whatever", imstype="pi", host=host, port=port)
assert c.handler.host == host
assert c.handler.port == port


@pytest.mark.skipif(
is_GITHUBACTION, reason="ODBC drivers unavailable in GitHub Actions"
)
def test_IP21_init_odbc_client_with_host_port():
host = "thehostname"
port = 999
c = IMSClient(datasource="whatever", imstype="ip21", host=host)
assert c.handler.host == host
assert c.handler.port == 10014
c = IMSClient(datasource="whatever", imstype="ip21", host=host, port=port)
assert c.handler.host == host
assert c.handler.port == port


@pytest.mark.skipif(
is_GITHUBACTION, reason="ODBC drivers unavailable in GitHub Actions"
)
def test_PI_connection_string_override():
connstr = "someuserspecifiedconnectionstring"
c = IMSClient(
datasource="whatever",
host="host",
imstype="pi",
handler_options={"connection_string": connstr},
)
assert c.handler.generate_connection_string() == connstr


@pytest.mark.skipif(
is_GITHUBACTION, reason="ODBC drivers unavailable in GitHub Actions"
)
def test_IP21_connection_string_override():
connstr = "someuserspecifiedconnectionstring"
c = IMSClient(
datasource="whatever",
host="host",
imstype="ip21",
handler_options={"connection_string": connstr},
)
assert c.handler.generate_connection_string() == connstr


@pytest.mark.skipif(
is_GITHUBACTION, reason="ODBC drivers unavailable in GitHub Actions"
)
Expand Down

0 comments on commit 7e17d36

Please sign in to comment.