コード例 #1
0
def test_add_all_ingredients(session, project):
    """Test the behavior of AraDefinition.add_all_ingredients."""
    # GIVEN
    process_id = '3a308f78-e341-f39c-8076-35a2c88292ad'
    process_name = 'mixing'
    allowed_names = ["gold nanoparticles", "methanol", "acetone"]
    process_link = LinkByUID('id', process_id)
    session.set_response(
        ProcessTemplate(process_name, uids={'id': process_id}, allowed_names=allowed_names).dump()
    )

    # WHEN we add all ingredients in a volume basis
    def1 = empty_defn().add_all_ingredients(process_template=process_link, project=project,
                                            quantity_dimension=IngredientQuantityDimension.VOLUME)
    # THEN there should be 2 variables and columns for each name, one for id and one for quantity
    assert len(def1.variables) == len(allowed_names) * 2
    assert len(def1.columns) == len(def1.variables)
    for name in allowed_names:
        assert next((var for var in def1.variables if name in var.headers
                     and isinstance(var, IngredientQuantityByProcessAndName)), None) is not None
        assert next((var for var in def1.variables if name in var.headers
                     and isinstance(var, IngredientIdentifierByProcessTemplateAndName)), None) is not None

    session.set_response(
        ProcessTemplate(process_name, uids={'id': process_id}, allowed_names=allowed_names).dump()
    )
    # WHEN we add all ingredients to the same Table Config as absolute quantities
    def2 = def1.add_all_ingredients(process_template=process_link, project=project,
                                    quantity_dimension=IngredientQuantityDimension.ABSOLUTE)
    # THEN there should be 1 new variable for each name, corresponding to the quantity
    #   There is already a variable for id
    #   There should be 2 new columns for each name, one for the quantity and one for the units
    new_variables = def2.variables[len(def1.variables):]
    new_columns = def2.columns[len(def1.columns):]
    assert len(new_variables) == len(allowed_names)
    assert len(new_columns) == len(allowed_names) * 2
    assert def2.config_uid == UUID("6b608f78-e341-422c-8076-35adc8828545")
    for name in allowed_names:
        assert next((var for var in new_variables if name in var.headers
                     and isinstance(var, IngredientQuantityByProcessAndName)), None) is not None

    session.set_response(
        ProcessTemplate(process_name, uids={'id': process_id}, allowed_names=allowed_names).dump()
    )
    # WHEN we add all ingredients to the same Table Config in a volume basis
    # THEN it raises an exception because these variables and columns already exist
    with pytest.raises(ValueError):
        def2.add_all_ingredients(process_template=process_link, project=project,
                                 quantity_dimension=IngredientQuantityDimension.VOLUME)

    # If the process template has an empty allowed_names list then an error should be raised
    session.set_response(
        ProcessTemplate(process_name, uids={'id': process_id}).dump()
    )
    with pytest.raises(RuntimeError):
        empty_defn().add_all_ingredients(process_template=process_link, project=project,
                                         quantity_dimension=IngredientQuantityDimension.VOLUME)
コード例 #2
0
def test_simple_deserialization(valid_data):
    """Ensure that a deserialized Process Spec looks sane."""
    process_spec: ProcessSpec = ProcessSpec.build(valid_data)
    assert process_spec.uids == {'id': valid_data['uids']['id']}
    assert process_spec.tags == ['baking::cakes', 'danger::low']
    assert process_spec.parameters[0] == Parameter(name='oven temp',
                                                   value=UniformReal(
                                                       195, 205, ''),
                                                   origin='specified')
    assert process_spec.conditions == []
    assert process_spec.template == \
           ProcessTemplate('the template', uids={'id': valid_data['template']['uids']['id']},
                           parameters=[
                               [ParameterTemplate('oven temp template',
                                                  bounds=RealBounds(175, 225, ''),
                                                  uids={'id': valid_data['template']['parameters'][0][0]['uids']['id']}),
                                RealBounds(175, 225, '')]
                           ],
                           description='a long description',
                           allowed_labels=['a', 'b'],
                           allowed_names=['a name'])
    assert process_spec.name == 'Process 1'
    assert process_spec.notes == 'make sure to use oven mitts'
    assert process_spec.file_links == [
        FileLink('cake_recipe.txt', 'www.baking.com')
    ]
    assert process_spec.typ == 'process_spec'
    assert process_spec.audit_info == AuditInfo(**valid_data['audit_info'])
