Esempio n. 1
0
def test_material_run():
    """
    Test the ability to create a MaterialRun that is linked to a MaterialSpec.

    Make sure all enumerated values are respected, and check consistency after
    serializing and deserializing.
    """
    # Define a property, and make sure that an inappropriate value for origin throws ValueError
    with pytest.raises(ValueError):
        prop = Property(name="A property",
                        origin="bad origin",
                        value=NominalReal(17, units=''))

    # Create a MaterialSpec with a property
    prop = Property(name="A property",
                    origin="specified",
                    value=NominalReal(17, units=''))
    mat_spec = MaterialSpec(name="a specification for a material",
                            properties=PropertyAndConditions(prop),
                            notes="Funny lookin'")

    # Make sure that when property is serialized, origin (an enumeration) is serialized as a string
    copy_prop = json.loads(dumps(mat_spec))
    copy_origin = copy_prop[0][0]["properties"][0]['property']['origin']
    assert isinstance(copy_origin, str)

    # Create a MaterialRun, and make sure an inappropriate value for sample_type throws ValueError
    with pytest.raises(ValueError):
        mat = MaterialRun(spec=mat_spec, sample_type="imaginary")
    mat = MaterialRun(spec=mat_spec, sample_type="virtual")

    # ensure that serialization does not change the MaterialRun
    copy = loads(dumps(mat))
    assert dumps(copy) == dumps(mat), \
        "Material run is modified by serialization or deserialization"
Esempio n. 2
0
def test_invalid_assignment():
    """Invalid assignments to `process` or `spec` throw a TypeError."""
    with pytest.raises(TypeError):
        MaterialRun(name=12)
    with pytest.raises(TypeError):
        MaterialRun("name", spec=ProcessRun("a process"))
    with pytest.raises(TypeError):
        MaterialRun("name", process=MaterialSpec("a spec"))
Esempio n. 3
0
def test_template_access():
    """A material run's template should be equal to its spec's template."""
    template = MaterialTemplate("material template", uids={'id': str(uuid4())})
    spec = MaterialSpec("A spec", uids={'id': str(uuid4())}, template=template)
    mat = MaterialRun("A run", uids=['id', str(uuid4())], spec=spec)
    assert mat.template == template

    mat.spec = LinkByUID.from_entity(spec)
    assert mat.template is None
Esempio n. 4
0
def test_build():
    """Test that build recreates the material."""
    spec = MaterialSpec(
        "A spec",
        properties=PropertyAndConditions(
            property=Property("a property", value=NominalReal(3, ''))),
        tags=["a tag"])
    mat = MaterialRun(name="a material", spec=spec)
    mat_dict = mat.as_dict()
    mat_dict['spec'] = mat.spec.as_dict()
    assert MaterialRun.build(mat_dict) == mat
Esempio n. 5
0
def test_equality():
    """Test that equality check works as expected."""
    spec = MaterialSpec(
        "A spec",
        properties=PropertyAndConditions(
            property=Property("a property", value=NominalReal(3, ''))),
        tags=["a tag"])
    mat1 = MaterialRun("A material", spec=spec)
    mat2 = MaterialRun("A material", spec=spec, tags=["A tag"])
    assert mat1 == deepcopy(mat1)
    assert mat1 != mat2
    assert mat1 != "A material"
Esempio n. 6
0
def test_process_reassignment():
    """Test that a material can be assigned to a new process."""
    drying = ProcessRun("drying")
    welding = ProcessRun("welding")
    powder = MaterialRun("Powder", process=welding)

    assert powder.process == welding
    assert welding.output_material == powder

    powder.process = drying
    assert powder.process == drying
    assert drying.output_material == powder
    assert welding.output_material is None
