Skip to content

Commit

Permalink
Merge pull request #24 from sanbales/main
Browse files Browse the repository at this point in the history
pymbe v0.17.2
  • Loading branch information
bjorncole authored Aug 4, 2021
2 parents bfba20c + 8f1b646 commit 1e3502a
Show file tree
Hide file tree
Showing 11 changed files with 53 additions and 92 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ on:
workflow_dispatch: null

env:
CACHE_EPOCH: 0
CONDA_EXE: mamba
PIP_DISABLE_PIP_VERSION_CHECK: '1'
PYTHONIOENCODING: utf-8
PYTHONUNBUFFERED: '1'
PIP_DISABLE_PIP_VERSION_CHECK: '1'
CONDA_EXE: mamba
SKIP_CONDA_PREFLIGHT: 1
CACHE_EPOCH: 0

jobs:
build:
Expand Down
8 changes: 5 additions & 3 deletions anaconda-project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ description: |
variables:
MAX_LINE_LENGTH: 99
SYSML_V2_API_REPO: https://github.com/Systems-Modeling/SysML-v2-API-Python-Client.git@2021-05

commands:
lab:
Expand All @@ -20,10 +19,9 @@ commands:
env_spec: developer
unix: |
git submodule update --init
pip install git+{{SYSML_V2_API_REPO}} --no-dependencies
pip install -e . --no-dependencies
windows: |
git submodule update --init & pip install git+{{SYSML_V2_API_REPO}} --no-dependencies & pip install -e . --no-dependencies
git submodule update --init & pip install -e . --no-dependencies
lint:
description: lint the code
env_spec: developer
Expand Down Expand Up @@ -81,6 +79,10 @@ commands:
notebooks/Tutorial.ipynb:
env_spec: user
notebook: notebooks/Tutorial.ipynb
vscode:
env_spec: developer
unix: code .
windows: code .

channels:
- conda-forge
Expand Down
2 changes: 1 addition & 1 deletion src/pymbe/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.17.1"
__version__ = "0.17.2"
85 changes: 23 additions & 62 deletions src/pymbe/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from warnings import warn

import requests
import sysml_v2_api_client as sysml2
import traitlets as trt
from dateutil import parser
from ipywidgets.widgets.trait_types import TypedTuple
Expand Down Expand Up @@ -64,11 +63,6 @@ class SysML2Client(trt.HasTraits):

paginate = trt.Bool(default_value=True)

_api_configuration: sysml2.Configuration = trt.Instance(sysml2.Configuration)
_commits_api: sysml2.CommitApi = trt.Instance(sysml2.CommitApi)
_elements_api: sysml2.ElementApi = trt.Instance(sysml2.ElementApi)
_projects_api: sysml2.ProjectApi = trt.Instance(sysml2.ProjectApi)

folder_path: Path = trt.Instance(Path, allow_none=True)
json_files: Tuple[Path] = TypedTuple(trt.Instance(Path))
json_file: Path = trt.Instance(Path, allow_none=True)
Expand All @@ -82,50 +76,28 @@ class SysML2Client(trt.HasTraits):

_next_url_regex = re.compile(r'<(http://.*)>; rel="next"')

@trt.default("_api_configuration")
def _make_api_configuration(self):
return sysml2.Configuration(host=self.host)

@trt.default("_commits_api")
def _make_commits_api(self):
with sysml2.ApiClient(self._api_configuration) as client:
api = sysml2.CommitApi(client)
return api

@trt.default("_elements_api")
def _make_elements_api(self):
with sysml2.ApiClient(self._api_configuration) as client:
api = sysml2.ElementApi(client)
return api

@trt.default("_projects_api")
def _make_projects_api(self):
with sysml2.ApiClient(self._api_configuration) as client:
# TODO: add check for a bad URL not to make this call
api = sysml2.ProjectApi(client)
return api

@trt.default("projects")
def _make_projects(self):
projects = self._projects_api.get_projects()

def process_project_safely(project) -> dict:
# protect against projects that can't be parsed
try:
name = project["name"]
created = parser.parse(
" ".join(project.name.split()[-6:]),
" ".join(name.split()[-6:]),
tzinfos=TIMEZONES,
).astimezone(timezone.utc)
except ValueError:
# TODO: revise this when the API server changes the project name
return dict()
return dict(
created=created,
full_name=project.name,
name=" ".join(project.name.split()[:-6]),
full_name=name,
name=" ".join(name.split()[:-6]),
)

results = {project.id: process_project_safely(project) for project in projects}
projects = self._retrieve_data(self.projects_url)

results = {project["@id"]: process_project_safely(project) for project in projects}

