From 0b2a2111568f956549a2c01e179952acc321d38a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= <sybren@stuvel.eu>
Date: Tue, 31 Jan 2017 18:03:57 +0100
Subject: [PATCH] Server: added TaskManager.api_find_job_enders() function for
 Francesco

---
 packages/flamenco/flamenco/tasks/__init__.py | 30 +++++++++++
 packages/flamenco/tests/test_task_manager.py | 52 +++++++++++++++++++-
 2 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/packages/flamenco/flamenco/tasks/__init__.py b/packages/flamenco/flamenco/tasks/__init__.py
index 4c8333d6..ef5791cb 100644
--- a/packages/flamenco/flamenco/tasks/__init__.py
+++ b/packages/flamenco/flamenco/tasks/__init__.py
@@ -107,6 +107,36 @@ class TaskManager(object):
         task.patch({'op': 'set-task-status',
                     'status': new_status}, api=api)
 
+    def api_find_job_enders(self, job_id):
+        """Returns a list of tasks that could be the last tasks of a job.
+
+        In other words, returns all tasks that are not a parent of other tasks.
+
+        :returns: list of task IDs
+        :rtype: list
+        """
+
+        from flamenco import current_flamenco
+
+        tasks_coll = current_flamenco.db('tasks')
+
+        # Get the distinct set of tasks used as parents.
+        parent_tasks = tasks_coll.aggregate([
+            {'$match': {'job': job_id}},
+            {'$project': {'parents': 1}},
+            {'$unwind': {'path': '$parents'}},
+            {'$group': {'_id': '$parents'}},
+        ])
+        parent_ids = [t['_id'] for t in parent_tasks]
+
+        # Get all the tasks that do not have such an ID.
+        tasks = tasks_coll.find({'job': job_id,
+                                 '_id': {'$nin': parent_ids}},
+                                projection={'_id': 1})
+
+        tids = [t['_id'] for t in tasks]
+        return tids
+
 
 def setup_app(app):
     from . import eve_hooks, patch
diff --git a/packages/flamenco/tests/test_task_manager.py b/packages/flamenco/tests/test_task_manager.py
index 1e9bc238..2a7570bc 100644
--- a/packages/flamenco/tests/test_task_manager.py
+++ b/packages/flamenco/tests/test_task_manager.py
@@ -34,7 +34,7 @@ class TaskManagerTest(AbstractFlamencoTest):
                     commands.Sleep(time_in_seconds=3),
                 ],
                 'sleep-1-13',
-            ),
+            )
 
         # Now test the database contents.
         with self.app.test_request_context():
@@ -57,3 +57,53 @@ class TaskManagerTest(AbstractFlamencoTest):
                     u'time_in_seconds': 3,
                 }
             }, dbtask['commands'][1])
+
+    def test_api_find_jobfinal_tasks(self):
+        from pillar.api.utils.authentication import force_cli_user
+        from flamenco.job_compilers import commands
+
+        manager, _, _ = self.create_manager_service_account()
+
+        with self.app.test_request_context():
+            force_cli_user()
+            job_doc = self.jmngr.api_create_job(
+                'test job',
+                u'Wörk wørk w°rk.',
+                'sleep', {
+                    'frames': '12-18, 20-22',
+                    'chunk_size': 7,
+                    'time_in_seconds': 3,
+                },
+                self.proj_id,
+                ctd.EXAMPLE_PROJECT_OWNER_ID,
+                manager['_id'],
+            )
+            job_id = job_doc['_id']
+
+            # Find the tasks created so far, use them as parents.
+            tasks = self.flamenco.db('tasks').find({'job': job_id},
+                                                   projection={'_id': 1})
+            task_ids = [t['_id'] for t in tasks]
+
+            # dependent task that is used as a single parent.
+            taskid1 = self.tmngr.api_create_task(
+                job_doc, [commands.Echo(message=u'ẑžƶźz')], 'zzz 1', parents=task_ids,
+            )
+
+            # task dependent on multiple tasks that is not used as a parent.
+            taskid2 = self.tmngr.api_create_task(
+                job_doc, [commands.Echo(message=u'ẑžƶźz')], 'zzz 2', parents=task_ids,
+            )
+
+            # task dependent on a single task that is not used as a parent.
+            taskid3 = self.tmngr.api_create_task(
+                job_doc, [commands.Echo(message=u'ẑžƶźz')], 'zzz 3', parents=[taskid1],
+            )
+
+            # independent task
+            taskid4 = self.tmngr.api_create_task(
+                job_doc, [commands.Echo(message=u'ẑžƶźz')], 'zzz 4',
+            )
+
+            job_enders = self.tmngr.api_find_job_enders(job_id)
+            self.assertEqual({taskid2, taskid3, taskid4}, set(job_enders))
-- 
GitLab