Exemple #1
0
def test_linkorelse_deserialize():
    loe = LinkOrElse()
    lbu = loe.deserialize({
        'type': LinkByUID.typ,
        'scope': 'foo',
        'id': uuid.uuid4()
    })
    assert isinstance(lbu, LinkByUID)
class Parameter(DataConcepts, Serializable['Parameter'], TaurusParameter):
    """
    A parameter attribute.

    Parameters are the non-environmental variables (typically specified and controlled) that may
    affect a process or measurement: e.g. oven dial temperature for a kiln firing, magnification
    for a measurement taken with an electron microscope.

    Parameters
    ----------
    name: str
        Required name of the parameter. Each parameter within an object must have a unique name.
    notes: str
        Optional free-form notes about the parameter.
    value: :py:class:`BaseValue <taurus.entity.value.base_value.BaseValue>`
        The value of the parameter.
    template: :class:`ParameterTemplate <citrine.resources.parameter_template.ParameterTemplate>`
        Parameter template that defines the allowed bounds of this parameter. If a template
        and value are both present then the value must be within the template bounds.
    origin: str
        The origin of the parameter. Must be one of "measured", "predicted", "summary",
        "specified", "computed", or "unknown." Default is "unknown."
    file_links: List[FileLink]
        Links to files associated with the parameter.

    """

    _response_key = TaurusParameter.typ  # 'parameter'

    name = String('name')
    notes = PropertyOptional(String(), 'notes')
    value = PropertyOptional(Object(BaseValue), 'value')
    template = PropertyOptional(LinkOrElse(), 'template')
    origin = PropertyOptional(String(), 'origin', default='unknown')
    file_links = PropertyOptional(PropertyList(Object(FileLink)), 'file_links')
    typ = String('type', default=_response_key, deserializable=False)

    def __init__(self,
                 name: str,
                 notes: Optional[str] = None,
                 value: Optional[BaseValue] = None,
                 template: Optional[TaurusParameterTemplate] = None,
                 origin: Optional[str] = "unknown",
                 file_links: Optional[List[FileLink]] = None):
        TaurusParameter.__init__(self,
                                 name=name,
                                 notes=notes,
                                 value=value,
                                 template=template,
                                 origin=origin,
                                 file_links=file_links)

    def __str__(self):
        return '<Parameter {!r}>'.format(self.name)
Exemple #3
0
class Property(DataConcepts, Serializable['Property'], TaurusProperty):
    """
    A property attribute.

    Properties are characteristics of a material that could be measured, e.g. chemical composition,
     density, yield strength.


    Parameters
    ----------
    name: str
        Required name of the property. Each property within an object must have a unique name.
    notes: str
        Optional free-form notes about the property.
    value: :py:class:`BaseValue <taurus.entity.value.base_value.BaseValue>`
        The value of the property.
    template: :class:`PropertyTemplate <citrine.resources.property_template.PropertyTemplate>`
        Property template that defines the allowed bounds of this property. If a template
        and value are both present then the value must be within the template bounds.
    origin: str
        The origin of the property. Must be one of "measured", "predicted", "summary",
        "specified", "computed", or "unknown." Default is "unknown."
    file_links: List[FileLink]
        Links to files associated with the property.

    """

    _response_key = TaurusProperty.typ  # 'property'

    name = String('name')
    notes = PropertyOptional(String(), 'notes')
    value = PropertyOptional(Object(BaseValue), 'value')
    template = PropertyOptional(LinkOrElse(), 'template')
    origin = PropertyOptional(String(), 'origin', default='unknown')
    file_links = PropertyOptional(PropertyList(Object(FileLink)), 'file_links')
    typ = String('type', default=_response_key, deserializable=False)

    def __init__(self,
                 name: str,
                 notes: Optional[str] = None,
                 value: Optional[BaseValue] = None,
                 template: Optional[TaurusPropertyTemplate] = None,
                 origin: Optional[str] = "unknown",
                 file_links: Optional[List[FileLink]] = None):
        TaurusProperty.__init__(self,
                                name=name,
                                notes=notes,
                                value=value,
                                template=template,
                                origin=origin,
                                file_links=file_links)

    def __str__(self):
        return '<Property {!r}>'.format(self.name)
