(backend) annotate number of accesses on documents

The new UI will display the number of accesses on each document.

/!\ Once team accesses will be used, this will not represent the number
    of people with access anymore and will have to be improved by
    computing the number of people in each team.
This commit is contained in:
Samuel Paccoud - DINUM
2024-11-09 18:33:48 +01:00
committed by Anthony LC
parent 89d9075850
commit 774c2ce248
6 changed files with 25 additions and 6 deletions

View File

@@ -28,6 +28,7 @@ and this project adheres to
## Added ## Added
- ✨(backend) annotate number of accesses on documents in list view #411
- ✨(backend) allow users to mark/unmark documents as favorite #411 - ✨(backend) allow users to mark/unmark documents as favorite #411
- 🌐(backend) add German translation #259 - 🌐(backend) add German translation #259
- 🌐(frontend) add German translation #255 - 🌐(frontend) add German translation #255

View File

@@ -141,6 +141,7 @@ class ListDocumentSerializer(BaseResourceSerializer):
"""Serialize documents with limited fields for display in lists.""" """Serialize documents with limited fields for display in lists."""
is_favorite = serializers.BooleanField(read_only=True) is_favorite = serializers.BooleanField(read_only=True)
nb_accesses = serializers.IntegerField(read_only=True)
class Meta: class Meta:
model = models.Document model = models.Document
@@ -152,6 +153,7 @@ class ListDocumentSerializer(BaseResourceSerializer):
"is_favorite", "is_favorite",
"link_role", "link_role",
"link_reach", "link_reach",
"nb_accesses",
"title", "title",
"updated_at", "updated_at",
] ]
@@ -162,6 +164,7 @@ class ListDocumentSerializer(BaseResourceSerializer):
"is_favorite", "is_favorite",
"link_role", "link_role",
"link_reach", "link_reach",
"nb_accesses",
"updated_at", "updated_at",
] ]
@@ -181,6 +184,7 @@ class DocumentSerializer(ListDocumentSerializer):
"is_favorite", "is_favorite",
"link_role", "link_role",
"link_reach", "link_reach",
"nb_accesses",
"title", "title",
"updated_at", "updated_at",
] ]
@@ -191,6 +195,7 @@ class DocumentSerializer(ListDocumentSerializer):
"is_avorite", "is_avorite",
"link_role", "link_role",
"link_reach", "link_reach",
"nb_accesses",
"updated_at", "updated_at",
] ]

View File

