Skip to content

Commit

Permalink
Display the current pipeline run step in the run status tag UI #300 (#…
Browse files Browse the repository at this point in the history
…558)

Signed-off-by: Thomas Druez <tdruez@nexb.com>
  • Loading branch information
tdruez authored Nov 7, 2022
1 parent 3d77795 commit cc6cc59
Show file tree
Hide file tree
Showing 9 changed files with 59 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ v31.1.0 (unreleased)
- Generate CycloneDX SBOM (Software Bill of Materials) as a new downloadable output.
https://github.com/nexB/scancode.io/issues/389

- Display the current active step of a running pipeline in the "Pipeline" section of
the project details view, inside the run status tag.
https://github.com/nexB/scancode.io/issues/300

- Refine the fields ordering in API Serializers based on the toolkit order.
https://github.com/nexB/scancode.io/issues/546

Expand Down
18 changes: 18 additions & 0 deletions scanpipe/migrations/0026_run_current_step.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.1.2 on 2022-11-02 12:41

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("scanpipe", "0025_remove_discoveredpackage_last_modified_date_and_more"),
]

operations = [
migrations.AddField(
model_name="run",
name="current_step",
field=models.CharField(blank=True, max_length=256),
),
]
1 change: 1 addition & 0 deletions scanpipe/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1260,6 +1260,7 @@ class Run(UUIDPKModel, ProjectRelatedModel, AbstractTaskFieldsModel):
created_date = models.DateTimeField(auto_now_add=True, db_index=True)
scancodeio_version = models.CharField(max_length=30, blank=True)
description = models.TextField(blank=True)
current_step = models.CharField(max_length=256, blank=True)
log = models.TextField(blank=True, editable=False)

objects = RunQuerySet.as_manager()
Expand Down
14 changes: 11 additions & 3 deletions scanpipe/pipelines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def get_graph(cls):
@classmethod
def get_info(cls):
"""
Returns a dictctionary of combined data about the current pipeline.
Returns a dictionary of combined data about the current pipeline.
"""
return {
"description": cls.get_doc(),
Expand All @@ -106,9 +106,16 @@ def log(self, message):

def execute(self):
self.log(f"Pipeline [{self.pipeline_name}] starting")
steps = self.get_steps()
steps_count = len(steps)

for step in self.get_steps():
self.log(f"Step [{step.__name__}] starting")
for current_index, step in enumerate(steps, start=1):
step_name = step.__name__

# The `current_step` value is saved in the DB during the `self.log` call.
self.run.current_step = f"{current_index}/{steps_count} {step_name}"[:256]

self.log(f"Step [{step_name}] starting")
start_time = timeit.default_timer()

try:
Expand All @@ -121,6 +128,7 @@ def execute(self):
run_time = timeit.default_timer() - start_time
self.log(f"Step [{step.__name__}] completed in {run_time:.2f} seconds")

self.run.current_step = ""
self.log(f"Pipeline completed")

return 0, ""
Expand Down
2 changes: 1 addition & 1 deletion scanpipe/templates/scanpipe/includes/project_inputs.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Inputs
</p>
{% for input in inputs_with_source %}
<div class="panel-block is-flex is-justify-content-space-between dropdown is-hoverable is-up is-cursor-help">
<div class="panel-block is-justify-content-space-between dropdown is-hoverable is-up is-cursor-help">
<div class="break-all pr-1">
<div class="panel-icon pt-1">
<div class="dropdown-trigger">
Expand Down
4 changes: 2 additions & 2 deletions scanpipe/templates/scanpipe/includes/project_pipelines.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
Pipelines
</p>
{% for run in project.runs.all %}
<a class="panel-block modal-button is-flex is-justify-content-space-between" data-target="run-detail-modal" data-uuid="{{ run.uuid }}" aria-haspopup="true">
<a class="panel-block modal-button is-justify-content-space-between" data-target="run-detail-modal" data-uuid="{{ run.uuid }}" aria-haspopup="true">
<span class="mr-1">{{ run.pipeline_name }}</span>
{% include "scanpipe/includes/run_status_tag.html" with run=run only %}
{% include "scanpipe/includes/run_status_tag.html" with run=run display_current_step=True only %}
</a>
{% endfor %}
<div class="panel-block">
Expand Down
13 changes: 10 additions & 3 deletions scanpipe/templates/scanpipe/includes/run_status_tag.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
{% if run.status == run.Status.RUNNING or run.status == run.Status.QUEUED %}
<span class="tag is-info" hx-get="{% url 'run_status' run.uuid %}?current_status={{ run.status }}" hx-trigger="load delay:10s" hx-swap="outerHTML">
<span class="tag is-info"
hx-get="{% url 'run_status' run.uuid %}?current_status={{ run.status }}{% if display_current_step %}&display_current_step={{ display_current_step }}{% endif %}"
hx-trigger="load delay:10s"
hx-swap="outerHTML"
>
{% if run.status == run.Status.RUNNING %}
Running <i class="fas fa-spinner fa-pulse ml-1" aria-hidden="true"></i>
<i class="fas fa-spinner fa-pulse mr-1" aria-hidden="true"></i>Running
{% if display_current_step and run.current_step %}
{{ run.current_step|truncatechars:30 }}
{% endif %}
{% elif run.status == run.Status.QUEUED %}
Queued <i class="fas fa-clock ml-1"></i>
<i class="fas fa-clock mr-1"></i>Queued
{% endif %}
</span>
{% elif run.status == run.Status.SUCCESS %}
Expand Down
12 changes: 10 additions & 2 deletions scanpipe/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,19 +386,27 @@ def test_scanpipe_views_run_status_view(self):
run.set_task_queued()
run.refresh_from_db()
response = self.client.get(url)
expected = 'Queued <i class="fas fa-clock ml-1"></i>'
expected = '<i class="fas fa-clock mr-1"></i>Queued'
self.assertContains(response, expected)
self.assertContains(response, f'hx-get="{url}?current_status={run.status}"')

run.current_step = "1/2 Step A"
run.set_task_started(run.pk)
run.refresh_from_db()
response = self.client.get(url)
expected = (
'Running <i class="fas fa-spinner fa-pulse ml-1" aria-hidden="true"></i>'
'<i class="fas fa-spinner fa-pulse mr-1" aria-hidden="true"></i>Running'
)
self.assertContains(response, expected)
self.assertContains(response, f'hx-get="{url}?current_status={run.status}"')

response = self.client.get(url, data={"display_current_step": True})
expected = (
f'hx-get="{url}?current_status={run.status}&display_current_step=True"'
)
self.assertContains(response, expected)
self.assertContains(response, "1/2 Step A")

run.set_task_ended(exitcode=1)
response = self.client.get(url)
expected = '<span class="tag is-danger">Failure</span>'
Expand Down
2 changes: 2 additions & 0 deletions scanpipe/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,8 @@ def run_status_view(request, uuid):
if current_status and current_status != run.status:
context["status_changed"] = True

context["display_current_step"] = request.GET.get("display_current_step")

return render(request, template, context)


Expand Down

0 comments on commit cc6cc59

Please # to comment.