Skip to content

Commit

Permalink
Merge pull request #4165 from opensafely-core/Jongmassey/Show-most-re…
Browse files Browse the repository at this point in the history
…cently-active-workspaces-on-yours-page

Show 5 most recently active workspaces on home page
"Recently active" being defined as the most recently 
modified , or created JobRequest for a workspace.
  • Loading branch information
Jongmassey committed Mar 1, 2024
2 parents 17a371e + ad70af7 commit 21f10ce
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 5 deletions.
21 changes: 20 additions & 1 deletion 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
Expand All @@ -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."""

Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion jobserver/views/index.py
Expand Up @@ -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 = {
Expand Down
28 changes: 28 additions & 0 deletions tests/unit/jobserver/models/test_workspace.py
Expand Up @@ -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
9 changes: 6 additions & 3 deletions tests/unit/jobserver/views/test_index.py
Expand Up @@ -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)

Expand All @@ -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
Expand Down

0 comments on commit 21f10ce

Please sign in to comment.