From 7d0d7b1072d00df03878e71e85fe330100de31ae Mon Sep 17 00:00:00 2001 From: Brian Helba Date: Thu, 8 Jan 2026 21:19:32 -0500 Subject: [PATCH 1/3] Add missing `__init__.py` files to `tasks` These are essential to making these into actual importable modules. Otherwise, they will be considered to be "implicit namespace modules", which have different behavior in subtle ways. --- bats_ai/tasks/__init__.py | 0 bats_ai/tasks/nabat/__init__.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 bats_ai/tasks/__init__.py create mode 100644 bats_ai/tasks/nabat/__init__.py diff --git a/bats_ai/tasks/__init__.py b/bats_ai/tasks/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bats_ai/tasks/nabat/__init__.py b/bats_ai/tasks/nabat/__init__.py new file mode 100644 index 00000000..e69de29b From 40c3c13a29d6204da5dbec9fbcb1fbd6cf08eb42 Mon Sep 17 00:00:00 2001 From: Brian Helba Date: Tue, 13 Jan 2026 11:15:32 -0500 Subject: [PATCH 2/3] Move `tasks` below the `bats_ai.core` package This is the canonical location for Celery tasks in Resonant. This is necessary to make `app.autodiscover_tasks()` work properly, as it scans the `INSTALLED_APPS` (in this case, `core`) for a `tasks` module. Perhaps this also wasn't working before due to the lack of `__init__.py`, so `tasks` wasn't considered a real module. --- bats_ai/core/admin/recording.py | 2 +- bats_ai/core/management/commands/generateNABat.py | 2 +- bats_ai/core/management/commands/importRecordings.py | 2 +- bats_ai/{ => core}/tasks/__init__.py | 0 bats_ai/{ => core}/tasks/export_task.py | 0 bats_ai/{ => core}/tasks/nabat/__init__.py | 0 bats_ai/{ => core}/tasks/nabat/nabat_data_retrieval.py | 0 bats_ai/{ => core}/tasks/nabat/nabat_export_task.py | 0 bats_ai/{ => core}/tasks/nabat/nabat_update_species.py | 0 bats_ai/{ => core}/tasks/nabat/tasks.py | 0 bats_ai/{ => core}/tasks/periodic.py | 0 bats_ai/{ => core}/tasks/tasks.py | 0 bats_ai/core/views/nabat/nabat_configuration.py | 4 ++-- bats_ai/core/views/nabat/nabat_recording.py | 2 +- bats_ai/core/views/recording.py | 2 +- 15 files changed, 7 insertions(+), 7 deletions(-) rename bats_ai/{ => core}/tasks/__init__.py (100%) rename bats_ai/{ => core}/tasks/export_task.py (100%) rename bats_ai/{ => core}/tasks/nabat/__init__.py (100%) rename bats_ai/{ => core}/tasks/nabat/nabat_data_retrieval.py (100%) rename bats_ai/{ => core}/tasks/nabat/nabat_export_task.py (100%) rename bats_ai/{ => core}/tasks/nabat/nabat_update_species.py (100%) rename bats_ai/{ => core}/tasks/nabat/tasks.py (100%) rename bats_ai/{ => core}/tasks/periodic.py (100%) rename bats_ai/{ => core}/tasks/tasks.py (100%) diff --git a/bats_ai/core/admin/recording.py b/bats_ai/core/admin/recording.py index 2da5f82d..a19ce27d 100644 --- a/bats_ai/core/admin/recording.py +++ b/bats_ai/core/admin/recording.py @@ -5,7 +5,7 @@ from django.utils.safestring import mark_safe from bats_ai.core.models import Recording -from bats_ai.tasks.tasks import recording_compute_spectrogram +from bats_ai.core.tasks.tasks import recording_compute_spectrogram @admin.register(Recording) diff --git a/bats_ai/core/management/commands/generateNABat.py b/bats_ai/core/management/commands/generateNABat.py index d70f12de..4b9d85a0 100644 --- a/bats_ai/core/management/commands/generateNABat.py +++ b/bats_ai/core/management/commands/generateNABat.py @@ -12,7 +12,7 @@ from bats_ai.core.models import Species from bats_ai.core.models.nabat import NABatRecording, NABatRecordingAnnotation -from bats_ai.tasks.nabat.tasks import generate_compress_spectrogram, generate_spectrogram +from bats_ai.core.tasks.nabat.tasks import generate_compress_spectrogram, generate_spectrogram fake = Faker() diff --git a/bats_ai/core/management/commands/importRecordings.py b/bats_ai/core/management/commands/importRecordings.py index 4f5558b9..43211804 100644 --- a/bats_ai/core/management/commands/importRecordings.py +++ b/bats_ai/core/management/commands/importRecordings.py @@ -8,8 +8,8 @@ from django.utils import timezone from bats_ai.core.models import Recording +from bats_ai.core.tasks.tasks import recording_compute_spectrogram from bats_ai.core.utils.guano_utils import extract_guano_metadata -from bats_ai.tasks.tasks import recording_compute_spectrogram logger = logging.getLogger(__name__) diff --git a/bats_ai/tasks/__init__.py b/bats_ai/core/tasks/__init__.py similarity index 100% rename from bats_ai/tasks/__init__.py rename to bats_ai/core/tasks/__init__.py diff --git a/bats_ai/tasks/export_task.py b/bats_ai/core/tasks/export_task.py similarity index 100% rename from bats_ai/tasks/export_task.py rename to bats_ai/core/tasks/export_task.py diff --git a/bats_ai/tasks/nabat/__init__.py b/bats_ai/core/tasks/nabat/__init__.py similarity index 100% rename from bats_ai/tasks/nabat/__init__.py rename to bats_ai/core/tasks/nabat/__init__.py diff --git a/bats_ai/tasks/nabat/nabat_data_retrieval.py b/bats_ai/core/tasks/nabat/nabat_data_retrieval.py similarity index 100% rename from bats_ai/tasks/nabat/nabat_data_retrieval.py rename to bats_ai/core/tasks/nabat/nabat_data_retrieval.py diff --git a/bats_ai/tasks/nabat/nabat_export_task.py b/bats_ai/core/tasks/nabat/nabat_export_task.py similarity index 100% rename from bats_ai/tasks/nabat/nabat_export_task.py rename to bats_ai/core/tasks/nabat/nabat_export_task.py diff --git a/bats_ai/tasks/nabat/nabat_update_species.py b/bats_ai/core/tasks/nabat/nabat_update_species.py similarity index 100% rename from bats_ai/tasks/nabat/nabat_update_species.py rename to bats_ai/core/tasks/nabat/nabat_update_species.py diff --git a/bats_ai/tasks/nabat/tasks.py b/bats_ai/core/tasks/nabat/tasks.py similarity index 100% rename from bats_ai/tasks/nabat/tasks.py rename to bats_ai/core/tasks/nabat/tasks.py diff --git a/bats_ai/tasks/periodic.py b/bats_ai/core/tasks/periodic.py similarity index 100% rename from bats_ai/tasks/periodic.py rename to bats_ai/core/tasks/periodic.py diff --git a/bats_ai/tasks/tasks.py b/bats_ai/core/tasks/tasks.py similarity index 100% rename from bats_ai/tasks/tasks.py rename to bats_ai/core/tasks/tasks.py diff --git a/bats_ai/core/views/nabat/nabat_configuration.py b/bats_ai/core/views/nabat/nabat_configuration.py index c1e68e08..17535f1d 100644 --- a/bats_ai/core/views/nabat/nabat_configuration.py +++ b/bats_ai/core/views/nabat/nabat_configuration.py @@ -15,8 +15,8 @@ from bats_ai.core.models import ExportedAnnotationFile, ProcessingTask, ProcessingTaskType from bats_ai.core.models.nabat import NABatRecording, NABatRecordingAnnotation -from bats_ai.tasks.nabat.nabat_export_task import export_nabat_annotations_task -from bats_ai.tasks.nabat.nabat_update_species import update_nabat_species +from bats_ai.core.tasks.nabat.nabat_export_task import export_nabat_annotations_task +from bats_ai.core.tasks.nabat.nabat_update_species import update_nabat_species logger = logging.getLogger(__name__) diff --git a/bats_ai/core/views/nabat/nabat_recording.py b/bats_ai/core/views/nabat/nabat_recording.py index 179e83ba..9ce57349 100644 --- a/bats_ai/core/views/nabat/nabat_recording.py +++ b/bats_ai/core/views/nabat/nabat_recording.py @@ -17,8 +17,8 @@ NABatRecording, NABatRecordingAnnotation, ) +from bats_ai.core.tasks.nabat.nabat_data_retrieval import nabat_recording_initialize from bats_ai.core.views.species import SpeciesSchema -from bats_ai.tasks.nabat.nabat_data_retrieval import nabat_recording_initialize logger = logging.getLogger(__name__) router = RouterPaginated() diff --git a/bats_ai/core/views/recording.py b/bats_ai/core/views/recording.py index 0bd8f35d..9c1c8e91 100644 --- a/bats_ai/core/views/recording.py +++ b/bats_ai/core/views/recording.py @@ -22,13 +22,13 @@ SequenceAnnotations, Species, ) +from bats_ai.core.tasks.tasks import recording_compute_spectrogram from bats_ai.core.views.recording_tag import RecordingTagSchema from bats_ai.core.views.sequence_annotations import ( SequenceAnnotationSchema, UpdateSequenceAnnotationSchema, ) from bats_ai.core.views.species import SpeciesSchema -from bats_ai.tasks.tasks import recording_compute_spectrogram from bats_ai.utils.spectrogram_utils import predict_from_compressed logger = logging.getLogger(__name__) From f2da94c519edf9eb0ff477782a2204321ef74a3d Mon Sep 17 00:00:00 2001 From: Brian Helba Date: Tue, 13 Jan 2026 14:10:39 -0500 Subject: [PATCH 3/3] Use `@app.task` instead of `@shared_task` Previously, `@shared_task` was preferred by Resonant, but I realized that `@app.task` will be able to provide better typechecking. `@shared_task` is essential only for reusable apps (and `core` isn't reusable). I'll make an upstream change to the Resonant cookiecutter to prefer `@app.task` too. --- bats_ai/core/tasks/periodic.py | 4 ++-- bats_ai/core/tasks/tasks.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bats_ai/core/tasks/periodic.py b/bats_ai/core/tasks/periodic.py index 9b62ab4f..8c87d8ee 100644 --- a/bats_ai/core/tasks/periodic.py +++ b/bats_ai/core/tasks/periodic.py @@ -1,10 +1,10 @@ -from celery import shared_task from django.utils import timezone +from bats_ai.celery import app from bats_ai.core.models import ExportedAnnotationFile -@shared_task +@app.task def delete_expired_exported_files(): now = timezone.now() expired_files = ExportedAnnotationFile.objects.filter(expires_at__lt=now) diff --git a/bats_ai/core/tasks/tasks.py b/bats_ai/core/tasks/tasks.py index 7d5e11f6..ef0d5097 100644 --- a/bats_ai/core/tasks/tasks.py +++ b/bats_ai/core/tasks/tasks.py @@ -2,10 +2,10 @@ import os import tempfile -from celery import shared_task from django.contrib.contenttypes.models import ContentType from django.core.files import File +from bats_ai.celery import app from bats_ai.core.models import ( CompressedSpectrogram, Configuration, @@ -21,7 +21,7 @@ logger = logging.getLogger('NABatDataRetrieval') -@shared_task +@app.task def recording_compute_spectrogram(recording_id: int): recording = Recording.objects.get(pk=recording_id)