Skip to content

Commit

Permalink
Add a QuerySet for the Run model #22
Browse files Browse the repository at this point in the history
Signed-off-by: Thomas Druez <tdruez@nexb.com>
  • Loading branch information
tdruez committed Sep 24, 2020
1 parent 92ee4cd commit 68e7c2b
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 19 deletions.
46 changes: 42 additions & 4 deletions scanpipe/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ def __str__(self):
return self.name

def save(self, *args, **kwargs):
"""
Setup the workspace directories on project creation.
"""
if not self.work_directory:
self.work_directory = get_project_work_directory(self)
self.setup_work_directory()
Expand Down Expand Up @@ -265,6 +268,9 @@ def input_root(self):
]

def add_input_file(self, file_object):
"""
Write the provided `file_object` to this project input/ directory.
"""
filename = file_object.name
file_path = Path(self.input_path / filename)

Expand All @@ -273,14 +279,27 @@ def add_input_file(self, file_object):
f.write(chunk)

def add_pipeline(self, pipeline):
"""
Create a new Run instance with the provided `pipeline` on this project.
"""
description = get_pipeline_doc(pipeline)
return Run.objects.create(
project=self, pipeline=pipeline, description=description
)

def get_next_run(self):
"""
Return the next non-executed Run instance assigned to this project.
"""
with suppress(ObjectDoesNotExist):
return self.runs.filter(task_id__isnull=True).earliest("created_date")
return self.runs.not_started().earliest("created_date")

def get_latest_failed_run(self):
"""
Return the latest failed Run instance of this project.
"""
with suppress(ObjectDoesNotExist):
return self.runs.failed().latest("created_date")


class ProjectRelatedQuerySet(models.QuerySet):
Expand All @@ -297,7 +316,7 @@ class ProjectRelatedModel(models.Model):
Project, related_name="%(class)ss", on_delete=models.CASCADE, editable=False
)

objects = models.Manager.from_queryset(ProjectRelatedQuerySet)()
objects = ProjectRelatedQuerySet.as_manager()

class Meta:
abstract = True
Expand Down Expand Up @@ -352,7 +371,7 @@ def check(cls, **kwargs):
@classmethod
def _check_project_field(cls, **kwargs):
"""
Check if `project` field is defined.
Check if `project` field is declared on the model.
"""

fields = [f.name for f in cls._meta.local_fields]
Expand All @@ -368,11 +387,30 @@ def _check_project_field(cls, **kwargs):
return []


class RunQuerySet(models.QuerySet):
def started(self):
return self.filter(task_start_date__isnull=False)

def not_started(self):
return self.filter(task_start_date__isnull=True)

def executed(self):
return self.filter(task_end_date__isnull=False)

def succeed(self):
return self.filter(task_exitcode=0)

def failed(self):
return self.filter(task_exitcode__gt=0)


class Run(UUIDPKModel, ProjectRelatedModel, AbstractTaskFieldsModel):
pipeline = models.CharField(max_length=1024)
created_date = models.DateTimeField(auto_now_add=True, db_index=True)
description = models.TextField(blank=True)

objects = RunQuerySet.as_manager()

class Meta:
ordering = ["created_date"]

Expand Down Expand Up @@ -538,7 +576,7 @@ class Type(models.TextChoices):
help_text=_("Descriptive file type for this resource."),
)

objects = models.Manager.from_queryset(CodebaseResourceQuerySet)()
objects = CodebaseResourceQuerySet.as_manager()

class Meta:
unique_together = (("project", "path"),)
Expand Down
85 changes: 70 additions & 15 deletions scanpipe/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class ScanPipeModelsTest(TestCase):
def setUp(self):
self.project1 = Project.objects.create(name="Analysis")

def create_run(self, **kwargs):
return Run.objects.create(project=self.project1, pipeline="pipeline", **kwargs)

def test_scanpipe_project_model_extra_data(self):
self.assertEqual({}, self.project1.extra_data)
project1_from_db = Project.objects.get(name=self.project1.name)
Expand Down Expand Up @@ -113,21 +116,47 @@ def test_scanpipe_project_model_add_pipeline(self):
def test_scanpipe_project_model_get_next_run(self):
self.assertEqual(None, self.project1.get_next_run())

run1 = Run.objects.create(project=self.project1, pipeline="pipeline1")
run2 = Run.objects.create(project=self.project1, pipeline="pipeline2")

run1 = self.create_run()
run2 = self.create_run()
self.assertEqual(run1, self.project1.get_next_run())
run1.task_id = 1
run1.save()