@@ -1,4 +1,5 @@
"""API endpoints""" """API endpoints"""
# pylint: disable=too-many-lines
import re import re
import uuid import uuid
@@ -10,6 +11,7 @@ from django.contrib.postgres.search import TrigramSimilarity
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.files.storage import default_storage from django.core.files.storage import default_storage
from django.db.models import ( from django.db.models import (
Count,
Exists, Exists,
Min, Min,
OuterRef, OuterRef,
@@ -334,6 +336,9 @@ class DocumentViewSet(
queryset = super().get_queryset() queryset = super().get_queryset()
user = self.request.user user = self.request.user
# Annotate the number of accesses associated with each document
queryset = queryset.annotate(nb_accesses=Count("accesses", distinct=True))
if not user.is_authenticated: if not user.is_authenticated:
# If the user is not authenticated, annotate `is_favorite` as False # If the user is not authenticated, annotate `is_favorite` as False
return queryset.annotate(is_favorite=Value(False)) return queryset.annotate(is_favorite=Value(False))
@@ -360,6 +365,7 @@ class DocumentViewSet(
"""Restrict resources returned by the list endpoint""" """Restrict resources returned by the list endpoint"""
queryset = self.filter_queryset(self.get_queryset()) queryset = self.filter_queryset(self.get_queryset())
user = self.request.user user = self.request.user
if user.is_authenticated: if user.is_authenticated:
queryset = queryset.filter( queryset = queryset.filter(
Q(accesses__user=user) Q(accesses__user=user)

View File

@@ -39,9 +39,11 @@ def test_api_documents_list_format():
client = APIClient() client = APIClient()
client.force_login(user) client.force_login(user)
other_users = factories.UserFactory.create_batch(3)
document = factories.DocumentFactory( document = factories.DocumentFactory(
users=[user, *factories.UserFactory.create_batch(2)], users=[user, *factories.UserFactory.create_batch(2)],
favorited_by=[user, *factories.UserFactory.create_batch(2)], favorited_by=[user, *other_users],
link_traces=other_users,
) )
response = client.get("/api/v1.0/documents/") response = client.get("/api/v1.0/documents/")
@@ -63,6 +65,7 @@ def test_api_documents_list_format():
"is_favorite": True, "is_favorite": True,
"link_reach": document.link_reach, "link_reach": document.link_reach,
"link_role": document.link_role, "link_role": document.link_role,
"nb_accesses": 3,
"title": document.title, "title": document.title,
"updated_at": document.updated_at.isoformat().replace("+00:00", "Z"), "updated_at": document.updated_at.isoformat().replace("+00:00", "Z"),
} }
@@ -92,9 +95,7 @@ def test_api_documents_list_authenticated_direct(django_assert_num_queries):
expected_ids = {str(document.id) for document in documents} expected_ids = {str(document.id) for document in documents}
with django_assert_num_queries(3): with django_assert_num_queries(3):
response = client.get( response = client.get("/api/v1.0/documents/")
"/api/v1.0/documents/",
)
assert response.status_code == 200 assert response.status_code == 200
results = response.json()["results"] results = response.json()["results"]

View File

@@ -38,6 +38,7 @@ def test_api_documents_retrieve_anonymous_public():
"versions_list": False, "versions_list": False,
"versions_retrieve": False, "versions_retrieve": False,
}, },
"nb_accesses": 0,
"content": document.content, "content": document.content,
"created_at": document.created_at.isoformat().replace("+00:00", "Z"), "created_at": document.created_at.isoformat().replace("+00:00", "Z"),
"is_favorite": False, "is_favorite": False,
@@ -102,6 +103,7 @@ def test_api_documents_retrieve_authenticated_unrelated_public_or_authenticated(
"is_favorite": False, "is_favorite": False,
"link_reach": reach, "link_reach": reach,
"link_role": document.link_role, "link_role": document.link_role,
"nb_accesses": 0,
"title": document.title, "title": document.title,
"updated_at": document.updated_at.isoformat().replace("+00:00", "Z"), "updated_at": document.updated_at.isoformat().replace("+00:00", "Z"),
} }
@@ -188,6 +190,7 @@ def test_api_documents_retrieve_authenticated_related_direct():
"is_favorite": False, "is_favorite": False,
"link_reach": document.link_reach, "link_reach": document.link_reach,
"link_role": document.link_role, "link_role": document.link_role,
"nb_accesses": 2,
"title": document.title, "title": document.title,
"updated_at": document.updated_at.isoformat().replace("+00:00", "Z"), "updated_at": document.updated_at.isoformat().replace("+00:00", "Z"),
} }
@@ -272,6 +275,7 @@ def test_api_documents_retrieve_authenticated_related_team_members(
assert response.json() == { assert response.json() == {
"id": str(document.id), "id": str(document.id),
"abilities": document.get_abilities(user), "abilities": document.get_abilities(user),
"nb_accesses": 5,
"content": document.content, "content": document.content,
"created_at": document.created_at.isoformat().replace("+00:00", "Z"), "created_at": document.created_at.isoformat().replace("+00:00", "Z"),
"is_favorite": False, "is_favorite": False,
@@ -326,6 +330,7 @@ def test_api_documents_retrieve_authenticated_related_team_administrators(
assert response.json() == { assert response.json() == {
"id": str(document.id), "id": str(document.id),
"abilities": document.get_abilities(user), "abilities": document.get_abilities(user),
"nb_accesses": 5,
"content": document.content, "content": document.content,
"created_at": document.created_at.isoformat().replace("+00:00", "Z"), "created_at": document.created_at.isoformat().replace("+00:00", "Z"),
"is_favorite": False, "is_favorite": False,
@@ -381,6 +386,7 @@ def test_api_documents_retrieve_authenticated_related_team_owners(
assert response.json() == { assert response.json() == {
"id": str(document.id), "id": str(document.id),
"abilities": document.get_abilities(user), "abilities": document.get_abilities(user),
"nb_accesses": 5,
"content": document.content, "content": document.content,
"created_at": document.created_at.isoformat().replace("+00:00", "Z"), "created_at": document.created_at.isoformat().replace("+00:00", "Z"),
"is_favorite": False, "is_favorite": False,

View File

@@ -216,7 +216,7 @@ def test_api_documents_update_authenticated_editor_administrator_or_owner(
document = models.Document.objects.get(pk=document.pk) document = models.Document.objects.get(pk=document.pk)
document_values = serializers.DocumentSerializer(instance=document).data document_values = serializers.DocumentSerializer(instance=document).data
for key, value in document_values.items(): for key, value in document_values.items():
if key in ["id", "created_at", "link_reach", "link_role"]: if key in ["id", "created_at", "link_reach", "link_role", "nb_accesses"]:
assert value == old_document_values[key] assert value == old_document_values[key]
elif key == "updated_at": elif key == "updated_at":
assert value > old_document_values[key] assert value > old_document_values[key]
@@ -255,7 +255,7 @@ def test_api_documents_update_authenticated_owners(via, mock_user_teams):
document = models.Document.objects.get(pk=document.pk) document = models.Document.objects.get(pk=document.pk)
document_values = serializers.DocumentSerializer(instance=document).data document_values = serializers.DocumentSerializer(instance=document).data
for key, value in document_values.items(): for key, value in document_values.items():
if key in ["id", "created_at", "link_reach", "link_role"]: if key in ["id", "created_at", "link_reach", "link_role", "nb_accesses"]:
assert value == old_document_values[key] assert value == old_document_values[key]
elif key == "updated_at": elif key == "updated_at":
assert value > old_document_values[key] assert value > old_document_values[key]