Run the interactive online version of this notebook (takes 1-2 minutes to load): Binder badge

10. Quality standards#

10.1. Imports#

[1]:
import asdf
import fs
import yaml

from weldx import WeldxFile
from weldx.config import QualityStandard
from weldx.measurement import MeasurementEquipment

10.2. Introduction#

The main purposes of WelDX is to provide a file format for experimental data that is self explanatory and contains all relevant information about the conducted experiment. This allows fellow researchers to understand and analyze the data without taking part in the experiment or having access to the persons who did. We assure this by introducing quality standards for each type of experiment and every other information that describes certain aspects of an experiment like coordinate systems, sensors or specimens. Every time a WelDX file is written or read, it is validated against those standards.

10.3. Schema files#

Every standard used by WelDX is defined by schema files. A schema file is a YAML-file that defines the allowed properties of an serializable object. How such a schema file is generated is not part of this tutorial, but you can find further information regarding this topic in the official documentation of the ASDF standard which is the foundation of a WelDX file. Furthermore, an overview of all pre-defined schema files can be found on the Standards page of this documentation.

10.4. Installing and using quality standards#

The WelDX API comes with a default schema for every object. So if you save an ASDF file using the WelDX extension you already employ the default WelDX quality standard. However, the standards we defined might not suit your needs. Therefore, the WelDX API offers a mechanism to override an arbitrary number of default schemas. Before we discuss, how a custom standard can be generated, let’s assume that you have found and downloaded one. Each quality standard that was created following the guidelines can be installed using pythons package manager pip. So the first step would be to install the standard as a python package:

pip install NAME_OF_THE_STANDARD

Installing the quality standard registers it to the WelDX API so that it knows that the standard exists and where to find the corresponding data. If you want to use the standard, you have to activate it first:

from weldx.config import enable_quality_standard

enable_quality_standard(NAME_OF_THE_STANDARD, VERSION_OF_THE_STANDARD)

The version number is optional. If you don’t provide one, the latest available version will be used. That’s all there is to know about using standards.

10.5. Creating custom standards#

Creating custom standards is fairly easy if you already know how a schema file looks like. To keep things simple, we won’t discuss here how you can provide an installable standard because this might be a bit confusing at the beginning. Instead we will just focus on the files and their content.

Let’s say we want to set a new quality standard for the MeasurementEquipment. In our short example, we will use the following file structure:

[2]:
mem_fs = fs.open_fs("mem://")
mem_fs.makedirs("resources/my_organization/manifests")
mem_fs.makedirs("resources/my_organization/schemas")
mem_fs.listdir("")

