Skip to content

Commit 57e3214

Browse files
Merge pull request #214 from sassoftware/model_versioning
Model versioning changes
2 parents e093418 + 013443f commit 57e3214

File tree

2 files changed

+567
-5
lines changed

2 files changed

+567
-5
lines changed

src/sasctl/_services/model_repository.py

Lines changed: 223 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,17 @@
88

99
import datetime
1010
from warnings import warn
11+
import requests
12+
from requests.exceptions import HTTPError
13+
import urllib
1114

12-
from ..core import HTTPError, current_session, delete, get, sasctl_command
15+
# import traceback
16+
# import sys
17+
18+
from ..core import current_session, delete, get, sasctl_command, RestObj
1319
from .service import Service
1420

21+
1522
FUNCTIONS = {
1623
"Analytical",
1724
"Classification",
@@ -615,11 +622,222 @@ def list_model_versions(cls, model):
615622
list
616623
617624
"""
618-
model = cls.get_model(model)
619-
if cls.get_model_link(model, "modelVersions") is None:
620-
raise ValueError("Unable to retrieve versions for model '%s'" % model)
621625

622-
return cls.request_link(model, "modelVersions")
626+
if current_session().version_info() < 4:
627+
model = cls.get_model(model)
628+
if cls.get_model_link(model, "modelVersions") is None:
629+
raise ValueError("Unable to retrieve versions for model '%s'" % model)
630+
631+
return cls.request_link(model, "modelVersions")
632+
else:
633+
link = cls.get_model_link(model, "modelHistory")
634+
if link is None:
635+
raise ValueError(
636+
"Cannot find link for version history for model '%s'" % model
637+
)
638+
639+
modelHistory = cls.request_link(
640+
link,
641+
"modelHistory",
642+
headers={"Accept": "application/vnd.sas.collection+json"},
643+
)
644+
645+
if isinstance(modelHistory, RestObj):
646+
return [modelHistory]
647+
return modelHistory
648+
649+
@classmethod
650+
def get_model_version(cls, model, version_id):
651+
"""Get a specific version of a model.
652+
653+
Parameters
654+
----------
655+
model : str or dict
656+
The name, id, or dictionary representation of a model.
657+
version_id: str
658+
The id of a model version.
659+
660+
Returns
661+
-------
662+
RestObj
663+
664+
"""
665+
666+
model_history = cls.list_model_versions(model)
667+
668+
for item in model_history:
669+
if item["id"] == version_id:
670+
return cls.request_link(
671+
item,
672+
"self",
673+
headers={"Accept": "application/vnd.sas.models.model.version+json"},
674+
)
675+
676+
raise ValueError("The version id specified could not be found.")
677+
678+
@classmethod
679+
def get_model_with_versions(cls, model):
680+
"""Get the current model with its version history.
681+
682+
Parameters
683+
----------
684+
model : str or dict
685+
The name, id, or dictionary representation of a model.
686+
687+
Returns
688+
-------
689+
list
690+
691+
"""
692+
693+
if cls.is_uuid(model):
694+
model_id = model
695+
elif isinstance(model, dict) and "id" in model:
696+
model_id = model["id"]
697+
else:
698+
model = cls.get_model(model)
699+
if not model:
700+
raise HTTPError(
701+
"This model may not exist in a project or the model may not exist at all."
702+
)
703+
model_id = model["id"]
704+
705+
versions_uri = f"/models/{model_id}/versions"
706+
try:
707+
version_history = cls.request(
708+
"GET",
709+
versions_uri,
710+
headers={"Accept": "application/vnd.sas.collection+json"},
711+
)
712+
except urllib.error.HTTPError as e:
713+
raise HTTPError(
714+
f"Request failed: Model id may be referencing a non-existing model."
715+
) from None
716+
717+
if isinstance(version_history, RestObj):
718+
return [version_history]
719+
720+
return version_history
721+
722+
@classmethod
723+
def get_model_or_version(cls, model, version_id):
724+
"""Get a specific version of a model but if model id and version id are the same, the current model is returned.
725+
726+
Parameters
727+
----------
728+
model : str or dict
729+
The name, id, or dictionary representation of a model.
730+
version_id: str
731+
The id of a model version.
732+
733+
Returns
734+
-------
735+
RestObj
736+
737+
"""
738+
739+
version_history = cls.get_model_with_versions(model)
740+
741+
for item in version_history:
742+
if item["id"] == version_id:
743+
return cls.request_link(
744+
item,
745+
"self",
746+
headers={
747+
"Accept": "application/vnd.sas.models.model.version+json, application/vnd.sas.models.model+json"
748+
},
749+
)
750+
751+
raise ValueError("The version id specified could not be found.")
752+
753+
@classmethod
754+
def get_model_version_contents(cls, model, version_id):
755+
"""Get the contents of a model version.
756+
757+
Parameters
758+
----------
759+
model : str or dict
760+
The name, id, or dictionary representation of a model.
761+
version_id: str
762+
The id of a model version.
763+
764+
Returns
765+
-------
766+
list
767+
768+
"""
769+
model_version = cls.get_model_version(model, version_id)
770+
version_contents = cls.request_link(
771+
model_version,
772+
"contents",
773+
headers={"Accept": "application/vnd.sas.collection+json"},
774+
)
775+
776+
if isinstance(version_contents, RestObj):
777+
return [version_contents]
778+
779+
return version_contents
780+
781+
@classmethod
782+
def get_model_version_content_metadata(cls, model, version_id, content_id):
783+
"""Get the content metadata header information for a model version.
784+
785+
Parameters
786+
----------
787+
model : str or dict
788+
The name, id, or dictionary representation of a model.
789+
version_id: str
790+
The id of a model version.
791+
content_id: str
792+
The id of the content file.
793+
794+
Returns
795+
-------
796+
RestObj
797+
798+
"""
799+
model_version_contents = cls.get_model_version_contents(model, version_id)
800+
801+
for item in model_version_contents:
802+
if item["id"] == content_id:
803+
return cls.request_link(
804+
item,
805+
"self",
806+
headers={"Accept": "application/vnd.sas.models.model.content+json"},
807+
)
808+
809+
raise ValueError("The content id specified could not be found.")
810+
811+
@classmethod
812+
def get_model_version_content(cls, model, version_id, content_id):
813+
"""Get the specific content inside the content file for a model version.
814+
815+
Parameters
816+
----------
817+
model : str or dict
818+
The name, id, or dictionary representation of a model.
819+
version_id: str
820+
The id of a model version.
821+
content_id: str
822+
The id of the specific content file.
823+
824+
Returns
825+
-------
826+
list
827+
828+
"""
829+
830+
metadata = cls.get_model_version_content_metadata(model, version_id, content_id)
831+
version_content_file = cls.request_link(
832+
metadata, "content", headers={"Accept": "text/plain"}
833+
)
834+
835+
if version_content_file is None:
836+
raise HTTPError("Something went wrong while accessing the metadata file.")
837+
838+
if isinstance(version_content_file, RestObj):
839+
return [version_content_file]
840+
return version_content_file
623841

624842
@classmethod
625843
def copy_analytic_store(cls, model):

0 commit comments

Comments
 (0)