Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions backend/api/admin/data/legacy_spectrogram_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ class LegacySpectrogramConfigurationAdmin(ExtendedModelAdmin):
"temporal_resolution",
"sensitivity_dB",
"peak_voltage",
"linear_frequency_scale",
"multi_linear_frequency_scale",
)
search_fields = [
"spectrogram_analysis__dataset__name",
Expand Down
6 changes: 6 additions & 0 deletions backend/api/admin/data/spectrogram_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,19 @@ class SpectrogramAnalysisAdmin(ExtendedModelAdmin):
"start",
"end",
"legacy",
"list_frequency_scale_parts",
)
search_fields = ["id", "name", "dataset__name"]

def dynamic(self, obj: SpectrogramAnalysis) -> str:
"""Get dynamic min and max in one field"""
return f"{obj.dynamic_min} - {obj.dynamic_max}"

@admin.display(description="Frequency scale parts")
def list_frequency_scale_parts(self, obj: SpectrogramAnalysis):
"""show_labels"""
return self.list_queryset(obj.frequency_scale_parts.all())

actions = [
"store_paths",
]
Expand Down
60 changes: 40 additions & 20 deletions backend/api/management/commands/seed.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@
Annotation,
)
from backend.api.models.annotation.annotation_campaign import AnnotationCampaignAnalysis
from backend.api.models.data.scales import get_frequency_scales
from backend.api.schema.enums import AnnotationType
from backend.api.models.data.linear_scale import (
get_frequency_scale_parts,
)
from backend.aplose.models import AploseUser
from backend.aplose.models.user import ExpertiseLevel, User
from backend.osmosewebsite.management.commands.seed import Command as WebsiteCommand
Expand Down Expand Up @@ -170,7 +171,12 @@ def _create_metadata(self):