コード例 #3
0
def test_object_template_serde():
    """Test serde of an object template."""
    length_template = PropertyTemplate("Length", bounds=RealBounds(2.0, 3.5, 'cm'))
    sub_bounds = RealBounds(2.5, 3.0, 'cm')
    color_template = PropertyTemplate("Color", bounds=CategoricalBounds(["red", "green", "blue"]))
    # Properties are a mixture of property templates and [template, bounds], pairs
    block_template = MaterialTemplate("Block", properties=[[length_template, sub_bounds],
                                                           color_template])
    copy_template = MaterialTemplate.build(block_template.dump())
    assert copy_template == block_template

    # Tests below exercise similar code, but for measurement and process templates
    pressure_template = ConditionTemplate("pressure", bounds=RealBounds(0.1, 0.11, 'MPa'))
    index_template = ParameterTemplate("index", bounds=IntegerBounds(2, 10))
    meas_template = MeasurementTemplate("A measurement of length", properties=[length_template],
                                        conditions=[pressure_template], description="Description",
                                        parameters=[index_template], tags=["foo"])
    assert MeasurementTemplate.build(meas_template.dump()) == meas_template

    proc_template = ProcessTemplate("Make an object", parameters=[index_template],
                                    conditions=[pressure_template], allowed_labels=["Label"],
                                    allowed_names=["first sample", "second sample"])
    assert ProcessTemplate.build(proc_template.dump()) == proc_template

    # Check that serde still works if the template is a LinkByUID
    pressure_template.uids['id'] = '12345'  # uids['id'] not populated by default
    proc_template.conditions[0][0] = LinkByUID('id', pressure_template.uids['id'])
    assert ProcessTemplate.build(proc_template.dump()) == proc_template
コード例 #4
0
def test_async_update_with_no_wait(dataset, session):
    """Check that async_update parses the response when not waiting"""

    obj = ProcessTemplate("foo", uids={'id': str(uuid4())})

    dataset.session.set_response(JobSubmissionResponseFactory())
    job_id = dataset.process_templates.async_update(obj,
                                                    wait_for_response=False)
    assert job_id is not None
コード例 #5
0
def test_async_update_and_no_dataset_id(dataset, session):
    """Ensure async_update requires a dataset id"""

    obj = ProcessTemplate("foo", uids={'id': str(uuid4())})

    dataset.session.set_response(JobSubmissionResponseFactory())
    dataset.uid = None

    with pytest.raises(RuntimeError):
        dataset.process_templates.async_update(obj, wait_for_response=False)
コード例 #6
0
def test_delete_data_concepts(dataset):
    """Check that delete routes to the correct collections"""
    expected = {
        MaterialTemplateCollection: MaterialTemplate("foo",
                                                     uids={"foo": "bar"}),
        MaterialSpecCollection: MaterialSpec("foo", uids={"foo": "bar"}),
        MaterialRunCollection: MaterialRun("foo", uids={"foo": "bar"}),
        ProcessTemplateCollection: ProcessTemplate("foo", uids={"foo": "bar"}),
    }

    for collection, obj in expected.items():
        dataset.delete(obj)
        assert dataset.session.calls[-1].path.split("/")[-3] == basename(
            collection._path_template)
コード例 #7
0
def test_template_assignment():
    """Test that an object and its attributes can both be assigned templates."""
    humidity_template = ConditionTemplate("Humidity",
                                          RealBounds(0.5, 0.75, ""))
    template = ProcessTemplate(
        "Dry", conditions=[[humidity_template,
                            RealBounds(0.5, 0.65, "")]])
    ProcessSpec("Dry a polymer",
                template=template,
                conditions=[
                    Condition("Humidity",
                              value=NominalReal(0.6, ""),
                              template=humidity_template)
                ])
コード例 #8
0
def test_flatten():
    """Test that gemd utility methods can be applied to citrine-python objects.
    Citrine-python resources extend the gemd data model, so the gemd operations
    should work on them.
    """

    bounds = CategoricalBounds(categories=["foo", "bar"])
    template = ProcessTemplate(
        "spam",
        conditions=[(ConditionTemplate(name="eggs", bounds=bounds), bounds)]
    )
    spec = ProcessSpec(name="spec", template=template)

    flat = flatten(spec, scope='testing')
    assert len(flat) == 3, "Expected 3 flattened objects"
コード例 #9
0
def test_async_update_and_wait_failure(dataset, session):
    """Check that async_update parses the failure correctly"""

    obj = ProcessTemplate("foo", uids={'id': str(uuid4())})
    fake_job_status_resp = {
        'job_type': 'some_typ',
        'status': 'Failure',
        'tasks': [],
        'output': {}
    }

    dataset.session.set_responses(JobSubmissionResponseFactory(),
                                  fake_job_status_resp)

    with pytest.raises(JobFailureError):
        dataset.process_templates.async_update(obj, wait_for_response=True)
