shellsmith 0.1.0__tar.gz → 0.2.0__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.
Files changed (37) hide show
  1. {shellsmith-0.1.0/src/shellsmith.egg-info → shellsmith-0.2.0}/PKG-INFO +7 -1
  2. {shellsmith-0.1.0 → shellsmith-0.2.0}/README.md +4 -0
  3. {shellsmith-0.1.0 → shellsmith-0.2.0}/pyproject.toml +5 -1
  4. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/__init__.py +1 -1
  5. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/cli/commands/__init__.py +1 -0
  6. shellsmith-0.2.0/src/shellsmith/cli/commands/sme.py +13 -0
  7. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/cli/main.py +22 -4
  8. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/cli/parser.py +35 -1
  9. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/config.py +5 -1
  10. shellsmith-0.2.0/src/shellsmith/neo4j.py +115 -0
  11. {shellsmith-0.1.0 → shellsmith-0.2.0/src/shellsmith.egg-info}/PKG-INFO +7 -1
  12. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith.egg-info/SOURCES.txt +3 -0
  13. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith.egg-info/requires.txt +3 -0
  14. shellsmith-0.2.0/tests/test_neo4j.py +128 -0
  15. {shellsmith-0.1.0 → shellsmith-0.2.0}/LICENSE +0 -0
  16. {shellsmith-0.1.0 → shellsmith-0.2.0}/setup.cfg +0 -0
  17. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/__main__.py +0 -0
  18. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/cli/__init__.py +0 -0
  19. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/cli/commands/info.py +0 -0
  20. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/cli/commands/nuke.py +0 -0
  21. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/cli/commands/shell.py +0 -0
  22. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/cli/commands/submodel.py +0 -0
  23. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/cli/commands/upload.py +0 -0
  24. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/crud/__init__.py +0 -0
  25. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/crud/shells.py +0 -0
  26. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/crud/submodels.py +0 -0
  27. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/services.py +0 -0
  28. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/upload.py +0 -0
  29. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith/utils.py +0 -0
  30. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith.egg-info/dependency_links.txt +0 -0
  31. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith.egg-info/entry_points.txt +0 -0
  32. {shellsmith-0.1.0 → shellsmith-0.2.0}/src/shellsmith.egg-info/top_level.txt +0 -0
  33. {shellsmith-0.1.0 → shellsmith-0.2.0}/tests/test_cli.py +0 -0
  34. {shellsmith-0.1.0 → shellsmith-0.2.0}/tests/test_cli_integration.py +0 -0
  35. {shellsmith-0.1.0 → shellsmith-0.2.0}/tests/test_crud.py +0 -0
  36. {shellsmith-0.1.0 → shellsmith-0.2.0}/tests/test_upload.py +0 -0
  37. {shellsmith-0.1.0 → shellsmith-0.2.0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shellsmith
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: A Python toolkit and CLI for managing Asset Administration Shells
5
5
  Author-email: Peter Stein <peterstein@dfki.de>
6
6
  License: MIT License
@@ -41,6 +41,8 @@ Requires-Dist: isort; extra == "test"
41
41
  Requires-Dist: pytest; extra == "test"
42
42
  Requires-Dist: pytest-cov; extra == "test"
43
43
  Requires-Dist: pytest-dotenv; extra == "test"
44
+ Provides-Extra: neo4j
45
+ Requires-Dist: neo4j; extra == "neo4j"
44
46
  Dynamic: license-file
45
47
 
46
48
  [![Python Package](https://github.com/ptrstn/shellsmith/actions/workflows/python-package.yaml/badge.svg)](https://github.com/ptrstn/shellsmith/actions/workflows/python-package.yaml)
@@ -92,8 +94,12 @@ Common commands:
92
94
  ```bash
93
95
  aas info # Show all shells and submodels
94
96
  aas upload <file|folder> # Upload AAS file or folder
97
+
95
98
  aas shell delete <id> # Delete a shell
96
99
  aas submodel delete <id> # Delete a submodel
100
+
101
+ aas sme get <id> <path> # Get Submodel element value
102
+ aas sme patch <id> <path> <new_value> # Set Submodel element value
97
103
  ```
98
104
 
99
105
  Use `--cascade` or `--unlink` to control deletion behavior:
@@ -47,8 +47,12 @@ Common commands:
47
47
  ```bash
48
48
  aas info # Show all shells and submodels
49
49
  aas upload <file|folder> # Upload AAS file or folder
50
+
50
51
  aas shell delete <id> # Delete a shell
51
52
  aas submodel delete <id> # Delete a submodel
53
+
54
+ aas sme get <id> <path> # Get Submodel element value
55
+ aas sme patch <id> <path> <new_value> # Set Submodel element value
52
56
  ```
53
57
 
54
58
  Use `--cascade` or `--unlink` to control deletion behavior:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "shellsmith"
7
- version = "0.1.0"
7
+ version = "0.2.0"
8
8
  description = "A Python toolkit and CLI for managing Asset Administration Shells"
9
9
  authors = [{ name = "Peter Stein", email = "peterstein@dfki.de" }]
10
10
  license = { file = "LICENSE" }
@@ -29,6 +29,10 @@ test = [
29
29
  "pytest-dotenv",
30
30
  ]
31
31
 
32
+ neo4j = [
33
+ "neo4j",
34
+ ]
35
+
32
36
  [project.scripts]
33
37
  aas = "shellsmith.cli.main:main"
34
38
 
@@ -1,4 +1,4 @@
1
- __version__ = "0.1.0"
1
+ __version__ = "0.2.0"
2
2
 
3
3
  from .crud.shells import ( # noqa: F401
4
4
  delete_shell,
@@ -2,4 +2,5 @@ from .info import info # noqa: F401
2
2
  from .nuke import nuke # noqa: F401
3
3
  from .shell import shell_delete # noqa: F401
4
4
  from .submodel import submodel_delete # noqa: F401
5
+ from .sme import submodel_element_get, submodel_element_patch # noqa: F401
5
6
  from .upload import upload # noqa: F401
@@ -0,0 +1,13 @@
1
+ import shellsmith
2
+
3
+
4
+ def submodel_element_get(submodel_id: str, id_short_path: str):
5
+ print(f"{submodel_id} -> {id_short_path}:")
6
+ element = shellsmith.get_submodel_element(submodel_id, id_short_path)
7
+ value = element.get("value")
8
+ print(value)
9
+
10
+
11
+ def submodel_element_patch(submodel_id: str, id_short_path: str, value: str):
12
+ shellsmith.patch_submodel_element_value(submodel_id, id_short_path, value)
13
+ print(f"✅ Updated '{id_short_path}' in Submodel '{submodel_id}'")
@@ -3,20 +3,28 @@ import requests
3
3
  from shellsmith import __version__, services
4
4
  from shellsmith.config import config
5
5
 
6
- from .commands import info, nuke, shell_delete, submodel_delete, upload
6
+ from .commands import (
7
+ info,
8
+ nuke,
9
+ shell_delete,
10
+ submodel_delete,
11
+ submodel_element_get,
12
+ submodel_element_patch,
13
+ upload,
14
+ )
7
15
  from .parser import build_parser
16
+ from ..utils import base64_decode, base64_encode
8
17
 
9
18
 
10
19
  def print_header():
11
20
  print("===============================================")
12
- print(f"Shellsmith - AAS Toolkit v{__version__}")
13
- print(f"Host: {config.host} ({services.health()})")
21
+ print(f" Shellsmith - AAS Toolkit v{__version__}")
22
+ print(f" Host: {config.host} ({services.health()})")
14
23
  print("===============================================")
15
24
  print()
16
25
 
17
26
 
18
27
  def main():
19
- print_header()
20
28
  parser = build_parser()
21
29
  args = parser.parse_args()
22
30
 
@@ -26,6 +34,12 @@ def main():
26
34
  "nuke": lambda _args: nuke(),
27
35
  "shell.delete": lambda _args: shell_delete(args.id, cascade=args.cascade),
28
36
  "submodel.delete": lambda _args: submodel_delete(args.id, unlink=args.unlink),
37
+ "sme.get": lambda _args: submodel_element_get(args.id, args.path),
38
+ "sme.patch": lambda _args: submodel_element_patch(
39
+ args.id, args.path, args.value
40
+ ),
41
+ "encode": lambda _args: print(base64_encode(args.id)),
42
+ "decode": lambda _args: print(base64_decode(args.value)),
29
43
  }
30
44
 
31
45
  try:
@@ -34,9 +48,13 @@ def main():
34
48
  key += f".{args.shell_command}"
35
49
  elif key == "submodel" and args.submodel_command:
36
50
  key += f".{args.submodel_command}"
51
+ elif key == "sme" and args.submodel_element_command:
52
+ key += f".{args.submodel_element_command}"
37
53
 
38
54
  handler = commands.get(key)
39
55
  if handler:
56
+ if key not in ("encode", "decode"):
57
+ print_header()
40
58
  handler(args)
41
59
  else:
42
60
  parser.print_help()
@@ -26,6 +26,7 @@ def build_parser():
26
26
  # ──────────────────────────── shell ─────────────────────────────
27
27
  shell_parser = subparsers.add_parser(
28
28
  "shell",
29
+ aliases=["sh"],
29
30
  help="Manage Asset Administration Shells",
30
31
  )
31
32
  shell_subparsers = shell_parser.add_subparsers(dest="shell_command")
@@ -42,7 +43,9 @@ def build_parser():
42
43
  )
43
44
 
44
45
  # ───────────────────────────── submodel ─────────────────────────────
45
- submodel_parser = subparsers.add_parser("submodel", help="Manage Submodels")
46
+ submodel_parser = subparsers.add_parser(
47
+ "submodel", aliases=["sm"], help="Manage Submodels"
48
+ )
46
49
  submodel_subparsers = submodel_parser.add_subparsers(dest="submodel_command")
47
50
  submodel_delete_parser = submodel_subparsers.add_parser(
48
51
  "delete",
@@ -60,4 +63,35 @@ def build_parser():
60
63
  help="Remove all Shell references to this Submodel",
61
64
  )
62
65
 
66
+ # ───────────────────── submodel-element ─────────────────────
67
+ sme_parser = subparsers.add_parser(
68
+ "submodel-element",
69
+ aliases=["sme"],
70
+ help="Manage Submodel Elements",
71
+ )
72
+ sme_subparsers = sme_parser.add_subparsers(dest="submodel_element_command")
73
+
74
+ sme_get_parser = sme_subparsers.add_parser(
75
+ "get",
76
+ help="Get value of a Submodel Element",
77
+ )
78
+ sme_get_parser.add_argument("id", type=str, help="Submodel ID")
79
+ sme_get_parser.add_argument("path", type=str, help="idShort path")
80
+
81
+ sme_patch_parser = sme_subparsers.add_parser(
82
+ "patch",
83
+ help="Patch value of a Submodel Element",
84
+ )
85
+ sme_patch_parser.add_argument("id", type=str, help="Submodel ID")
86
+ sme_patch_parser.add_argument("path", type=str, help="idShort path")
87
+ sme_patch_parser.add_argument("value", type=str, help="New value")
88
+
89
+ # ───────────────────────────── encode ─────────────────────────────
90
+ encode_parser = subparsers.add_parser("encode", help="Encode a string to base64")
91
+ encode_parser.add_argument("id", type=str, help="Text to encode")
92
+
93
+ # ───────────────────────────── decode ─────────────────────────────
94
+ decode_parser = subparsers.add_parser("decode", help="Decode a base64 string")
95
+ decode_parser.add_argument("value", type=str, help="Base64 string to decode")
96
+
63
97
  return parser
@@ -9,7 +9,11 @@ class Settings(BaseSettings):
9
9
  def host(self):
10
10
  return self.basyx_env_host
11
11
 
12
- model_config = SettingsConfigDict(env_file=".env", env_prefix="SHELLSMITH_")
12
+ model_config = SettingsConfigDict(
13
+ env_file=".env",
14
+ env_prefix="SHELLSMITH_",
15
+ extra="ignore",
16
+ )
13
17
 
14
18
 
15
19
  config = Settings()
@@ -0,0 +1,115 @@
1
+ from typing import Any, Dict, List, Optional
2
+
3
+ from neo4j import Driver, GraphDatabase
4
+
5
+ from shellsmith.config import config
6
+
7
+ _driver: Optional[Driver] = None
8
+
9
+
10
+ def get_driver() -> Driver:
11
+ global _driver
12
+ if _driver is None:
13
+ _driver = GraphDatabase.driver(config.neo4j_uri, auth=None)
14
+ return _driver
15
+
16
+
17
+ def close_driver():
18
+ global _driver
19
+ if _driver is not None:
20
+ _driver.close()
21
+ _driver = None
22
+
23
+
24
+ ##################
25
+ # Shells
26
+ ##################
27
+
28
+
29
+ def get_shells() -> List[Dict[str, Any]]:
30
+ query = """
31
+ MATCH (shell:AssetAdministrationShell)
32
+ RETURN shell;
33
+ """
34
+ with get_driver().session() as session:
35
+ result = session.run(query)
36
+ shells = [dict(record["shell"]) for record in result]
37
+ return shells
38
+
39
+
40
+ def get_shell(shell_id: str) -> Optional[Dict[str, Any]]:
41
+ query = """
42
+ MATCH (shell:AssetAdministrationShell {id: $shell_id})
43
+ RETURN shell;
44
+ """
45
+ with get_driver().session() as session:
46
+ result = session.run(query, shell_id=shell_id)
47
+ record = result.single()
48
+ return dict(record["shell"]) if record else None
49
+
50
+
51
+ ##################
52
+ # Submodels
53
+ ##################
54
+
55
+
56
+ def get_submodels() -> List[Dict[str, Any]]:
57
+ query = """
58
+ MATCH (submodel:Submodel)
59
+ RETURN submodel
60
+ """
61
+ with get_driver().session() as session:
62
+ result = session.run(query)
63
+ submodels = [dict(record["submodel"]) for record in result]
64
+ return submodels
65
+
66
+
67
+ def get_submodel(submodel_id: str) -> Dict[str, Any]:
68
+ query = """
69
+ MATCH (submodel:Submodel {id: $submodel_id})
70
+ RETURN submodel
71
+ """
72
+ with get_driver().session() as session:
73
+ result = session.run(query, submodel_id=submodel_id)
74
+ record = result.single()
75
+ return dict(record["submodel"]) if record else None
76
+
77
+
78
+ def get_submodel_elements(submodel_id: str) -> List[Dict[str, Any]]:
79
+ query = """
80
+ MATCH (sme:SubmodelElement {smId: $submodel_id})
81
+ RETURN sme;
82
+ """
83
+ with get_driver().session() as session:
84
+ result = session.run(query, submodel_id=submodel_id)
85
+ return [dict(record["sme"]) for record in result]
86
+
87
+
88
+ def get_submodel_element(
89
+ submodel_id: str, id_short_path: str
90
+ ) -> Optional[Dict[str, Any]]:
91
+ query = """
92
+ MATCH (sme:SubmodelElement {smId: $submodel_id, idShortPath: $id_short_path})
93
+ RETURN sme;
94
+ """
95
+ with get_driver().session() as session:
96
+ result = session.run(
97
+ query,
98
+ submodel_id=submodel_id,
99
+ id_short_path=id_short_path,
100
+ )
101
+ record = result.single()
102
+ return dict(record["sme"]) if record else None
103
+
104
+
105
+ def detach_delete_all():
106
+ """
107
+ Dangerous!
108
+ Deletes all nodes and relationships in the graph.
109
+ """
110
+ query = """
111
+ MATCH (n)
112
+ DETACH DELETE n;
113
+ """
114
+ with get_driver().session() as session:
115
+ session.run(query)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shellsmith
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: A Python toolkit and CLI for managing Asset Administration Shells
5
5
  Author-email: Peter Stein <peterstein@dfki.de>
6
6
  License: MIT License
@@ -41,6 +41,8 @@ Requires-Dist: isort; extra == "test"
41
41
  Requires-Dist: pytest; extra == "test"
42
42
  Requires-Dist: pytest-cov; extra == "test"
43
43
  Requires-Dist: pytest-dotenv; extra == "test"
44
+ Provides-Extra: neo4j
45
+ Requires-Dist: neo4j; extra == "neo4j"
44
46
  Dynamic: license-file
45
47
 
46
48
  [![Python Package](https://github.com/ptrstn/shellsmith/actions/workflows/python-package.yaml/badge.svg)](https://github.com/ptrstn/shellsmith/actions/workflows/python-package.yaml)
@@ -92,8 +94,12 @@ Common commands:
92
94
  ```bash
93
95
  aas info # Show all shells and submodels
94
96
  aas upload <file|folder> # Upload AAS file or folder
97
+
95
98
  aas shell delete <id> # Delete a shell
96
99
  aas submodel delete <id> # Delete a submodel
100
+
101
+ aas sme get <id> <path> # Get Submodel element value
102
+ aas sme patch <id> <path> <new_value> # Set Submodel element value
97
103
  ```
98
104
 
99
105
  Use `--cascade` or `--unlink` to control deletion behavior:
@@ -4,6 +4,7 @@ pyproject.toml
4
4
  src/shellsmith/__init__.py
5
5
  src/shellsmith/__main__.py
6
6
  src/shellsmith/config.py
7
+ src/shellsmith/neo4j.py
7
8
  src/shellsmith/services.py
8
9
  src/shellsmith/upload.py
9
10
  src/shellsmith/utils.py
@@ -20,6 +21,7 @@ src/shellsmith/cli/commands/__init__.py
20
21
  src/shellsmith/cli/commands/info.py
21
22
  src/shellsmith/cli/commands/nuke.py
22
23
  src/shellsmith/cli/commands/shell.py
24
+ src/shellsmith/cli/commands/sme.py
23
25
  src/shellsmith/cli/commands/submodel.py
24
26
  src/shellsmith/cli/commands/upload.py
25
27
  src/shellsmith/crud/__init__.py
@@ -28,5 +30,6 @@ src/shellsmith/crud/submodels.py
28
30
  tests/test_cli.py
29
31
  tests/test_cli_integration.py
30
32
  tests/test_crud.py
33
+ tests/test_neo4j.py
31
34
  tests/test_upload.py
32
35
  tests/test_utils.py
@@ -1,6 +1,9 @@
1
1
  pydantic-settings
2
2
  requests
3
3
 
4
+ [neo4j]
5
+ neo4j
6
+
4
7
  [test]
5
8
  black
6
9
  flake8
@@ -0,0 +1,128 @@
1
+ import os
2
+ import time
3
+
4
+ import pytest
5
+
6
+ from shellsmith import neo4j
7
+ from shellsmith.upload import upload_aas_folder
8
+
9
+ if not os.getenv("SHELLSMITH_NEO4J_ENABLED"):
10
+ pytest.skip(
11
+ reason="Skipping Neo4j tests. Set SHELLSMITH_NEO4J_ENABLED=1 to enable.",
12
+ allow_module_level=True,
13
+ )
14
+
15
+ GRAPH_READY_TIMEOUT = 15 # seconds
16
+ EXPECTED_NODE_COUNT = 39
17
+ EXPECTED_SUBMODEL_ELEMENT_COUNT = 17 # or whatever your total is
18
+
19
+
20
+ def wait_for_graph_node_count(
21
+ expected_count: int = EXPECTED_NODE_COUNT,
22
+ timeout: int = GRAPH_READY_TIMEOUT,
23
+ ) -> bool:
24
+ query = "MATCH (n) RETURN count(n) AS count"
25
+ start = time.time()
26
+
27
+ while True:
28
+ try:
29
+ with neo4j.get_driver().session() as session:
30
+ result = session.run(query)
31
+ record = result.single()
32
+ if record and record["count"] == expected_count:
33
+ return True
34
+ except Exception: # noqa
35
+ pass # wait and retry
36
+
37
+ if time.time() - start > timeout:
38
+ return False
39
+
40
+ time.sleep(0.5)
41
+
42
+
43
+ def test_wait_for_graph():
44
+ neo4j.detach_delete_all()
45
+ assert wait_for_graph_node_count(0), "Graph was not fully destructed within timeout"
46
+ upload_aas_folder("aas")
47
+ assert wait_for_graph_node_count(39), "Graph was not fully built within timeout"
48
+ time.sleep(1)
49
+
50
+
51
+ def test_get_shells(semitrailer, workpiece_carrier_a1):
52
+ shells = neo4j.get_shells()
53
+ assert len(shells) == 2
54
+
55
+ shell_id_shorts = {shell["idShort"] for shell in shells}
56
+ shell_ids = {shell["id"] for shell in shells}
57
+ assert semitrailer.id in shell_ids
58
+ assert workpiece_carrier_a1.id in shell_ids
59
+ assert semitrailer.id_short in shell_id_shorts
60
+ assert workpiece_carrier_a1.id_short in shell_id_shorts
61
+
62
+
63
+ def test_get_shell(
64
+ semitrailer,
65
+ workpiece_carrier_a1,
66
+ ):
67
+ shell = neo4j.get_shell(semitrailer.id)
68
+ assert shell["id"] == semitrailer.id
69
+ assert shell["idShort"] == semitrailer.id_short
70
+
71
+ shell = neo4j.get_shell(workpiece_carrier_a1.id)
72
+ assert shell["id"] == workpiece_carrier_a1.id
73
+ assert shell["idShort"] == workpiece_carrier_a1.id_short
74
+
75
+
76
+ def test_get_submodels(semitrailer, workpiece_carrier_a1):
77
+ submodels = neo4j.get_submodels()
78
+ assert len(submodels) == 4
79
+
80
+
81
+ def test_get_submodel(
82
+ semitrailer,
83
+ workpiece_carrier_a1,
84
+ ):
85
+ submodel = neo4j.get_submodel(semitrailer.product_identification.id)
86
+ assert submodel["idShort"] == semitrailer.product_identification.id_short
87
+
88
+ submodel = neo4j.get_submodel(semitrailer.production_plan.id)
89
+ assert submodel["idShort"] == semitrailer.production_plan.id_short
90
+
91
+ submodel = neo4j.get_submodel(workpiece_carrier_a1.good_information.id)
92
+ assert submodel["idShort"] == workpiece_carrier_a1.good_information.id_short
93
+
94
+ submodel = neo4j.get_submodel(workpiece_carrier_a1.asset_location.id)
95
+ assert submodel["idShort"] == workpiece_carrier_a1.asset_location.id_short
96
+
97
+
98
+ def test_get_submodel_elements(semitrailer, workpiece_carrier_a1):
99
+ elements = neo4j.get_submodel_elements(semitrailer.product_identification.id)
100
+ assert len(elements) == 2
101
+ id_shorts = {el["idShort"] for el in elements}
102
+ assert "Identifier" in id_shorts
103
+ assert "ProductName" in id_shorts
104
+
105
+ elements = neo4j.get_submodel_elements(workpiece_carrier_a1.good_information.id)
106
+ assert len(elements) == 7
107
+ id_shorts = {el["idShort"] for el in elements}
108
+ assert "CurrentProduct" in id_shorts
109
+ assert "ListTransportableProducts" in id_shorts
110
+ assert "ProductName" in id_shorts
111
+
112
+
113
+ def test_get_submodel_element(semitrailer, workpiece_carrier_a1):
114
+ element = neo4j.get_submodel_element(
115
+ semitrailer.product_identification.id, "ProductName"
116
+ )
117
+ assert element["idShort"] == "ProductName"
118
+ assert element["value"] == "Semitrailer"
119
+
120
+ element = neo4j.get_submodel_element(
121
+ workpiece_carrier_a1.asset_location.id, "CurrentFences[0].FenceName"
122
+ )
123
+ assert element["idShort"] == "FenceName"
124
+ assert element["value"] == "TSN-Module"
125
+
126
+
127
+ def test_close_driver():
128
+ neo4j.close_driver()
File without changes
File without changes
File without changes