mem_fs.create("resources/my_organization/manifests/my_standard-1.0.0.yaml")
mem_fs.create(
    "resources/my_organization/schemas/my_measurement_equipment_schema-0.1.0.yaml"
)
mem_fs.tree()
`-- resources
    `-- my_organization
        |-- manifests
        |   `-- my_standard-1.0.0.yaml
        `-- schemas
            `-- my_measurement_equipment_schema-0.1.0.yaml

The directory structure is based on how an installable standard has to be organized but the only thing that really matters for us at the moment is the content of the my_organization directory. It is subdivided into manifests and schemas. All of your custom schemas go into the schemas directory. The manifests directory contains so called manifest files. Their purpose is to manage which schema belongs to which version of your standard. All manifests are YAML files and must be named like your standard with the corresponding version number attached to the end. Here we will only use a single manifest file called my_standard-1.0.0.yaml.

Now before we start looking into the different files, let’s create an instance of the MeasurementEquipment class that we want to serialize using a new quality standard:

[3]:
my_equipment = MeasurementEquipment("my_equipment")

Every WelDX type gives you the option to add arbitrary meta data (open tutorial) to it that will also be stored in an ASDF file. To do so, we just need to assigns a python dictionary to the reserved member variable wx_metadata. The content of the meta data is not restricted in any way by the default WelDX standard and purely optional. We want to change this now with our custom standard so that there must be a meta data attached to the object that contains an integer representing the serial number of the equipment. Our starting point is the default schema used by WelDX which looks like this:

%YAML 1.1
---
$schema: "http://stsci.edu/schemas/yaml-schema/draft-01"
id: "asdf://weldx.bam.de/weldx/schemas/equipment/measurement_equipment-0.1.0"

type: object
properties:
  name:
    type: string
  sources:
    type: array
    items:
      tag: "asdf://weldx.bam.de/weldx/tags/measurement/source-0.1.*"
  transformations:
    type: array
    items:
      tag: "asdf://weldx.bam.de/weldx/tags/measurement/signal_transformation-0.1.*"

propertyOrder: [name, sources, transformations]
required: [name]

flowStyle: block
...

We achieve our goal by simply adding the following additional property:

wx_metadata:
  type: object
  properties:
    serial_number:
      type: number
  required: [serial_number]

Additionally, we add wx_matadata to the required fields. The new schema looks like this:

We save the code in the file my_measurement_equipment_schema-0.1.0.yaml:

[5]:
# write to the file
with mem_fs.open(
    "resources/my_organization/schemas/my_measurement_equipment_schema-0.1.0.yaml", "w"
) as file:
    file.write(schema_file)

What remains now is to write the corresponding manifest file. The manifest file consists of two sections. A header section that contains some relevant meta data about the standard and the mapping section that assigns new schemas to an existing uri. Let’s have a look at the manifest file that we are going to use before we discuss the details:

[7]:
# write to the file
with mem_fs.open(
    "resources/my_organization/manifests/my_standard-1.0.0.yaml", "w"
) as file:
    file.write(manifest_file)

The header section should be rather self explanatory. Its id is the manifest file’s filepath relative to the resource directory with a preceding http://. The composition of the extension uri is quite similar except that manifests gets replaced by standards.

The mapping section is a YAML list with the name tags. Each of its items need a uri field that specifies the URI of the object that should get a new schema assigned to it. If you are not sure about the exact URI just have a look into the original schema of the object. You can find all schemas here. In our example we want to replace the schema for the URI asdf://weldx.bam.de/weldx/schemas/equipment/measurement_equipment-0.1.0. Additionally, each item of the YAML list needs a file property which specifies the relative file path of the new schema inside the schemas directory, omitting the file extension. So in our case we use file: "my_measurement_equipment_schema-1.0.0

All that remains is to register our new standard to WelDX and activate it. Since we didn’t install it, we need to do the registration manually using the ‘add_quality_standard’ method. To do so, we create an instance of the QualityStandard class. This class needs to know where the root directory of your standard is located (the directory that contains the manifests and schemas directories). You can either provide the location as a string or a Python Path object:

Note that you can also provide a filesystem from the PyFilesystem package, which we will do here since we used a virtual file system in this tutorial. But this isn’t the default approach you should choose unless you know what you are doing. So ignore the mem_fs.opendir part in the next command and treat it as if we provided a normal path.

[8]:
qs = QualityStandard(mem_fs.opendir("resources/my_organization"))

Next we register our standard:

[9]:
from weldx.config import add_quality_standard

add_quality_standard(qs)

At this point there isn’t any difference to an installed standard anymore. WelDX now knows about your standard and where to find its resources. We can activate it by using the same name we used for our manifest files. Since there is only one version, we can omit the version number:

[10]:
from weldx.config import enable_quality_standard

enable_quality_standard("my_standard")

So let’s try to store our MeasurementEquipment we created earlier. We will use the WeldxFile class (click here to get to the tutorial) to save our ASDF file into a buffer and not to the hard drive:

[11]:
try:
    WeldxFile(tree={"equipment": my_equipment}, mode="rw")
except asdf.ValidationError:
    print("Ups..., got some lengthy validation error...")
else:
    raise Exception("Expected exception not raised")
Ups..., got some lengthy validation error...

As you can see, we get an ValidationError. The reason is that our new standard requires from our MeasurementEquipment that it has some meta data attached to it and we didn’t do that. Let’s attach a serial_number as meta data and try it again:

[12]:
my_equipment.wx_metadata = {"serial_number": "not a number"}

try:
    WeldxFile(tree={"equipment": my_equipment}, mode="rw")
except asdf.ValidationError:
    print("Still getting a validation error...")
else:
    raise Exception("Expected exception not raised")
Still getting a validation error...

We still get a validation error since we required that the serial number has to be a number and not a string. So let’s correct this mistake and try it one last time:

[13]:
my_equipment.wx_metadata = {"serial_number": 1234}
file = WeldxFile(tree={"equipment": my_equipment}, mode="rw")

This time we succeeded because our equipment data satisfies the new standard. Taking a look into the written file proofs that it worked:

[14]:
file.header()
[14]:
#ASDF 1.0.0
#ASDF_STANDARD 1.5.0
%YAML 1.1
%TAG ! tag:stsci.edu:asdf/
%TAG !weldx! asdf://weldx.bam.de/weldx/tags/
--- !core/asdf-1.1.0
asdf_library: !core/software-1.0.0 {author: The ASDF Developers, homepage: 'http://github.com/asdf-format/asdf',
  name: asdf, version: 2.13.0}
history:
  extensions:
  - !core/extension_metadata-1.0.0
    extension_class: asdf.extension.BuiltinExtension
    software: !core/software-1.0.0 {name: asdf, version: 2.13.0}
  - !core/extension_metadata-1.0.0
    extension_class: weldx.asdf.extension.WeldxExtension
    extension_uri: asdf://weldx.bam.de/weldx/extensions/weldx-0.1.1
    software: !core/software-1.0.0 {name: weldx, version: 0.6.2.dev51+ga617b06.d20221107}
equipment: !weldx!equipment/measurement_equipment-0.1.0
  name: my_equipment
  sources: []
  transformations: []
  wx_metadata: {serial_number: 1234}

10.6. Make your standard installable#

If you like to create your own installable quality standard, follow the instructions of this template repository on GitHub.


Generated by nbsphinx from a Jupyter notebook.