def test_submodels(self): env = EnvironmentVarGuard() env.set('CONFIG__DOT__wc_kb__DOT__io__DOT__strict', '0') with env: cls.kb = wc_kb.io.Reader().run( 'tests/fixtures/min_model_kb.xlsx', 'tests/fixtures/min_model_seq.fna', )[wc_kb.KnowledgeBase][0] cls.model = prokaryote.ProkaryoteModelGenerator( knowledge_base=cls.kb).run() cytosol = self.model.compartments.get_one(id='c') extracellular_space = self.model.compartments.get_one(id='e') mean_doubling_time = self.model.parameters.get_one( id='mean_doubling_time') errors = obj_tables.Validator().run(self.model, get_related=True) self.assertEqual(errors, None, msg=wc_utils.util.string.indent_forest(errors)) self.assertEqual(5, len(self.model.submodels))
def run(self, dirname): """ Read `wc_lang` models from SBML-encoded XML files, one for each submodel Args: dirname (:obj:`str`): path to directory that contains SBML-encoded submodels of a model Returns: model (:obj:`wc_lang.core.Model`): `wc_lang` model """ merged_model = None sbml_reader = LibSbmlInterface.call_libsbml(libsbml.SBMLReader) paths = glob.glob(os.path.join(dirname, '*.xml')) core_path = os.path.join(dirname, 'core.xml') if core_path in paths: paths.remove(core_path) paths.insert(0, core_path) for path in paths: # read model from XML file sbml_doc = LibSbmlInterface.call_libsbml( sbml_reader.readSBMLFromFile, path) LibSbmlInterface.raise_if_error( sbml_doc, 'Model could not be read from {}'.format(path)) # convert SBML-encoded model to wc_lang model = SbmlImporter.run(sbml_doc) error = obj_tables.Validator().run(model, get_related=True) assert error is None, str(error) # merge models if merged_model is None: merged_model = model else: merged_model.merge(model) # return merged model return merged_model
def test(self): self.dir = tempfile.mkdtemp() gen = kb_gen.KbGenerator(options={ 'component': { 'PropertiesGenerator': { 'mean_volume': 1e-15, 'mean_doubling_time': 1000., }, 'GenomeGenerator': { 'num_chromosomes': 10, 'mean_num_genes': 200, 'seq_path': os.path.join(self.dir, 'kb_seq.fna'), }, }, }) kb = gen.run() self.assertEqual(len(kb.cell.species_types.get(__type=wc_kb.core.DnaSpeciesType)), 10) errors = obj_tables.Validator().run(kb, get_related=True) self.assertEqual(errors, None, msg=wc_utils.util.string.indent_forest(errors)) shutil.rmtree(self.dir)
def run(self, path, models=None, ignore_missing_models=None, ignore_sheet_order=None, include_all_attributes=False, ignore_missing_attributes=None, ignore_extra_attributes=None, ignore_attribute_order=None, group_objects_by_model=True, validate=None): """ Read a list of model objects from file(s) and, optionally, validate them Args: path (:obj:`str`): path to file(s) models (:obj:`types.TypeType` or :obj:`list` of :obj:`types.TypeType`, optional): type of object to read or list of types of objects to read ignore_missing_models (:obj:`bool`, optional): if :obj:`False`, report an error if a worksheet/ file is missing for one or more models ignore_sheet_order (:obj:`bool`, optional): if :obj:`True`, do not require the sheets to be provided in the canonical order include_all_attributes (:obj:`bool`, optional): if :obj:`True`, export all attributes including those not explictly included in `Model.Meta.attribute_order` ignore_missing_attributes (:obj:`bool`, optional): if :obj:`False`, report an error if a worksheet/file doesn't contain all of attributes in a model in `models` ignore_extra_attributes (:obj:`bool`, optional): if :obj:`True`, do not report errors if attributes in the data are not in the model ignore_attribute_order (:obj:`bool`): if :obj:`True`, do not require the attributes to be provided in the canonical order group_objects_by_model (:obj:`bool`, optional): if :obj:`True`, group decoded objects by their types validate (:obj:`bool`, optional): if :obj:`True`, validate the data Returns: :obj:`dict`: model objects grouped by `obj_tables.Model` class Raises: :obj:`ValueError`: if the file defines zero or multiple models or the model defined in the file(s) is invalid """ if issubclass(self.get_reader(path), obj_tables.io.WorkbookReader): Writer.validate_implicit_relationships() if models is None: models = self.MODELS config = wc_lang.config.core.get_config()['wc_lang']['io'] if ignore_missing_models is None: ignore_missing_models = not config['strict'] if ignore_sheet_order is None: ignore_sheet_order = not config['strict'] if ignore_missing_attributes is None: ignore_missing_attributes = not config['strict'] if ignore_extra_attributes is None: ignore_extra_attributes = not config['strict'] if ignore_attribute_order is None: ignore_attribute_order = not config['strict'] objects = super(Reader, self).run( path, models=models, ignore_missing_models=ignore_missing_models, ignore_sheet_order=ignore_sheet_order, include_all_attributes=include_all_attributes, ignore_missing_attributes=ignore_missing_attributes, ignore_extra_attributes=ignore_extra_attributes, ignore_attribute_order=ignore_attribute_order, group_objects_by_model=group_objects_by_model, validate=False) # check that file only has 1 model if len(objects[core.Model]) != 1: raise ValueError('"{}" should define one model'.format(path)) model = objects[core.Model][0] # add implicit relationships to `Model` if issubclass(self.get_reader(path), obj_tables.io.WorkbookReader): for cls, cls_objects in objects.items(): for attr in cls.Meta.attributes.values(): if isinstance(attr, obj_tables.RelatedAttribute) and \ attr.related_class == core.Model: for cls_obj in cls_objects: setattr(cls_obj, attr.name, model) # validate config = wc_lang.config.core.get_config()['wc_lang']['io'] if (validate is not None and validate) or (validate is None and config['validate']): objs = [] for cls_objs in objects.values(): objs.extend(cls_objs) errors = obj_tables.Validator().validate(objs) if errors: raise ValueError( indent_forest([ 'The model cannot be loaded because it fails to validate:', [errors] ])) # return model return objects
def run(self, core_path, seq_path='', rewrite_seq_path=True, taxon='prokaryote', models=None, ignore_missing_models=None, ignore_extra_models=None, ignore_sheet_order=None, include_all_attributes=False, ignore_missing_attributes=None, ignore_extra_attributes=None, ignore_attribute_order=None, group_objects_by_model=True, validate=True, read_metadata=False): """ Read knowledge base from file(s) Args: core_path (:obj:`str`): path to core knowledge base seq_path (:obj:`str`): path to genome sequence rewrite_seq_path (:obj:`bool`, optional): if :obj:`True`, the path to genome sequence in the knowledge base will be updated to the provided seq_path taxon (:obj:`str`, optional): type of model order to use models (:obj:`types.TypeType` or :obj:`list` of :obj:`types.TypeType`, optional): type of object to read or list of types of objects to read ignore_missing_models (:obj:`bool`, optional): if :obj:`False`, report an error if a worksheet/ file is missing for one or more models ignore_extra_models (:obj:`bool`, optional): if :obj:`True` and all `models` are found, ignore other worksheets or files ignore_sheet_order (:obj:`bool`, optional): if :obj:`True`, do not require the sheets to be provided in the canonical order include_all_attributes (:obj:`bool`, optional): if :obj:`True`, export all attributes including those not explictly included in `Model.Meta.attribute_order` ignore_missing_attributes (:obj:`bool`, optional): if :obj:`False`, report an error if a worksheet/file doesn't contain all of attributes in a model in `models` ignore_extra_attributes (:obj:`bool`, optional): if :obj:`True`, do not report errors if attributes in the data are not in the model ignore_attribute_order (:obj:`bool`): if :obj:`True`, do not require the attributes to be provided in the canonical order group_objects_by_model (:obj:`bool`, optional): if :obj:`True`, group decoded objects by their types validate (:obj:`bool`, optional): if :obj:`True`, validate the data read_metadata (:obj:`bool`, optional): if :obj:`True`, read metadata models Returns: :obj:`dict`: model objects grouped by `obj_tables.Model` class Raises: :obj:`ValueError`: if :obj:`core_path` * Defines multiple knowledge bases or cells * Represents objects that cannot be linked to a knowledge base and/or cell """ if issubclass(self.get_reader(core_path), obj_tables.io.WorkbookReader): Writer.validate_implicit_relationships() if taxon == 'prokaryote': models = PROKARYOTE_MODELS elif taxon == 'eukaryote': models = EUKARYOTE_MODELS else: raise ValueError('Unsupported taxon "{}"'.format(taxon)) if read_metadata: models = list(models) + [ obj_tables.utils.DataRepoMetadata, obj_tables.utils.SchemaRepoMetadata ] ignore_missing_models = True ignore_sheet_order = True config = wc_kb.config.core.get_config()['wc_kb']['io'] if ignore_missing_models is None: ignore_missing_models = not config['strict'] if ignore_extra_models is None: ignore_extra_models = not config['strict'] if ignore_sheet_order is None: ignore_sheet_order = not config['strict'] if ignore_missing_attributes is None: ignore_missing_attributes = not config['strict'] if ignore_extra_attributes is None: ignore_extra_attributes = not config['strict'] if ignore_attribute_order is None: ignore_attribute_order = not config['strict'] # read core objects from file objects = super(Reader, self).run( core_path, schema_name='wc_kb.' + taxon, models=models, ignore_missing_models=ignore_missing_models, ignore_extra_models=ignore_extra_models, ignore_sheet_order=ignore_sheet_order, include_all_attributes=include_all_attributes, ignore_missing_attributes=ignore_missing_attributes, ignore_extra_attributes=ignore_extra_attributes, ignore_attribute_order=ignore_attribute_order, group_objects_by_model=True, validate=False) # Check if sequence pathes are consistent for idx, chromosome in enumerate(objects[wc_kb.core.DnaSpeciesType]): if (chromosome.sequence_path is None) or (chromosome.sequence_path == ''): chromosome.sequence_path = seq_path # Set seq_path to be what is provided to wc_kb.io.Reader() if idx != 0: warnings.warn( 'Same sequence file is associated with multiple chromosomes, ' 'make sure seq file is formatted accordingly!') else: if chromosome.sequence_path != seq_path: warnings.warn( 'Sequence path ({}) provided in KB file ({}) is different from \ seq_path provided to wc_kb.io.Reader ({}).' .format(chromosome.sequence_path, core_path, seq_path)) # check that file has 1 knowledge base if len(objects[core.KnowledgeBase]) != 1: raise ValueError( '"{}" should define one knowledge base'.format(core_path)) kb = objects[core.KnowledgeBase][0] # check that file has 0 or 1 cells if not objects[core.Cell]: cell = None elif len(objects[core.Cell]) == 1: cell = objects[core.Cell][0] else: raise ValueError( '"{}" should define zero or one cells'.format(core_path)) # add implict relationships to `KnowledgeBase` and `Cell` kb.cell = cell for model, model_objects in objects.items(): for attr in model.Meta.attributes.values(): if isinstance(attr, obj_tables.RelatedAttribute ) and attr.related_class == core.Cell: for model_obj in model_objects: setattr(model_obj, attr.name, cell) # link path to genome sequence to the DNA species types if rewrite_seq_path is True if rewrite_seq_path: for dna in Bio.SeqIO.parse(seq_path, "fasta"): species_type = kb.cell.species_types.get_one(id=dna.id) species_type.sequence_path = seq_path # validate config = wc_kb.config.core.get_config()['wc_kb']['io'] if (validate is not None and validate) or (validate is None and config['validate']): objs = [] for cls_objs in objects.values(): objs.extend(cls_objs) errors = obj_tables.Validator().validate(objs) if errors: raise ValueError( indent_forest([ 'The knowledge base cannot be loaded because it fails to validate:', [errors] ])) # if `group_objects_by_model` is False, flatten objects into list if not group_objects_by_model: flat_objects = [] for model_objs in objects.values(): flat_objects.extend(list(model_objs)) objects = flat_objects return objects
def run(): ######################### # import schema import schema as address_book PersonType = address_book.Person.type.enum_class ######################### # Create companies apple = address_book.Company(name='Apple', url='https://www.apple.com/', address=address_book.Address( street='10600 N Tantau Ave', city='Cupertino', state='CA', zip_code='95014', country='US')) facebook = address_book.Company(name='Facebook', url='https://www.facebook.com/', address=address_book.Address( street='1 Hacker Way #15', city='Menlo Park', state='CA', zip_code='94025', country='US')) google = address_book.Company(name='Google', url='https://www.google.com/', address=address_book.Address( street='1600 Amphitheatre Pkwy', city='Mountain View', state='CA', zip_code='94043', country='US')) netflix = address_book.Company(name='Netflix', url='https://www.netflix.com/', address=address_book.Address( street='100 Winchester Cir', city='Los Gatos', state='CA', zip_code='95032', country='US')) companies = [apple, facebook, google, netflix] ######################### # Create CEOs cook = address_book.Person(name='Tim Cook', type=PersonType.business, company=apple, email_address='*****@*****.**', phone_number='408-996-1010', address=apple.address) hastings = address_book.Person(name='Reed Hastings', type=PersonType.business, company=netflix, email_address='*****@*****.**', phone_number='408-540-3700', address=netflix.address) pichai = address_book.Person(name='Sundar Pichai', type=PersonType.business, company=google, email_address='*****@*****.**', phone_number='650-253-0000', address=google.address) zuckerberg = address_book.Person(name='Mark Zuckerberg', type=PersonType.family, company=facebook, email_address='*****@*****.**', phone_number='650-543-4800', address=facebook.address) ceos = [cook, hastings, pichai, zuckerberg] ######################### # Validate address book import obj_tables errors = obj_tables.Validator().run(companies + ceos) assert errors is None ######################### # Get a property of a company assert facebook.url == 'https://www.facebook.com/' ######################### # Edit a property of a company facebook.url = 'https://about.fb.com/' ######################### # Export address book import obj_tables.io import os import tempfile dirname = tempfile.mkdtemp() filename_xlsx = os.path.join(dirname, 'address_book.xlsx') obj_tables.io.Writer().run( filename_xlsx, companies + ceos, models=[address_book.Company, address_book.Person]) ######################### # Import address book objects = obj_tables.io.Reader().run( filename_xlsx, models=[address_book.Company, address_book.Person], group_objects_by_model=False, ignore_sheet_order=True) ######################### # Chek if two CEOs are semantically equivalent zuckerberg_copy = next(el for el in objects if isinstance(el, address_book.Person) and el.name == 'Mark Zuckerberg') assert zuckerberg_copy.is_equal(zuckerberg) assert zuckerberg_copy.difference(zuckerberg) == '' ######################### # Cleanup temporary directory import shutil shutil.rmtree(dirname)
def test_successful_roundtrip(self): """ Args: species_coefficient_creation_method (:obj:`str`): name of method to use to get or create an instance of :obj:`wc_lang.SpeciesCoefficient` """ model = Model(id='test_model', version='0.0.0') comp = model.compartments.create(id='compartment_1') comp.init_density = model.parameters.create( id='density_compartment_1', value=1100, units=unit_registry.parse_units('g l^-1')) species_type_1 = model.species_types.create( id='species_type_1', structure=ChemicalStructure( empirical_formula=EmpiricalFormula('CHO'), charge=1)) species_type_2 = model.species_types.create( id='species_type_2', structure=ChemicalStructure( empirical_formula=EmpiricalFormula('C3H3O3'), charge=3)) species_1 = comp.species.create(species_type=species_type_1, model=model) species_2 = comp.species.create(species_type=species_type_2, model=model) species_1.id = species_1.gen_id() species_2.id = species_2.gen_id() submdl = model.submodels.create(id='submodel_1') # create a DistributionInitConcentration so that Species are provided to ExpressionAttribute.deserialize() species_1.distribution_init_concentration = DistributionInitConcentration( model=model, mean=1, units=unit_registry.parse_units('M')) species_1.distribution_init_concentration.id = species_1.distribution_init_concentration.gen_id( ) objects = {Species: {}} objects[Species][species_1.id] = species_1 observable_1 = Expression.make_obj(model, Observable, 'observable_1', species_1.id, objects) objects = {Observable: {'observable_1': observable_1}} observable_2 = Expression.make_obj(model, Observable, 'observable_2', 'obs_1', objects) param_1 = model.parameters.create( id='param_1', value=1., units=unit_registry.parse_units('dimensionless')) param_2 = model.parameters.create( id='param_2', value=1., units=unit_registry.parse_units('s^-1')) objects = { Parameter: { 'param_1': param_1, 'param_2': param_2, }, } func_1 = Expression.make_obj(model, Function, 'func_1', 'param_1', objects) func_1.units = unit_registry.parse_units('dimensionless') rxn_species_coeffs = [ species_1.species_coefficients.get_or_create(coefficient=-3.), species_2.species_coefficients.get_or_create(coefficient=1.), ] rxn = submdl.reactions.create(id='reaction_1', model=model) rxn.participants.extend(rxn_species_coeffs) rl = rxn.rate_laws.create(direction=RateLawDirection.forward, units=unit_registry.parse_units('s^-1'), model=model) rl.id = rl.gen_id() rl.expression, error = RateLawExpression.deserialize( 'param_2', objects) self.assertEqual(error, None) errors = obj_tables.Validator().run(model, get_related=True) self.assertEqual(errors, None, str(errors)) filename = os.path.join(self.tmp_dir, 'model.xlsx') Writer().run(filename, model, data_repo_metadata=False) model_2 = Reader().run(filename)[Model][0] self.assertEqual(model.difference(model_2), '') self.assertTrue(model_2.is_equal(model)) self.assertTrue(model.is_equal(model_2))
def test_write_error(self): model = Model(id='test_model', version='0.0.0') comp = model.compartments.create(id='compartment_1') comp.init_density = model.parameters.create( id='density_compartment_1', value=1100, units=unit_registry.parse_units('g l^-1')) species_type_1 = model.species_types.create( id='species_type_1', structure=ChemicalStructure( empirical_formula=EmpiricalFormula('CHO'), charge=1)) species_type_2 = model.species_types.create( id='species_type_2', structure=ChemicalStructure( empirical_formula=EmpiricalFormula('C2H2O2'), charge=2)) species_1 = comp.species.create(species_type=species_type_1, id='', model=model) species_2 = comp.species.create(species_type=species_type_2, id='', model=model) submdl = model.submodels.create(id='submodel_1') # create a DistributionInitConcentration so that Species are provided to ExpressionAttribute.deserialize() species_1.distribution_init_concentration = DistributionInitConcentration( model=model, mean=1, units=unit_registry.parse_units('M')) species_1.distribution_init_concentration.id = species_1.distribution_init_concentration.gen_id( ) objects = {Species: {}} objects[Species][species_1.id] = species_1 observable_1 = Expression.make_obj(model, Observable, 'observable_1', species_1.id, objects) rxn_species_coeffs = [ species_1.species_coefficients.create(coefficient=-2.), species_2.species_coefficients.create(coefficient=1.), ] rxn = submdl.reactions.create(id='reaction_1', model=model) rxn.participants.extend(rxn_species_coeffs) rl = rxn.rate_laws.create(direction=RateLawDirection.forward, units=unit_registry.parse_units('s^-1'), model=model) rl.id = rl.gen_id() param_1 = model.parameters.create( id='param_1', value=1., units=unit_registry.parse_units('s^-1')) rl.expression, error = RateLawExpression.deserialize( 'param_1', {Parameter: { 'param_1': param_1 }}) self.assertEqual(error, None) errors = obj_tables.Validator().run(model, get_related=True) self.assertNotEqual(errors, None) filename = os.path.join(self.tmp_dir, 'model.xlsx') with pytest.warns(obj_tables.io.IoWarning, match='objects are not valid'): Writer().run(filename, model, data_repo_metadata=False) with self.assertRaisesRegex(ValueError, re.escape('contains error(s)')): Reader().run(filename)