コード例 #10
0
def test_async_update_and_wait(dataset, session):
    """Check that async_update parses the response when waiting"""

    obj = ProcessTemplate("foo", uids={'id': str(uuid4())})
    fake_job_status_resp = {
        'job_type': 'some_typ',
        'status': 'Success',
        'tasks': [],
        'output': {}
    }

    dataset.session.set_responses(JobSubmissionResponseFactory(),
                                  fake_job_status_resp)

    # This returns None on successful update with wait.
    dataset.process_templates.async_update(obj, wait_for_response=True)
コード例 #11
0
def test_object_template_validation():
    """Test that attribute templates are validated against given bounds."""
    length_template = PropertyTemplate("Length", bounds=RealBounds(2.0, 3.5, 'cm'))
    dial_template = ConditionTemplate("dial", bounds=IntegerBounds(0, 5))
    color_template = ParameterTemplate("Color", bounds=CategoricalBounds(["red", "green", "blue"]))

    with pytest.raises(TypeError):
        MaterialTemplate()

    with pytest.raises(ValueError):
        MaterialTemplate("Block", properties=[[length_template, RealBounds(3.0, 4.0, 'cm')]])

    with pytest.raises(ValueError):
        ProcessTemplate("a process", conditions=[[color_template, CategoricalBounds(["zz"])]])
        
    with pytest.raises(ValueError):
        MeasurementTemplate("A measurement", parameters=[[dial_template, IntegerBounds(-3, -1)]])
コード例 #12
0
def test_async_update_timeout(dataset, session):
    """Ensure the proper exception is thrown on a timeout error"""

    obj = ProcessTemplate("foo", uids={'id': str(uuid4())})
    fake_job_status_resp = {
        'job_type': 'some_typ',
        'status': 'Pending',
        'tasks': [],
        'output': {}
    }

    dataset.session.set_responses(JobSubmissionResponseFactory(),
                                  fake_job_status_resp)

    with pytest.raises(PollingTimeoutError):
        dataset.process_templates.async_update(obj,
                                               wait_for_response=True,
                                               timeout=-1.0)
コード例 #13
0
def test_register_data_concepts(dataset):
    """Check that register routes to the correct collections"""
    expected = {
        MaterialTemplateCollection:
        MaterialTemplate("foo"),
        MaterialSpecCollection:
        MaterialSpec("foo"),
        MaterialRunCollection:
        MaterialRun("foo"),
        ProcessTemplateCollection:
        ProcessTemplate("foo"),
        ProcessSpecCollection:
        ProcessSpec("foo"),
        ProcessRunCollection:
        ProcessRun("foo"),
        MeasurementTemplateCollection:
        MeasurementTemplate("foo"),
        MeasurementSpecCollection:
        MeasurementSpec("foo"),
        MeasurementRunCollection:
        MeasurementRun("foo"),
        IngredientSpecCollection:
        IngredientSpec("foo"),
        IngredientRunCollection:
        IngredientRun(),
        PropertyTemplateCollection:
        PropertyTemplate("bar", bounds=IntegerBounds(0, 1)),
        ParameterTemplateCollection:
        ParameterTemplate("bar", bounds=IntegerBounds(0, 1)),
        ConditionTemplateCollection:
        ConditionTemplate("bar", bounds=IntegerBounds(0, 1))
    }

    for collection, obj in expected.items():
        assert len(obj.uids) == 0
        registered = dataset.register(obj)
        assert len(obj.uids) == 1
        assert len(registered.uids) == 1
        assert basename(dataset.session.calls[-1].path) == basename(
            collection._path_template)
        for pair in obj.uids.items():
            assert pair[1] == registered.uids[pair[0]]