Esempio n. 7
0
def test_material_soft_link():
    """Test that a measurement run can link to a material run, and that it survives serde."""
    dye = MaterialRun("rhodamine",
                      file_links=FileLink(filename='a.csv', url='/a/path'))
    assert dye.measurements == [], "default value of .measurements should be an empty list"

    # The .measurements member should not be settable
    with pytest.raises(AttributeError):
        dye.measurements = [MeasurementRun()]

    absorbance = MeasurementRun(name="Absorbance",
                                uids={'id': str(uuid4())},
                                properties=[
                                    Property(name='Abs at 500 nm',
                                             value=NominalReal(0.1, ''))
                                ])
    assert absorbance.material is None, "Measurements should have None as the material by default"
    absorbance.material = dye
    assert absorbance.material == dye, "Material not set correctly for measurement"
    assert dye.measurements == [
        absorbance
    ], "Soft-link from material to measurement not created"

    fluorescence = MeasurementRun(name="Fluorescence",
                                  uids={'id': str(uuid4())},
                                  properties=[
                                      Property(name='PL counts at 550 nm',
                                               value=NominalReal(30000, ''))
                                  ],
                                  material=dye)

    assert fluorescence.material == dye, "Material not set correctly for measurement"
    assert dye.measurements == [absorbance, fluorescence], \
        "Soft-link from material to measurements not created"

    assert loads(dumps(absorbance)) == absorbance, \
        "Measurement should remain unchanged when serialized"
    assert loads(dumps(fluorescence)) == fluorescence, \
        "Measurement should remain unchanged when serialized"

    # Serializing the material breaks the material-->measurement link.
    assert loads(dumps(dye)).measurements == [], \
        "Measurement information should be removed when material is serialized"

    assert 'measurements' in repr(dye)
    assert 'material' in repr(fluorescence)
    assert 'material' in repr(absorbance)

    substitute_links(dye.measurements)
    assert 'measurements' in repr(dye)
Esempio n. 8
0
def test_native_id_substitution():
    """Test that the native id gets serialized, when specified."""
    native_id = 'id1'
    # Create measurement and material with two ids
    mat = MaterialRun("A material",
                      uids={
                          native_id: str(uuid4()),
                          "an_id": str(uuid4()),
                          "another_id": str(uuid4())
                      })
    meas = MeasurementRun("A measurement",
                          material=mat,
                          uids={
                              "some_id": str(uuid4()),
                              native_id: str(uuid4()),
                              "an_id": str(uuid4())
                          })

    # Turn the material pointer into a LinkByUID using native_id
    subbed = substitute_links(meas, native_uid=native_id)
    assert subbed.material == LinkByUID.from_entity(mat, name=native_id)

    # Put the measurement into a list and convert that into a LinkByUID using native_id
    measurements_list = [meas]
    subbed = substitute_links(measurements_list, native_uid=native_id)
    assert subbed == [LinkByUID.from_entity(meas, name=native_id)]
Esempio n. 9
0
def test_order_objects():
    """Test that sorting works when given objects."""
    unsorted = [MaterialRun("bar"), ProcessRun("foo")]
    sorted_list = sorted(unsorted, key=lambda x: writable_sort_order(x))

    assert isinstance(sorted_list[0], ProcessRun)
    assert isinstance(sorted_list[1], MaterialRun)
Esempio n. 10
0
def test_dict_serialization():
    """Test that a dictionary can be serialized and then deserialized as a taurus object."""
    process = ProcessRun("A process")
    mat = MaterialRun("A material", process=process)
    meas = MeasurementRun("A measurement", material=mat)
    copy = loads(dumps(meas.as_dict()))
    assert copy == meas
Esempio n. 11
0
def test_flatten_empty_history():
    """Test that flatten works when the objects are empty and go through a whole history."""
    procured = ProcessSpec(name="procured")
    input = MaterialSpec(name="foo", process=procured)
    transform = ProcessSpec(name="transformed")
    ingredient = IngredientSpec(name="input",
                                material=input,
                                process=transform)

    procured_run = ProcessRun(name="procured", spec=procured)
    input_run = MaterialRun(name="foo", process=procured_run, spec=input)
    transform_run = ProcessRun(name="transformed", spec=transform)
    ingredient_run = IngredientRun(material=input_run,
                                   process=transform_run,
                                   spec=ingredient)

    assert len(flatten(procured)) == 1
    assert len(flatten(input)) == 1
    assert len(flatten(ingredient)) == 3
    assert len(flatten(transform)) == 3

    assert len(flatten(procured_run)) == 3
    assert len(flatten(input_run)) == 3
    assert len(flatten(ingredient_run)) == 7
    assert len(flatten(transform_run)) == 7
Esempio n. 12
0
def test_process_id_link():
    """Test that a process run can house a LinkByUID object, and that it survives serde."""
    uid = str(uuid4())
    proc_link = LinkByUID(scope='id', id=uid)
    mat_run = MaterialRun("Another cake", process=proc_link)
    copy_material = loads(dumps(mat_run))
    assert dumps(copy_material) == dumps(mat_run)
