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

Add V1 endpoints for factory, machine, machine_type #24

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ae370cd
comment get_cycles, get_parts, get_downtimes methods for ClientV0 bas…
Dec 6, 2022
259faf1
add v1 endpoint for get_factory and get_machine, need to fix get_mach…
Dec 7, 2022
45288ca
add v1 endpoint for factory and machine, need to fix arg passing issue
Dec 12, 2022
e1e36f9
add v1 endpoint for factory
Dec 12, 2022
fa4c575
add get_machines, get_machine_types
Dec 13, 2022
e685cf4
add v1 endpoint for get_machine_names and get_machine_type_names
Dec 13, 2022
daf5261
add check_kw, remove explicit None, add return type for get_machines,…
Dec 15, 2022
65a7b8f
fix limit and offset args to work with changes, return generator for …
Dec 22, 2022
99cb7b8
add wraper around get_factory, get_machine and get_machine_types method
Dec 23, 2022
9fec588
removed unwanted code
Jan 11, 2023
05556db
removed code duplicacy, removed print and added logs
Jan 17, 2023
4d5eb96
fix machine_type and machine_type_names
Jan 25, 2023
3f7cfa1
fix per_page not eorking for value less thn 5, remove v0 endpoint for…
Jan 25, 2023
b3e6f03
retain funcntion definition, remove exception, replace _only and _ord…
Feb 13, 2023
14b5313
remove duplicate get_machine_types call, replace if with elif, chnage…
Feb 28, 2023
e3a0631
resolve conflicts
Mar 28, 2023
13fe6bc
add escape_mongo_field_name to escape mongo field instead of string r…
Mar 28, 2023
c954b27
remove print
Mar 28, 2023
220bdbe
add description for check_kw function
Mar 30, 2023
c3add50
resolve conflicts
May 3, 2023
d3ac6ef
resolve test failures for auth
May 11, 2023
2b1d17a
format with black
May 11, 2023
407e9d8
fix test cases with v1 endpoints
May 11, 2023
ae793a7
Merge branch 'master' of github.com:sightmachine/sightmachine-sdk int…
May 11, 2023
4d12842
fix black format error
May 11, 2023
69e0706
remove dict_to_df func duplication by moving it to util module
May 11, 2023
101f987
format with black
May 11, 2023
b0440c2
update imports to use v1 modules
May 11, 2023
3070b87
Merge branch 'master' of github.com:sightmachine/sightmachine-sdk int…
May 15, 2023
fd75a78
add test for factory
May 17, 2023
b0ae92f
resolve conflicts
May 17, 2023
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
4 changes: 2 additions & 2 deletions smsdk/Auth/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ def check_auth(self):
Determine if SDK has access to the client by checking the Cycle API.
"""
try:
url = "{}{}".format(self.host, ENDPOINTS["Cycle"]["alt_url"])
resp = self._get_records(url, _limit=1, _only=["_id"])
url = "{}{}".format(self.host, ENDPOINTS["Cycle"]["url_v1"])
resp = self._get_records_v1(url, _limit=1, _only=["_id"])
return isinstance(resp, list) and "error" not in resp
except Exception: # pylint:disable=broad-except
return False
172 changes: 137 additions & 35 deletions smsdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
except ImportError:
from pandas.io.json import json_normalize

from smsdk.utils import get_url
from smsdk.utils import get_url, escape_mongo_field_name, dict_to_df
from smsdk.Auth.auth import Authenticator, X_SM_DB_SCHEMA, X_SM_WORKSPACE_ID
from smsdk.tool_register import smsdkentities
from smsdk.client_v0 import ClientV0
Expand All @@ -37,35 +37,9 @@ def time_string_to_epoch(time_string):
return time_epoch


def dict_to_df(data, normalize=True):
if normalize:
# special case to handle the 'stats' block
if data and "stats" in data[0]:
if isinstance(data[0]["stats"], dict):
# part stats are dict
df = json_normalize(data)
else:
# machine type stats are list
cols = [*data[0]]
cols.remove("stats")
df = json_normalize(data, "stats", cols, record_prefix="stats.")
else:
try:
df = json_normalize(data)
except:
# From cases like _distinct which don't have a "normal" return format
return pd.DataFrame({"values": data})
else:
df = pd.DataFrame(data)

if len(df) > 0:
if "_id" in df.columns:
df.set_index("_id", inplace=True)

if "id" in df.columns:
df.set_index("id", inplace=True)

return df
def generator_to_df(generator) -> pd.DataFrame:
data = pd.concat([page for page in generator])
return data


# We don't have a downtime schema, so hard code one
Expand Down Expand Up @@ -170,15 +144,26 @@ def get_data_v1(self, ename, util_name, normalize=True, *args, **kwargs):
# dict params strictly follow {'key':'value'} format

# sub_kwargs = kwargs
if util_name in ["get_cycles", "get_downtime", "get_parts"]:
if util_name in [
"get_cycles",
"get_downtime",
"get_parts",
"get_factories",
"get_machines",
"get_machine_types",
]:
sub_kwargs = [kwargs]
else:
sub_kwargs = self.fix_only(kwargs)

if len(sub_kwargs) == 1:
data = dict_to_df(
getattr(cls, util_name)(*args, **sub_kwargs[0]), normalize
)
if util_name in ["get_factories", "get_machines", "get_machine_types"]:
# data = dict_to_df(getattr(cls, util_name)(*args, **sub_kwargs[0]), normalize)
return getattr(cls, util_name)(normalize, *args, **sub_kwargs[0])
mks-sight marked this conversation as resolved.
Show resolved Hide resolved
else:
data = dict_to_df(
getattr(cls, util_name)(*args, **sub_kwargs[0]), normalize
)
else:
data = dict_to_df(
getattr(cls, util_name)(*args, **sub_kwargs[0]), normalize
Expand All @@ -192,7 +177,7 @@ def get_data_v1(self, ename, util_name, normalize=True, *args, **kwargs):
data.drop(joined_cols, axis=1)

# To keep consistent, rename columns back from '.' to '__'
data.columns = [name.replace(".", "__") for name in data.columns]
data.columns = [escape_mongo_field_name(name) for name in data.columns]

else:
# raise error if requested for unregistered utility
Expand Down Expand Up @@ -352,6 +337,123 @@ def get_fields_of_machine_type(

return fields

def _get_factories(self, *args, normalize=True, **kwargs):
"""
Get list of factories and associated metadata. Note this includes extensive internal metadata.

