Cover Profiles, conformance, validation and resourcedescriptors.
Mash implements a concept of profiles that serve as a use-case specific container for information models, documentation, example data, validation shapes, vocabulary and anything related to the use case.
Those who have worked with semantic technologies might recognize the term, and it is used to mean much the same here. Mash both allows you to make your own profiles for internal usage, and to utilize profiles defined by other actors.
A central thing one can do with a profiles is to state that a dataset conforms to it. This is done using the Dataset.conforms_to list. Doing so helps consumers of the data understand what it contains and how that information is expressed.
Furthermore if the profiles contain validation shapes one can easily validate data towards it - using mash.validate_dataset(dataset, Equipment) (where Equipment is a profile).
Profiles are defined like below (the example Nemo is a profile used in the Norwegian grid industry):
from mash.core.profiles import MashProfile
from mash.core.utilities.resource_descriptors import from_webpage
from mash.grid.standards import CIM
from mash import find_semantic_models_in_module
from . import models
NemoProfile = MashProfile(
title="Nemo",
identifier="https://elbits.no/tjenester/nemo/",
token="nemo",
description="A simplified grid model consisting of central referance data made for organizational purposes",
is_profile_of=[CIM],
has_resource=[from_webpage("https://nemo.elbits.no/")],
models=find_semantic_models_in_module(models),
namespaces={
"nek": "http://NEK.no/NK57/CIM/CIM100-Extension/1/0#",
"elb": "http://www.elbits.no/ELB#",
},
)
Another nice aspect about profiles is that they can tell Mash when to use certain models and namespaces. For example the Nemo model has some weird namespaces that we typically don’t like. We can tell mash to only use the namespaces from the profile in a specific context, by doing the following:
import mash
from mash.grid.profiles.nemo import NemoProfile, Substation
with NemoProfile:
substations = mash.instantiate(m, model=Substation)
Profiles are based on the ontology prof, and there are two different kinds of profiles. Basic profiles use the class Profile which is a copy of the prof:Profile, while mash provides an extended version MashProfile which contains models and namespaces in addition to be used like above.
The most important parts of the profiles are common between the two, and relate to the resources they can link to. This is done through what prof calls ResourceDescriptors - which are objects that contain and describe resources attached to the profile.
In the intro it was mentioned that example data, validation shapes, documentation and the like can be contained by a profile. This is done through the ResourceDescriptors. A profile has a list Profile.hasResource which contains the,
Each descriptor has a literal ResourceRole which desribes what the resource is. Examples include FullConstraints (which describes validation shapes), Documentation and Example.
When mash is told to validate some data against a profile, this is where it finds the resources to do so,
Usage of the profile concept extends into Mash core. We have a set of implementations of SemanticModel that describe the metadata that Mash uses, and these have been grouped into a MetadataProfile in mash/core/profiles/mash_profile.py. Mash uses the features for enabling profiles to both remember the models, and to remember the namespaces they are connected to like below in the top-level __init__.py:
from mash.core.profiles import MashProfile, MetadataProfile
def globally_use_profile(profile: MashProfile):
_get_impl().namespaces.add_layer(profile.namespaces)
_model_registry.add_layer(profile.models)
globally_use_profile(MetadataProfile)
Recommended next read: