✨(backend) refactor indexation signals and fix circular import issues
Signed-off-by: Fabre Florian <ffabre@hybird.org>
This commit is contained in:
committed by
Quentin BEY
parent
24460ffc3a
commit
bf978b5376
@@ -9,6 +9,9 @@ and this project adheres to
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
- ✨(backend) allow to create a new user in a marketing system
|
- ✨(backend) allow to create a new user in a marketing system
|
||||||
|
- ✨(backend) add async indexation of documents on save (or access save) #1276
|
||||||
|
- ✨(backend) add debounce mechanism to limit indexation jobs #1276
|
||||||
|
- ✨(api) add API route to search for indexed documents in Find #1276
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
"""Impress Core application"""
|
"""Impress Core application"""
|
||||||
# from django.apps import AppConfig
|
|
||||||
# from django.utils.translation import gettext_lazy as _
|
from django.apps import AppConfig
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
# class CoreConfig(AppConfig):
|
class CoreConfig(AppConfig):
|
||||||
# """Configuration class for the impress core app."""
|
"""Configuration class for the impress core app."""
|
||||||
|
|
||||||
# name = "core"
|
name = "core"
|
||||||
# app_label = "core"
|
app_label = "core"
|
||||||
# verbose_name = _("impress core application")
|
verbose_name = _("Impress core application")
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
"""
|
||||||
|
Import signals when the app is ready.
|
||||||
|
"""
|
||||||
|
# pylint: disable=import-outside-toplevel, unused-import
|
||||||
|
from . import signals # noqa: PLC0415
|
||||||
|
|||||||
@@ -20,9 +20,7 @@ from django.core.files.base import ContentFile
|
|||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models import signals
|
|
||||||
from django.db.models.functions import Left, Length
|
from django.db.models.functions import Left, Length
|
||||||
from django.dispatch import receiver
|
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
@@ -41,7 +39,6 @@ from .choices import (
|
|||||||
RoleChoices,
|
RoleChoices,
|
||||||
get_equivalent_link_definition,
|
get_equivalent_link_definition,
|
||||||
)
|
)
|
||||||
from .tasks.find import trigger_document_indexer
|
|
||||||
from .validators import sub_validator
|
from .validators import sub_validator
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
@@ -955,16 +952,6 @@ class Document(MP_Node, BaseModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@receiver(signals.post_save, sender=Document)
|
|
||||||
def document_post_save(sender, instance, **kwargs): # pylint: disable=unused-argument
|
|
||||||
"""
|
|
||||||
Asynchronous call to the document indexer at the end of the transaction.
|
|
||||||
Note : Within the transaction we can have an empty content and a serialization
|
|
||||||
error.
|
|
||||||
"""
|
|
||||||
trigger_document_indexer(instance, on_commit=True)
|
|
||||||
|
|
||||||
|
|
||||||
class LinkTrace(BaseModel):
|
class LinkTrace(BaseModel):
|
||||||
"""
|
"""
|
||||||
Relation model to trace accesses to a document via a link by a logged-in user.
|
Relation model to trace accesses to a document via a link by a logged-in user.
|
||||||
@@ -1195,15 +1182,6 @@ class DocumentAccess(BaseAccess):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@receiver(signals.post_save, sender=DocumentAccess)
|
|
||||||
def document_access_post_save(sender, instance, created, **kwargs): # pylint: disable=unused-argument
|
|
||||||
"""
|
|
||||||
Asynchronous call to the document indexer at the end of the transaction.
|
|
||||||
"""
|
|
||||||
if not created:
|
|
||||||
trigger_document_indexer(instance.document, on_commit=True)
|
|
||||||
|
|
||||||
|
|
||||||
class DocumentAskForAccess(BaseModel):
|
class DocumentAskForAccess(BaseModel):
|
||||||
"""Relation model to ask for access to a document."""
|
"""Relation model to ask for access to a document."""
|
||||||
|
|
||||||
|
|||||||
28
src/backend/core/signals.py
Normal file
28
src/backend/core/signals.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
"""
|
||||||
|
Declare and configure the signals for the impress core application
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.db.models import signals
|
||||||
|
from django.dispatch import receiver
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
from .tasks.find import trigger_document_indexer
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(signals.post_save, sender=models.Document)
|
||||||
|
def document_post_save(sender, instance, **kwargs): # pylint: disable=unused-argument
|
||||||
|
"""
|
||||||
|
Asynchronous call to the document indexer at the end of the transaction.
|
||||||
|
Note : Within the transaction we can have an empty content and a serialization
|
||||||
|
error.
|
||||||
|
"""
|
||||||
|
trigger_document_indexer(instance, on_commit=True)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(signals.post_save, sender=models.DocumentAccess)
|
||||||
|
def document_access_post_save(sender, instance, created, **kwargs): # pylint: disable=unused-argument
|
||||||
|
"""
|
||||||
|
Asynchronous call to the document indexer at the end of the transaction.
|
||||||
|
"""
|
||||||
|
if not created:
|
||||||
|
trigger_document_indexer(instance.document, on_commit=True)
|
||||||
@@ -7,12 +7,6 @@ from django.conf import settings
|
|||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
|
||||||
from core import models
|
|
||||||
from core.services.search_indexers import (
|
|
||||||
get_batch_accesses_by_users_and_teams,
|
|
||||||
get_document_indexer_class,
|
|
||||||
)
|
|
||||||
|
|
||||||
from impress.celery_app import app
|
from impress.celery_app import app
|
||||||
|
|
||||||
logger = getLogger(__file__)
|
logger = getLogger(__file__)
|
||||||
@@ -52,6 +46,13 @@ def document_indexer_task(document_id):
|
|||||||
logger.info("Skip document %s indexation", document_id)
|
logger.info("Skip document %s indexation", document_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# pylint: disable=import-outside-toplevel
|
||||||
|
from core import models # noqa: PLC0415
|
||||||
|
from core.services.search_indexers import ( # noqa: PLC0415
|
||||||
|
get_batch_accesses_by_users_and_teams,
|
||||||
|
get_document_indexer_class,
|
||||||
|
)
|
||||||
|
|
||||||
doc = models.Document.objects.get(pk=document_id)
|
doc = models.Document.objects.get(pk=document_id)
|
||||||
indexer = get_document_indexer_class()()
|
indexer = get_document_indexer_class()()
|
||||||
accesses = get_batch_accesses_by_users_and_teams((doc.path,))
|
accesses = get_batch_accesses_by_users_and_teams((doc.path,))
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ Unit tests for the Document model
|
|||||||
"""
|
"""
|
||||||
# pylint: disable=too-many-lines
|
# pylint: disable=too-many-lines
|
||||||
|
|
||||||
from operator import itemgetter
|
|
||||||
import random
|
import random
|
||||||
import smtplib
|
import smtplib
|
||||||
import time
|
import time
|
||||||
from logging import Logger
|
from logging import Logger
|
||||||
|
from operator import itemgetter
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from django.contrib.auth.models import AnonymousUser
|
from django.contrib.auth.models import AnonymousUser
|
||||||
@@ -1651,13 +1651,13 @@ def test_models_documents_post_save_indexer(mock_push, settings):
|
|||||||
|
|
||||||
indexer = FindDocumentIndexer()
|
indexer = FindDocumentIndexer()
|
||||||
|
|
||||||
assert sorted(data, key=itemgetter('id')) == sorted(
|
assert sorted(data, key=itemgetter("id")) == sorted(
|
||||||
[
|
[
|
||||||
indexer.serialize_document(doc1, accesses),
|
indexer.serialize_document(doc1, accesses),
|
||||||
indexer.serialize_document(doc2, accesses),
|
indexer.serialize_document(doc2, accesses),
|
||||||
indexer.serialize_document(doc3, accesses),
|
indexer.serialize_document(doc3, accesses),
|
||||||
],
|
],
|
||||||
key=itemgetter('id'),
|
key=itemgetter("id"),
|
||||||
)
|
)
|
||||||
|
|
||||||
# The debounce counters should be reset
|
# The debounce counters should be reset
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ class FakeDocumentIndexer(BaseDocumentIndexer):
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="fake_indexer_settings")
|
@pytest.fixture(name="fake_indexer_settings")
|
||||||
def fake_indexer_settings_fixture(settings):
|
def fake_indexer_settings_fixture(settings):
|
||||||
"""Fixture to switch the indexer to the FakeDocumentIndexer."""
|
"""Fixture to switch the indexer to the FakeDocumentIndexer."""
|
||||||
@@ -103,6 +102,7 @@ def test_services_search_indexer_class(fake_indexer_settings):
|
|||||||
"core.tests.test_services_search_indexers.FakeDocumentIndexer"
|
"core.tests.test_services_search_indexers.FakeDocumentIndexer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_services_search_indexer_url_is_none(settings):
|
def test_services_search_indexer_url_is_none(settings):
|
||||||
"""
|
"""
|
||||||
Indexer should raise RuntimeError if SEARCH_INDEXER_URL is None or empty.
|
Indexer should raise RuntimeError if SEARCH_INDEXER_URL is None or empty.
|
||||||
|
|||||||
Reference in New Issue
Block a user