Repository: Duke-GCB/bespin-cli

Issue:
Allow listing all workflow versions
Allow users to see all version of workflows so the can pick older tags.
Initially we thought users could just change the `v#` portion of the tags, but due to changes in the suffix portion of tags this is not possible.

Discussion:

Patch:
diff --git a/bespin/argparser.py b/bespin/argparser.py
index 5fc8fa3..f97d735 100644
--- a/bespin/argparser.py
+++ b/bespin/argparser.py
@@ -40,6 +40,8 @@ class ArgParser(object):
         jobs_subparser = jobs_parser.add_subparsers()
 
         list_jobs_parser = jobs_subparser.add_parser('list', description='list workflows')
+        list_jobs_parser.add_argument('-a', '--all', action='store_true',
+                                      help='show all workflow versions instead of just the most recent.')
         list_jobs_parser.set_defaults(func=self._run_list_workflows)
 
     def _create_job_parser(self, subparsers):
@@ -79,8 +81,8 @@ class ArgParser(object):
     def _run_list_jobs(self, _):
         self.target_object.jobs_list()
 
-    def _run_list_workflows(self, _):
-        self.target_object.workflows_list()
+    def _run_list_workflows(self, args):
+        self.target_object.workflows_list(all_versions=args.all)
 
     def _run_init_job(self, args):
         self.target_object.init_job(args.tag, args.outfile)
diff --git a/bespin/commands.py b/bespin/commands.py
index 8f7271d..c8c1fb6 100644
--- a/bespin/commands.py
+++ b/bespin/commands.py
@@ -44,6 +44,7 @@ USER_PLACEHOLDER_DICT = {
     }
 }
 
+
 class Commands(object):
     """
     Commands run based on command line input.
@@ -61,12 +62,13 @@ class Commands(object):
         config = ConfigFile().read_or_create_config()
         return BespinApi(config, user_agent_str=self.user_agent_str)
 
-    def workflows_list(self):
+    def workflows_list(self, all_versions):
         """
         Print out a table of workflows/questionnaires