class MaterialSpec(ObjectSpec, Resource['MaterialSpec'], GEMDMaterialSpec):
    """
    A material specification.

    Parameters
    ----------
    name: str
        Name of the material spec.
    uids: Map[str, str], optional
        A collection of
        `unique IDs <https://citrineinformatics.github.io/gemd-docs/
        specification/unique-identifiers/>`_.
    tags: List[str], optional
        `Tags <https://citrineinformatics.github.io/gemd-docs/specification/tags/>`_
        are hierarchical strings that store information about an entity. They can be used
        for filtering and discoverability.
    notes: str, optional
        Long-form notes about the material spec.
    process: ProcessSpec
        Process that produces this material.
    properties: List[PropertyAndConditions], optional
        Properties of the material, along with an optional list of conditions under which
        those properties are measured.
    template: MaterialTemplate, optional
        A template bounding the valid values for this material's properties.
    file_links: List[FileLink], optional
        Links to associated files, with resource paths into the files API.

    """

    _response_key = GEMDMaterialSpec.typ  # 'material_spec'

    name = String('name', override=True)
    uids = Mapping(String('scope'), String('id'), 'uids', override=True)
    tags = PropertyOptional(PropertyList(String()), 'tags', override=True)
    notes = PropertyOptional(String(), 'notes', override=True)
    process = PropertyOptional(LinkOrElse(), 'process', override=True)
    properties = PropertyOptional(PropertyList(Object(PropertyAndConditions)),
                                  'properties',
                                  override=True)
    template = PropertyOptional(LinkOrElse(), 'template', override=True)
    file_links = PropertyOptional(PropertyList(Object(FileLink)),
                                  'file_links',
                                  override=True)
    typ = String('type')

    def __init__(self,
                 name: str,
                 *,
                 uids: Optional[Dict[str, str]] = None,
                 tags: Optional[List[str]] = None,
                 notes: Optional[str] = None,
                 process: Optional[GEMDProcessSpec] = None,
                 properties: Optional[List[PropertyAndConditions]] = None,
                 template: Optional[GEMDMaterialTemplate] = None,
                 file_links: Optional[List[FileLink]] = None):
        if uids is None:
            uids = dict()
        DataConcepts.__init__(self, GEMDMaterialSpec.typ)
        GEMDMaterialSpec.__init__(self,
                                  name=name,
                                  uids=uids,
                                  tags=tags,
                                  process=process,
                                  properties=properties,
                                  template=template,
                                  file_links=file_links,
                                  notes=notes)

    def __str__(self):
        return '<Material spec {!r}>'.format(self.name)
Exemple #5
0
def test_linkorelse_deserialize_requires_scope_and_id():
    loe = LinkOrElse()
    with pytest.raises(ValueError):
        loe.deserialize({'type': LinkByUID.typ})
Exemple #6
0
def test_linkorelse_deserialize_requires_serializable():
    loe = LinkOrElse()
    with pytest.raises(Exception):
        loe.deserialize({})
class MeasurementSpec(ObjectSpec, Resource['MeasurementSpec'],
                      GEMDMeasurementSpec):
    """
    A measurement specification.

    Parameters
    ----------
    name: str
        Name of the measurement spec.
    uids: Map[str, str], optional
        A collection of
        `unique IDs <https://citrineinformatics.github.io/gemd-docs/
        specification/unique-identifiers/>`_.
    tags: List[str], optional
        `Tags <https://citrineinformatics.github.io/gemd-docs/specification/tags/>`_
        are hierarchical strings that store information about an entity. They can be used
        for filtering and discoverability.
    notes: str, optional
        Long-form notes about the measurement spec.
    conditions: List[Condition], optional
        Conditions under which this measurement spec occurs.
    parameters: List[Parameter], optional
        Parameters of this measurement spec.
    template: MeasurementTemplate
        A template bounding the valid values for the measurement's properties, parameters,
        and conditions.
    file_links: List[FileLink], optional
        Links to associated files, with resource paths into the files API.

    """

    _response_key = GEMDMeasurementSpec.typ  # 'measurement_spec'

    name = String('name')
    uids = Mapping(String('scope'), String('id'), 'uids')
    tags = PropertyOptional(PropertyList(String()), 'tags')
    notes = PropertyOptional(String(), 'notes')
    conditions = PropertyOptional(PropertyList(Object(Condition)),
                                  'conditions')
    parameters = PropertyOptional(PropertyList(Object(Parameter)),
                                  'parameters')
    template = PropertyOptional(LinkOrElse(), 'template')
    file_links = PropertyOptional(PropertyList(Object(FileLink)), 'file_links')
    typ = String('type')

    def __init__(self,
                 name: str,
                 *,
                 uids: Optional[Dict[str, str]] = None,
                 tags: Optional[List[str]] = None,
                 notes: Optional[str] = None,
                 conditions: Optional[List[Condition]] = None,
                 parameters: Optional[List[Parameter]] = None,
                 template: Optional[GEMDMeasurementTemplate] = None,
                 file_links: Optional[List[FileLink]] = None):
        if uids is None:
            uids = dict()
        DataConcepts.__init__(self, GEMDMeasurementSpec.typ)
        GEMDMeasurementSpec.__init__(self,
                                     name=name,
                                     uids=uids,
                                     tags=tags,
                                     conditions=conditions,
                                     parameters=parameters,
                                     template=template,
                                     file_links=file_links,
                                     notes=notes)

    def __str__(self):
        return '<Measurement spec {!r}>'.format(self.name)