def __get_analysis(
self, dataset: Dataset
) -> ([SpectrogramAnalysis], [LegacySpectrogramConfiguration]):
) -> tuple[
list[SpectrogramAnalysis],
list[LegacySpectrogramConfiguration],
list[SpectrogramAnalysis.frequency_scale_parts.through],
]:
scales_rel = []
analysis = [
SpectrogramAnalysis(
# AbstractDataset
Expand All @@ -194,9 +200,15 @@ def __get_analysis(
dynamic_max=0,
)
]
linear_scale, multi_linear_scale = get_frequency_scales(

for scale in get_frequency_scale_parts(
name=dataset.name, sample_rate=self.legacy_fft.sampling_frequency
)
):
scales_rel.append(
SpectrogramAnalysis.frequency_scale_parts.through(
spectrogramanalysis=analysis[0], linearscale=scale
)
)
legacy_configurations = [
LegacySpectrogramConfiguration(
spectrogram_analysis=analysis[0],
Expand All @@ -208,20 +220,15 @@ def __get_analysis(
hp_filter_min_frequency=0,
window_type="Hamming",
frequency_resolution=0,
linear_frequency_scale=linear_scale,
multi_linear_frequency_scale=multi_linear_scale,
)
]

if dataset.name == "Test Dataset":
for scale in ["porp_delph", "dual_lf_hf", "audible"]:
linear_scale, multi_linear_scale = get_frequency_scales(
name=scale, sample_rate=self.legacy_fft.sampling_frequency
)
for scale_name in ["porp_delph", "dual_lf_hf", "audible"]:
a = SpectrogramAnalysis(
# AbstractDataset
name=f"4096_4096_90_{scale}",
path=f"processed/spectrogram/4096_4096_90_{scale}",
name=f"4096_4096_90_{scale_name}",
path=f"processed/spectrogram/4096_4096_90_{scale_name}",
owner=self.admin,
legacy=True,
# AbstractAnalysis
Expand All @@ -239,27 +246,33 @@ def __get_analysis(
dynamic_min=0,
dynamic_max=0,
)
for scale in get_frequency_scale_parts(
name=scale_name, sample_rate=self.legacy_fft.sampling_frequency
):
scales_rel.append(
SpectrogramAnalysis.frequency_scale_parts.through(
spectrogramanalysis=analysis[0], linearscale=scale
)
)
analysis.append(a)
legacy_configurations.append(
LegacySpectrogramConfiguration(
spectrogram_analysis=a,
folder=f"4096_4096_90_{scale}",
folder=f"4096_4096_90_{scale_name}",
zoom_level=3,
spectrogram_normalization="density",
data_normalization="0",
zscore_duration="0",
hp_filter_min_frequency=0,
window_type="Hamming",
frequency_resolution=0,
linear_frequency_scale=linear_scale,
multi_linear_frequency_scale=multi_linear_scale,
)
)
return analysis, legacy_configurations
return analysis, legacy_configurations, scales_rel

def __get_spectrograms(
self, analysis: [SpectrogramAnalysis]
) -> ([Spectrogram], [Spectrogram.analysis.through]):
self, analysis: list[SpectrogramAnalysis]
) -> tuple[list[Spectrogram], list[Spectrogram.analysis.through]]:
spectrograms = []
rels = []
for k in range(1, self.files_nb):
Expand Down Expand Up @@ -290,6 +303,7 @@ def _create_datasets(self):
spectrograms = []
spectrogram_rels = []
analysis = []
analysis_scales_relations = []
legacy_configurations = []
for name in self.dataset_names:

Expand All @@ -304,8 +318,11 @@ def _create_datasets(self):
datasets.append(dataset)

# Create analysis
dataset_analysis, dataset_legacy_conf = self.__get_analysis(dataset)
dataset_analysis, dataset_legacy_conf, scales_rel = self.__get_analysis(
dataset
)
analysis += dataset_analysis
analysis_scales_relations += scales_rel
legacy_configurations += dataset_legacy_conf

# Create spectrograms
Expand All @@ -315,6 +332,9 @@ def _create_datasets(self):

Dataset.objects.bulk_create(datasets)
SpectrogramAnalysis.objects.bulk_create(analysis)
SpectrogramAnalysis.frequency_scale_parts.through.objects.bulk_create(
analysis_scales_relations
)
LegacySpectrogramConfiguration.objects.bulk_create(legacy_configurations)
Spectrogram.objects.bulk_create(spectrograms)
Spectrogram.analysis.through.objects.bulk_create(spectrogram_rels)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Generated by Django 3.2.25 on 2026-05-28 12:47

from django.db import migrations, models


def reverse_insert(apps, _):
SpectrogramAnalysis = apps.get_model("api", "SpectrogramAnalysis")
MultiLinearScale = apps.get_model("api", "MultiLinearScale")

for a in SpectrogramAnalysis.objects.all():
if a.frequency_scale_parts.exists() and a.legacy_configuration:
multi_scale = MultiLinearScale.objects.create()
for linear_scale in a.frequency_scale_parts.all():
multi_scale.inner_scales.add(linear_scale)
a.legacy_configuration.multi_linear_frequency_scale = multi_scale
a.legacy_configuration.save()


class Migration(migrations.Migration):

dependencies = [
("api", "0005_alter_spectrogramanalysisrelation_analysis"),
]

operations = [
migrations.AddField(
model_name="spectrogramanalysis",
name="frequency_scale_parts",
field=models.ManyToManyField(
related_name="spectrogram_analysis", to="api.LinearScale"
),
),
migrations.RunSQL(
"""
INSERT INTO api_spectrogramanalysis_frequency_scale_parts (spectrogramanalysis_id, linearscale_id)
SELECT
conf.spectrogram_analysis_id,
conf.linear_frequency_scale_id
FROM api_legacyspectrogramconfiguration conf
WHERE conf.linear_frequency_scale_id is not null ;
INSERT INTO api_spectrogramanalysis_frequency_scale_parts (spectrogramanalysis_id, linearscale_id)
SELECT
conf.spectrogram_analysis_id,
innerscales.linearscale_id
FROM api_legacyspectrogramconfiguration conf
LEFT JOIN api_multilinearscale multiscale ON multiscale.id = conf.multi_linear_frequency_scale_id
LEFT JOIN api_multilinearscale_inner_scales innerscales ON innerscales.multilinearscale_id = multiscale.id
WHERE conf.multi_linear_frequency_scale_id is not null ;
""",
migrations.RunSQL.noop,
),
migrations.RunPython(migrations.RunPython.noop, reverse_insert),
migrations.RemoveConstraint(
model_name="legacyspectrogramconfiguration",
name="legacy_spectrogram_configuration_max_one_scale",
),
migrations.RemoveField(
model_name="legacyspectrogramconfiguration",
name="linear_frequency_scale",
),
migrations.RemoveField(
model_name="legacyspectrogramconfiguration",
name="multi_linear_frequency_scale",
),
]
2 changes: 1 addition & 1 deletion backend/api/models/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
from .dataset import Dataset
from .fft import FFT
from .legacy_spectrogram_configuration import LegacySpectrogramConfiguration
from .scales import LinearScale, MultiLinearScale
from .linear_scale import LinearScale, MultiLinearScale
from .spectrogram import Spectrogram, SpectrogramAnalysisRelation
from .spectrogram_analysis import SpectrogramAnalysis
30 changes: 0 additions & 30 deletions backend/api/models/data/legacy_spectrogram_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,12 @@
from django.contrib.postgres.fields import ArrayField
from django.db import models

from .scales import LinearScale, MultiLinearScale


class LegacySpectrogramConfiguration(models.Model):
"""
Table containing spectrogram configuration used for datasets and annotation campaigns.
"""

class Meta:
constraints = [
models.CheckConstraint(
name="legacy_spectrogram_configuration_max_one_scale",
check=(
models.Q(
linear_frequency_scale__isnull=True,
multi_linear_frequency_scale__isnull=False,
)
| models.Q(
linear_frequency_scale__isnull=False,
multi_linear_frequency_scale__isnull=True,
)
| models.Q(
linear_frequency_scale__isnull=True,
multi_linear_frequency_scale__isnull=True,
)
),
),
]

def __str__(self):
return self.folder

Expand All @@ -57,13 +34,6 @@ def __str__(self):
temporal_resolution = models.FloatField(null=True, blank=True)
gain_dB = models.FloatField(null=True, blank=True)

linear_frequency_scale = models.ForeignKey(
LinearScale, on_delete=models.SET_NULL, blank=True, null=True
)
multi_linear_frequency_scale = models.ForeignKey(
MultiLinearScale, on_delete=models.SET_NULL, blank=True, null=True
)

# TODO:
# def zoom_tiles(self, tile_name):
# """Generate zoom tile filenames for SpectrogramConfiguration"""
Expand Down
63 changes: 63 additions & 0 deletions backend/api/models/data/linear_scale.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Spectrogram scale models"""

from django.db import models


class LinearScale(models.Model):
"""Linear spectrogram scale"""

def __str__(self):
if self.name:
return self.name
return f"Linear ({self.min_value} - {self.max_value})[{self.ratio}]"

name = models.CharField(max_length=255, blank=True, null=True)
ratio = models.FloatField(default=1)
min_value = models.FloatField()
max_value = models.FloatField()


class MultiLinearScale(models.Model):
"""Multi-linear spectrogram scale"""

def __str__(self):
if self.name:
return self.name
return f"Multi-Linear {self.id}"

name = models.CharField(max_length=255, blank=True, null=True)
inner_scales = models.ManyToManyField(LinearScale, related_name="outer_scales")


def get_frequency_scale_parts(name: str | None, sample_rate: int) -> list[LinearScale]:
"""return scale type, min freq, max freq and parameters for multiscale"""
if name is None:
return []
if name.lower() == "porp_delph":
return [
LinearScale.objects.get_or_create(ratio=0.5, min_value=0, max_value=30_000)[
0
],
LinearScale.objects.get_or_create(
ratio=0.7, min_value=30_000, max_value=80_000
)[0],
LinearScale.objects.get_or_create(
ratio=1, min_value=80_000, max_value=sample_rate / 2
)[0],
]
if name.lower() == "dual_lf_hf":
return [
LinearScale.objects.get_or_create(ratio=0.5, min_value=0, max_value=22_000)[
0
],
LinearScale.objects.get_or_create(
ratio=1, min_value=100_000, max_value=sample_rate / 2
)[0],
]
if name.lower() == "audible":
return [
LinearScale.objects.get_or_create(
name="audible", min_value=0, max_value=22_000
)[0]
]
return []
Loading
Loading