Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 45 additions & 10 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
version: 2.1

workflows:
workflows:
code-commit:
jobs:
- black-python-code-formatter:
Expand All @@ -9,6 +9,10 @@ workflows:
context: org-sightmachine
- run-unit-tests:
context: org-sightmachine
- code-coverage-report:
context: org-sightmachine
requires:
- run-unit-tests
- cve-repo-scan:
context: org-sightmachine
nightly-cve:
Expand Down Expand Up @@ -128,22 +132,53 @@ jobs:
pip install -r requirements-codecoverage.txt
pip install .
mkdir test-results
coverage run -m pytest -vvv --nbmake --nbmake-timeout=300 -n=auto --junitxml=test-results/junit.xml tests
- run:
name: Compile Coverage Report
command: |
coverage html -d htmlcov
codecov --required
pytest -vvv --nbmake --nbmake-timeout=300 -n=auto --cov=smsdk --cov-config=.coveragerc --cov-report= --junitxml=test-results/junit.xml tests

# Store the test results on each node so we can see failures
- store_test_results:
path: test-results

- store_artifacts:
name: Save Unit Test Results
path: htmlcov
path: test-results

- send_slack_msg_on_fail
- persist_to_workspace:
root: .
paths:
- .coverage

- send_slack_msg_on_fail

## ------------------ Generate Code Coverage Report ------------------

code-coverage-report:
docker:
# https://circleci.com/developer/images/image/cimg/python
- image: cimg/python:3.11.0
steps:
# Coverage html requires source code to build HTML views
- checkout
# Need workspace for the coverage report
- attach_workspace:
at: /tmp/workspace
- run:
name: Compile Coverage Report
command: |
set -x
pip install -r requirements-codecoverage.txt
# Check if coverage file exists before generating report
if [ -f /tmp/workspace/.coverage ]; then
cp /tmp/workspace/.coverage .
coverage html -d htmlcov
else
echo "No coverage file found. Skipping coverage report generation."
mkdir -p htmlcov
echo "<html><body><h1>No coverage data available</h1></body></html>" > htmlcov/index.html
fi
- store_artifacts:
name: Save Coverage Report
path: htmlcov/
- codecov/upload

## ------------------ Check Black python code formate ------------------
black-python-code-formatter:
Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,34 @@ When accessing Sight Machine via the SDK, the first step is always to initialize
will point to the name of the tenant on Sight Machine. For example, if you access Sight Machine at the URL *mycompany*.sightmachine.io, then *mycompany* is
the name of the tenant you will use. For purposes of this Quick Start documentation, we will use demo as the tenant name.

To initialize a Client:
To initialize a Client:

```
from smsdk import client
tenant = 'demo'
cli = client.Client(tenant)
```

#### Nested Path Support

For deployments where Sight Machine is hosted under a nested path (e.g., `https://tenant.sightmachine.io/nested/one/two/`), you can specify the base path in two ways:

**Method 1: Explicit base_path parameter**
```python
cli = client.Client('demo', base_path='/nested/one/two')
```

**Method 2: Include path in tenant URL**
```python
cli = client.Client('https://demo.sightmachine.io/nested/one/two')
```

The SDK will automatically construct URLs with the correct path prefix for all API calls. For example:
- Without path: `https://demo.sightmachine.io/v1/datatab/cycle`
- With path: `https://demo.sightmachine.io/nested/one/two/v1/datatab/cycle`

**Note:** The `base_path` parameter takes precedence if both methods are used.

### Authenticating

Sight Machine currently supports two methods of authentication via the SDK:
Expand Down
1 change: 1 addition & 0 deletions requirements-codecoverage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
codecov==2.1.13
coverage==7.3.2
nbmake==1.4.6
pytest-cov==4.1.0
pytest-xdist==3.5.0
1 change: 1 addition & 0 deletions smsdk/Auth/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def __init__(self, client):
client.tenant,
client.config["site.domain"],
client.config["port"],
client.config.get("base.path"),
)
self.session.headers = default_headers()

Expand Down
30 changes: 28 additions & 2 deletions smsdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import pandas as pd
import numpy as np
import typing as t_

try:
# for newer pandas versions >1.X
Expand Down Expand Up @@ -128,7 +129,11 @@ class Client(ClientV0):
"""Connection point to the Sight Machine platform to retrieve data"""

def __init__(
self, tenant: str, site_domain: str = "sightmachine.io", protocol: str = "https"
self,
tenant: str,
site_domain: str = "sightmachine.io",
protocol: str = "https",
base_path: t_.Optional[str] = None,
):
"""
Initialize the client.
Expand All @@ -139,9 +144,15 @@ def __init__(
The site domain to connect to. Necessary to change if deploying in
a non-standard environment.
:type site_domain: :class:`string`
:param protocol: Protocol to use (https or http).
:type protocol: :class:`string`
:param base_path: Optional path prefix for nested deployments (e.g., "/nested/one/two")
:type base_path: :class:`string` or None
"""