Exemple #8
0
class MeasurementRun(DataConcepts, Resource['MeasurementRun'],
                     TaurusMeasurementRun):
    """
    A measurement run.

    Parameters
    ----------
    name: str
        Name of the measurement run.
    uids: Map[str, str], optional
        A collection of
        `unique IDs <https://citrineinformatics.github.io/taurus-documentation/
        specification/unique-identifiers/>`_.
    tags: List[str], optional
        `Tags <https://citrineinformatics.github.io/taurus-documentation/specification/tags/>`_
        are hierarchical strings that store information about an entity. They can be used
        for filtering and discoverability.
    notes: str, optional
        Long-form notes about the measurement run.
    conditions: List[Condition], optional
        Conditions under which this measurement run occurs.
    parameters: List[Parameter], optional
        Parameters of this measurement run.
    properties: List[Property], optional
        Properties that are measured during this measurement run.
    spec: MeasurementSpec
        The measurement specification of which this is an instance.
    material: MaterialRun
        The material run being measured.
    spec: MaterialSpec
        The material specification of which this is an instance.
    file_links: List[FileLink], optional
        Links to associated files, with resource paths into the files API.
    source: PerformedSource, optional
        Information about the person who performed the run and when.

    """

    _response_key = TaurusMeasurementRun.typ  # 'measurement_run'

    name = String('name')
    uids = Mapping(String('scope'), String('id'), 'uids')
    tags = PropertyOptional(PropertyList(String()), 'tags')
    notes = PropertyOptional(String(), 'notes')
    conditions = PropertyOptional(PropertyList(Object(Condition)),
                                  'conditions')
    parameters = PropertyOptional(PropertyList(Object(Parameter)),
                                  'parameters')
    properties = PropertyOptional(PropertyList(Object(Property)), 'properties')
    spec = PropertyOptional(LinkOrElse(), 'spec')
    material = PropertyOptional(LinkOrElse(), "material")
    file_links = PropertyOptional(PropertyList(Object(FileLink)), 'file_links')
    source = PropertyOptional(Object(PerformedSource), "source")
    typ = String('type')

    def __init__(self,
                 name: str,
                 uids: Optional[Dict[str, str]] = None,
                 tags: Optional[List[str]] = None,
                 notes: Optional[str] = None,
                 conditions: Optional[List[Condition]] = None,
                 properties: Optional[List[Property]] = None,
                 parameters: Optional[List[Parameter]] = None,
                 spec: Optional[TaurusMeasurementSpec] = None,
                 material: Optional[TaurusMaterialRun] = None,
                 file_links: Optional[List[FileLink]] = None,
                 source: Optional[PerformedSource] = None):
        DataConcepts.__init__(self, TaurusMeasurementRun.typ)
        TaurusMeasurementRun.__init__(self,
                                      name=name,
                                      uids=set_default_uid(uids),
                                      material=material,
                                      tags=tags,
                                      conditions=conditions,
                                      properties=properties,
                                      parameters=parameters,
                                      spec=spec,
                                      file_links=file_links,
                                      notes=notes,
                                      source=source)

    def __str__(self):
        return '<Measurement run {!r}>'.format(self.name)