Esempio n. 13
0
def test_substitution_without_id():
    """Test that trying to substitute links if uids haven't been assigned throws an error."""
    mat = MaterialRun("A material with no id")
    meas = MeasurementRun("A measurement with no id", material=mat)
    with pytest.raises(ValueError):
        substitute_links(
            meas
        ), "subbed = substitute_links should fail if objects don't have uids"

    with pytest.raises(ValueError):
        substitute_links([meas, mat]), \
            "subbed = substitute_links should fail if objects don't have uids"

    with pytest.raises(ValueError):
        substitute_links(meas.as_dict()), \
            "subbed = substitute_links should fail if objects don't have uids"

    # Create a dictionary in which either the key or value is missing a uid
    meas.add_uid('id', str(uuid4()))
    with pytest.raises(ValueError):
        substitute_links({mat: meas}), \
            "subbed = substitute_links should fail if objects don't have uids"
    with pytest.raises(ValueError):
        substitute_links({meas: mat}), \
            "subbed = substitute_links should fail if objects don't have uids"
Esempio n. 14
0
def test_uid_deser():
    """Test that uids continue to be a CaseInsensitiveDict after deserialization."""
    material = MaterialRun("Input material", tags="input", uids={'Sample ID': '500-B'})
    ingredient = IngredientRun(material=material)
    ingredient_copy = loads(dumps(ingredient))
    assert isinstance(ingredient_copy.uids, CaseInsensitiveDict)
    assert ingredient_copy.material == material
    assert ingredient_copy.material.uids['sample id'] == material.uids['Sample ID']
Esempio n. 15
0
def test_measurement_reassignment():
    """Check that a measurement run can be re-assigned to a new material run."""
    sample1 = MaterialRun("Sample 1")
    sample2 = MaterialRun("Sample 2")
    mass = MeasurementRun("Mass of sample", material=sample1)
    volume = MeasurementRun("Volume of sample", material=sample1)
    assert mass.material == sample1
    assert set(sample1.measurements) == {mass, volume}
    assert sample2.measurements == []

    mass.material = sample2
    assert mass.material == sample2
    assert sample1.measurements == [volume]
    assert sample2.measurements == [mass]

    mass.material = None
    assert mass.material is None
    assert sample2.measurements == []
Esempio n. 16
0
def test_recursive_foreach():
    """Test that recursive foreach will actually walk through a material history."""
    mat_run = MaterialRun("foo")
    process_run = ProcessRun("bar")
    IngredientRun(process=process_run, material=mat_run)
    output = MaterialRun(process=process_run)

    # property templates are trickier than templates because they are referenced in attributes
    template = PropertyTemplate("prop", bounds=RealBounds(0, 1, ""))
    prop = Property("prop", value=NominalReal(1.0, ""), template=template)
    MeasurementRun("check", material=output, properties=prop)

    types = []
    recursive_foreach(output, lambda x: types.append(x.typ))

    expected = [
        "ingredient_run", "material_run", "material_run", "process_run",
        "measurement_run", "property_template"
    ]
    assert sorted(types) == sorted(expected)
Esempio n. 17
0
def test_serialize():
    """Serializing a nested object should be identical to individually serializing each piece."""
    condition = Condition(name="A condition", value=NominalReal(7, ''))
    parameter = Parameter(name="A parameter", value=NormalReal(mean=17, std=1, units=''))
    input_material = MaterialRun(tags="input")
    process = ProcessRun(tags="A tag on a process run")
    ingredient = IngredientRun(material=input_material, process=process)
    material = MaterialRun(tags=["A tag on a material"], process=process)
    measurement = MeasurementRun(tags="A tag on a measurement", conditions=condition,
                                 parameters=parameter, material=material)

    # serialize the root of the tree
    native_object = json.loads(dumps(measurement))
    # ingredients don't get serialized on the process
    assert(len(native_object[0]) == 3)
    assert(native_object[1]["type"] == LinkByUID.typ)

    # serialize all of the nodes
    native_batch = json.loads(dumps([material, process, measurement, ingredient]))
    assert(len(native_batch[0]) == 5)
    assert(len(native_batch[1]) == 4)
    assert(all(x["type"] == LinkByUID.typ for x in native_batch[1]))