run1.task_start_date = timezone.now()
run1.save()
self.assertEqual(run2, self.project1.get_next_run())
run2.task_id = 2
run2.save()

run2.task_start_date = timezone.now()
run2.save()
self.assertEqual(None, self.project1.get_next_run())

def test_scanpipe_project_model_get_latest_failed_run(self):
self.assertEqual(None, self.project1.get_latest_failed_run())

run1 = self.create_run()
run2 = self.create_run()
self.assertEqual(None, self.project1.get_latest_failed_run())

run1.task_exitcode = 0
run1.save()
self.assertEqual(None, self.project1.get_latest_failed_run())

run1.task_exitcode = 1
run1.save()
self.assertEqual(run1, self.project1.get_latest_failed_run())

run2.task_exitcode = 0
run2.save()
self.assertEqual(run1, self.project1.get_latest_failed_run())

run2.task_exitcode = 1
run2.save()
self.assertEqual(run2, self.project1.get_latest_failed_run())

run1.task_exitcode = None
run1.save()
self.assertEqual(run2, self.project1.get_latest_failed_run())

def test_scanpipe_run_model_task_methods(self):
run1 = Run.objects.create(project=self.project1, pipeline="pipeline")
run1 = self.create_run()
self.assertFalse(run1.task_succeeded)

run1.task_exitcode = 0
Expand All @@ -139,7 +168,7 @@ def test_scanpipe_run_model_task_methods(self):
self.assertFalse(run1.task_succeeded)

def test_scanpipe_run_model_task_execution_time_property(self):
run1 = Run.objects.create(project=self.project1, pipeline="pipeline")
run1 = self.create_run()

self.assertIsNone(run1.execution_time)

Expand All @@ -152,9 +181,7 @@ def test_scanpipe_run_model_task_execution_time_property(self):
self.assertEqual(25.0, run1.execution_time)

def test_scanpipe_run_model_reset_task_values_method(self):
run1 = Run.objects.create(
project=self.project1,
pipeline="pipeline",
run1 = self.create_run(
task_id=uuid.uuid4(),
task_start_date=timezone.now(),
task_end_date=timezone.now(),
Expand All @@ -170,7 +197,7 @@ def test_scanpipe_run_model_reset_task_values_method(self):
self.assertEqual("", run1.task_output)

def test_scanpipe_run_model_set_task_started_method(self):
run1 = Run.objects.create(project=self.project1, pipeline="pipeline")
run1 = self.create_run()

task_id = uuid.uuid4()
run1.set_task_started(task_id)
Expand All @@ -181,7 +208,7 @@ def test_scanpipe_run_model_set_task_started_method(self):
self.assertFalse(run1.task_end_date)

def test_scanpipe_run_model_set_task_ended_method(self):
run1 = Run.objects.create(project=self.project1, pipeline="pipeline")
run1 = self.create_run()

run1.set_task_ended(exitcode=0, output="output")

Expand All @@ -191,7 +218,7 @@ def test_scanpipe_run_model_set_task_ended_method(self):
self.assertTrue(run1.task_end_date)

def test_scanpipe_run_model_get_run_id_method(self):
run1 = Run.objects.create(project=self.project1, pipeline="pipeline")
run1 = self.create_run()

self.assertIsNone(run1.get_run_id())

Expand All @@ -207,6 +234,34 @@ def test_scanpipe_run_model_get_run_id_method(self):
run1.save()
self.assertEqual("123", run1.get_run_id())

def test_scanpipe_run_model_queryset_methods(self):
now = timezone.now()

started = self.create_run(task_start_date=now)
not_started = self.create_run()
executed = self.create_run(task_start_date=now, task_end_date=now)
succeed = self.create_run(task_start_date=now, task_exitcode=0)
failed = self.create_run(task_start_date=now, task_exitcode=1)

qs = Run.objects.started()
self.assertEqual(4, len(qs))
self.assertIn(started, qs)
self.assertIn(executed, qs)
self.assertIn(succeed, qs)
self.assertIn(failed, qs)

qs = Run.objects.not_started()
self.assertEqual([not_started], list(qs))

qs = Run.objects.executed()
self.assertEqual([executed], list(qs))

qs = Run.objects.succeed()
self.assertEqual([succeed], list(qs))

qs = Run.objects.failed()
self.assertEqual([failed], list(qs))

def test_scanpipe_codebase_resource_model_methods(self):
resource = CodebaseResource.objects.create(
project=self.project1, path="filename.ext"
Expand Down

0 comments on commit 68e7c2b

Please sign in to comment.