Exemple #9
0
class IngredientSpec(ObjectSpec, Resource['IngredientSpec'], GEMDIngredientSpec):
    """
    An ingredient specification.

    Ingredients annotate a material with information about its usage in a process.

    Parameters
    ----------
    uids: Map[str, str], optional
        A collection of
        `unique IDs <https://citrineinformatics.github.io/gemd-docs/
        specification/unique-identifiers/>`_.
    tags: List[str], optional
        `Tags <https://citrineinformatics.github.io/gemd-docs/specification/tags/>`_
        are hierarchical strings that store information about an entity. They can be used
        for filtering and discoverability.
    notes: str, optional
        Long-form notes about the ingredient spec.
    material: MaterialSpec
        Material that this ingredient is.
    process: ProcessSpec
        Process that this ingredient is used in.
    mass_fraction: :py:class:`ContinuousValue \
    <gemd.entity.value.continuous_value.ContinuousValue>`, optional
        The mass fraction of the ingredient in the process.
    volume_fraction: :py:class:`ContinuousValue \
    <gemd.entity.value.continuous_value.ContinuousValue>`, optional
        The volume fraction of the ingredient in the process.
    number_fraction: :py:class:`ContinuousValue \
    <gemd.entity.value.continuous_value.ContinuousValue>`, optional
        The number fraction of the ingredient in the process.
    absolute_quantity: :py:class:`ContinuousValue \
    <gemd.entity.value.continuous_value.ContinuousValue>`, optional
        The absolute quantity of the ingredient in the process.
    name: str
        Label on the ingredient that is unique within the process that contains it.
    labels: List[str], optional
        Additional labels on the ingredient.
    file_links: List[FileLink], optional
        Links to associated files, with resource paths into the files API.

    """

    _response_key = GEMDIngredientSpec.typ  # 'ingredient_spec'

    uids = Mapping(String('scope'), String('id'), 'uids', override=True)
    tags = PropertyOptional(PropertyList(String()), 'tags', override=True)
    notes = PropertyOptional(String(), 'notes', override=True)
    material = PropertyOptional(LinkOrElse(), 'material', override=True)
    process = PropertyOptional(LinkOrElse(), 'process', override=True)
    mass_fraction = PropertyOptional(Object(ContinuousValue), 'mass_fraction', override=True)
    volume_fraction = PropertyOptional(Object(ContinuousValue), 'volume_fraction', override=True)
    number_fraction = PropertyOptional(Object(ContinuousValue), 'number_fraction', override=True)
    absolute_quantity = PropertyOptional(
        Object(ContinuousValue), 'absolute_quantity', override=True)
    name = String('name', override=True)
    labels = PropertyOptional(PropertyList(String()), 'labels', override=True)
    file_links = PropertyOptional(PropertyList(Object(FileLink)), 'file_links', override=True)
    typ = String('type')

    def __init__(self,
                 name: str,
                 *,
                 uids: Optional[Dict[str, str]] = None,
                 tags: Optional[List[str]] = None,
                 notes: Optional[str] = None,
                 material: Optional[GEMDMaterialSpec] = None,
                 process: Optional[GEMDProcessSpec] = None,
                 mass_fraction: Optional[ContinuousValue] = None,
                 volume_fraction: Optional[ContinuousValue] = None,
                 number_fraction: Optional[ContinuousValue] = None,
                 absolute_quantity: Optional[ContinuousValue] = None,
                 labels: Optional[List[str]] = None,
                 file_links: Optional[List[FileLink]] = None):
        if uids is None:
            uids = dict()

        DataConcepts.__init__(self, GEMDIngredientSpec.typ)
        GEMDIngredientSpec.__init__(self, uids=uids, tags=tags, notes=notes,
                                    material=material, process=process,
                                    mass_fraction=mass_fraction, volume_fraction=volume_fraction,
                                    number_fraction=number_fraction,
                                    absolute_quantity=absolute_quantity, labels=labels,
                                    name=name, file_links=file_links)

    def __str__(self):
        return '<Ingredient spec {!r}>'.format(self.name)