:param normalize: Flatten nested data structures
:type normalize: bool
:return: pandas dataframe
"""
return self.get_data_v1(
"factory_v1", "get_factories", normalize, *args, **kwargs
)

def _get_machines(self, *args, normalize=True, **kwargs) -> pd.DataFrame:
"""
Get list of machines and associated metadata. Note this includes extensive internal metadata. If you only want to get a list of machine names
then see also get_machine_names().

:param normalize: Flatten nested data structures
:type normalize: bool
:return: pandas dataframe
"""
return self.get_data_v1(
"machine_v1", "get_machines", normalize, *args, **kwargs
)

def _get_machine_types(self, *args, normalize=True, **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
then see also get_machine_type_names().

:param normalize: Flatten nested data structures
:type normalize: bool
:return: pandas dataframe
"""

return self.get_data_v1(
"machine_type_v1", "get_machine_types", normalize, *args, **kwargs
)

def get_factories(self, *args, normalize=True, **kwargs):
generator = self._get_factories(normalize=normalize, *args, **kwargs)
data = generator_to_df(generator)
return data

def get_machines(self, *args, normalize=True, **kwargs):
generator = self._get_machines(normalize=normalize, *args, **kwargs)
data = generator_to_df(generator)
return data

def get_machine_types(self, *args, normalize=True, **kwargs):
generator = self._get_machine_types(normalize=normalize, *args, **kwargs)
data = generator_to_df(generator)
return data

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().

:param source_type: filter the list to only the specified source_type
:type source_type: str
:param clean_strings_out: If true, return the list using the UI-based display names. If false, the list contains the Sight Machine internal machine names.
:return: list
"""

query_params = {
"select": ["source", "source_clean", "source_type"],
"order_by": [{"name": "source_clean"}],
}

if source_type:
# Double check the type
mt = self.get_machine_types(source_type=source_type)
# If it was found, then no action to take, otherwise try looking up from clean string
mt = (
self.get_machine_types(source_type_clean=source_type)
if not len(mt)
else []
)
if len(mt):
source_type = mt["source_type"].iloc[0]
else:
log.error("Machine Type not found")
return []

query_params["source_type"] = source_type

machines = self.get_data_v1(
"machine_v1", "get_machines", normalize=True, **query_params
)
machines = generator_to_df(machines)

if clean_strings_out:
return machines["source_clean"].to_list()
else:
return machines["source"].to_list()

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().

:param clean_strings_out: If true, return the list using the UI-based display names. If false, the list contains the Sight Machine internal machine types.
:return: list
"""
query_params = {
"select": ["source_type", "source_type_clean"],
"order_by": [{"name": "source_type_clean"}],
}
machine_types = self.get_data_v1(
"machine_type_v1", "get_machine_types", normalize=True, **query_params
)
machine_types = generator_to_df(machine_types)

if clean_strings_out:
return machine_types["source_type_clean"].to_list()
else:
return machine_types["source_type"].to_list()