+        :param all_versions: bool: when true show all versions otherwise show most recent
         """
         api = self._create_api()
-        workflow_data = WorkflowDetails(api)
+        workflow_data = WorkflowDetails(api, all_versions)
         print(Table(workflow_data.column_names, workflow_data.get_column_data()))
 
     def jobs_list(self):
@@ -167,10 +169,11 @@ class WorkflowDetails(object):
     """
     Creates column data based on workflows/questionnaires
     """
-    TAG_COLUMN_NAME = "latest version tag"
+    TAG_COLUMN_NAME = "version tag"
 
-    def __init__(self, api):
+    def __init__(self, api, all_versions):
         self.api = api
+        self.all_versions = all_versions
         self.column_names = ["id", "name", self.TAG_COLUMN_NAME]
 
     def get_column_data(self):
@@ -181,10 +184,13 @@ class WorkflowDetails(object):
         data = []
         for workflow in self.api.workflows_list():
             if len(workflow['versions']):
-                latest_version = workflow['versions'][-1]
-                for questionnaire in self.api.questionnaires_list(workflow_version=latest_version):
-                    workflow[self.TAG_COLUMN_NAME] = questionnaire['tag']
-                    data.append(workflow)
+                versions = workflow['versions']
+                if not self.all_versions:
+                    versions = versions[-1:]
+                for version in versions:
+                    for questionnaire in self.api.questionnaires_list(workflow_version=version):
+                        workflow[self.TAG_COLUMN_NAME] = questionnaire['tag']
+                        data.append(dict(workflow))
         return data
 
 


Test patch:
diff --git a/bespin/test_argparse.py b/bespin/test_argparse.py
index 1f1cd6e..1af15be 100644
--- a/bespin/test_argparse.py
+++ b/bespin/test_argparse.py
@@ -10,9 +10,17 @@ class ArgParserTestCase(TestCase):
         self.target_object = Mock()
         self.arg_parser = ArgParser(version_str='1.0', target_object=self.target_object)
 
-    def test_workflows_list(self):
+    def test_workflows_list_current_versions(self):
         self.arg_parser.parse_and_run_commands(["workflows", "list"])
-        self.target_object.workflows_list.assert_called()
+        self.target_object.workflows_list.assert_called_with(all_versions=False)
+
+    def test_workflows_list_all_versions(self):
+        self.arg_parser.parse_and_run_commands(["workflows", "list", "--all"])
+        self.target_object.workflows_list.assert_called_with(all_versions=True)
+
+    def test_workflows_list_all_versions_short_flag(self):
+        self.arg_parser.parse_and_run_commands(["workflows", "list", "-a"])
+        self.target_object.workflows_list.assert_called_with(all_versions=True)
 
     def test_init_job(self):
         self.arg_parser.parse_and_run_commands(["jobs", "init", "--tag", "exome/v1/human"])
diff --git a/bespin/test_commands.py b/bespin/test_commands.py
index e3756c8..5bd78a8 100644
--- a/bespin/test_commands.py
+++ b/bespin/test_commands.py
@@ -18,13 +18,30 @@ class CommandsTestCase(TestCase):
     @patch('bespin.commands.WorkflowDetails')
     @patch('bespin.commands.Table')
     @patch('bespin.commands.print')
-    def test_workflows_list(self, mock_print, mock_table, mock_workflow_details, mock_bespin_api, mock_config_file):
+    def test_workflows_list_latest_versions(self, mock_print, mock_table, mock_workflow_details, mock_bespin_api,
+                                            mock_config_file):
         commands = Commands(self.version_str, self.user_agent_str)
-        commands.workflows_list()
+        commands.workflows_list(all_versions=False)
         workflow_details = mock_workflow_details.return_value
         mock_table.assert_called_with(workflow_details.column_names,
                                       workflow_details.get_column_data.return_value)
         mock_print.assert_called_with(mock_table.return_value)
+        mock_workflow_details.assert_called_with(mock_bespin_api.return_value, False)
+
+    @patch('bespin.commands.ConfigFile')
+    @patch('bespin.commands.BespinApi')
+    @patch('bespin.commands.WorkflowDetails')
+    @patch('bespin.commands.Table')
+    @patch('bespin.commands.print')
+    def test_workflows_list_all_versions(self, mock_print, mock_table, mock_workflow_details, mock_bespin_api,
+                                         mock_config_file):
+        commands = Commands(self.version_str, self.user_agent_str)
+        commands.workflows_list(all_versions=True)
+        workflow_details = mock_workflow_details.return_value
+        mock_table.assert_called_with(workflow_details.column_names,
+                                      workflow_details.get_column_data.return_value)
+        mock_print.assert_called_with(mock_table.return_value)
+        mock_workflow_details.assert_called_with(mock_bespin_api.return_value, True)
 
     @patch('bespin.commands.ConfigFile')
     @patch('bespin.commands.BespinApi')
@@ -156,9 +173,9 @@ class WorkflowDetailsTestCase(TestCase):
         mock_api.questionnaires_list.return_value = [
             {'tag': 'exome/v2/human'}
         ]
-        details = WorkflowDetails(mock_api)
+        details = WorkflowDetails(mock_api, all_versions=False)
         expected_data = [{'id': 1,
-                          'latest version tag': 'exome/v2/human',
+                          'version tag': 'exome/v2/human',
                           'name': 'exome',
                           'versions': [1, 2]}]
         column_data = details.get_column_data()
@@ -166,12 +183,50 @@ class WorkflowDetailsTestCase(TestCase):
         self.assertEqual(column_data, expected_data)
         mock_api.questionnaires_list.assert_called_with(workflow_version=2)
 
-    def test_ignores_workflows_without_versions(self):
+        mock_api.questionnaires_list.reset_mock()
+        mock_api.questionnaires_list.side_effect = [
+            [{'tag': 'exome/v1/human'}],
+            [{'tag': 'exome/v2/human'}]
+        ]
+        details = WorkflowDetails(mock_api, all_versions=True)
+        expected_data = [
+            {
+                'id': 1,
+                'version tag': 'exome/v1/human',
+                'name': 'exome',
+                'versions': [1, 2]
+            },
+            {
+                'id': 1,
+                'version tag': 'exome/v2/human',
+                'name': 'exome',
+                'versions': [1, 2]
+            },
+        ]
+        column_data = details.get_column_data()
+        self.assertEqual(len(column_data), 2)
+        self.assertEqual(column_data, expected_data)
+        mock_api.questionnaires_list.assert_has_calls([
+            call(workflow_version=1),
+            call(workflow_version=2),
+        ])
+
+    def test_ignores_workflows_without_versions_when_latest(self):
+        mock_api = Mock()
+        mock_api.workflows_list.return_value = [
+            {'id': 1, 'name': 'no-versions', 'versions': []},
+        ]
+        details = WorkflowDetails(mock_api, all_versions=False)
+        column_data = details.get_column_data()
+        self.assertEqual(len(column_data), 0)
+        mock_api.questionnaires_list.assert_not_called()
+
+    def test_ignores_workflows_without_versions_when_all(self):
         mock_api = Mock()
         mock_api.workflows_list.return_value = [
             {'id': 1, 'name': 'no-versions', 'versions': []},
         ]
-        details = WorkflowDetails(mock_api)
+        details = WorkflowDetails(mock_api, all_versions=True)
         column_data = details.get_column_data()
         self.assertEqual(len(column_data), 0)
         mock_api.questionnaires_list.assert_not_called()