Exemple #10
0
class IngredientRun(DataConcepts, Resource['IngredientRun'],
                    TaurusIngredientRun):
    """
    An ingredient run.

    Ingredients annotate a material with information about its usage in a process.

    Parameters
    ----------
    uids: Map[str, str], optional
        A collection of
        `unique IDs <https://citrineinformatics.github.io/taurus-documentation/
        specification/unique-identifiers/>`_.
    tags: List[str], optional
        `Tags <https://citrineinformatics.github.io/taurus-documentation/specification/tags/>`_
        are hierarchical strings that store information about an entity. They can be used
        for filtering and discoverability.
    notes: str, optional
        Long-form notes about the ingredient run.
    material: MaterialRun
        Material that this ingredient is.
    process: ProcessRun
        Process that this ingredient is used in.
    mass_fraction: :py:class:`ContinuousValue \
    <taurus.entity.value.continuous_value.ContinuousValue>`, optional
        The mass fraction of the ingredient in the process.
    volume_fraction: :py:class:`ContinuousValue \
    <taurus.entity.value.continuous_value.ContinuousValue>`, optional
        The volume fraction of the ingredient in the process.
    number_fraction: :py:class:`ContinuousValue \
    <taurus.entity.value.continuous_value.ContinuousValue>`, optional
        The number fraction of the ingredient in the process.
    absolute_quantity: :py:class:`ContinuousValue \
    <taurus.entity.value.continuous_value.ContinuousValue>`, optional
        The absolute quantity of the ingredient in the process.
    name: str
        Label on the ingredient that is unique within the process that contains it.
    labels: List[str], optional
        Additional labels on the ingredient that must be unique.
    spec: IngredientSpec
        The specification of which this ingredient is a realization.
    file_links: List[FileLink], optional
        Links to associated files, with resource paths into the files API.

    """

    _response_key = TaurusIngredientRun.typ  # 'ingredient_run'

    uids = Mapping(String('scope'), String('id'), 'uids')
    tags = PropertyOptional(PropertyList(String()), 'tags')
    notes = PropertyOptional(String(), 'notes')
    material = PropertyOptional(LinkOrElse(), 'material')
    process = PropertyOptional(LinkOrElse(), 'process')
    mass_fraction = PropertyOptional(Object(ContinuousValue), 'mass_fraction')
    volume_fraction = PropertyOptional(Object(ContinuousValue),
                                       'volume_fraction')
    number_fraction = PropertyOptional(Object(ContinuousValue),
                                       'number_fraction')
    absolute_quantity = PropertyOptional(Object(ContinuousValue),
                                         'absolute_quantity')
    name = String('name')
    labels = PropertyOptional(PropertyList(String()), 'labels')
    spec = PropertyOptional(LinkOrElse(), 'spec')
    file_links = PropertyOptional(PropertyList(Object(FileLink)), 'file_links')
    typ = String('type')

    def __init__(self,
                 name: str,
                 uids: Optional[Dict[str, str]] = None,
                 tags: Optional[List[str]] = None,
                 notes: Optional[str] = None,
                 material: Optional[TaurusMaterialRun] = None,
                 process: Optional[TaurusProcessRun] = None,
                 mass_fraction: Optional[ContinuousValue] = None,
                 volume_fraction: Optional[ContinuousValue] = None,
                 number_fraction: Optional[ContinuousValue] = None,
                 absolute_quantity: Optional[ContinuousValue] = None,
                 labels: Optional[List[str]] = None,
                 spec: Optional[TaurusIngredientSpec] = None,
                 file_links: Optional[List[FileLink]] = None):
        DataConcepts.__init__(self, TaurusIngredientRun.typ)
        TaurusIngredientRun.__init__(self,
                                     uids=set_default_uid(uids),
                                     tags=tags,
                                     notes=notes,
                                     material=material,
                                     process=process,
                                     mass_fraction=mass_fraction,
                                     volume_fraction=volume_fraction,
                                     number_fraction=number_fraction,
                                     absolute_quantity=absolute_quantity,
                                     labels=labels,
                                     name=name,
                                     spec=spec,
                                     file_links=file_links)

    def __str__(self):
        return '<Ingredient run {!r}>'.format(self.name)
class ProcessRun(ObjectRun, Resource['ProcessRun'], GEMDProcessRun):
    """
    A process run.

    Processes transform zero or more input materials into exactly one output material.

    Parameters
    ----------
    name: str
        Name of the process run.
    uids: Map[str, str], optional
        A collection of
        `unique IDs <https://citrineinformatics.github.io/gemd-docs/
        specification/unique-identifiers/>`_.
    tags: List[str], optional
        `Tags <https://citrineinformatics.github.io/gemd-docs/specification/tags/>`_
        are hierarchical strings that store information about an entity. They can be used
        for filtering and discoverability.
    notes: str, optional
        Long-form notes about the process run.
    conditions: List[Condition], optional
        Conditions under which this process run occurs.
    parameters: List[Parameter], optional
        Parameters of this process run.
    spec: ProcessSpec
        Spec for this process run.
    file_links: List[FileLink], optional
        Links to associated files, with resource paths into the files API.
    source: PerformedSource, optional
        Information about the person who performed the run and when.

    Attributes
    ----------
    output_material: MaterialRun
        The material run that this process run produces. The link is established by creating
        the material run and settings its `process` field to this process run.

    ingredients: List[IngredientRun]
        Ingredient runs that act as inputs to this process run. The link is established by
        creating each ingredient run and setting its `process` field to this process run.

    """

    _response_key = GEMDProcessRun.typ  # 'process_run'

    name = String('name', override=True)
    uids = Mapping(String('scope'), String('id'), 'uids', override=True)
    tags = PropertyOptional(PropertyList(String()), 'tags', override=True)
    notes = PropertyOptional(String(), 'notes', override=True)
    conditions = PropertyOptional(PropertyList(Object(Condition)),
                                  'conditions',
                                  override=True)
    parameters = PropertyOptional(PropertyList(Object(Parameter)),
                                  'parameters',
                                  override=True)
    spec = PropertyOptional(LinkOrElse(), 'spec', override=True)
    file_links = PropertyOptional(PropertyList(Object(FileLink)),
                                  'file_links',
                                  override=True)
    source = PropertyOptional(Object(PerformedSource), "source", override=True)
    typ = String('type')

    def __init__(self,
                 name: str,
                 *,
                 uids: Optional[Dict[str, str]] = None,
                 tags: Optional[List[str]] = None,
                 notes: Optional[str] = None,
                 conditions: Optional[List[Condition]] = None,
                 parameters: Optional[List[Parameter]] = None,
                 spec: Optional[GEMDProcessSpec] = None,
                 file_links: Optional[List[FileLink]] = None,
                 source: Optional[PerformedSource] = None):
        if uids is None:
            uids = dict()
        DataConcepts.__init__(self, GEMDProcessRun.typ)
        GEMDProcessRun.__init__(self,
                                name=name,
                                uids=uids,
                                tags=tags,
                                conditions=conditions,
                                parameters=parameters,
                                spec=spec,
                                file_links=file_links,
                                notes=notes,
                                source=source)

    def __str__(self):
        return '<Process run {!r}>'.format(self.name)
