diff --git a/tests/test_core.py b/tests/test_core.py index f250dd7..e152851 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -565,7 +565,7 @@ def test_ComplexSpeciesType(self): species2 = core.Species(species_type=prot2, compartment=comp1) species_coeff1 = core.SpeciesCoefficient(species=species1, coefficient=2) species_coeff2 = core.SpeciesCoefficient(species=species2, coefficient=3) - reaction1 = core.Reaction(participants=[species_coeff1,species_coeff2]) + reaction1 = core.Reaction(participants=[species_coeff1, species_coeff2]) complex1.formation_reaction = reaction1 self.assertEqual(complex1.get_subunits(), [prot1, prot1, prot2, prot2, prot2]) @@ -579,5 +579,6 @@ def test_ComplexSpeciesType(self): # test get_empirical_formula self.assertEqual(complex1.get_empirical_formula(), chem.EmpiricalFormula('C273H408N76O82S2')) + class ReactionParticipantAttributeTestCase(unittest.TestCase): def test_ReactionParticipantAttribute(self): pass diff --git a/tests/test_io.py b/tests/test_io.py index c3d4488..799749f 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -54,14 +54,14 @@ def test_write_read(self): seq_path = os.path.join(self.dir, 'seq.fna') writer = io.Writer() - writer.run(self.kb, core_path, seq_path) + writer.run(self.kb, core_path, seq_path, set_repo_metadata_from_path=False) reader = io.Reader() kb = reader.run(core_path, seq_path) core_path = os.path.join(self.dir, 'core2.xlsx') seq_path = os.path.join(self.dir, 'seq2.fna') - writer.run(kb, core_path, seq_path) + writer.run(kb, core_path, seq_path, set_repo_metadata_from_path=False) self.assertTrue(self.kb.is_equal(kb)) @@ -79,14 +79,14 @@ def test_write_without_cell_relationships(self): writer = io.Writer() with self.assertRaisesRegexp(ValueError, 'must be set to the instance of `Cell`'): - writer.run(self.kb, core_path, seq_path) + writer.run(self.kb, core_path, seq_path, set_repo_metadata_from_path=False) def test_write_read_sloppy(self): core_path = os.path.join(self.dir, 'core.xlsx') seq_path = os.path.join(self.dir, 'seq.fna') writer = io.Writer() - writer.run(self.kb, core_path, seq_path) + writer.run(self.kb, core_path, seq_path, set_repo_metadata_from_path=False) wb = wc_utils.workbook.io.read(core_path) row = wb['Knowledge base'].pop(0) @@ -166,7 +166,7 @@ def test_convert(self): path_seq_2 = os.path.join(self.dir, 'seq_2.fna') path_seq_3 = os.path.join(self.dir, 'seq_3.fna') - io.Writer().run(self.kb, path_core_1, path_seq_1) + io.Writer().run(self.kb, path_core_1, path_seq_1, set_repo_metadata_from_path=False) io.convert(path_core_1, path_seq_1, path_core_2, path_seq_2) kb = io.Reader().run(path_core_2, path_seq_2) @@ -184,7 +184,7 @@ def test_convert_sloppy(self): path_seq_2 = os.path.join(self.dir, 'seq_2.fna') path_seq_3 = os.path.join(self.dir, 'seq_3.fna') - io.Writer().run(self.kb, path_core_1, path_seq_1) + io.Writer().run(self.kb, path_core_1, path_seq_1, set_repo_metadata_from_path=False) wb = wc_utils.workbook.io.read(path_core_1) row = wb['Knowledge base'].pop(0) @@ -204,7 +204,7 @@ def test_convert_sloppy(self): def test_create_template(self): path_core = os.path.join(self.dir, 'core.xlsx') path_seq = os.path.join(self.dir, 'seq.fna') - io.create_template(path_core, path_seq) + io.create_template(path_core, path_seq, set_repo_metadata_from_path=False) kb = io.Reader().run(path_core, path_seq) def test_validate_implicit_relationships(self): diff --git a/tests/test_main.py b/tests/test_main.py index c12b7ec..cced1d9 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -37,7 +37,7 @@ def test_validate(self): self.assertEqual(Validator().run(kb, get_related=True), None) filename_core = path.join(self.tempdir, 'core.xlsx') filename_seq = path.join(self.tempdir, 'seq.fna') - io.Writer().run(kb, filename_core, filename_seq) + io.Writer().run(kb, filename_core, filename_seq, set_repo_metadata_from_path=False) with CaptureOutput() as capturer: with __main__.App(argv=['validate', filename_core, filename_seq]) as app: @@ -53,7 +53,7 @@ def test_validate_exception(self): self.assertNotEqual(Validator().run(kb, get_related=True), None) filename_core = path.join(self.tempdir, 'core.xlsx') filename_seq = path.join(self.tempdir, 'seq.fna') - io.Writer().run(kb, filename_core, filename_seq) + io.Writer().run(kb, filename_core, filename_seq, set_repo_metadata_from_path=False) with self.assertRaisesRegexp(ValueError, '^Knowledge base is invalid: '): with __main__.App(argv=['validate', filename_core, filename_seq]) as app: @@ -63,17 +63,17 @@ def test_difference(self): kb1 = wc_kb.KnowledgeBase(id='kb', name='KB', version='0.0.1a', wc_kb_version='0.0.0') filename_core_1 = path.join(self.tempdir, 'core1.xlsx') filename_seq_1 = path.join(self.tempdir, 'seq1.fna') - io.Writer().run(kb1, filename_core_1, filename_seq_1) + io.Writer().run(kb1, filename_core_1, filename_seq_1, set_repo_metadata_from_path=False) kb2 = wc_kb.KnowledgeBase(id='kb', name='KB', version='0.0.1a', wc_kb_version='0.0.0') filename_core_2 = path.join(self.tempdir, 'core2.xlsx') filename_seq_2 = path.join(self.tempdir, 'seq2.fna') - io.Writer().run(kb2, filename_core_2, filename_seq_2) + io.Writer().run(kb2, filename_core_2, filename_seq_2, set_repo_metadata_from_path=False) kb3 = wc_kb.KnowledgeBase(id='kb', name='KB', version='0.0.1a', wc_kb_version='0.0.1') filename_core_3 = path.join(self.tempdir, 'core3.xlsx') filename_seq_3 = path.join(self.tempdir, 'seq3.fna') - io.Writer().run(kb3, filename_core_3, filename_seq_3) + io.Writer().run(kb3, filename_core_3, filename_seq_3, set_repo_metadata_from_path=False) with CaptureOutput() as capturer: with __main__.App(argv=['difference', @@ -117,7 +117,7 @@ def test_normalize(self): filename_seq_2 = path.join(self.tempdir, 'seq-2.fna') kb = wc_kb.KnowledgeBase(id='kb', name='KB', version='0.0.1a', wc_kb_version='0.0.0') - io.Writer().run(kb, filename_core_1, filename_seq_1) + io.Writer().run(kb, filename_core_1, filename_seq_1, set_repo_metadata_from_path=False) # with same dest with __main__.App(argv=['normalize', filename_core_1, filename_seq_1]) as app: @@ -141,7 +141,7 @@ def test_convert(self): filename_out_seq = path.join(self.tempdir, 'out.seq.fna') kb = wc_kb.KnowledgeBase(id='kb', name='KB', version='0.0.1a', wc_kb_version='0.0.0') - io.Writer().run(kb, filename_in_core, filename_in_seq) + io.Writer().run(kb, filename_in_core, filename_in_seq, set_repo_metadata_from_path=False) with __main__.App(argv=['convert', filename_in_core, filename_in_seq, @@ -155,21 +155,21 @@ def test_create_template(self): filename_core = path.join(self.tempdir, 'core.xlsx') filename_seq = path.join(self.tempdir, 'seq.fna') - with __main__.App(argv=['create-template', filename_core, filename_seq]) as app: + with __main__.App(argv=['create-template', filename_core, filename_seq, '--ignore-repo-metadata']) as app: app.run() self.assertTrue(path.isfile(filename_core)) self.assertTrue(path.isfile(filename_seq)) - def test_update_wc_kb_version(self): + def test_update_version_metadata(self): filename_core = path.join(self.tempdir, 'core.xlsx') filename_seq = path.join(self.tempdir, 'seq.fna') kb = wc_kb.KnowledgeBase(id='kb', name='KB', version='0.0.1a', wc_kb_version='0.0.0') self.assertNotEqual(kb.wc_kb_version, wc_kb.__version__) - io.Writer().run(kb, filename_core, filename_seq) + io.Writer().run(kb, filename_core, filename_seq, set_repo_metadata_from_path=False) - with __main__.App(argv=['update-wc-kb-version', filename_core, filename_seq]) as app: + with __main__.App(argv=['update-version-metadata', filename_core, filename_seq, '--ignore-repo-metadata']) as app: app.run() kb = io.Reader().run(filename_core, filename_seq) diff --git a/tests/test_util.py b/tests/test_util.py index 6cb04d8..58ad7d1 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -8,6 +8,8 @@ from wc_kb import core from wc_kb import util +import shutil +import tempfile import unittest @@ -20,3 +22,26 @@ def test_get_models(self): self.assertIn(core.KnowledgeBase, util.get_models(inline=False)) self.assertIn(core.Cell, util.get_models(inline=False)) + + def test_set_git_repo_metadata_from_path(self): + kb = core.KnowledgeBase() + self.assertEqual(kb.url, '') + + util.set_git_repo_metadata_from_path(kb, path='.') + self.assertIn(kb.url, [ + 'https://github.com/KarrLab/wc_kb.git', + 'ssh://git@github.com/KarrLab/wc_kb.git', + 'git@github.com:KarrLab/wc_kb.git', + ]) + + def test_set_git_repo_metadata_from_path_error(self): + tempdir = tempfile.mkdtemp() + + kb = core.KnowledgeBase() + self.assertEqual(kb.url, '') + + with self.assertRaisesRegexp(ValueError, 'is not a Git repository'): + util.set_git_repo_metadata_from_path(kb, path=tempdir) + self.assertEqual(kb.url, '') + + shutil.rmtree(tempdir) diff --git a/wc_kb/__main__.py b/wc_kb/__main__.py index a365cef..af411db 100644 --- a/wc_kb/__main__.py +++ b/wc_kb/__main__.py @@ -121,9 +121,9 @@ def default(self): args = self.app.pargs kb = io.Reader().run(args.source_core, args.source_seq, strict=args.strict) if args.dest_core or args.dest_seq: - io.Writer().run(kb, args.dest_core, args.dest_seq) + io.Writer().run(kb, args.dest_core, args.dest_seq, set_repo_metadata_from_path=False) else: - io.Writer().run(kb, args.source_core, args.source_seq) + io.Writer().run(kb, args.source_core, args.source_seq, set_repo_metadata_from_path=False) class ConvertController(CementBaseController): @@ -159,27 +159,33 @@ class Meta: stacked_on = 'base' stacked_type = 'nested' arguments = [ - (['path_core'], dict(type=str, help='Path to save a template of the core of a knowledge base')), - (['path_seq'], dict(type=str, help='Path to save a template of the genome sequence of a knowledge base')), + (['path_core'], dict(metavar='path-core', type=str, help='Path to save a template of the core of a knowledge base')), + (['path_seq'], dict(metavar='path-seq', type=str, help='Path to save a template of the genome sequence of a knowledge base')), + (['--ignore-repo-metadata'], dict(dest='set_repo_metadata_from_path', default=True, action='store_false', + help=('If set, do not set the Git repository metadata for the knowledge base from ' + 'the parent directory of `path-core`'))), ] @expose(hide=True) def default(self): args = self.app.pargs - io.create_template(args.path_core, args.path_seq) + io.create_template(args.path_core, args.path_seq, set_repo_metadata_from_path=args.set_repo_metadata_from_path) -class UpdateWcKbVersionController(CementBaseController): - """ Update wc_kb_version of a knowledge base """ +class UpdateVersionMetadataController(CementBaseController): + """ Update version metadata of a knowledge base (URL, branch, revision, wc_kb version) """ class Meta: - label = 'update-wc-kb-version' - description = 'Update wc_kb_version of a knowledge base' + label = 'update-version-metadata' + description = 'Update version metadata of a knowledge base (URL, branch, revision, wc_kb version)' stacked_on = 'base' stacked_type = 'nested' arguments = [ (['path_core'], dict(type=str, help='Path to the core of the knowledge base')), (['path_seq'], dict(type=str, help='Path to the FASTA-formatted genome sequence of a knowledge base')), + (['--ignore-repo-metadata'], dict(dest='set_repo_metadata_from_path', default=True, action='store_false', + help=('If set, do not set the Git repository metadata for the knowledge base from ' + 'the parent directory of `path-core`'))), (['--sloppy'], dict(dest='strict', default=True, action='store_false', help='If set, do not validate the format of the knowledge base file(s)')), ] @@ -189,7 +195,7 @@ def default(self): args = self.app.pargs kb = io.Reader().run(args.path_core, args.path_seq, strict=args.strict) kb.wc_kb_version = wc_kb.__version__ - io.Writer().run(kb, args.path_core, args.path_seq) + io.Writer().run(kb, args.path_core, args.path_seq, set_repo_metadata_from_path=args.set_repo_metadata_from_path) class App(CementApp): @@ -204,7 +210,7 @@ class Meta: NormalizeController, ConvertController, CreateTemplateController, - UpdateWcKbVersionController, + UpdateVersionMetadataController, ] diff --git a/wc_kb/core.py b/wc_kb/core.py index 15ae93d..76d2fb7 100644 --- a/wc_kb/core.py +++ b/wc_kb/core.py @@ -8,7 +8,6 @@ """ from wc_utils.util import chem -import wc_utils.util.git as git import abc import Bio.Seq import Bio.SeqUtils @@ -125,10 +124,6 @@ class Meta(obj_model.core.Model.Meta): tabular_orientation = obj_model.core.TabularOrientation.column def __init__(self, **kwargs): - md = git.get_repo_metadata() - self.url = md.url - self.branch = md.branch - self.revision = md.revision super(KnowledgeBase, self).__init__(**kwargs) diff --git a/wc_kb/io.py b/wc_kb/io.py index 241e280..b5128f3 100644 --- a/wc_kb/io.py +++ b/wc_kb/io.py @@ -13,6 +13,7 @@ """ from . import core +from . import util from wc_utils.util.string import indent_forest import Bio.SeqIO import Bio.SeqRecord @@ -41,13 +42,15 @@ class Writer(object): core.Reaction, ) - def run(self, knowledge_base, core_path, seq_path): + def run(self, knowledge_base, core_path, seq_path, set_repo_metadata_from_path=True): """ Write knowledge base to file(s) Args: knowledge_base (:obj:`core.KnowledgeBase`): knowledge base core_path (:obj:`str`): path to save core knowledge base seq_path (:obj:`str`): path to save genome sequence + set_repo_metadata_from_path (:obj:`bool`, optional): if :obj:`True`, set the Git repository metadata (URL, + branch, revision) for the knowledge base from the parent directory of :obj:`core_path` Raises: :obj:`ValueError`: if any of the relationships with knowledge bases and cells are not set @@ -66,6 +69,10 @@ def run(self, knowledge_base, core_path, seq_path): if val is None or val != cell: raise ValueError('{}.{} must be set to the instance of `Cell`'.format(obj.__class__.__name__, attr.name)) + # set Git repository metadata from the parent directories of :obj:`core_path` + if set_repo_metadata_from_path: + util.set_git_repo_metadata_from_path(knowledge_base, core_path) + # gather DNA sequences dna_seqs = [] if cell: @@ -135,8 +142,8 @@ def run(self, core_path, seq_path, strict=True): Args: core_path (:obj:`str`): path to core knowledge base - seq_path (:obj:`str`): path to genome sequence - strict (:obj:`str`, optional): if :obj:`True`, validate that the the model file(s) strictly follow the + seq_path (:obj:`str`): path to genome sequence + strict (:obj:`bool`, optional): if :obj:`True`, validate that the the model file(s) strictly follow the :obj:`obj_model` serialization format: * The worksheets are in the expected order @@ -241,7 +248,7 @@ def convert(source_core, source_seq, dest_core, dest_seq, strict=True): source_seq (:obj:`str`): path to the genome sequence of the source knowledge base dest_core (:obj:`str`): path to save the converted core of the knowledge base dest_seq (:obj:`str`): path to save the converted genome sequence of the knowledge base - strict (:obj:`str`, optional): if :obj:`True`, validate that the the model file(s) strictly follow the + strict (:obj:`bool`, optional): if :obj:`True`, validate that the the model file(s) strictly follow the :obj:`obj_model` serialization format: * The worksheets are in the expected order @@ -252,15 +259,17 @@ def convert(source_core, source_seq, dest_core, dest_seq, strict=True): * There are no extra columns """ kb = Reader().run(source_core, source_seq, strict=strict) - Writer().run(kb, dest_core, dest_seq) + Writer().run(kb, dest_core, dest_seq, set_repo_metadata_from_path=False) -def create_template(core_path, seq_path): +def create_template(core_path, seq_path, set_repo_metadata_from_path=True): """ Create file with knowledge base template, including row and column headings Args: core_path (:obj:`str`): path to save temploate of core knowledge base seq_path (:obj:`str`): path to save genome sequence + set_repo_metadata_from_path (:obj:`bool`, optional): if :obj:`True`, set the Git repository metadata (URL, + branch, revision) for the knowledge base from the parent directory of :obj:`core_path` """ kb = core.KnowledgeBase(id='template', name='Template', version=wc_kb.__version__) - Writer().run(kb, core_path, seq_path) + Writer().run(kb, core_path, seq_path, set_repo_metadata_from_path=set_repo_metadata_from_path) diff --git a/wc_kb/util.py b/wc_kb/util.py index c4ae44d..d850520 100644 --- a/wc_kb/util.py +++ b/wc_kb/util.py @@ -7,8 +7,10 @@ """ from . import core +from wc_utils.util import git import obj_model.core + def get_models(inline=True): """ Get list of models @@ -20,3 +22,16 @@ def get_models(inline=True): """ return obj_model.core.get_models(module=core, inline=inline) + + +def set_git_repo_metadata_from_path(kb, path='.'): + """ Use Git to set the Git repository URL, branch, and revision metadata for a knowledge base + + Args: + kb (:obj:`core.KnowledgeBase`): knowledge base + path (:obj:`str`, optional): path to the Git repository for the knowledge base + """ + md = git.get_repo_metadata(dirname=path) + kb.url = md.url + kb.branch = md.branch + kb.revision = md.revision