Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Releases/v1.26.0 branch: Implementation of SDK Version comparison for API's #60

Merged
merged 7 commits into from
Jan 3, 2024
Merged
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Change Log

## v1.26.0

---
1. Added release version checks for API's
2.
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ include README.md LICENSE
include smsdk/config/*

recursive-include tests *
include requirements.txt
include CHANGELOG.md
2 changes: 1 addition & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ follow_imports = silent


# Files free from Static type check errors
files = smsdk/utils.py, smsdk/tool_register.py, smsdk/ma_session.py
files = smsdk/_version.py, smsdk/utils.py, smsdk/tool_register.py, smsdk/ma_session.py
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from setuptools import setup, find_packages
from smsdk._version import version

with open("README.md", "r") as fh:
long_description = fh.read()
Expand All @@ -12,7 +13,7 @@

setup(
name="smsdk",
version="1.0.26",
version=version,
packages=find_packages(exclude=["test*"]),
include_package_data=True,
install_requires=install_requires,
Expand Down
122 changes: 122 additions & 0 deletions smsdk/_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#
"""
Track version information for sightmachine-sdk module here.
"""
import datetime
import typing as t_
import itertools
import requests
import warnings
import functools
import re


class VersionInfo(t_.NamedTuple):
major: int
minor: int
patchlevel: int
releaselevel: t_.Optional[str]
serial: t_.Optional[int]

def __str__(self) -> str:
result = "".join(
itertools.chain(
".".join(str(i) for i in self[:3] if i is not None),
(str(i) for i in self[3:] if i is not None),
)
)
return result


version_info = VersionInfo(1, 26, 0, "", None)
version = str(f"v{version_info}")


def sort_releases_descending(
releases: t_.List[t_.Dict[str, t_.Any]]
) -> t_.List[t_.Dict[str, t_.Any]]:
def version_key(release: t_.Dict[str, t_.Any]) -> t_.List[t_.Union[int, str]]:
return [
(int(i) if i.isdigit() else i)
for i in re.split(r"(\d+|\W+)", release["tag_name"].lower())
if i
]

return (
sorted(releases, key=version_key, reverse=True)
if len(releases) > 1
else releases
)


def get_latest_release_version(
releases: t_.List[t_.Dict[str, t_.Any]]
) -> t_.Optional[t_.Any]:
if releases:
sorted_releases = sort_releases_descending(releases)
return sorted_releases[0]["tag_name"]
return None


def get_latest_sdk_release() -> t_.Optional[t_.Any]:
api_url = f"https://api.github.com/repos/{owner}/{repo}/releases"
JMkrish marked this conversation as resolved.
Show resolved Hide resolved

try:
response = requests.get(api_url, timeout=10)
response.raise_for_status() # To raise an HTTPError for any bad responses
releases = response.json()

if releases:
return get_latest_release_version(releases)
except requests.RequestException as e:
print(f"Error fetching latest SDK release: {e}")

return None


class VersionCheckDecorator:
api_version_printed = False
last_version_check_time = None
Comment on lines +78 to +79
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, do these need to be separate flags? Could have a method/property on the class which does the equivalent.


@classmethod
def version_check_decorator(cls, func: t_.Any) -> t_.Any:
@functools.wraps(func)
def wrapper(*args: t_.Any, **kwargs: t_.Any) -> t_.Any:
current_time = datetime.datetime.now()

# Check if a week has passed since the last version check
if cls.last_version_check_time is None or (
current_time - cls.last_version_check_time > datetime.timedelta(days=7)
):
cls.api_version_printed = False
cls.last_version_check_time = current_time

if not cls.api_version_printed:
latest_sdk_release = get_latest_sdk_release()
installed_sdk_release = version

if (
installed_sdk_release is not None
and latest_sdk_release is not None
and latest_sdk_release != installed_sdk_release
):
warnings.warn(
f"Installed SDK Version: {installed_sdk_release}. "
f"It is recommended to install release version ({latest_sdk_release}).",
DeprecationWarning,
)
cls.api_version_printed = True
JMkrish marked this conversation as resolved.
Show resolved Hide resolved

return func(*args, **kwargs)

return wrapper


# Initialize the flag to False
api_version_printed = False

owner = "sightmachine"
repo = "sightmachine-sdk"

# Define version_check_decorator here
version_check_decorator = VersionCheckDecorator.version_check_decorator
28 changes: 28 additions & 0 deletions smsdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# coding: utf-8
""" Sight Machine SDK Client """
from __future__ import unicode_literals, absolute_import
from smsdk._version import version_check_decorator

import pandas as pd
import numpy as np
Expand Down Expand Up @@ -151,17 +152,20 @@ def __init__(self, tenant, site_domain="sightmachine.io", protocol="https"):
self.auth = Authenticator(self)
self.session = self.auth.session

@version_check_decorator
def select_db_schema(self, schema_name):
# remove X_SM_WRKSPACE_ID from self.session.headers
self.session.headers.update({X_SM_DB_SCHEMA: schema_name})
if X_SM_WORKSPACE_ID in self.session.headers:
del self.session.headers[X_SM_WORKSPACE_ID]

@version_check_decorator
def select_workspace_id(self, workspace_id):
self.session.headers.update({X_SM_WORKSPACE_ID: str(workspace_id)})
if X_SM_DB_SCHEMA in self.session.headers:
del self.session.headers[X_SM_DB_SCHEMA]

@version_check_decorator
def get_data_v1(self, ename, util_name, normalize=True, *args, **kwargs):
"""
Main data fetching function for all the entities. Note this is the general data fetch function. You probably want to use the model-specific functions such as get_cycles().
Expand Down Expand Up @@ -234,6 +238,7 @@ def get_data_v1(self, ename, util_name, normalize=True, *args, **kwargs):

return data

@version_check_decorator
@ClientV0.validate_input
@ClientV0.cycle_decorator
def get_cycles(
Expand All @@ -248,6 +253,7 @@ def get_cycles(

return df

@version_check_decorator
@ClientV0.validate_input
@ClientV0.downtime_decorator
def get_downtimes(
Expand All @@ -262,6 +268,7 @@ def get_downtimes(

return df

@version_check_decorator
@ClientV0.validate_input
@ClientV0.part_decorator
def get_parts(
Expand All @@ -277,13 +284,15 @@ def get_parts(

return df

@version_check_decorator
def get_kpis(self, **kwargs):
kpis = smsdkentities.get("kpi")
base_url = get_url(
self.config["protocol"], self.tenant, self.config["site.domain"]
)
return kpis(self.session, base_url).get_kpis(**kwargs)

@version_check_decorator
def get_machine_type_from_clean_name(self, kwargs):
# Get machine_types dataframe to check display name
machine_types_df = self.get_machine_types()
Expand All @@ -304,6 +313,7 @@ def get_machine_type_from_clean_name(self, kwargs):

return machine_types

@version_check_decorator
def get_machine_source_from_clean_name(self, kwargs):
# Get machines dataframe to check display/clean name
machine_sources_df = self.get_machines()
Expand All @@ -324,6 +334,7 @@ def get_machine_source_from_clean_name(self, kwargs):

return machine_sources

@version_check_decorator
def get_kpis_for_asset(self, **kwargs):
kpis = smsdkentities.get("kpi")
base_url = get_url(
Expand All @@ -343,6 +354,7 @@ def get_kpis_for_asset(self, **kwargs):

return kpis(self.session, base_url).get_kpis_for_asset(**kwargs)

@version_check_decorator
def get_kpi_data_viz(
self,
machine_sources=None,
Expand Down Expand Up @@ -390,6 +402,7 @@ def get_kpi_data_viz(
] = self.get_machine_source_from_clean_name(kwargs)
return kpi_entity(self.session, base_url).get_kpi_data_viz(**kwargs)

@version_check_decorator
def get_type_from_machine(self, machine_source=None, **kwargs):
machine = smsdkentities.get("machine")
base_url = get_url(
Expand All @@ -399,6 +412,7 @@ def get_type_from_machine(self, machine_source=None, **kwargs):
machine_source, **kwargs
)

@version_check_decorator
def get_machine_schema(
self,
machine_source=None,
Expand Down Expand Up @@ -436,6 +450,7 @@ def get_machine_schema(

return frame

@version_check_decorator
def get_fields_of_machine_type(
self,
machine_type=None,
Expand All @@ -460,6 +475,7 @@ def get_fields_of_machine_type(

return fields

@version_check_decorator
def get_cookbooks(self, **kwargs):
"""
Gets all of the cookbooks accessable to the logged in user.
Expand All @@ -471,6 +487,7 @@ def get_cookbooks(self, **kwargs):
)
return cookbook(self.session, base_url).get_cookbooks(**kwargs)

@version_check_decorator
def get_cookbook_top_results(self, recipe_group_id=None, limit=10, **kwargs):
"""
Gets the top runs for a recipe group.
Expand All @@ -486,6 +503,7 @@ def get_cookbook_top_results(self, recipe_group_id=None, limit=10, **kwargs):
recipe_group_id, limit, **kwargs
)

@version_check_decorator
def get_cookbook_current_value(self, variables=[], minutes=1440, **kwargs):
"""
Gets the current value of a field.
Expand All @@ -501,6 +519,7 @@ def get_cookbook_current_value(self, variables=[], minutes=1440, **kwargs):
variables, minutes, **kwargs
)

@version_check_decorator
def normalize_constraint(self, constraint):
"""
Takes a constraint and returns a string version of it's to and from fields.
Expand All @@ -513,6 +532,7 @@ def normalize_constraint(self, constraint):
from_symbol = "]" if constraint.get("from_is_inclusive") else ")"
return "{}{},{}{}".format(to_symbol, to_val, from_val, from_symbol)

@version_check_decorator
def normalize_constraints(self, constraints):
"""
Takes a list of constraint and returns string versions of their to and from fields.
Expand All @@ -524,6 +544,7 @@ def normalize_constraints(self, constraints):
constraints_normal.append(self.normalize_constraint(constraint))
return constraints_normal

@version_check_decorator
def get_lines(self, **kwargs):
"""
Returns all the lines for the facility
Expand All @@ -534,6 +555,7 @@ def get_lines(self, **kwargs):
)
return lines(self.session, base_url).get_lines(**kwargs)

@version_check_decorator
def get_line_data(
self,
assets=None,
Expand Down Expand Up @@ -583,6 +605,7 @@ def get_line_data(
limit=limit, offset=offset, **kwargs
)

@version_check_decorator
def create_share_link(
self,
assets=None,
Expand Down Expand Up @@ -626,6 +649,7 @@ def create_share_link(
*args, assets, chartType, yAxis, xAxis, model, time_selection, **kwargs
)

@version_check_decorator
def get_machines(self, normalize=True, *args, **kwargs):
"""
Get list of machines and associated metadata. Note this includes extensive internal metadata. If you only want to get a list of machine names
Expand All @@ -639,6 +663,7 @@ def get_machines(self, normalize=True, *args, **kwargs):
"machine_v1", "get_machines", normalize, *args, **kwargs
)

@version_check_decorator
def get_machine_names(self, source_type=None, clean_strings_out=True):
"""
Get a list of machine names. This is a simplified version of get_machines().
Expand Down Expand Up @@ -676,6 +701,7 @@ def get_machine_names(self, source_type=None, clean_strings_out=True):
else:
return machines["source"].to_list()

@version_check_decorator
def get_machine_types(self, source_type=None, *args, **kwargs):
"""
Get list of machine types and associated metadata. Note this includes extensive internal metadata. If you only want to get a list of machine type names
Expand All @@ -695,6 +721,7 @@ def get_machine_types(self, source_type=None, *args, **kwargs):

return mts

@version_check_decorator
def get_machine_type_names(self, clean_strings_out=True):
"""
Get a list of machine type names. This is a simplified version of get_machine_types().
Expand All @@ -715,6 +742,7 @@ def get_machine_type_names(self, clean_strings_out=True):
else:
return machine_types["source_type"].unique().tolist()

@version_check_decorator
def get_raw_data(
self,
raw_data_table=None,
Expand Down