-
Notifications
You must be signed in to change notification settings - Fork 11
Implement class masking using the post-processing framework #999
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mohamedelabbas1996
wants to merge
49
commits into
main
Choose a base branch
from
feat/postprocessing-class-masking
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 32 commits
Commits
Show all changes
49 commits
Select commit
Hold shift + click to select a range
f46e88c
feat: add base and runner classes for generic post-processing framework
mohamedelabbas1996 d86ea4d
feat: add post-processing framework base post-processing task class
mohamedelabbas1996 2c0f78f
feat: add small size filter post-processing task class
mohamedelabbas1996 ffba709
feat: add post processing job type
mohamedelabbas1996 63cd84b
feat: trigger small size filter post processing task from admin page
mohamedelabbas1996 cab62bf
feat: add a new algorithm task type for post-processing
mohamedelabbas1996 6d0e284
chore: deleted runner.py
mohamedelabbas1996 4cfe2d8
feat: add migration for creating a new job type
mohamedelabbas1996 b42e069
fix: fix an import error with the AlgorithmTaskType
mohamedelabbas1996 cb7c83a
feat: update identification history of occurrences in SmallSizeFilter
mohamedelabbas1996 10103db
feat: add rank rollup
mohamedelabbas1996 2e81d90
feat: add class masking post processing task
mohamedelabbas1996 0baf8ce
feat: trigger class masking from admin page
mohamedelabbas1996 f3caa18
fix: modified log messages
mohamedelabbas1996 65d4fef
fix: set the classification algorithm to the rank rollup Algorithm w…
mohamedelabbas1996 e13afc1
feat: trigger rank rollup from admin page
mohamedelabbas1996 7ecc18c
Remove class_masking.py from framework branch
mohamedelabbas1996 f214025
fix: initialize post-processing tasks with job context and simplify r…
mohamedelabbas1996 20ff4b6
feat: add permission to run post-processing jobs
mohamedelabbas1996 5b66ae3
chore: remove class_masking import
mohamedelabbas1996 0419eff
refactor: redesign BasePostProcessingTask with job-aware logging, pro…
mohamedelabbas1996 1ad1e76
refactor: adapt RankRollupTask to new BasePostProcessingTask with sel…
mohamedelabbas1996 d97e8e0
refactor: update SmallSizeFilter to use BasePostProcessingTask loggin…
mohamedelabbas1996 2922c86
migrations: update Project options to include post-processing job per…
mohamedelabbas1996 9012d7f
migrations: update Algorithm.task_type choices to include post-proces…
mohamedelabbas1996 319bb3d
Merge branch 'main' into feat/postprocessing-framework
mohamedelabbas1996 787ac0b
migrations: merged migrations
mohamedelabbas1996 5e85b75
refactor: refactor job runner to initialize post-processing tasks wit…
mohamedelabbas1996 88ffba8
chore: rebase feat/postprocessing-class-masking onto feat/postprocess…
mohamedelabbas1996 9519600
chore: remove class masking trigger (moved to feat/postprocessing-cla…
mohamedelabbas1996 21e6648
feat: improved progress tracking
mohamedelabbas1996 7135e15
Merge branch 'feat/postprocessing-framework' into feat/postprocessing…
mohamedelabbas1996 6632c31
feat: add applied_to field to Classification to track source classifi…
mohamedelabbas1996 23f80fb
tests: added tests for small size filter and rank roll up post-proces…
mohamedelabbas1996 336636a
fix: create only terminal classifications and remove identification c…
mohamedelabbas1996 0d90cde
refactor: remove inner transaction.atomic for cleaner transaction man…
mohamedelabbas1996 23469e2
tests: fixed small size filter test
mohamedelabbas1996 001464e
Merge branch 'feat/postprocessing-framework' into feat/postprocessing…
mohamedelabbas1996 916d652
Merge branch 'main' of github.com:RolnickLab/antenna into feat/postpr…
mihow 1b8700e
draft: work towards class masking in new framework
mihow e4639f6
Merge remote-tracking branch 'origin/main' into feat/postprocessing-c…
mihow a466a52
feat: add class masking tests, management command, and fix registry
mihow a107597
fix: address review feedback on class masking and rank rollup
mihow da9b081
feat: replace hardcoded admin action with dynamic class masking form
mihow fc3f9e1
docs: add class masking screenshots for PR review
mihow c96a865
fix: address review feedback — N+1 query, distinct, HTML, test ordering
mihow 6be1239
feat: expose applied_to field in Classification API serializers
mihow c4311aa
feat: make applied_to a nested object with algorithm details
mihow daed538
fix: add prefetch for applied_to on occurrence detail endpoint
mihow File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # Generated by Django 4.2.10 on 2025-09-30 12:25 | ||
|
|
||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
| dependencies = [ | ||
| ("jobs", "0017_alter_job_logs_alter_job_progress"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AlterField( | ||
| model_name="job", | ||
| name="job_type_key", | ||
| field=models.CharField( | ||
| choices=[ | ||
| ("ml", "ML pipeline"), | ||
| ("populate_captures_collection", "Populate captures collection"), | ||
| ("data_storage_sync", "Data storage sync"), | ||
| ("unknown", "Unknown"), | ||
| ("data_export", "Data Export"), | ||
| ("post_processing", "Post Processing"), | ||
| ], | ||
| default="unknown", | ||
| max_length=255, | ||
| verbose_name="Job Type", | ||
| ), | ||
| ), | ||
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| # Generated by Django 4.2.10 on 2025-10-14 05:01 | ||
|
|
||
| from django.db import migrations | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
| dependencies = [ | ||
| ("main", "0074_taxon_cover_image_credit_taxon_cover_image_url_and_more"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AlterModelOptions( | ||
| name="project", | ||
| options={ | ||
| "ordering": ["-priority", "created_at"], | ||
| "permissions": [ | ||
| ("create_identification", "Can create identifications"), | ||
| ("update_identification", "Can update identifications"), | ||
| ("delete_identification", "Can delete identifications"), | ||
| ("create_job", "Can create a job"), | ||
| ("update_job", "Can update a job"), | ||
| ("run_ml_job", "Can run/retry/cancel ML jobs"), | ||
| ("run_populate_captures_collection_job", "Can run/retry/cancel Populate Collection jobs"), | ||
| ("run_data_storage_sync_job", "Can run/retry/cancel Data Storage Sync jobs"), | ||
| ("run_data_export_job", "Can run/retry/cancel Data Export jobs"), | ||
| ("run_single_image_ml_job", "Can process a single capture"), | ||
| ("run_post_processing_job", "Can run/retry/cancel Post-Processing jobs"), | ||
| ("delete_job", "Can delete a job"), | ||
| ("create_deployment", "Can create a deployment"), | ||
| ("delete_deployment", "Can delete a deployment"), | ||
| ("update_deployment", "Can update a deployment"), | ||
| ("sync_deployment", "Can sync images to a deployment"), | ||
| ("create_sourceimagecollection", "Can create a collection"), | ||
| ("update_sourceimagecollection", "Can update a collection"), | ||
| ("delete_sourceimagecollection", "Can delete a collection"), | ||
| ("populate_sourceimagecollection", "Can populate a collection"), | ||
| ("create_sourceimage", "Can create a source image"), | ||
| ("update_sourceimage", "Can update a source image"), | ||
| ("delete_sourceimage", "Can delete a source image"), | ||
| ("star_sourceimage", "Can star a source image"), | ||
| ("create_sourceimageupload", "Can create a source image upload"), | ||
| ("update_sourceimageupload", "Can update a source image upload"), | ||
| ("delete_sourceimageupload", "Can delete a source image upload"), | ||
| ("create_s3storagesource", "Can create storage"), | ||
| ("delete_s3storagesource", "Can delete storage"), | ||
| ("update_s3storagesource", "Can update storage"), | ||
| ("test_s3storagesource", "Can test storage connection"), | ||
| ("create_site", "Can create a site"), | ||
| ("delete_site", "Can delete a site"), | ||
| ("update_site", "Can update a site"), | ||
| ("create_device", "Can create a device"), | ||
| ("delete_device", "Can delete a device"), | ||
| ("update_device", "Can update a device"), | ||
| ("view_private_data", "Can view private data"), | ||
| ("trigger_exports", "Can trigger data exports"), | ||
| ], | ||
| }, | ||
| ), | ||
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Generated by Django 4.2.10 on 2025-10-14 14:26 | ||
|
|
||
| from django.db import migrations | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
| dependencies = [ | ||
| ("main", "0075_alter_project_options"), | ||
| ("main", "0076_add_occurrence_composite_indexes"), | ||
| ] | ||
|
|
||
| operations = [] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| # Generated by Django 4.2.10 on 2025-10-14 05:01 | ||
|
|
||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
| dependencies = [ | ||
| ("ml", "0024_fix_classifications_missing_category_maps"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AlterField( | ||
| model_name="algorithm", | ||
| name="task_type", | ||
| field=models.CharField( | ||
| choices=[ | ||
| ("detection", "Detection"), | ||
| ("localization", "Localization"), | ||
| ("segmentation", "Segmentation"), | ||
| ("classification", "Classification"), | ||
| ("embedding", "Embedding"), | ||
| ("tracking", "Tracking"), | ||
| ("tagging", "Tagging"), | ||
| ("regression", "Regression"), | ||
| ("captioning", "Captioning"), | ||
| ("generation", "Generation"), | ||
| ("translation", "Translation"), | ||
| ("summarization", "Summarization"), | ||
| ("question_answering", "Question Answering"), | ||
| ("depth_estimation", "Depth Estimation"), | ||
| ("pose_estimation", "Pose Estimation"), | ||
| ("size_estimation", "Size Estimation"), | ||
| ("post_processing", "Post Processing"), | ||
| ("other", "Other"), | ||
| ("unknown", "Unknown"), | ||
| ], | ||
| default="unknown", | ||
| max_length=255, | ||
| null=True, | ||
| ), | ||
| ), | ||
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| from . import class_masking, rank_rollup, small_size_filter # noqa: F401 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| # ami/ml/post_processing/base.py | ||
|
|
||
| import abc | ||
| import logging | ||
| from typing import Any | ||
|
|
||
| from ami.jobs.models import Job | ||
| from ami.ml.models import Algorithm | ||
| from ami.ml.models.algorithm import AlgorithmTaskType | ||
|
|
||
| # Registry of available post-processing tasks | ||
| POSTPROCESSING_TASKS: dict[str, type["BasePostProcessingTask"]] = {} | ||
|
|
||
|
|
||
| def register_postprocessing_task(task_cls: type["BasePostProcessingTask"]): | ||
| """ | ||
| Decorator to register a post-processing task in the global registry. | ||
| Each task must define a unique `key`. | ||
| Ensures an Algorithm entry exists for this task. | ||
| """ | ||
| if not hasattr(task_cls, "key") or not task_cls.key: | ||
| raise ValueError(f"Task {task_cls.__name__} missing required 'key' attribute") | ||
|
|
||
| # Register the task | ||
| POSTPROCESSING_TASKS[task_cls.key] = task_cls | ||
| return task_cls | ||
|
|
||
|
|
||
| def get_postprocessing_task(name: str) -> type["BasePostProcessingTask"] | None: | ||
| """ | ||
| Get a task class by its registry key. | ||
| Returns None if not found. | ||
| """ | ||
| return POSTPROCESSING_TASKS.get(name) | ||
|
|
||
|
|
||
| class BasePostProcessingTask(abc.ABC): | ||
| """ | ||
| Abstract base class for all post-processing tasks. | ||
| """ | ||
|
|
||
| # Each task must override these | ||
| key: str = "" | ||
| name: str = "" | ||
|
|
||
| def __init__( | ||
| self, | ||
| job: Job | None = None, | ||
| logger: logging.Logger | None = None, | ||
| **config: Any, | ||
| ): | ||
| self.job = job | ||
| self.config = config | ||
| # Choose the right logger | ||
| if logger is not None: | ||
| self.logger = logger | ||
| elif job is not None: | ||
| self.logger = job.logger | ||
| else: | ||
| self.logger = logging.getLogger(f"ami.post_processing.{self.key}") | ||
|
|
||
| algorithm, _ = Algorithm.objects.get_or_create( | ||
| name=self.__class__.__name__, | ||
| defaults={ | ||
| "description": f"Post-processing task: {self.key}", | ||
| "task_type": AlgorithmTaskType.POST_PROCESSING.value, | ||
| }, | ||
| ) | ||
| self.algorithm: Algorithm = algorithm | ||
|
|
||
| self.logger.info(f"Initialized {self.__class__.__name__} with config={self.config}, job={job}") | ||
|
|
||
| def update_progress(self, progress: float): | ||
| """ | ||
| Update progress if job is present, otherwise just log. | ||
| """ | ||
|
|
||
| if self.job: | ||
| self.job.progress.update_stage(self.job.job_type_key, progress=progress) | ||
| self.job.save(update_fields=["progress"]) | ||
|
|
||
| else: | ||
| # No job object — fallback to plain logging | ||
| self.logger.info(f"[{self.name}] Progress {progress:.0%}") | ||
|
|
||
| @abc.abstractmethod | ||
| def run(self) -> None: | ||
| """ | ||
| Run the task logic. | ||
| Must be implemented by subclasses. | ||
| """ | ||
| raise NotImplementedError("BasePostProcessingTask subclasses must implement run()") |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.