Exemple #12
0
class MaterialRun(ObjectRun, Resource['MaterialRun'], GEMDMaterialRun):
    """
    A material run.

    Parameters
    ----------
    name: str
        Name of the material run.
    uids: Map[str, str], optional
        A collection of
        `unique IDs <https://citrineinformatics.github.io/gemd-docs/
        specification/unique-identifiers/>`_.
    tags: List[str], optional
        `Tags <https://citrineinformatics.github.io/gemd-docs/specification/tags/>`_
        are hierarchical strings that store information about an entity. They can be used
        for filtering and discoverability.
    notes: str, optional
        Long-form notes about the material run.
    process: ProcessRun
        Process that produces this material.
    sample_type: str, optional
        The form of this sample. Optionals are "experimental", "virtual", "production", or
        "unknown." Default is "unknown."
    spec: MaterialSpec
        The material specification of which this is an instance.
    file_links: List[FileLink], optional
        Links to associated files, with resource paths into the files API.

    Attributes
    ----------
    measurements: List[MeasurementRun], optional
        Measurements performed on this material. The link is established by creating the
        measurement run and settings its `material` field to this material run.

    """

    _response_key = GEMDMaterialRun.typ  # 'material_run'

    name = String('name')
    uids = Mapping(String('scope'), String('id'), 'uids')
    tags = PropertyOptional(PropertyList(String()), 'tags')
    notes = PropertyOptional(String(), 'notes')
    process = PropertyOptional(LinkOrElse(), 'process')
    sample_type = String('sample_type')
    spec = PropertyOptional(LinkOrElse(), 'spec')
    file_links = PropertyOptional(PropertyList(Object(FileLink)), 'file_links')
    typ = String('type')

    def __init__(self,
                 name: str,
                 *,
                 uids: Optional[Dict[str, str]] = None,
                 tags: Optional[List[str]] = None,
                 notes: Optional[str] = None,
                 process: Optional[GEMDProcessRun] = None,
                 sample_type: Optional[str] = "unknown",
                 spec: Optional[GEMDMaterialSpec] = None,
                 file_links: Optional[List[FileLink]] = None):
        if uids is None:
            uids = dict()
        DataConcepts.__init__(self, GEMDMaterialRun.typ)
        GEMDMaterialRun.__init__(self,
                                 name=name,
                                 uids=uids,
                                 tags=tags,
                                 process=process,
                                 sample_type=sample_type,
                                 spec=spec,
                                 file_links=file_links,
                                 notes=notes)

    def __str__(self):
        return '<Material run {!r}>'.format(self.name)