return {
project_id: project_data
Expand All @@ -135,19 +107,6 @@ def process_project_safely(project) -> dict:

@trt.observe("host_url", "host_port")
def _update_api_configuration(self, *_):
old_api_configuration = self._api_configuration
self._api_configuration = self._make_api_configuration()
if old_api_configuration:
del old_api_configuration

@trt.observe("_api_configuration")
def _update_apis(self, *_):
for api_type in ("commit", "element", "project"):
api_attr = f"_{api_type}s_api"
old_api = getattr(self, api_attr)
api_maker = getattr(self, f"_make{api_attr}")
setattr(self, api_attr, api_maker())
del old_api
self.projects = self._make_projects()

@trt.observe("selected_commit")
Expand Down Expand Up @@ -180,19 +139,27 @@ def _update_elements_from_file(self, change: trt.Bunch = None):
def host(self):
return f"{self.host_url}:{self.host_port}"

@property
def projects_url(self):
return f"{self.host}/projects"

@property
def commits_url(self):
return f"{self.projects_url}/{self.selected_project}/commits"

@property
def elements_url(self):
if not self.paginate:
warn(
"By default, disabling pagination still retrieves 100 "
"records at a time! True pagination is not supported yet."
)
return (
(f"{self.host}/projects/{self.selected_project}/commits/{self.selected_commit}")
+ f"/elements?page[size]={self.page_size}"
if self.page_size
else ""
)
if not self.selected_project:
raise SystemError("No selected project!")
if not self.selected_commit:
raise SystemError("No selected commit!")
arguments = f"?page[size]={self.page_size}" if self.page_size else ""
return f"{self.commits_url}/{self.selected_commit}/elements{arguments}"

@lru_cache
def _retrieve_data(self, url: str) -> List[Dict]:
Expand All @@ -203,7 +170,7 @@ def _retrieve_data(self, url: str) -> List[Dict]:

if not response.ok:
raise requests.HTTPError(
f"Failed to retrieve elements from '{url}', " f"reason: {response.reason}"
f"Failed to retrieve elements from '{url}', reason: {response.reason}"
)

result += response.json()
Expand All @@ -221,13 +188,7 @@ def _retrieve_data(self, url: str) -> List[Dict]:
return result

def _get_project_commits(self):
# TODO: add more info about the commit when API provides it
return [
commit.id
for commit in self._commits_api.get_commits_by_project(
self.selected_project,
)
]
return {commit["@id"]: commit for commit in self._retrieve_data(self.commits_url)}

def _download_elements(self):
elements = self._retrieve_data(self.elements_url)
Expand Down
2 changes: 1 addition & 1 deletion src/pymbe/graph/rdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def import_context(self, jsonld_item: dict) -> dict:
data = response.json()
if "@context" not in data:
raise ValueError(
"Download context does not have a " f"@context key: {list(data.keys())}"
f"Download context does not have a @context key: {list(data.keys())}"
)
self._cached_contexts[context_url] = data["@context"]
jsonld_item["@context"].update(self._cached_contexts[context_url])
Expand Down
6 changes: 3 additions & 3 deletions src/pymbe/label.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ def get_label_for_expression(
return f"FRE.{referent_name}"

prefix = ""

if "input" in expression._data:
inputs = [
expression._model.elements[an_input["@id"]] for an_input in expression._data["input"]
Expand All @@ -82,7 +81,7 @@ def get_label_for_expression(
inputs = []
if isinstance(inputs, Element):
inputs = [inputs]
input_names = [an_input.name for an_input in inputs]
input_names = [an_input.name for an_input in inputs if an_input.name]
try:
result: Element = expression.result
except AttributeError:
Expand Down Expand Up @@ -112,7 +111,8 @@ def get_label_for_expression(
path_step_names.append(refered.get("name") or refered._id)

prefix = ".".join(path_step_names)
return f"""{prefix} ({", ".join(input_names)}) => {result.name}"""
inputs = f""" ({", ".join(input_names)})""" if input_names else ""
return f"""{prefix}{inputs} => {result.name or "Unnamed Result"}"""


def get_label_for_multiplicity(multiplicity: Element) -> str:
Expand Down
6 changes: 3 additions & 3 deletions src/pymbe/widget/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def _make_project_selector(self):
def _make_commit_selector(self):
selector = ipyw.Dropdown(
description="Commit:",
options=self._get_project_commits(),
options=list(self._get_project_commits()),
)
trt.link((selector, "value"), (self, "selected_commit"))
return selector
Expand Down Expand Up @@ -143,13 +143,13 @@ def _make_progress_bar(self):
progress_bar.layout.visibility = "hidden"
return progress_bar

@trt.observe("_api_configuration")
@trt.observe("projects")
def _update_apis(self, *_):
self.project_selector.options = self._get_project_options()

@trt.observe("selected_project")
def _update_commit_options(self, *_):
self.commit_selector.options = self._get_project_commits()
self.commit_selector.options = list(self._get_project_commits())

def _download_elements(self, *_):
progress = self.progress_bar
Expand Down
9 changes: 3 additions & 6 deletions tests/client/test_client_loading.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import pytest
import requests
import urllib3

from tests.conftest import all_kerbal_names, kerbal_client, kerbal_ids_by_type

Expand Down Expand Up @@ -34,9 +33,9 @@ def test_client_load_find_types(kerbal_ids_by_type):


def test_bad_connection(kerbal_client):
with pytest.raises(urllib3.exceptions.MaxRetryError) as exc:
with pytest.raises(requests.exceptions.ConnectionError) as exc:
kerbal_client.host_url = "http://some.bad.url"
assert "Max retries exceeded" in exc.value.args[0]
assert exc.value.args[0].url == "/projects"


@pytest.mark.skipif(
Expand All @@ -52,9 +51,7 @@ def test_remote_connection(kerbal_client):
client.page_size = 20

client.selected_project = list(client.projects)[0]
client.selected_commit = client._commits_api.get_commits_by_project(client.selected_project)[
0
].id
client.selected_commit = list(client._get_project_commits())[0]
client._download_elements()

model = client.model
Expand Down
15 changes: 8 additions & 7 deletions tests/interpretation/test_calculation_ordering.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import logging

from pymbe.interpretation.calc_dependencies import generate_execution_order
from pymbe.interpretation.results import *
from tests.conftest import kerbal_lpg, kerbal_random_stage_5_complete, kerbal_stable_names

ROCKET_BUILDING = "Model::Kerbal::Rocket Building::"
PARTS_LIBRARY = "Model::Kerbal::Parts Library::"
Expand All @@ -16,19 +14,22 @@ def test_kerbal_calc_order1(kerbal_lpg, kerbal_random_stage_5_complete, kerbal_s

number_liquid_stages = len(kerbal_random_stage_5_complete[liquid_stage_type])

if number_liquid_stages < 1:
print(">>> Didn't make any liquid stages!!!")

dcg = generate_execution_order(kerbal_lpg)

# the execution order will be ordered for examination

sum_1_result = qualified_name_to_id[
f"{ROCKET_BUILDING}Liquid Stage::Full Mass: Real::+ (sum (engines.Mass"
+ f" (FRE.engines)), sum (tanks.Full Mass (FRE.tanks))) => $result::sum"
+ f" (tanks.Full Mass (FRE.tanks)) => $result::tanks.Full Mass (FRE.tanks) <<Feature>>"
" (FRE.engines)), sum (tanks.Full Mass (FRE.tanks))) => $result::sum"
" (tanks.Full Mass (FRE.tanks)) => $result::tanks.Full Mass (FRE.tanks) <<Feature>>"
]
top_plus = qualified_name_to_id[
f"{ROCKET_BUILDING}Liquid Stage::Full Mass: Real::+ (sum (engines.Mass "
f"(FRE.engines)), sum (tanks.Full Mass (FRE.tanks))) => "
f"$result <<OperatorExpression>>"
"(FRE.engines)), sum (tanks.Full Mass (FRE.tanks))) => "
"$result <<OperatorExpression>>"
]

sum_1_in_dcg = None
Expand Down
2 changes: 1 addition & 1 deletion tests/interpretation/test_graph_visit_orderings.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def test_feature_sequence_templates4(simple_parts_lpg, simple_parts_stable_names
f"{SIMPLE_MODEL}Power Group: Part::Power User: Part <<PartUsage>>"
]
power_in_port_id = qualified_name_to_id[
f"{SIMPLE_MODEL}Power Group: Part::Power User: " f"Part::Power In: Port <<PortUsage>>"
f"{SIMPLE_MODEL}Power Group: Part::Power User: Part::Power In: Port <<PortUsage>>"
]

print(seq_templates)
Expand Down
4 changes: 2 additions & 2 deletions tests/query/test_feature_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def test_banded_graph_paths3(simple_parts_lpg, simple_parts_stable_names):
power_group_id = qualified_name_to_id[f"{SIMPLE_MODEL}Power Group: Part <<PartUsage>>"]

power_in_port_id = qualified_name_to_id[
f"{SIMPLE_MODEL}Power Group: Part::Power User: " f"Part::Power In: Port <<PortUsage>>"
f"{SIMPLE_MODEL}Power Group: Part::Power User: Part::Power In: Port <<PortUsage>>"
]
power_out_id = qualified_name_to_id[
f"{SIMPLE_MODEL}Power Group: Part::Power Source: Part::Power Out: Port <<PortUsage>>"
Expand Down Expand Up @@ -200,7 +200,7 @@ def test_type_multiplicity_rollup2(simple_parts_lpg, simple_parts_stable_names):
*_, qualified_name_to_id = simple_parts_stable_names

power_out_id = qualified_name_to_id[
f"{SIMPLE_MODEL}Power Group: Part::Power Source: " f"Part::Power Out: Port <<PortUsage>>"
f"{SIMPLE_MODEL}Power Group: Part::Power Source: Part::Power Out: Port <<PortUsage>>"
]
power_in_id = qualified_name_to_id[
f"{SIMPLE_MODEL}Power Group: Part::Power User: Part::Power In: Port <<PortUsage>>"
Expand Down

0 comments on commit 1e3502a

Please sign in to comment.