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))
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    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
Ejemplo n.º 5
0
Archivo: io.py Proyecto: KarrLab/wc_kb
    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
Ejemplo n.º 6
0
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)
Ejemplo n.º 7
0
    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))
Ejemplo n.º 8
0
    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)