class MaterialRun(DataConcepts, Resource['MaterialRun'], TaurusMaterialRun):
    """
    A material run.

    Parameters
    ----------
    name: str
        Name of the material run.
    uids: Map[str, str], optional
        A collection of
        `unique IDs <https://citrineinformatics.github.io/taurus-documentation/
        specification/unique-identifiers/>`_.
    tags: List[str], optional
        `Tags <https://citrineinformatics.github.io/taurus-documentation/specification/tags/>`_
        are hierarchical strings that store information about an entity. They can be used
        for filtering and discoverability.
    notes: str, optional
        Long-form notes about the material run.
    process: ProcessRun
        Process that produces this material.
    sample_type: str, optional
        The form of this sample. Optionals are "experimental", "virtual", "production", or
        "unknown." Default is "unknown."
    spec: MaterialSpec
        The material specification of which this is an instance.
    file_links: List[FileLink], optional
        Links to associated files, with resource paths into the files API.

    Attributes
    ----------
    measurements: List[MeasurementRun], optional
        Measurements performed on this material. The link is established by creating the
        measurement run and settings its `material` field to this material run.

    """

    _response_key = TaurusMaterialRun.typ  # 'material_run'

    name = String('name')
    uids = Mapping(String('scope'), String('id'), 'uids')
    tags = PropertyOptional(PropertyList(String()), 'tags')
    notes = PropertyOptional(String(), 'notes')
    process = PropertyOptional(LinkOrElse(), 'process')
    sample_type = String('sample_type')
    spec = PropertyOptional(LinkOrElse(), 'spec')
    file_links = PropertyOptional(PropertyList(Object(FileLink)), 'file_links')
    typ = String('type')

    def __init__(self,
                 name: str,
                 uids: Optional[Dict[str, str]] = None,
                 tags: Optional[List[str]] = None,
                 notes: Optional[str] = None,
                 process: Optional[TaurusProcessRun] = None,
                 sample_type: Optional[str] = "unknown",
                 spec: Optional[TaurusMaterialSpec] = None,
                 file_links: Optional[List[FileLink]] = None):
        DataConcepts.__init__(self, TaurusMaterialRun.typ)
        TaurusMaterialRun.__init__(self,
                                   name=name,
                                   uids=set_default_uid(uids),
                                   tags=tags,
                                   process=process,
                                   sample_type=sample_type,
                                   spec=spec,
                                   file_links=file_links,
                                   notes=notes)

    def __str__(self):
        return '<Material run {!r}>'.format(self.name)

    @classmethod
    def _build_discarded_objects(cls,
                                 obj,
                                 obj_with_soft_links,
                                 session: Session = None):
        """
        Build the MeasurementRun objects that this MaterialRun has soft links to.

        The measurement runs are found in `obj_with_soft_link`

        This method modifies the object in place.

        Parameters
        ----------
        obj: MaterialRun
            A MaterialRun object that might be missing some links to MeasurementRun objects.
        obj_with_soft_links: dict or \
        :py:class:`DictSerializable <taurus.entity.dict_serializable.DictSerializable>`
            A representation of the MaterialRun in which the MeasurementRuns are encoded.
            We consider both the possibility that this is a dictionary with a 'measurements' key
            and that it is a
            :py:class:`DictSerializable <taurus.entity.dict_serializable.DictSerializable>`
            (presumably a
            :py:class:`TaurusMeasurementRun <taurus.entity.measurement_run.MeasurementRun>`)
            with a .measurements field.
        session: Session, optional
            Citrine session used to connect to the database.

        Returns
        -------
        None
            The MaterialRun object is modified so that it has links to its MeasurementRuns.

        """
        from citrine.resources.measurement_run import MeasurementRun
        DataConcepts._build_list_of_soft_links(obj,
                                               obj_with_soft_links,
                                               field='measurements',
                                               reverse_field='material',
                                               linked_type=MeasurementRun,
                                               session=session)
class ProcessRun(DataConcepts, Resource['ProcessRun'], TaurusProcessRun):
    """
    A process run.

    Processes transform zero or more input materials into exactly one output material.

    Parameters
    ----------
    name: str
        Name of the process run.
    uids: Map[str, str], optional
        A collection of
        `unique IDs <https://citrineinformatics.github.io/taurus-documentation/
        specification/unique-identifiers/>`_.
    tags: List[str], optional
        `Tags <https://citrineinformatics.github.io/taurus-documentation/specification/tags/>`_
        are hierarchical strings that store information about an entity. They can be used
        for filtering and discoverability.
    notes: str, optional
        Long-form notes about the process run.
    conditions: List[Condition], optional
        Conditions under which this process run occurs.
    parameters: List[Parameter], optional
        Parameters of this process run.
    spec: ProcessSpec
        Spec for this process run.
    file_links: List[FileLink], optional
        Links to associated files, with resource paths into the files API.
    source: PerformedSource, optional
        Information about the person who performed the run and when.

    Attributes
    ----------
    output_material: MaterialRun
        The material run that this process run produces. The link is established by creating
        the material run and settings its `process` field to this process run.

    ingredients: List[IngredientRun]
        Ingredient runs that act as inputs to this process run. The link is established by
        creating each ingredient run and setting its `process` field to this process run.

    """

    _response_key = TaurusProcessRun.typ  # 'process_run'

    name = String('name')
    uids = Mapping(String('scope'), String('id'), 'uids')
    tags = PropertyOptional(PropertyList(String()), 'tags')
    notes = PropertyOptional(String(), 'notes')
    conditions = PropertyOptional(PropertyList(Object(Condition)),
                                  'conditions')
    parameters = PropertyOptional(PropertyList(Object(Parameter)),
                                  'parameters')
    spec = PropertyOptional(LinkOrElse(), 'spec')
    file_links = PropertyOptional(PropertyList(Object(FileLink)), 'file_links')
    source = PropertyOptional(Object(PerformedSource), "source")
    typ = String('type')

    def __init__(self,
                 name: str,
                 uids: Optional[Dict[str, str]] = None,
                 tags: Optional[List[str]] = None,
                 notes: Optional[str] = None,
                 conditions: Optional[List[Condition]] = None,
                 parameters: Optional[List[Parameter]] = None,
                 spec: Optional[TaurusProcessSpec] = None,
                 file_links: Optional[List[FileLink]] = None,
                 source: Optional[PerformedSource] = None):
        DataConcepts.__init__(self, TaurusProcessRun.typ)
        TaurusProcessRun.__init__(self,
                                  name=name,
                                  uids=set_default_uid(uids),
                                  tags=tags,
                                  conditions=conditions,
                                  parameters=parameters,
                                  spec=spec,
                                  file_links=file_links,
                                  notes=notes,
                                  source=source)

    def __str__(self):
        return '<Process run {!r}>'.format(self.name)

    @classmethod
    def _build_discarded_objects(cls,
                                 obj,
                                 obj_with_soft_links,
                                 session: Session = None):
        """
        Build the IngredientRun objects that this ProcessRun has soft links to.

        The ingredient runs are found in `obj_with_soft_link`

        This method modifies the object in place.

        Parameters
        ----------
        obj: ProcessRun
            A ProcessRun object that might be missing some links to IngredientRun objects.
        obj_with_soft_links: dict or \
        :py:class:`DictSerializable <taurus.entity.dict_serializable.DictSerializable>`
            A representation of the ProcessRun in which the IngredientRuns are encoded.
            We consider both the possibility that this is a dictionary with an 'ingredients' key
            and that it is a
            :py:class:`DictSerializable <taurus.entity.dict_serializable.DictSerializable>`
            (presumably a
            :py:class:`TaurusProcessRun <taurus.entity.process_run.ProcessRun>`)
            with a .ingredients field.
        session: Session, optional
            Citrine session used to connect to the database.

        Returns
        -------
        None
            The ProcessRun object is modified so that it has links to its IngredientRuns.

        """
        from citrine.resources.ingredient_run import IngredientRun
        DataConcepts._build_list_of_soft_links(obj,
                                               obj_with_soft_links,
                                               field='ingredients',
                                               reverse_field='process',
                                               linked_type=IngredientRun,
                                               session=session)