コード例 #14
0
def test_register_all_data_concepts(dataset):
    """Check that register_all registers everything and routes to all collections"""
    bounds = IntegerBounds(0, 1)
    property_template = PropertyTemplate("bar", bounds=bounds)
    parameter_template = ParameterTemplate("bar", bounds=bounds)
    condition_template = ConditionTemplate("bar", bounds=bounds)
    foo_process_template = ProcessTemplate(
        "foo",
        conditions=[[condition_template, bounds]],
        parameters=[[parameter_template, bounds]])
    foo_process_spec = ProcessSpec("foo", template=foo_process_template)
    foo_process_run = ProcessRun("foo", spec=foo_process_spec)
    foo_material_template = MaterialTemplate(
        "foo", properties=[[property_template, bounds]])
    foo_material_spec = MaterialSpec("foo",
                                     template=foo_material_template,
                                     process=foo_process_spec)
    foo_material_run = MaterialRun("foo",
                                   spec=foo_material_spec,
                                   process=foo_process_run)
    baz_template = MaterialTemplate("baz")
    foo_measurement_template = MeasurementTemplate(
        "foo",
        conditions=[[condition_template, bounds]],
        parameters=[[parameter_template, bounds]],
        properties=[[property_template, bounds]])
    foo_measurement_spec = MeasurementSpec("foo",
                                           template=foo_measurement_template)
    foo_measurement_run = MeasurementRun("foo",
                                         spec=foo_measurement_spec,
                                         material=foo_material_run)
    foo_ingredient_spec = IngredientSpec("foo",
                                         material=foo_material_spec,
                                         process=foo_process_spec)
    foo_ingredient_run = IngredientRun(spec=foo_ingredient_spec,
                                       material=foo_material_run,
                                       process=foo_process_run)
    baz_run = MeasurementRun("baz")

    # worst order possible
    expected = {
        foo_ingredient_run: IngredientRunCollection,
        foo_ingredient_spec: IngredientSpecCollection,
        foo_measurement_run: MeasurementRunCollection,
        foo_measurement_spec: MeasurementSpecCollection,
        foo_measurement_template: MeasurementTemplateCollection,
        foo_material_run: MaterialRunCollection,
        foo_material_spec: MaterialSpecCollection,
        foo_material_template: MaterialTemplateCollection,
        foo_process_run: ProcessRunCollection,
        foo_process_spec: ProcessSpecCollection,
        foo_process_template: ProcessTemplateCollection,
        baz_template: MaterialTemplateCollection,
        baz_run: MeasurementRunCollection,
        property_template: PropertyTemplateCollection,
        parameter_template: ParameterTemplateCollection,
        condition_template: ConditionTemplateCollection
    }
    for obj in expected:
        assert len(obj.uids) == 0  # All should be without ids
    registered = dataset.register_all(expected.keys())
    assert len(registered) == len(expected)

    seen_ids = set()
    for obj in expected:
        assert len(obj.uids) == 1  # All should now have exactly 1 id
        for pair in obj.uids.items():
            assert pair not in seen_ids  # All ids are different
            seen_ids.add(pair)
    for obj in registered:
        for pair in obj.uids.items():
            assert pair in seen_ids  # registered items have the same ids

    call_basenames = [
        call.path.split('/')[-2] for call in dataset.session.calls
    ]
    collection_basenames = [
        basename(collection._path_template)
        for collection in expected.values()
    ]
    assert set(call_basenames) == set(collection_basenames)
    assert len(set(call_basenames)) == len(
        call_basenames)  # calls are batched internally

    # spot check order. Does not check every constraint
    assert call_basenames.index(
        basename(
            IngredientRunCollection._path_template)) > call_basenames.index(
                basename(IngredientSpecCollection._path_template))
    assert call_basenames.index(basename(
        MaterialRunCollection._path_template)) > call_basenames.index(
            basename(MaterialSpecCollection._path_template))
    assert call_basenames.index(
        basename(
            MeasurementRunCollection._path_template)) > call_basenames.index(
                basename(MeasurementSpecCollection._path_template))
    assert call_basenames.index(basename(
        ProcessRunCollection._path_template)) > call_basenames.index(
            basename(ProcessSpecCollection._path_template))
    assert call_basenames.index(basename(
        MaterialSpecCollection._path_template)) > call_basenames.index(
            basename(MaterialTemplateCollection._path_template))
    assert call_basenames.index(
        basename(
            MeasurementSpecCollection._path_template)) > call_basenames.index(
                basename(MeasurementTemplateCollection._path_template))
    assert call_basenames.index(basename(
        ProcessSpecCollection._path_template)) > call_basenames.index(
            basename(ProcessTemplateCollection._path_template))
    assert call_basenames.index(basename(
        MaterialSpecCollection._path_template)) > call_basenames.index(
            basename(ProcessSpecCollection._path_template))
    assert call_basenames.index(basename(
        MaterialSpecCollection._path_template)) > call_basenames.index(
            basename(MeasurementSpecCollection._path_template))
    assert call_basenames.index(
        basename(MeasurementTemplateCollection._path_template)
    ) > call_basenames.index(
        basename(ConditionTemplateCollection._path_template))
    assert call_basenames.index(
        basename(MeasurementTemplateCollection._path_template)
    ) > call_basenames.index(
        basename(ParameterTemplateCollection._path_template))
    assert call_basenames.index(
        basename(
            MaterialTemplateCollection._path_template)) > call_basenames.index(
                basename(PropertyTemplateCollection._path_template))