super().__init__(tenant, site_domain=site_domain, protocol=protocol)
super().__init__(
tenant, site_domain=site_domain, protocol=protocol, base_path=base_path
)

@version_check_decorator
def select_db_schema(self, schema_name):
Expand Down Expand Up @@ -176,6 +187,7 @@ def get_data_v1(self, ename, util_name, normalize=True, *args, **kwargs):
self.tenant,
self.config["site.domain"],
self.config["port"],
self.config.get("base.path"),
)

df = pd.DataFrame()
Expand Down Expand Up @@ -292,6 +304,7 @@ def get_kpis(self, **kwargs):
self.tenant,
self.config["site.domain"],
self.config["port"],
self.config.get("base.path"),
)
return kpis(self.session, base_url).get_kpis(**kwargs)

Expand Down Expand Up @@ -345,6 +358,7 @@ def get_kpis_for_asset(self, **kwargs):
self.tenant,
self.config["site.domain"],
self.config["port"],
self.config.get("base.path"),
)
if "machine_type" in kwargs["asset_selection"]:
# updating kwargs with machine_type's system name in case of user provides display name.
Expand Down Expand Up @@ -394,6 +408,7 @@ def get_kpi_data_viz(
self.tenant,
self.config["site.domain"],
self.config["port"],
self.config.get("base.path"),
)

if "asset_selection" in kwargs and "machine_type" in kwargs["asset_selection"]:
Expand All @@ -419,6 +434,7 @@ def get_type_from_machine(self, machine_source=None, **kwargs):
self.tenant,
self.config["site.domain"],
self.config["port"],
self.config.get("base.path"),
)
return machine(self.session, base_url).get_type_from_machine_name(
machine_source, **kwargs
Expand All @@ -440,6 +456,7 @@ def get_machine_schema(
self.tenant,
self.config["site.domain"],
self.config["port"],
self.config.get("base.path"),
)
fields = machineType(self.session, base_url).get_fields(machine_type, **kwargs)
fields = [
Expand Down Expand Up @@ -479,6 +496,7 @@ def get_fields_of_machine_type(
self.tenant,
self.config["site.domain"],
self.config["port"],
self.config.get("base.path"),
)
fields = machineType(self.session, base_url).get_fields(machine_type, **kwargs)
fields = [
Expand All @@ -505,6 +523,7 @@ def get_cookbooks(self, **kwargs):
self.tenant,
self.config["site.domain"],
self.config["port"],
self.config.get("base.path"),
)
return cookbook(self.session, base_url).get_cookbooks(**kwargs)

Expand All @@ -522,6 +541,7 @@ def get_cookbook_top_results(self, recipe_group_id=None, limit=10, **kwargs):
self.tenant,
self.config["site.domain"],
self.config["port"],
self.config.get("base.path"),
)
return cookbook(self.session, base_url).get_top_results(
recipe_group_id, limit, **kwargs
Expand All @@ -541,6 +561,7 @@ def get_cookbook_current_value(self, variables=[], minutes=1440, **kwargs):
self.tenant,
self.config["site.domain"],
self.config["port"],
self.config.get("base.path"),
)
return cookbook(self.session, base_url).get_current_value(
variables, minutes, **kwargs
Expand Down Expand Up @@ -582,6 +603,7 @@ def get_lines(self, **kwargs):
self.tenant,
self.config["site.domain"],
self.config["port"],
self.config.get("base.path"),
)
return lines(self.session, base_url).get_lines(**kwargs)

Expand Down Expand Up @@ -613,6 +635,7 @@ def get_line_data(
self.tenant,
self.config["site.domain"],
self.config["port"],
self.config.get("base.path"),
)

asset_selection = []
Expand Down Expand Up @@ -664,6 +687,7 @@ def get_line_data_lineviz(
self.tenant,
self.config["site.domain"],
self.config["port"],
self.config.get("base.path"),
)

if i_vars:
Expand Down Expand Up @@ -707,6 +731,7 @@ def create_share_link(
self.tenant,
self.config["site.domain"],
self.config["port"],
self.config.get("base.path"),
)
if assets and model == "cycle" or assets and model == "kpi":
machine_types = []
Expand Down Expand Up @@ -851,6 +876,7 @@ def get_raw_data(
self.tenant,
self.config["site.domain"],
self.config["port"],
self.config.get("base.path"),
)
select = [{"name": field} for field in fields]
kwargs["asset_selection"] = {"raw_data_table": raw_data_table}
Expand Down
Loading
Loading