class ProcessSpec(ObjectSpec, Resource['ProcessSpec'], GEMDProcessSpec):
    """
    A process specification.

    Processes transform zero or more input materials into exactly one output material.

    Parameters
    ----------
    name: str
        Name of the process spec.
    uids: Map[str, str], optional
        A collection of
        `unique IDs <https://citrineinformatics.github.io/gemd-docs/
        specification/unique-identifiers/>`_.
    tags: List[str], optional
        `Tags <https://citrineinformatics.github.io/gemd-docs/specification/tags/>`_
        are hierarchical strings that store information about an entity. They can be used
        for filtering and discoverability.
    notes: str, optional
        Long-form notes about the process spec.
    conditions: List[Condition], optional
        Conditions under which this process spec occurs.
    parameters: List[Parameter], optional
        Parameters of this process spec.
    template: ProcessTemplate, optional
        A template bounding the valid values for this process's parameters and conditions.
    file_links: List[FileLink], optional
        Links to associated files, with resource paths into the files API.

    Attributes
    ----------
    output_material: MaterialSpec
        The material spec that this process spec produces. The link is established by creating
        the material spec and settings its `process` field to this process spec.

    ingredients: List[IngredientSpec], optional
        Ingredient specs that act as inputs to this process spec. The link is established by
        creating each ingredient spec and setting its `process` field to this process spec.

    """

    _response_key = GEMDProcessSpec.typ  # 'process_spec'

    name = String('name')
    uids = Mapping(String('scope'), String('id'), 'uids')
    tags = PropertyOptional(PropertyList(String()), 'tags')
    notes = PropertyOptional(String(), 'notes')
    conditions = PropertyOptional(PropertyList(Object(Condition)),
                                  'conditions')
    parameters = PropertyOptional(PropertyList(Object(Parameter)),
                                  'parameters')
    template = PropertyOptional(LinkOrElse(), 'template')
    file_links = PropertyOptional(PropertyList(Object(FileLink)), 'file_links')
    typ = String('type')

    def __init__(self,
                 name: str,
                 *,
                 uids: Optional[Dict[str, str]] = None,
                 tags: Optional[List[str]] = None,
                 notes: Optional[str] = None,
                 conditions: Optional[List[Condition]] = None,
                 parameters: Optional[List[Parameter]] = None,
                 template: Optional[GEMDProcessTemplate] = None,
                 file_links: Optional[List[FileLink]] = None):
        if uids is None:
            uids = dict()
        DataConcepts.__init__(self, GEMDProcessSpec.typ)
        GEMDProcessSpec.__init__(self,
                                 name=name,
                                 uids=uids,
                                 tags=tags,
                                 conditions=conditions,
                                 parameters=parameters,
                                 template=template,
                                 file_links=file_links,
                                 notes=notes)

    def __str__(self):
        return '<Process spec {!r}>'.format(self.name)