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
def _build_child_objects(cls, data: dict, session: Session = None): """ Build the property templates and bounds. Parameters ---------- data: dict A serialized material template. session: Session, optional Citrine session used to connect to the database. Returns ------- None The serialized material template is modified so that its properties are [PropertyTemplate, Bounds]. """ if 'properties' in data and len(data['properties']) != 0: # Each entry in the list data['properties'] has a property template as the 1st entry # and a base bounds as the 2nd entry. They are built in different ways. data['properties'] = [[ PropertyTemplate.build(prop[0].as_dict()), loads(dumps(prop[1])) ] for prop in data['properties']]
def test_no_data_objects(): objs = [ PropertyTemplate("pt", bounds=CategoricalBounds()), ConditionTemplate("ct", bounds=CategoricalBounds()) ] templates, data_objects = split_templates_from_objects(objs) assert len(templates) == 2 assert len(data_objects) == 0
def test_both_present(): objs = [ ProcessSpec("ps"), PropertyTemplate("pt", bounds=CategoricalBounds()), MeasurementSpec("ms"), ConditionTemplate("ct", bounds=CategoricalBounds()) ] templates, data_objects = split_templates_from_objects(objs) assert len(templates) == 2 assert len(data_objects) == 2
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)]])
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]]
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))
def test_property_template(): """Test creation and serde of condition templates.""" bounds = CategoricalBounds(['solid', 'liquid', 'gas']) template = PropertyTemplate("State", bounds=bounds, uids={'my_id': '0'}) assert PropertyTemplate.build(template.dump()) == template