Adds endpoint to get workflow stats. Adds a test for the endpoint.

This commit is contained in:
Aaron Louie 2020-03-11 14:28:53 -04:00
parent 1119bb2b6c
commit bbfe9291e0
4 changed files with 116 additions and 61 deletions

View File

@ -500,6 +500,27 @@ paths:
responses:
'204':
description: The workflow was removed
/workflow/{workflow_id}/stats:
parameters:
- name: workflow_id
in: path
required: true
description: The id of the workflow
schema:
type: integer
format: int32
get:
operationId: crc.api.stats.get_workflow_stats
summary: Stats about a given workflow
tags:
- Workflows and Tasks
responses:
'200':
description: Returns counts of complete, incomplete, and total number of tasks
content:
application/json:
schema:
$ref: "#/components/schemas/Workflow"
# /v1.0/workflow/0/task/0
/workflow/{workflow_id}/task/{task_id}:
parameters:

57
crc/api/stats.py Normal file
View File

@ -0,0 +1,57 @@
from datetime import datetime
from flask import g
from crc import session
from crc.models.stats import WorkflowStatsModel, WorkflowStatsModelSchema, TaskEventModel, TaskEventModelSchema
def get_workflow_stats(workflow_id):
schema = WorkflowStatsModelSchema()
workflow_model = session.query(WorkflowStatsModel).filter_by(id=workflow_id).first()
return schema.dump(workflow_model)
def update_workflow_stats(workflow_model, workflow_api_model):
stats = session.query(WorkflowStatsModel) \
.filter_by(study_id=workflow_model.study_id) \
.filter_by(workflow_id=workflow_model.id) \
.filter_by(workflow_spec_id=workflow_model.workflow_spec_id) \
.filter_by(spec_version=workflow_model.spec_version) \
.first()
if stats is None:
stats = WorkflowStatsModel(
study_id=workflow_model.study_id,
workflow_id=workflow_model.id,
workflow_spec_id=workflow_model.workflow_spec_id,
spec_version=workflow_model.spec_version,
)
complete_states = ['CANCELLED', 'COMPLETED']
incomplete_states = ['MAYBE', 'LIKELY', 'FUTURE', 'WAITING', 'READY']
tasks = list(workflow_api_model.user_tasks)
stats.num_tasks_total = len(tasks)
stats.num_tasks_complete = sum(1 for t in tasks if t.state in complete_states)
stats.num_tasks_incomplete = sum(1 for t in tasks if t.state in incomplete_states)
stats.last_updated = datetime.now()
session.add(stats)
session.commit()
return WorkflowStatsModelSchema().dump(stats)
def log_task_complete(workflow_model, task_id):
task_event = TaskEventModel(
study_id=workflow_model.study_id,
user_uid=g.user.uid,
workflow_id=workflow_model.id,
workflow_spec_id=workflow_model.workflow_spec_id,
spec_version=workflow_model.spec_version,
task_id=task_id,
task_state='COMPLETE',
date=datetime.now(),
)
session.add(task_event)
session.commit()
return TaskEventModelSchema().dump(task_event)

View File

