diff --git a/ami/main/models.py b/ami/main/models.py index 3662b4107..18095f7bd 100644 --- a/ami/main/models.py +++ b/ami/main/models.py @@ -2,6 +2,7 @@ import contextlib import datetime import functools +import inspect import logging import textwrap import time @@ -4913,8 +4914,23 @@ def populate_sample(self, job: "Job | None" = None): else: task_logger.info(f"Sampling using method '{method_name}' with params: {kwargs}") method = getattr(self, method_name) + method_sig = inspect.signature(method) + accepted_kwargs = { + name + for name, param in method_sig.parameters.items() + if name != "self" + and param.kind in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY) + } + filtered_kwargs = {key: value for key, value in kwargs.items() if key in accepted_kwargs} + ignored_kwargs = sorted(set(kwargs) - set(filtered_kwargs)) + if ignored_kwargs: + task_logger.warning( + "Ignoring unsupported arguments for samplng method %s: %s", + method_name, + ", ".join(ignored_kwargs), + ) task_logger.info(f"Sampling and saving captures to {self}") - self.images.set(method(**kwargs)) + self.images.set(method(**filtered_kwargs)) self.save() task_logger.info(f"Done sampling and saving captures to {self}") @@ -5044,6 +5060,7 @@ def sample_common_combined( def sample_interval( self, minute_interval: int = 10, + max_num: int | None = None, exclude_events: list[int] = [], deployment_id: int | None = None, # Deprecated hour_start: int | None = None, @@ -5090,7 +5107,7 @@ def sample_interval( captures: set[SourceImage] = set() for dep in deps: dep_qs = qs.filter(deployment=dep) - for c in sample_captures_by_interval(minute_interval=minute_interval, qs=dep_qs): + for c in sample_captures_by_interval(minute_interval=minute_interval, qs=dep_qs, max_num=max_num): captures.add(c) # Return results in a deterministic order. Sort by timestamp (oldest first), diff --git a/ami/main/tests.py b/ami/main/tests.py index c9e3b0d8f..87d36f13c 100644 --- a/ami/main/tests.py +++ b/ami/main/tests.py @@ -1418,7 +1418,7 @@ def test_interval_with_excluded_events(self): assert image.event != excluded_event def test_extra_arguments(self): - # Assert that a value error is raised when trying to call a sampling method with extra arguments + # Extra kwargs in stored collection config should be ignored, not fail population. from ami.main.models import SourceImageCollection collection = SourceImageCollection.objects.create( @@ -1428,9 +1428,23 @@ def test_extra_arguments(self): kwargs={"birthday": True, "cake": "chocolate"}, ) collection.save() + collection.populate_sample() + + self.assertGreater(collection.images.count(), 0) + + def test_interval_sample_accepts_max_num(self): + from ami.main.models import SourceImageCollection + + collection = SourceImageCollection.objects.create( + name="Test Interval Sample With Max Num", + project=self.project_one, + method="interval", + kwargs={"minute_interval": 1, "max_num": 1}, + ) + collection.save() + collection.populate_sample() - with self.assertRaises(TypeError): - collection.populate_sample() + self.assertEqual(collection.images.count(), 1) def test_last_and_random(self): from ami.main.models import SourceImageCollection