From ad70af72aa40033eb60edfac104af9788b1bfbfe Mon Sep 17 00:00:00 2001 From: Jon Massey Date: Thu, 29 Feb 2024 17:15:32 +0000 Subject: [PATCH] Sort homepage workspaces by most recent activity i.e. workspace updated or jobrequest created --- jobserver/models/workspace.py | 21 +++++++++++++- jobserver/views/index.py | 3 +- tests/unit/jobserver/models/test_workspace.py | 28 +++++++++++++++++++ tests/unit/jobserver/views/test_index.py | 9 ++++-- 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/jobserver/models/workspace.py b/jobserver/models/workspace.py index 29f8d2d9e..b1630d3e8 100644 --- a/jobserver/models/workspace.py +++ b/jobserver/models/workspace.py @@ -1,6 +1,7 @@ import structlog from django.db import connection, models -from django.db.models import Q +from django.db.models import Max, Q +from django.db.models.functions import Greatest from django.urls import reverse from django.utils import timezone from furl import furl @@ -9,6 +10,22 @@ logger = structlog.get_logger(__name__) +class WorkspaceQuerySet(models.QuerySet): + def with_most_recent_activity_at(self): + return ( + self.prefetch_related("job_requests") + .annotate( + last_jobrequest_created_at=Max("job_requests__created_at"), + max_updated_at=Max("updated_at"), + ) + .annotate( + most_recent_activity_at=Greatest( + "last_jobrequest_created_at", "max_updated_at" + ) + ) + ) + + class Workspace(models.Model): """Models a working directory on a Backend server.""" @@ -55,6 +72,8 @@ class Workspace(models.Model): related_name="workspaces_updated", ) + objects = WorkspaceQuerySet.as_manager() + class Meta: constraints = [ # mirror Django's validate_slug validator into the database diff --git a/jobserver/views/index.py b/jobserver/views/index.py index 5887f8a4d..a7e3a1928 100644 --- a/jobserver/views/index.py +++ b/jobserver/views/index.py @@ -49,7 +49,8 @@ def get(self, request, *args, **kwargs): is_archived=False, project__in=self.request.user.projects.all() ) .select_related("project") - .order_by("-created_at") + .with_most_recent_activity_at() + .order_by("-most_recent_activity_at") ) counts = { diff --git a/tests/unit/jobserver/models/test_workspace.py b/tests/unit/jobserver/models/test_workspace.py index c13d9be97..5d723b0b3 100644 --- a/tests/unit/jobserver/models/test_workspace.py +++ b/tests/unit/jobserver/models/test_workspace.py @@ -416,3 +416,31 @@ def test_workspace_is_interactive(): def test_workspace_str(): workspace = WorkspaceFactory(name="corellian-engineering-corporation") assert str(workspace) == "corellian-engineering-corporation" + + +def test_workspace_with_most_recent_activity_at_jobrequest(): + workspace = WorkspaceFactory() + JobRequestFactory(workspace=workspace) + + w = Workspace.objects.with_most_recent_activity_at().get(pk=workspace.pk) + + assert w.most_recent_activity_at == w.last_jobrequest_created_at + + +def test_workspace_with_most_recent_activity_at_no_jobrequest(): + workspace = WorkspaceFactory() + + w = Workspace.objects.with_most_recent_activity_at().get(pk=workspace.pk) + + assert w.most_recent_activity_at == w.updated_at + + +def test_workspace_with_most_recent_activity_at_updated_and_jobrequest(): + workspace = WorkspaceFactory() + JobRequestFactory(workspace=workspace) + workspace.purpose = "update the workspace" + workspace.save() + + w = Workspace.objects.with_most_recent_activity_at().get(pk=workspace.pk) + + assert w.most_recent_activity_at == w.updated_at diff --git a/tests/unit/jobserver/views/test_index.py b/tests/unit/jobserver/views/test_index.py index 36a80a738..c972b41c6 100644 --- a/tests/unit/jobserver/views/test_index.py +++ b/tests/unit/jobserver/views/test_index.py @@ -23,7 +23,9 @@ def test_index_authenticated( project1 = ProjectFactory() project_membership(project=project1, user=user) - workspace1 = WorkspaceFactory(project=project1) + workspace1, workspace2, workspace3, workspace4, workspace5 = ( + WorkspaceFactory.create_batch(5, project=project1) + ) JobRequestFactory(workspace=workspace1, created_by=user) JobRequestFactory(workspace=workspace1, created_by=user) @@ -36,9 +38,10 @@ def test_index_authenticated( # create a lot of objects the user has access to so we can check our limits # are working as expected - JobRequestFactory.create_batch(10, workspace=workspace1) + for w in [workspace1, workspace2, workspace3, workspace4, workspace5]: + JobRequestFactory.create_batch(10, workspace=w) project_memberships(10, user=user) - WorkspaceFactory.create_batch(10, project=project2) + WorkspaceFactory.create_batch(6, project=project2) request = rf.get("/") request.user = user