@ -1,15 +1,13 @@
import uuid
from datetime import datetime
from flask import g
from crc.api.file import delete_file
from api.stats import update_workflow_stats, log_task_complete
from crc import session
from crc.api.common import ApiError, ApiErrorSchema
from crc.api.file import delete_file
from crc.models.api_models import Task, WorkflowApi, WorkflowApiSchema
from crc.models.file import FileModel
from crc.models.workflow import WorkflowModel, WorkflowSpecModelSchema, WorkflowSpecModel
from crc.services.workflow_processor import WorkflowProcessor
from crc.models.file import FileModel
from crc.models.stats import WorkflowStatsModel, WorkflowStatsModelSchema, TaskEventModel, TaskEventModelSchema
def all_specifications():
@ -102,22 +100,6 @@ def get_task(workflow_id, task_id):
return workflow.bpmn_workflow().get_task(task_id)
def log_task_complete(workflow_model, task_id):
task_event = TaskEventModel(
study_id=workflow_model.study_id,
user_uid=g.user.uid,
workflow_id=workflow_model.id,
workflow_spec_id=workflow_model.workflow_spec_id,
spec_version=workflow_model.spec_version,
task_id=task_id,
task_state='COMPLETE',
date=datetime.now(),
)
session.add(task_event)
session.commit()
return TaskEventModelSchema().dump(task_event)
def update_task(workflow_id, task_id, body):
workflow_model = session.query(WorkflowModel).filter_by(id=workflow_id).first()
processor = WorkflowProcessor(workflow_model)
@ -135,35 +117,3 @@ def update_task(workflow_id, task_id, body):
update_workflow_stats(workflow_model, workflow_api_model)
log_task_complete(workflow_model, task_id)
return WorkflowApiSchema().dump(workflow_api_model)
def update_workflow_stats(workflow_model, workflow_api_model):
stats = session.query(WorkflowStatsModel)\
.filter_by(study_id=workflow_model.study_id)\
.filter_by(workflow_id=workflow_model.id)\
.filter_by(workflow_spec_id=workflow_model.workflow_spec_id)\
.filter_by(spec_version=workflow_model.spec_version)\
.first()
if stats is None:
stats = WorkflowStatsModel(
study_id=workflow_model.study_id,
workflow_id=workflow_model.id,
workflow_spec_id=workflow_model.workflow_spec_id,
spec_version=workflow_model.spec_version,
)
complete_states = ['CANCELLED', 'COMPLETED']
incomplete_states = ['MAYBE', 'LIKELY', 'FUTURE', 'WAITING', 'READY']
tasks = list(workflow_api_model.user_tasks)
stats.num_tasks_total = len(tasks)
stats.num_tasks_complete = sum(1 for t in tasks if t.state in complete_states)
stats.num_tasks_incomplete = sum(1 for t in tasks if t.state in incomplete_states)
stats.last_updated = datetime.now()
session.add(stats)
session.commit()
return WorkflowStatsModelSchema().dump(stats)

View File

@ -4,10 +4,10 @@ import os
from crc import session, app
from crc.models.api_models import WorkflowApiSchema, Task
from crc.models.file import FileModelSchema
from crc.models.stats import WorkflowStatsModel, TaskEventModel
from crc.models.study import StudyModel
from crc.models.workflow import WorkflowSpecModelSchema, WorkflowModel, WorkflowStatus
from crc.services.workflow_processor import WorkflowProcessor
from crc.models.stats import WorkflowStatsModel, TaskEventModel
from tests.base_test import BaseTest
@ -240,3 +240,30 @@ class TestTasksApi(BaseTest):
workflow_api = self.get_workflow_api(workflow, hard_reset=True)
self.assertTrue(workflow_api.spec_version.startswith("v2 "))
self.assertTrue(workflow_api.is_latest_spec)
def test_get_workflow_stats(self):
self.load_example_data()
workflow = self.create_workflow('exclusive_gateway')
response_before = self.app.get('/v1.0/workflow/%i/stats' % workflow.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(response_before)
db_stats_before = session.query(WorkflowStatsModel).filter_by(workflow_id=workflow.id).first()
self.assertIsNone(db_stats_before)
# Start the workflow.
tasks = self.get_workflow_api(workflow).user_tasks
self.complete_form(workflow, tasks[0], {"has_bananas": True})
response_after = self.app.get('/v1.0/workflow/%i/stats' % workflow.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(response_after)
db_stats_after = session.query(WorkflowStatsModel).filter_by(workflow_id=workflow.id).first()
self.assertIsNotNone(db_stats_after)
self.assertGreater(db_stats_after.num_tasks_complete, 0)
self.assertGreater(db_stats_after.num_tasks_incomplete, 0)
self.assertGreater(db_stats_after.num_tasks_total, 0)
self.assertEqual(db_stats_after.num_tasks_total,
db_stats_after.num_tasks_complete + db_stats_after.num_tasks_incomplete)