Esempio n. 18
0
def test_dictionary_substitution():
    """substitute_objects() should substitute LinkByUIDs that occur in dict keys and values."""
    proc = ProcessRun("A process", uids={'id': '123'})
    mat = MaterialRun("A material", uids={'generic id': '38f8jf'})

    proc_link = LinkByUID.from_entity(proc)
    mat_link = LinkByUID.from_entity(mat)
    index = {(mat_link.scope.lower(), mat_link.id): mat,
             (proc_link.scope.lower(), proc_link.id): proc}

    test_dict = {LinkByUID.from_entity(proc): LinkByUID.from_entity(mat)}
    substitute_objects(test_dict, index)
    assert test_dict[proc] == mat
Esempio n. 19
0
def test_process_run():
    """Test that a process run can house a material, and that it survives serde."""
    process_run = ProcessRun("Bake a cake", uids={'My_ID': 17})
    material_run = MaterialRun("A cake", process=process_run)

    # Check that a bi-directional link is established
    assert material_run.process == process_run
    assert process_run.output_material == material_run

    copy_material = loads(dumps(material_run))
    assert dumps(copy_material) == dumps(material_run)

    assert 'output_material' in repr(process_run)
    assert 'process' in repr(material_run)
Esempio n. 20
0
def test_many_ingredients():
    """Test that ingredients remain connected to processes when round-robined through json."""
    proc = ProcessRun("foo", spec=ProcessSpec("sfoo"))
    expected = []
    for i in range(10):
        mat = MaterialRun(name=str(i), spec=MaterialSpec("s{}".format(i)))
        i_spec = IngredientSpec(name="i{}".format(i), material=mat.spec, process=proc.spec)
        IngredientRun(process=proc, material=mat, spec=i_spec)
        expected.append("i{}".format(i))

    reloaded = loads(dumps(proc))
    assert len(list(reloaded.ingredients)) == 10
    names = [x.name for x in reloaded.ingredients]
    assert sorted(names) == sorted(expected)
Esempio n. 21
0
def test_thin_dumps():
    """Test that thin_dumps turns pointers into links and doesn't work on non-BaseEntity."""
    mat = MaterialRun("The actual material")
    meas_spec = MeasurementSpec("measurement", uids={'my_scope': '324324'})
    meas = MeasurementRun("The measurement", spec=meas_spec, material=mat)

    thin_copy = MeasurementRun.build(json.loads(thin_dumps(meas)))
    assert isinstance(thin_copy, MeasurementRun)
    assert isinstance(thin_copy.material, LinkByUID)
    assert isinstance(thin_copy.spec, LinkByUID)
    assert thin_copy.spec.id == meas_spec.uids['my_scope']

    with pytest.raises(TypeError):
        thin_dumps(LinkByUID('scope', 'id'))
Esempio n. 22
0
def test_table():
    """Convert a dictionary to a pandas dataframe and then to a table."""
    material = MaterialRun()
    df = pd.DataFrame.from_records(data)
    result = ingest_table(material, df)
    assert isinstance(result, MaterialRun)
    assert len(result.measurements) == len(data)

    filename = "/tmp/table_example.json"

    # write to a file (for human inspection)
    with open(filename, "w") as f:
        dump(result, f, indent=2)

    # read it back
    with open(filename, "r") as f:
        copy = load(f)

    assert isinstance(copy, MaterialRun)
Esempio n. 23
0
def test_thin_dumps():
    """Test that thin_dumps turns pointers into links."""
    mat = MaterialRun("The actual material")
    meas_spec = MeasurementSpec("measurement", uids={'my_scope': '324324'})
    meas = MeasurementRun("The measurement", spec=meas_spec, material=mat)

    thin_copy = MeasurementRun.build(json.loads(TaurusJson().thin_dumps(meas)))
    assert isinstance(thin_copy, MeasurementRun)
    assert isinstance(thin_copy.material, LinkByUID)
    assert isinstance(thin_copy.spec, LinkByUID)
    assert thin_copy.spec.id == meas_spec.uids['my_scope']

    # Check that LinkByUID objects are correctly converted their JSON equivalent
    expected_json = '{"id": "my_id", "scope": "scope", "type": "link_by_uid"}'
    assert TaurusJson().thin_dumps(LinkByUID('scope', 'my_id')) == expected_json

    # Check that objects lacking .uid attributes will raise an exception when dumped
    with pytest.raises(TypeError):
        TaurusJson().thin_dumps({{'key': 'value'}})
