bruce-models 0.3.0__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bruce_models-0.3.0 → bruce_models-0.3.2}/PKG-INFO +1 -1
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/__init__.py +1 -1
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/change/change_set.py +8 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/change/change_set_builder.py +13 -2
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_change_set.py +6 -3
- bruce_models-0.3.2/bruce_models/cli/run_import_ifc.py +255 -0
- bruce_models-0.3.2/bruce_models/cli/seed_ifc.py +403 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/suites.py +18 -1
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/client_file/client_file.py +70 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models.egg-info/SOURCES.txt +2 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/MANIFEST.in +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/README.md +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/__main__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/account/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/account/account.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/account/account_audit.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/api/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/api/api.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/api/bruce_api.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/api/errors.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/api/guardian_api.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/api/response.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/change/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/change/edit_store.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/baseline.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/baselines/benchmark.json +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/bench.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/cmd_accounts.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/cmd_audit.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/cmd_auth.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/cmd_indexes.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/cmd_render.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/cmd_run.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/cmd_status.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/commands.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/config.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/exits.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/humanize.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/identity.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/live.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/main.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/report.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_bench.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_data_model.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_data_transform.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_datalab.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_entity_lod.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_gis.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_historic.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_import_brz.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_import_csv.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_import_geojson.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_import_json.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_import_kml.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_import_xlsx.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_merge.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_relation.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/run_scale.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/runner.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_bench.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_brz.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_change_set.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_data_model.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_data_transform.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_datalab.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_geojson.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_gis.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_historic.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_json.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_kml.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_lod.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_merge.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_relation.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_scale.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/seed_xlsx.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/cli/style.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/client_file/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/client_file/client_file_query_builder.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/client_file/client_file_texture.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/client_file/client_file_value_map.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/common/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/common/bounds.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/common/geometry.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/common/geometry_builder.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/common/lru_cache.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/data_lab/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/data_lab/data_lab.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/data_lab/data_lab_builder.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/data_lab/data_lab_pairing.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/data_mapping/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/data_mapping/data_mapping.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/data_mapping/data_mapping_builder.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/data_transform/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/data_transform/data_transform.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/entity/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/entity/entity.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/entity/entity_historic_data.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/entity/entity_index.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/entity/entity_link.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/entity/entity_type.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/entity/entity_ucs.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_assembly/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_assembly/assembly_import.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_brz/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_brz/brz.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_csv/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_csv/csv_import.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_geojson/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_geojson/geojson.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_geojson/geojson_import.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_json/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_json/json_import.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_kml/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_kml/kml.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_kml/kml_import.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_xlsx/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_xlsx/xlsx.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/format_xlsx/xlsx_import.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/geometry/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/geometry/glb.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/hosting/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/hosting/hosting_location.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/internal/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/internal/uploader.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/lod/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/lod/entity_lod.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/lod/lod_category.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/relation/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/relation/entity_relation.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/relation/entity_relation_type.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/scenario/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/scenario/scenario.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/server/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/server/api_version.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/server/pending_action.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/tileset/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/tileset/tileset.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/user/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/user/session.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/util/__init__.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/util/import_utils.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/util/optional.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/util/path_utils.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/util/query_plan.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/bruce_models/util/required.py +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/pyproject.toml +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/requirements.txt +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/setup.cfg +0 -0
- {bruce_models-0.3.0 → bruce_models-0.3.2}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bruce-models
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: This is a library which defines data-models for the Nextspace API and provides functions for communicating with the endpoints.
|
|
5
5
|
Author: Matvey Lavrinovich
|
|
6
6
|
Author-email: matveylavrinovich@gmail.com
|
|
@@ -62,7 +62,7 @@ from .util.path_utils import PathUtils
|
|
|
62
62
|
|
|
63
63
|
# The one place the version is written. setup.py reads it from here without importing the package,
|
|
64
64
|
# and pypi-release.sh reads this same file, so a source checkout and a wheel can never disagree.
|
|
65
|
-
__version__ = "0.3.
|
|
65
|
+
__version__ = "0.3.2"
|
|
66
66
|
|
|
67
67
|
__all__ = [
|
|
68
68
|
"__version__",
|
|
@@ -188,6 +188,14 @@ class ChangeSet:
|
|
|
188
188
|
def status_of(response: Any) -> Optional[str]:
|
|
189
189
|
return (response or {}).get("Status")
|
|
190
190
|
|
|
191
|
+
@staticmethod
|
|
192
|
+
def action_id_of(response: Any) -> Optional[int]:
|
|
193
|
+
"""
|
|
194
|
+
The Pending Action a commit started, from the record the route answers with.
|
|
195
|
+
"""
|
|
196
|
+
response = response or {}
|
|
197
|
+
return response.get("PendingActionID") or response.get("PendingAction.ID")
|
|
198
|
+
|
|
191
199
|
@staticmethod
|
|
192
200
|
def is_uncommitted(response: Any) -> bool:
|
|
193
201
|
"""
|
|
@@ -5,6 +5,7 @@ from bruce_models.util.required import required
|
|
|
5
5
|
from bruce_models.change.change_set import ChangeSet
|
|
6
6
|
from bruce_models.change.edit_store import EditStore
|
|
7
7
|
from bruce_models.entity.entity import Entity
|
|
8
|
+
from bruce_models.server.pending_action import PendingAction
|
|
8
9
|
|
|
9
10
|
MAX_REPORTED = 1000
|
|
10
11
|
|
|
@@ -381,14 +382,24 @@ class ChangeSetBuilder:
|
|
|
381
382
|
self._record = ChangeSet.get(self.api, self.change_id)
|
|
382
383
|
return self._record
|
|
383
384
|
|
|
384
|
-
|
|
385
|
+
# A commit is a job, so the record still reads Uncommitted for a moment after the route answers.
|
|
386
|
+
COMMIT_POLL_SECONDS = 1.0
|
|
387
|
+
|
|
388
|
+
def commit(self, wait: bool = False, timeout: Optional[float] = None) -> Any:
|
|
385
389
|
"""
|
|
386
390
|
Applies the set. Anything queued locally is saved first, so nothing is left behind.
|
|
387
391
|
"""
|
|
388
392
|
self._refuse_repeats("be committed")
|
|
389
393
|
if len(self) or not self.change_id:
|
|
390
394
|
self.save()
|
|
391
|
-
|
|
395
|
+
response = ChangeSet.commit(self.api, required(self.change_id, "change set ID"))
|
|
396
|
+
if wait:
|
|
397
|
+
action_id = ChangeSet.action_id_of(response)
|
|
398
|
+
if action_id:
|
|
399
|
+
PendingAction.on_completion(self.api, action_id,
|
|
400
|
+
interval=self.COMMIT_POLL_SECONDS, timeout=timeout)
|
|
401
|
+
self.refresh()
|
|
402
|
+
return response
|
|
392
403
|
|
|
393
404
|
def reject(self) -> Any:
|
|
394
405
|
"""
|
|
@@ -16,6 +16,10 @@ from bruce_models.cli import seed_change_set
|
|
|
16
16
|
from bruce_models.cli import seed_datalab
|
|
17
17
|
from bruce_models.cli.runner import (ImportFailed, Result, Timer, _finish, _own_type, _report_step)
|
|
18
18
|
|
|
19
|
+
# A commit of this size is seconds of work, so anything approaching this is a stuck job rather than
|
|
20
|
+
# a slow one.
|
|
21
|
+
COMMIT_TIMEOUT = 120
|
|
22
|
+
|
|
19
23
|
|
|
20
24
|
def run_change_set(api: BruceApi, rows: int = 0, on_progress=None, on_message=None,
|
|
21
25
|
cleanup: bool = True) -> Result:
|
|
@@ -174,8 +178,7 @@ def _check_commit(api: BruceApi, result: Result, type_id: str, entity_ids: List[
|
|
|
174
178
|
try:
|
|
175
179
|
if builder is None:
|
|
176
180
|
raise ImportFailed("nothing was staged to commit")
|
|
177
|
-
builder.commit()
|
|
178
|
-
time.sleep(2)
|
|
181
|
+
builder.commit(wait=True, timeout=COMMIT_TIMEOUT)
|
|
179
182
|
problems = []
|
|
180
183
|
changed = _count_at(api, type_id, entity_ids, seed_change_set.AFTER)
|
|
181
184
|
if changed != step.expected:
|
|
@@ -186,7 +189,7 @@ def _check_commit(api: BruceApi, result: Result, type_id: str, entity_ids: List[
|
|
|
186
189
|
step.ok = not problems
|
|
187
190
|
step.detail = (", ".join(problems) if problems
|
|
188
191
|
else f"{changed} applied, {ChangeSet.COMMITTED}")
|
|
189
|
-
except (ApiError, ImportFailed, ValueError) as e:
|
|
192
|
+
except (ApiError, ImportFailed, TimeoutError, ValueError) as e:
|
|
190
193
|
step.detail = str(e)
|
|
191
194
|
_report_step(report, step)
|
|
192
195
|
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""
|
|
2
|
+
IFC imports: whether a generated document converts, and whether each representation shape survives.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
|
|
8
|
+
from bruce_models.api.bruce_api import BruceApi
|
|
9
|
+
from bruce_models.api.errors import ApiError
|
|
10
|
+
from bruce_models.client_file.client_file import ClientFile
|
|
11
|
+
from bruce_models.entity.entity import Entity
|
|
12
|
+
from bruce_models.entity.entity_type import EntityType
|
|
13
|
+
from bruce_models.format_assembly.assembly_import import AssemblyImport
|
|
14
|
+
from bruce_models.server.pending_action import EStatus
|
|
15
|
+
from bruce_models.cli import seed_bench
|
|
16
|
+
from bruce_models.cli import seed_ifc
|
|
17
|
+
from bruce_models.cli.runner import (ImportFailed, Result, Timer, _finish, _job_outcome,
|
|
18
|
+
_message_relay, _report_step)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# How deep the walk over an imported tree is allowed to go before it gives up.
|
|
22
|
+
# The documents here are a handful of nodes, so anything approaching this is a cycle rather than a deep model.
|
|
23
|
+
MAX_DEPTH = 12
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _import_document(api: BruceApi, result: Result, document: seed_ifc.Document,
|
|
27
|
+
suffix: str, on_message=None) -> dict:
|
|
28
|
+
"""
|
|
29
|
+
Uploads one generated IFC and runs both phases of the import against it.
|
|
30
|
+
"""
|
|
31
|
+
name = f"ifc-{document.key}-{suffix}"
|
|
32
|
+
file_name = f"{name}.ifc"
|
|
33
|
+
try:
|
|
34
|
+
uploaded = ClientFile.upload_temp_bytes(api, document.data, file_name)
|
|
35
|
+
except ValueError as e:
|
|
36
|
+
raise ImportFailed(f"{document.key}: {e}") from None
|
|
37
|
+
|
|
38
|
+
# The upload registers a Client File alongside the temp file, and teardown has to know about it.
|
|
39
|
+
client_file_id = uploaded.get("ClientFile.ID")
|
|
40
|
+
if client_file_id:
|
|
41
|
+
result.client_file_ids.append(client_file_id)
|
|
42
|
+
|
|
43
|
+
parent_type_id = EntityType.create(api, f"bruce test {name}")
|
|
44
|
+
result.assembly_type_ids.append(parent_type_id)
|
|
45
|
+
|
|
46
|
+
outcome = AssemblyImport.run(api, assembly_name=file_name,
|
|
47
|
+
parent_type_name=f"bruce test {name}",
|
|
48
|
+
temp_file_id=uploaded["TempFile.ID"],
|
|
49
|
+
temp_file_name=file_name,
|
|
50
|
+
parent_type_id=parent_type_id,
|
|
51
|
+
on_message=_message_relay(on_message))
|
|
52
|
+
assembly_id = (outcome.get("result") or {}).get("Assembly.ID")
|
|
53
|
+
if assembly_id:
|
|
54
|
+
result.assembly_ids.append(assembly_id)
|
|
55
|
+
result.entity_ids.append(outcome["root_entity_id"])
|
|
56
|
+
return outcome
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _node_count(api: BruceApi, root_entity_id: str) -> int:
|
|
60
|
+
"""
|
|
61
|
+
How many Entities the imported tree holds, root included.
|
|
62
|
+
"""
|
|
63
|
+
seen = set()
|
|
64
|
+
pending = [(root_entity_id, 0)]
|
|
65
|
+
while pending:
|
|
66
|
+
entity_id, depth = pending.pop()
|
|
67
|
+
if not entity_id or entity_id in seen or depth > MAX_DEPTH:
|
|
68
|
+
continue
|
|
69
|
+
seen.add(entity_id)
|
|
70
|
+
for child in Entity.get_children(api, entity_id):
|
|
71
|
+
pending.append((Entity.child_id(child), depth + 1))
|
|
72
|
+
return len(seen)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _leaf_ids(api: BruceApi, root_entity_id: str) -> List[str]:
|
|
76
|
+
"""
|
|
77
|
+
The Entities in the tree with nothing under them, which is where element properties land.
|
|
78
|
+
"""
|
|
79
|
+
leaves = []
|
|
80
|
+
pending = [(root_entity_id, 0)]
|
|
81
|
+
seen = set()
|
|
82
|
+
while pending:
|
|
83
|
+
entity_id, depth = pending.pop()
|
|
84
|
+
if not entity_id or entity_id in seen or depth > MAX_DEPTH:
|
|
85
|
+
continue
|
|
86
|
+
seen.add(entity_id)
|
|
87
|
+
children = Entity.get_children(api, entity_id)
|
|
88
|
+
if not children:
|
|
89
|
+
leaves.append(entity_id)
|
|
90
|
+
for child in children:
|
|
91
|
+
pending.append((Entity.child_id(child), depth + 1))
|
|
92
|
+
return leaves
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _find_property(api: BruceApi, leaf_ids: List[str]) -> Optional[str]:
|
|
96
|
+
"""
|
|
97
|
+
The first value the generated property set put on any leaf, or None when none carried one.
|
|
98
|
+
|
|
99
|
+
Which leaf carries it is not asserted: an importer is free to arrange the tree how it likes, and
|
|
100
|
+
the question here is whether the properties crossed the conversion at all.
|
|
101
|
+
"""
|
|
102
|
+
for entity_id in leaf_ids:
|
|
103
|
+
try:
|
|
104
|
+
entity = Entity.get(api, entity_id)
|
|
105
|
+
except (ApiError, ValueError):
|
|
106
|
+
continue
|
|
107
|
+
# Segments rather than a dotted string: a Nextspace path quotes its parts, so
|
|
108
|
+
# "Pset_BruceTest.Label" parses as one segment naming an attribute nothing answers to.
|
|
109
|
+
for path in ([seed_ifc.PSET_NAME, seed_ifc.PSET_LABEL], [seed_ifc.PSET_LABEL]):
|
|
110
|
+
value = Entity.get_value(entity, path)
|
|
111
|
+
if value:
|
|
112
|
+
return str(value)
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def run_import_ifc(
|
|
117
|
+
api: BruceApi,
|
|
118
|
+
rows: int = 0,
|
|
119
|
+
on_progress=None,
|
|
120
|
+
on_message=None,
|
|
121
|
+
cleanup: bool = True
|
|
122
|
+
) -> Result:
|
|
123
|
+
"""
|
|
124
|
+
Imports one generated IFC and checks the tree and the properties it produced.
|
|
125
|
+
"""
|
|
126
|
+
def report(message):
|
|
127
|
+
if on_progress:
|
|
128
|
+
on_progress(message)
|
|
129
|
+
|
|
130
|
+
result = Result()
|
|
131
|
+
started = time.monotonic()
|
|
132
|
+
document = seed_ifc.simple()
|
|
133
|
+
expected_nodes = seed_ifc.SPATIAL_LEVELS + document.element_count
|
|
134
|
+
|
|
135
|
+
step = result.step("Generate IFC")
|
|
136
|
+
with Timer(step):
|
|
137
|
+
step.ok = True
|
|
138
|
+
step.detail = (f"{document.element_count} elements, {document.size_bytes / 1024:.1f} KB "
|
|
139
|
+
f"(no file fixture)")
|
|
140
|
+
_report_step(report, step)
|
|
141
|
+
|
|
142
|
+
step = result.step("Import IFC")
|
|
143
|
+
imported = None
|
|
144
|
+
with Timer(step):
|
|
145
|
+
try:
|
|
146
|
+
imported = _import_document(api, result, document, seed_bench.new_suffix(),
|
|
147
|
+
on_message)
|
|
148
|
+
step.ok = True
|
|
149
|
+
step.detail = _job_outcome(EStatus.COMPLETE, imported["dispatch"])
|
|
150
|
+
except (ApiError, ImportFailed, ValueError, TimeoutError) as e:
|
|
151
|
+
step.detail = str(e)
|
|
152
|
+
_report_step(report, step)
|
|
153
|
+
|
|
154
|
+
if imported:
|
|
155
|
+
_check_tree(api, result, document, imported["root_entity_id"], expected_nodes, report)
|
|
156
|
+
_check_properties(api, result, imported["root_entity_id"], report)
|
|
157
|
+
|
|
158
|
+
return _finish(api, result, started, cleanup, report)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _check_tree(api: BruceApi, result: Result, document: seed_ifc.Document,
|
|
162
|
+
root_entity_id: str, expected_nodes: int, report) -> None:
|
|
163
|
+
"""
|
|
164
|
+
The converted tree must carry a node for every element plus the spatial levels above them.
|
|
165
|
+
"""
|
|
166
|
+
step = result.step("Check hierarchy")
|
|
167
|
+
step.expected = expected_nodes
|
|
168
|
+
with Timer(step):
|
|
169
|
+
try:
|
|
170
|
+
found = _node_count(api, root_entity_id)
|
|
171
|
+
step.actual = found
|
|
172
|
+
if found != expected_nodes:
|
|
173
|
+
step.detail = (f"expected {expected_nodes} Entities for "
|
|
174
|
+
f"{document.element_count} elements, found {found}")
|
|
175
|
+
else:
|
|
176
|
+
step.ok = True
|
|
177
|
+
step.detail = f"{found} Entities, {document.element_count} elements"
|
|
178
|
+
except (ApiError, ValueError) as e:
|
|
179
|
+
step.detail = str(e)
|
|
180
|
+
_report_step(report, step)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _check_properties(api: BruceApi, result: Result, root_entity_id: str, report) -> None:
|
|
184
|
+
"""
|
|
185
|
+
An IFC property set has to survive the conversion, or the import carries geometry and nothing else.
|
|
186
|
+
"""
|
|
187
|
+
step = result.step("Check properties")
|
|
188
|
+
with Timer(step):
|
|
189
|
+
try:
|
|
190
|
+
leaves = _leaf_ids(api, root_entity_id)
|
|
191
|
+
found = _find_property(api, leaves)
|
|
192
|
+
if not found:
|
|
193
|
+
step.detail = (f"no '{seed_ifc.PSET_NAME}.{seed_ifc.PSET_LABEL}' on any of "
|
|
194
|
+
f"{len(leaves)} leaf Entities")
|
|
195
|
+
else:
|
|
196
|
+
step.ok = True
|
|
197
|
+
step.detail = f"{seed_ifc.PSET_NAME} read back on a leaf"
|
|
198
|
+
except (ApiError, ValueError) as e:
|
|
199
|
+
step.detail = str(e)
|
|
200
|
+
_report_step(report, step)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def run_ifc_variants(
|
|
204
|
+
api: BruceApi,
|
|
205
|
+
rows: int = 0,
|
|
206
|
+
on_progress=None,
|
|
207
|
+
on_message=None,
|
|
208
|
+
cleanup: bool = True
|
|
209
|
+
) -> Result:
|
|
210
|
+
"""
|
|
211
|
+
Imports one IFC per representation shape, so a converter regression names the shape that broke.
|
|
212
|
+
"""
|
|
213
|
+
def report(message):
|
|
214
|
+
if on_progress:
|
|
215
|
+
on_progress(message)
|
|
216
|
+
|
|
217
|
+
result = Result()
|
|
218
|
+
started = time.monotonic()
|
|
219
|
+
documents = seed_ifc.variants()
|
|
220
|
+
suffix = seed_bench.new_suffix()
|
|
221
|
+
|
|
222
|
+
step = result.step("Generate variants")
|
|
223
|
+
with Timer(step):
|
|
224
|
+
total = sum(document.size_bytes for document in documents)
|
|
225
|
+
step.ok = True
|
|
226
|
+
step.detail = f"{len(documents)} documents, {total / 1024:.1f} KB total"
|
|
227
|
+
_report_step(report, step)
|
|
228
|
+
|
|
229
|
+
for document in documents:
|
|
230
|
+
_run_variant(api, result, document, suffix, on_message, report)
|
|
231
|
+
|
|
232
|
+
return _finish(api, result, started, cleanup, report)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _run_variant(api: BruceApi, result: Result, document: seed_ifc.Document, suffix: str,
|
|
236
|
+
on_message, report) -> None:
|
|
237
|
+
"""
|
|
238
|
+
One representation shape: import it, then check every element reached the tree.
|
|
239
|
+
"""
|
|
240
|
+
expected_nodes = seed_ifc.SPATIAL_LEVELS + document.element_count
|
|
241
|
+
step = result.step(f"Import {document.key}")
|
|
242
|
+
step.expected = expected_nodes
|
|
243
|
+
with Timer(step):
|
|
244
|
+
try:
|
|
245
|
+
imported = _import_document(api, result, document, suffix, on_message)
|
|
246
|
+
found = _node_count(api, imported["root_entity_id"])
|
|
247
|
+
step.actual = found
|
|
248
|
+
if found != expected_nodes:
|
|
249
|
+
step.detail = f"expected {expected_nodes} Entities, found {found}"
|
|
250
|
+
else:
|
|
251
|
+
step.ok = True
|
|
252
|
+
step.detail = f"{found} Entities, {document.description}"
|
|
253
|
+
except (ApiError, ImportFailed, ValueError, TimeoutError) as e:
|
|
254
|
+
step.detail = f"{document.description}: {e}"
|
|
255
|
+
_report_step(report, step)
|