def get_cookbooks(self, **kwargs):
"""
Gets all of the cookbooks accessable to the logged in user.
Expand Down
62 changes: 17 additions & 45 deletions smsdk/client_v0.py
Copy link

Choose a reason for hiding this comment

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

What is this module kept around for? What v0 interfaces remain?

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
except ImportError:
from pandas.io.json import json_normalize

from smsdk.utils import get_url
from smsdk.utils import get_url, escape_mongo_field_name, dict_to_df
from smsdk.Auth.auth import Authenticator
from smsdk.tool_register import smsdkentities

Expand All @@ -42,37 +42,6 @@ def time_string_to_epoch(time_string):
return time_epoch


def dict_to_df(data, normalize=True):
if normalize:
# special case to handle the 'stats' block
if data and "stats" in data[0]:
if isinstance(data[0]["stats"], dict):
# part stats are dict
df = json_normalize(data)
else:
# machine type stats are list
cols = [*data[0]]
cols.remove("stats")
df = json_normalize(data, "stats", cols, record_prefix="stats.")
else:
try:
df = json_normalize(data)
except:
# From cases like _distinct which don't have a "normal" return format
return pd.DataFrame({"values": data})
else:
df = pd.DataFrame(data)

if len(df) > 0:
if "_id" in df.columns:
df.set_index("_id", inplace=True)

if "id" in df.columns:
df.set_index("id", inplace=True)

return df


# We don't have a downtime schema, so hard code one
downmap = {
"machine__source": "Machine",
Expand Down Expand Up @@ -271,7 +240,7 @@ def get_data(self, ename, util_name, normalize=True, *args, **kwargs):
data.drop(joined_cols, axis=1)

# To keep consistent, rename columns back from '.' to '__'
data.columns = [name.replace(".", "__") for name in data.columns]
data.columns = [escape_mongo_field_name(name) for name in data.columns]

else:
# raise error if requested for unregistered utility
Expand Down Expand Up @@ -526,13 +495,13 @@ def inner(
try:
stats = self.get_machine_types(
normalize=False, _limit=1, source_type=machine_type
)["stats"][0]
)
except KeyError:
# explicitly embed string to machine type names esp JCP
machine_type = f"'{machine_type}'"
stats = self.get_machine_types(
normalize=False, _limit=1, source_type=machine_type
)["stats"][0]
)
except Exception as ex:
print(f"Exception in getting machine type stats {ex}")
kwargs["stats"] = stats
Expand Down Expand Up @@ -715,7 +684,7 @@ def get_downtimes_with_cycles(

return merged

def get_factories(self, normalize=True, *args, **kwargs):
def get_factories(self, *args, normalize=True, **kwargs):
"""
Get list of factories and associated metadata. Note this includes extensive internal metadata.

Expand All @@ -725,7 +694,7 @@ def get_factories(self, normalize=True, *args, **kwargs):
"""
return self.get_data("factory", "get_factories", normalize, *args, **kwargs)

def get_machines(self, normalize=True, *args, **kwargs):
def get_machines(self, *args, normalize=True, **kwargs) -> pd.DataFrame:
"""
Get list of machines and associated metadata. Note this includes extensive internal metadata. If you only want to get a list of machine names
then see also get_machine_names().
Expand Down Expand Up @@ -755,13 +724,16 @@ def get_machine_names(self, source_type=None, clean_strings_out=True):
# Double check the type
mt = self.get_machine_types(source_type=source_type)
# If it was found, then no action to take, otherwise try looking up from clean string
if not len(mt):
mt = self.get_machine_types(source_type_clean=source_type)
if len(mt):
source_type = mt["source_type"].iloc[0]
else:
log.error("Machine Type not found")
return []
mt = (
self.get_machine_types(source_type_clean=source_type)
if not len(mt)
else []
)
if len(mt):
source_type = mt["source_type"].iloc[0]
else:
log.error("Machine Type not found")
return []

query_params["source_type"] = source_type

Expand Down Expand Up @@ -822,7 +794,7 @@ def get_machine_timezone(self, machine_source):

return timezone

def get_machine_types(self, normalize=True, *args, **kwargs):
def get_machine_types(self, *args, normalize=True, **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
then see also get_machine_type_names().
Expand Down
12 changes: 4 additions & 8 deletions smsdk/config/api_endpoints.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,23 @@
"url": "/auth/password/login"
},
"Cycle": {
"url_v1": "/v1/datatab/cycle",
"url": "/api/cycle",
"alt_url": "/api/cycle"
"url_v1": "/v1/datatab/cycle"
},
"Factory": {
"url" : "/api/factory"
"url_v1" : "/v1/obj/factory"
},
"MachineType": {
"url" : "/api/machinetype",
"url_v1" : "/v1/obj/machine_type",
"fields": "/v1/selector/datatab/cycle/{}/field"
},
"Machine": {
"url" : "/api/machine"
"url_v1" : "/v1/obj/machine"
},
"Parts": {
"url" : "/api/part",
"url_v1": "/v1/datatab/part",
"part_schema": "/v1/obj/part_type"
},
"Downtime": {
"url" : "/api/downtime",
"url_v1" : "/v1/datatab/downtime"

},
Expand Down
Loading