Repository: BAMWelDX/weldx

Issue:
[CSM] add possibility to remove assigned data again.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/BAMWelDX/weldx/pull/645?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX) Report
> Merging [#645](https://codecov.io/gh/BAMWelDX/weldx/pull/645?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX) (dc79955) into [master](https://codecov.io/gh/BAMWelDX/weldx/commit/04327b7981ac6bf2bd577826a488b1f7cc69e6ee?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX) (04327b7) will **decrease** coverage by `0.00%`.
> The diff coverage is `87.50%`.

[![Impacted file tree graph](https://codecov.io/gh/BAMWelDX/weldx/pull/645/graphs/tree.svg?width=650&height=150&src=pr&token=wdof1qQTsn&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX)](https://codecov.io/gh/BAMWelDX/weldx/pull/645?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX)

```diff
@@            Coverage Diff             @@
##           master     #645      +/-   ##
==========================================
- Coverage   94.49%   94.49%   -0.01%     
==========================================
  Files          93       93              
  Lines        6018     6026       +8     
==========================================
+ Hits         5687     5694       +7     
- Misses        331      332       +1     
```


| [Impacted Files](https://codecov.io/gh/BAMWelDX/weldx/pull/645?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX) | Coverage Δ | |
|---|---|---|
| [weldx/transformations/cs\_manager.py](https://codecov.io/gh/BAMWelDX/weldx/pull/645/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX#diff-d2VsZHgvdHJhbnNmb3JtYXRpb25zL2NzX21hbmFnZXIucHk=) | `98.58% <87.50%> (-0.19%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/BAMWelDX/weldx/pull/645?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/BAMWelDX/weldx/pull/645?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX). Last update [04327b7...dc79955](https://codecov.io/gh/BAMWelDX/weldx/pull/645?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=BAMWelDX).

github-actions[bot]: ## Unit Test Results
       1 files  ±0         1 suites  ±0   1m 39s [:stopwatch:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v1.20/README.md#the-symbols "duration of all tests") -10s
1 931 tests +1  1 931 [:heavy_check_mark:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v1.20/README.md#the-symbols "passed tests") +1  0 [:zzz:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v1.20/README.md#the-symbols "skipped / disabled tests") ±0  0 [:x:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v1.20/README.md#the-symbols "failed tests") ±0 

Results for commit dc79955b. ± Comparison against base commit 04327b79.

marscher: @vhirtham Now the data is also deleted for subsystems. Do I understand correctly, that subsystems are created exclusively by deleting and merging CSMs?
vhirtham: > @vhirtham Now the data is also deleted for subsystems. Do I understand correctly, that subsystems are created exclusively by deleting and merging CSMs?

Basically yes. The disassembly is also most relevant for the asdf serialization. In practice, we probably only use the merge function.
CagtayFabry: good to have this option 👍 

Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 55814de..dc0d0f9 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -56,3 +56,8 @@ repos:
       - id: jupyter-notebook-cleanup
         args:
           - --remove-kernel-metadata
+  - repo: https://github.com/codespell-project/codespell/
+    rev: v2.1.0
+    hooks:
+      - id: codespell
+        exclude: doc/legal-notice.rst
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 25677fa..dd121c5 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -9,6 +9,9 @@
 added
 =====
 
+- `CoordinateSystemManager` can now delete already assigned data with `CoordinateSystemManager.delete_data`.
+  `[#645] <https://github.com/BAMWelDX/weldx/pull/645>`__
+
 - `WeldxFile` handles an ``array_inline_threshold`` parameter to indicate if short arrays
   will be serialized as strings, or as binary block. Note that this does not affect arrays,
   which are being shared across several objects in the same file.
diff --git a/tutorials/geometry_01_profiles.ipynb b/tutorials/geometry_01_profiles.ipynb
index 5f528c3..cc867ca 100644
--- a/tutorials/geometry_01_profiles.ipynb
+++ b/tutorials/geometry_01_profiles.ipynb
@@ -8,13 +8,13 @@
     "\n",
     "## Introduction\n",
     "\n",
-    "This tutorial is about generating custom 2d profiles using the geometry package. The process can be divided into 3 seperate steps.\n",
+    "This tutorial is about generating custom 2d profiles using the geometry package. The process can be divided into 3 separate steps.\n",
     "\n",
     "- Create segments\n",
     "- Create Shapes from segments\n",
     "- Create a profile from multiple shapes\n",
     "\n",
-    "Each individual step will be discussed seperately.\n",
+    "Each individual step will be discussed separately.\n",
     "\n",
     "Before we can start, we need to import some packages:"
    ]
@@ -213,7 +213,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "As for the `LineSegment`, an `ArcSegment` can also be constructed using the class constructor. It takes a 2x3 matrix and a winding order as parameters, were the columns are start, end and center point. However, for the same reasons mentioned in the section about the `LineSegment`, it is better to use the \"constuct\" methods.\n",
+    "As for the `LineSegment`, an `ArcSegment` can also be constructed using the class constructor. It takes a 2x3 matrix and a winding order as parameters, were the columns are start, end and center point. However, for the same reasons mentioned in the section about the `LineSegment`, it is better to use the \"construct\" methods.\n",
     "\n",
     "## Shape\n",
     "\n",
@@ -580,7 +580,7 @@
     "\n",
     "## Custom segments\n",
     "\n",
-    "It might happen that lines and arcs are not enough to sufficiently describe a certain shape or profile. For this reason it is possible to define custom segment types. A segment which is useable with the `Shape` and `Profile` classes is a python class that needs at least a `rasterize` method and a `point_start` and `point_end` property. However, you want to use the Shapes transformation functions, you also need to define the segments `translate`, `transform`, `apply_translation` and `apply_transformation` functions. \n",
+    "It might happen that lines and arcs are not enough to sufficiently describe a certain shape or profile. For this reason it is possible to define custom segment types. A segment which is usable with the `Shape` and `Profile` classes is a python class that needs at least a `rasterize` method and a `point_start` and `point_end` property. However, you want to use the Shapes transformation functions, you also need to define the segments `translate`, `transform`, `apply_translation` and `apply_transformation` functions. \n",
     "\n",
     "As a small example, we will create a sinusoidal wave segment. It should generate a sinusoidal wave in normal direction to the line from the segments start to its end. Since the start and end points must be included in the segments shape (otherwise we get visual gaps during rasterization) we can only use waves with wave lengths $N\\pi$, were $N$ is the number half waves. Additionally we will add the option to vary the waves amplitude. The constructor of the class looks as follows:\n",
     "\n",
@@ -765,7 +765,7 @@
    "source": [
     "As you can see, we have successfully implemented our custom segment. \n",
     "\n",
-    "If we want to apply transformations to the shape, we need to add the corresponding functionality to the segment type. We generate a new class which inherets from the `SineWaveSegmentBase`. Then we add the following functions, which perform \"in-place\" transformations:\n",
+    "If we want to apply transformations to the shape, we need to add the corresponding functionality to the segment type. We generate a new class which inherits from the `SineWaveSegmentBase`. Then we add the following functions, which perform \"in-place\" transformations:\n",
     "\n",
     "~~~ python\n",
     "def apply_translation(self, vector):\n",
diff --git a/tutorials/groove_types_01.ipynb b/tutorials/groove_types_01.ipynb
index ba448dd..ff42c8a 100644
--- a/tutorials/groove_types_01.ipynb
+++ b/tutorials/groove_types_01.ipynb
@@ -67,7 +67,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "As shown above you pass the `groove_type` along with the `attributes` and get your `Groove class`. Function `get_groove` has a detailled description for all Groove Types and their Attributes.\n",
+    "As shown above you pass the `groove_type` along with the `attributes` and get your `Groove class`. Function `get_groove` has a detailed description for all Groove Types and their Attributes.\n",
     "\n",
     "Note: All classes can also be created separately with the classes, but this is not recommended.\n",
     "```Python\n",
diff --git a/tutorials/measurement_chain.ipynb b/tutorials/measurement_chain.ipynb
index f052ddf..7a1ffe9 100644
--- a/tutorials/measurement_chain.ipynb
+++ b/tutorials/measurement_chain.ipynb
@@ -528,7 +528,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Remember, that the returned `TimeSeries` posseses a plot function that lets you create a plot of the time dependent data:"
+    "Remember, that the returned `TimeSeries` possesses a plot function that lets you create a plot of the time dependent data:"
    ]
   },
   {
diff --git a/tutorials/quality_standards.ipynb b/tutorials/quality_standards.ipynb
index 02703c3..0e6938e 100644
--- a/tutorials/quality_standards.ipynb
+++ b/tutorials/quality_standards.ipynb
@@ -193,7 +193,7 @@
     "  A piece of measurement equipment.\n",
     "description: |\n",
     "  This schema describes a piece of measurement equipment that is part of a measurement chain.\n",
-    "  Equipments can be associated with signal sources and data transformations.\n",
+    "  Equipment can be associated with signal sources and data transformations.\n",
     "\n",
     "type: object\n",
     "properties:\n",
@@ -253,7 +253,7 @@
     "  A piece of measurement equipment.\n",
     "description: |\n",
     "  This schema describes a piece of measurement equipment that is part of a measurement chain.\n",
-    "  Equipments can be associated with signal sources and data transformations.\n",
+    "  Equipment can be associated with signal sources and data transformations.\n",
     "\n",
     "type: object\n",
     "properties:\n",
diff --git a/tutorials/weldxfile.ipynb b/tutorials/weldxfile.ipynb
index 3e9d789..4ab3e62 100644
--- a/tutorials/weldxfile.ipynb
+++ b/tutorials/weldxfile.ipynb
@@ -100,7 +100,7 @@
    "metadata": {},
    "source": [
     "You might have noticed, that we got a warning about the in-memory operation during showing the file in Jupyter.\n",
-    "Now we have passed the additional argument mode=\"rw\", which indiciates, that we want to perform write operations just in memory,\n",
+    "Now we have passed the additional argument mode=\"rw\", which indicates, that we want to perform write operations just in memory,\n",
     "or alternatively to the passed physical file. So this warning went away."
    ]
   },
@@ -198,7 +198,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Note, that we closed the file here explictly. Before closing, we wanted to write a simple item to tree. But lets see what happens, if we open the file once again."
+    "Note, that we closed the file here explicitly. Before closing, we wanted to write a simple item to tree. But lets see what happens, if we open the file once again."
    ]
   },
   {
@@ -370,7 +370,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "## Handeling of custom schemas\n",
+    "## Handling of custom schemas\n",
     "An important aspect of WelDX or ASDF files is, that you can validate them to comply with a defined schema. A schema defines required and optional attributes a tree structure has to provide to pass the schema validation. Further the types of these attributes can be defined, e.g. the data attribute should be a NumPy array, or a timestamp should be of type `pandas.Timestamp`.\n",
     "There are several schemas provided by WelDX, which can be used by passing them to the `custom_schema` argument. It is expected to be a path-like type, so a string (`str`) or `pathlib.Path` is accepted. The provided utility function `get_schema_path` returns the path to named schema. So its output can directly be used in WeldxFile(schema=...)"
    ]
@@ -497,8 +497,8 @@
    "metadata": {},
    "source": [
     "Here we see, that a `signal` tag is expected, but a `asdf/core/ndarray-1.0.0` was received. \n",
-    "The ASDF library assignes tags to certain types to handle their storage in the file format. \n",
-    "As shown, the `signal` tag is contained in `weldx/measurement` container, provided by `weldx.bam.de`. The tags and schemas also provide a version number, so future updates in the software become managable.\n",
+    "The ASDF library assigns tags to certain types to handle their storage in the file format. \n",
+    "As shown, the `signal` tag is contained in `weldx/measurement` container, provided by `weldx.bam.de`. The tags and schemas also provide a version number, so future updates in the software become manageable.\n",
     "\n",
     "Custom schemas can be used to define own protocols or standards describing your data."
    ]
diff --git a/weldx/asdf/util.py b/weldx/asdf/util.py
index 84eb25d..936ca7b 100644
--- a/weldx/asdf/util.py
+++ b/weldx/asdf/util.py
@@ -380,7 +380,7 @@ def dataclass_serialization_class(
     class_type :
         The type of the dataclass
     class_name :
-        The value that should ba stored as the classes name property
+        The value that should be stored as the classes name property
     version :
         The version number
     to_yaml_tree_mod :
diff --git a/weldx/schemas/weldx.bam.de/legacy/datamodels/single_pass_weld-1.0.0.schema.yaml b/weldx/schemas/weldx.bam.de/legacy/datamodels/single_pass_weld-1.0.0.schema.yaml
index 8c4a5f6..792a53a 100644
--- a/weldx/schemas/weldx.bam.de/legacy/datamodels/single_pass_weld-1.0.0.schema.yaml
+++ b/weldx/schemas/weldx.bam.de/legacy/datamodels/single_pass_weld-1.0.0.schema.yaml
@@ -103,7 +103,7 @@ properties:
     tag: "tag:weldx.bam.de:weldx/core/transformations/coordinate_system_hierarchy-1.0.0"
   equipment:
     description: |
-      A list of equipments used for measurements and describing the weld seam.
+      A list of equipment used for measurements and describing the weld seam.
     type: array
     items:
       tag: "tag:weldx.bam.de:weldx/equipment/measurement_equipment-1.0.0"
diff --git a/weldx/schemas/weldx.bam.de/legacy/equipment/measurement_equipment-1.0.0.yaml b/weldx/schemas/weldx.bam.de/legacy/equipment/measurement_equipment-1.0.0.yaml
index f75b049..babd2a0 100644
--- a/weldx/schemas/weldx.bam.de/legacy/equipment/measurement_equipment-1.0.0.yaml
+++ b/weldx/schemas/weldx.bam.de/legacy/equipment/measurement_equipment-1.0.0.yaml
@@ -8,7 +8,7 @@ title: |
   A piece of measurement equipment.
 description: |
   This schema describes a piece of measurement equipment that is part of a measurement chain.
-  Equipments can be associated with signal sources and data transformations.
+  Equipment can be associated with signal sources and data transformations.
 
 examples:
   -
diff --git a/weldx/schemas/weldx.bam.de/weldx/core/transformations/coordinate_system_hierarchy-0.1.0.yaml b/weldx/schemas/weldx.bam.de/weldx/core/transformations/coordinate_system_hierarchy-0.1.0.yaml
index 7a04574..d84a994 100644
--- a/weldx/schemas/weldx.bam.de/weldx/core/transformations/coordinate_system_hierarchy-0.1.0.yaml
+++ b/weldx/schemas/weldx.bam.de/weldx/core/transformations/coordinate_system_hierarchy-0.1.0.yaml
@@ -130,7 +130,7 @@ definitions:
 
   subsystem_info:
     description: |
-      A list of a coordinate system hierarchy's subsytems.
+      A list of a coordinate system hierarchy's subsystems.
     type: array
     items:
       type: object
@@ -167,7 +167,7 @@ definitions:
             type: string
         subsystems:
           description: |
-            A list of nested subsytems.
+            A list of nested subsystems.
           $ref: "#/definitions/subsystem_info"
 
 properties:
diff --git a/weldx/schemas/weldx.bam.de/weldx/datamodels/single_pass_weld-0.1.0.yaml b/weldx/schemas/weldx.bam.de/weldx/datamodels/single_pass_weld-0.1.0.yaml
index e5bbe71..82e75bd 100644
--- a/weldx/schemas/weldx.bam.de/weldx/datamodels/single_pass_weld-0.1.0.yaml
+++ b/weldx/schemas/weldx.bam.de/weldx/datamodels/single_pass_weld-0.1.0.yaml
@@ -103,7 +103,7 @@ properties:
     tag: "asdf://weldx.bam.de/weldx/tags/core/transformations/coordinate_system_hierarchy-0.1.*"
   equipment:
     description: |
-      A list of equipments used for measurements and describing the weld seam.
+      A list of equipment used for measurements and describing the weld seam.
     type: array
     items:
       tag: "asdf://weldx.bam.de/weldx/tags/equipment/measurement_equipment-0.1.*"
diff --git a/weldx/schemas/weldx.bam.de/weldx/equipment/measurement_equipment-0.1.0.yaml b/weldx/schemas/weldx.bam.de/weldx/equipment/measurement_equipment-0.1.0.yaml
index a895534..fb9dbc7 100644
--- a/weldx/schemas/weldx.bam.de/weldx/equipment/measurement_equipment-0.1.0.yaml
+++ b/weldx/schemas/weldx.bam.de/weldx/equipment/measurement_equipment-0.1.0.yaml
@@ -7,7 +7,7 @@ title: |
   A piece of measurement equipment.
 description: |
   This schema describes a piece of measurement equipment that is part of a measurement chain.
-  Equipments can be associated with signal sources and data transformations.
+  Equipment can be associated with signal sources and data transformations.
 
 examples:
   -
diff --git a/weldx/time.py b/weldx/time.py
index a651f3f..1c58b06 100644
--- a/weldx/time.py
+++ b/weldx/time.py
@@ -718,7 +718,7 @@ class Time:
     union = _UnionDescriptor()
     """Calculate the union of multiple time-like objects.
 
-    This method can eiter be used as a class or instance method. When used on an
+    This method can either be used as a class or instance method. When used on an
     instance, its values are included in the calculated time union.
 
     Note that any reference time information will be dropped.
diff --git a/weldx/transformations/cs_manager.py b/weldx/transformations/cs_manager.py
index cf3691d..e37abcc 100644
--- a/weldx/transformations/cs_manager.py
+++ b/weldx/transformations/cs_manager.py
@@ -610,6 +610,32 @@ class CoordinateSystemManager:
 
         self._graph.nodes[reference_system]["data"][data_name] = data
 
+    def delete_data(self, data_name: str):
+        """Remove the assigned data with given name.
+
+        Parameters
+        ----------
+        data_name :
+            The previously assigned data name.
+
+        Raises
+        ------
+        ValueError
+            If data_name has not been assigned yet.
+        """
+        # remove data from subsystems
+        for s in self._subsystems:
+            s.data.discard(data_name)
+
+        nodes = self.graph.nodes
+        for cs in nodes:
+            for name in nodes[cs]["data"].keys():
+                if name == data_name:
+                    nodes[cs]["data"].pop(data_name)
+                    return
+
+        raise ValueError(f"Given data_name '{data_name}' has not been assigned.")
+
     def create_cs(
         self,
         coordinate_system_name: str,
@@ -793,7 +819,7 @@ class CoordinateSystemManager:
         """Delete a coordinate system from the coordinate system manager.
 
         If the Coordinate system manager has attached sub system, there are multiple
-        possible  consequences.
+        possible consequences.
 
         - All subsystems attached to the deleted coordinate system or one of
           its child systems are removed from the coordinate system manager


Test patch:
diff --git a/weldx/tests/data/quality_standard/resources/test_organization/schemas/alternative_measurement_equipment_schema-1.0.0.yaml b/weldx/tests/data/quality_standard/resources/test_organization/schemas/alternative_measurement_equipment_schema-1.0.0.yaml
index ef5cfd3..f83a2d5 100644
--- a/weldx/tests/data/quality_standard/resources/test_organization/schemas/alternative_measurement_equipment_schema-1.0.0.yaml
+++ b/weldx/tests/data/quality_standard/resources/test_organization/schemas/alternative_measurement_equipment_schema-1.0.0.yaml
@@ -8,7 +8,7 @@ title: |
   A piece of measurement equipment.
 description: |
   This schema describes a piece of measurement equipment that is part of a measurement chain.
-  Equipments can be associated with signal sources and data transformations.
+  Equipment can be associated with signal sources and data transformations.
 
 type: object
 properties:
diff --git a/weldx/tests/transformations/test_cs_manager.py b/weldx/tests/transformations/test_cs_manager.py
index b89234b..34276b8 100644
--- a/weldx/tests/transformations/test_cs_manager.py
+++ b/weldx/tests/transformations/test_cs_manager.py
@@ -2230,6 +2230,39 @@ def test_assign_data_exceptions(arguments, exception_type, test_name):
         csm.assign_data(*arguments)
 
 
+def test_delete_data(list_of_csm_and_lcs_instances):
+    """Test delete data (with subsystems)."""
+    csm = list_of_csm_and_lcs_instances[0]
+
+    data_name = "foo"
+    csm[0].assign_data([[1, 2, 3], [3, 2, 1]], data_name, "lcs0")
+    assert data_name in csm[0].data_names
+
+    csm_n3 = deepcopy(csm[3])
+    csm_n3.merge(csm[5])
+
+    csm_n2 = deepcopy(csm[2])
+    csm_n2.merge(csm_n3)
+
+    csm_mg = deepcopy(csm[0])
+    csm_mg.merge(csm[1])
+    csm_mg.merge(csm[4])
+    csm_mg.merge(csm_n2)
+
+    csm[0].delete_data(data_name)
+    csm_mg.delete_data(data_name)
+    assert data_name not in csm[0].data_names
+    for sub_sys in csm_mg.subsystems:
+        assert data_name not in sub_sys.data_names
+
+
+def test_delete_non_existent_data():
+    """Ensure we receive an exception upon deleting non-existent data."""
+    csm = tf.CoordinateSystemManager("root")
+    with pytest.raises(ValueError):
+        csm.delete_data("no")
+
+
 # test_has_data_exceptions -------------------------------------------------------------
 
 
@@ -2421,6 +2454,7 @@ def test_unmerge_multi_data():
 @pytest.mark.parametrize("data_cs_parent", ["rp", "a", "m"])
 @pytest.mark.parametrize("data_cs_child", ["rc", "b", "m"])
 def test_merge_data_name_collision(data_cs_parent, data_cs_child):
+    """Check name collisions are handled appropriately."""
     csm_parent = CSM("rp", "parent")
     csm_parent.create_cs("a", "rp")
     csm_parent.create_cs("m", "rp")