Esempio n. 24
0
def make_data_island(density, bulk_modulus, firing_temperature, binders, powders, tag=None):
    """Helper function to create a relatively involved data island."""
    binder_specs = keymap(lambda x: MaterialSpec(name=x), binders)
    powder_specs = keymap(lambda x: MaterialSpec(name=x), powders)

    binder_runs = keymap(lambda x: MaterialRun(spec=x), binder_specs)
    powder_runs = keymap(lambda x: MaterialRun(spec=x), powder_specs)

    all_input_materials = keymap(lambda x: x.spec.name, merge(binder_runs, powder_runs))
    mixing_composition = Condition(
        name="composition",
        value=NominalComposition(all_input_materials)
    )
    mixing_process = ProcessRun(
        tags=["mixing"],
        conditions=[mixing_composition]
    )
    binder_ingredients = []
    for run in binder_runs:
        binder_ingredients.append(
            IngredientRun(
                material=run,
                process=mixing_process,
                mass_fraction=NominalReal(binders[run.spec.name], ''),
            )
        )

    powder_ingredients = []
    for run in powder_runs:
        powder_ingredients.append(
            IngredientRun(
                material=run,
                process=mixing_process,
                mass_fraction=NominalReal(powders[run.spec.name], ''),
            )
        )

    green_sample = MaterialRun(process=mixing_process)

    measured_firing_temperature = Condition(
        name="Firing Temperature",
        value=UniformReal(firing_temperature - 0.5, firing_temperature + 0.5, 'degC'),
        template=firing_temperature_template
    )

    specified_firing_setting = Parameter(
        name="Firing setting",
        value=DiscreteCategorical("hot")
    )
    firing_spec = ProcessSpec(template=firing_template)
    firing_process = ProcessRun(
        conditions=[measured_firing_temperature],
        parameters=[specified_firing_setting],
        spec=firing_spec
    )
    IngredientRun(
        green_sample,
        process=firing_process,
        mass_fraction=NormalReal(1.0, 0.0, ''),
        volume_fraction=NormalReal(1.0, 0.0, ''),
        number_fraction=NormalReal(1.0, 0.0, '')
    )

    measured_density = Property(
        name="Density",
        value=NominalReal(density, ''),
        template=density_template
    )
    measured_modulus = Property(
        name="Bulk modulus",
        value=NormalReal(bulk_modulus, bulk_modulus / 100.0, '')
    )
    measurement_spec = MeasurementSpec(template=measurement_template)
    measurement = MeasurementRun(
        properties=[measured_density, measured_modulus],
        spec=measurement_spec
    )

    tags = [tag] if tag else []

    material_spec = MaterialSpec(template=material_template)
    material_run = MaterialRun(process=firing_process, tags=tags, spec=material_spec)
    measurement.material = material_run
    return material_run
Esempio n. 25
0
def ingest_material_run(data, material_spec=None, process_run=None):
    """Ingest material run with data, a material spec, and an originating process run."""
    if isinstance(data, list):
        return [ingest_material_run(x, material_spec) for x in data]

    if not isinstance(data, dict):
        raise ValueError("This ingester operates on dict, but got {}".format(type(data)))

    material = MaterialRun()

    sample_id = data.get("sample_id")
    if sample_id:
        material.add_uid("given_sample_id", sample_id)

    tags = data.get("tags")
    if tags:
        material.tags = tags

    for experiment in data.get("experiments", []):
        measurement = MeasurementRun()

        for name in set(known_properties.keys()).intersection(experiment.keys()):
            prop = Property(
                name=name,
                template=known_properties[name],
                value=_parse_value(experiment[name])
            )
            measurement.properties.append(prop)

        for name in set(known_conditions.keys()).intersection(experiment.keys()):
            cond = Condition(
                name=name,
                template=known_conditions[name],
                value=_parse_value(experiment[name])
            )
            measurement.conditions.append(cond)

        for name in set(known_parameters.keys()).intersection(experiment.keys()):
            param = Parameter(
                name=name,
                template=known_parameters[name],
                value=_parse_value(experiment[name])
            )
            measurement.parameters.append(param)

        scan_id = experiment.get("scan_id")
        if scan_id:
            measurement.add_uid("given_scan_id", scan_id)

        tags = experiment.get("tags")
        if tags:
            measurement.tags = tags

        measurement.material = material

    if material_spec:
        material.material_spec = material_spec

    if process_run:
        material.process = process_run

    return material