Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9299681
[WIP] Adding selenium to the requirements to download bokeh widgets a…
Bezbakri Sep 30, 2025
c99db55
[WIP] Installing geckodriver and firefox in the dockerfile so that se…
Bezbakri Oct 3, 2025
593432a
Merged with main
Bezbakri Oct 3, 2025
290deb6
[WIP] Using Django-storages to use the S3 bucket as a public media st…
Bezbakri Oct 20, 2025
dfafea8
[WIP] Testing S3 bucket as a public media storage
Bezbakri Oct 20, 2025
fb805c1
Merge branch 'main' of https://github.com/scimma/blast into rishabh-f…
Bezbakri Oct 20, 2025
70e23aa
[WIP] Using a filefield to store the images
Bezbakri Oct 21, 2025
dc33935
[WIP] Display pngs for cutouts from bytes
Bezbakri Nov 11, 2025
9d4ea1a
Merge branch 'main' of https://github.com/scimma/blast into rishabh-f…
Bezbakri Nov 13, 2025
04f5ee5
[WIP] endpoint for plotting the cutout FITS file, AJAX request to rea…
Bezbakri Nov 18, 2025
bdff103
Merge remote-tracking branch 'origin' into rishabh-fasterresultspage
Bezbakri Nov 20, 2025
577cc7b
Clicking on cutout PNG now loads the FITS files
Bezbakri Nov 21, 2025
0a1481d
[WIP] Compressed thumbnails, clicking on thumbnails now displays spinner
Bezbakri Dec 10, 2025
4137fdb
[WIP] using jpegs instead of pngs
Bezbakri Dec 15, 2025
59ca212
Merge branch 'main' into rishabh-fasterresultspage
manning-ncsa Dec 16, 2025
d3c89c4
Remove unnecessary boto3 dependency
manning-ncsa Dec 17, 2025
830b8da
Excise TransientInformation task
manning-ncsa Dec 17, 2025
d3b5d68
Reorganize Dockerfile
manning-ncsa Dec 17, 2025
3c00733
Replace periodic image trim task with new workflow Task
manning-ncsa Dec 18, 2025
551b0ec
Fix Prospector module load error when database has not been initialized
manning-ncsa Dec 18, 2025
4aa0d1b
Remove Transient information task from prerequisites
manning-ncsa Dec 18, 2025
fb7eab7
Create TransientTaskRunner classes for generating thumbnails
manning-ncsa Dec 18, 2025
330c0fd
Redesign Celery workflow incorporating crop and thumbnail tasks
manning-ncsa Dec 18, 2025
13d8ae9
Generate thumbnail in workflow
manning-ncsa Dec 19, 2025
629fa4d
Refactor results view to display thumbnail
manning-ncsa Dec 19, 2025
440dd75
Remove Transient information task from the database
manning-ncsa Dec 19, 2025
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
8 changes: 7 additions & 1 deletion app/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,16 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive && apt-get install -y --no-
libnetcdf-dev \
jq \
xxd \
firefox-esr \
&& rm -rf /var/lib/apt/list/*

COPY ./debug/debug_ipython.py /root/.ipython/profile_default/startup/
# Mozilla geckodriver is required for thumbnail generation
RUN wget https://github.com/mozilla/geckodriver/releases/download/v0.36.0/geckodriver-v0.36.0-linux64.tar.gz && \
tar -xvzf geckodriver-v0.36.0-linux64.tar.gz && \
chmod +x geckodriver && \
mv geckodriver /usr/local/bin/

COPY ./debug/debug_ipython.py /root/.ipython/profile_default/startup/
COPY --from=deps /usr/local/lib/python3.11/site-packages/ /usr/local/lib/python3.11/site-packages/
COPY --from=deps /usr/local/bin/ /usr/local/bin/

Expand Down
1 change: 0 additions & 1 deletion app/api/fixtures/test/test_transient_data.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
dec_deg: 2.1707639
host:
- PSO J080624.103+010209.859
image_trim_status: "not ready"
- model: host.Survey
pk: 1
fields:
Expand Down
1 change: 0 additions & 1 deletion app/app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"


MEDIA_URL = "/cutouts/"
MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), "data")

Expand Down
24 changes: 0 additions & 24 deletions app/host/base_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,6 @@
"""This module contains the base classes for TaskRunner in Blast."""


def get_image_trim_status(transient):
# for image trimming to be ready, we need either
# 1) image trimming is already finished
# 2) local AND global photometry validation are not 'not processed'
# 3) progress is complete

if transient.image_trim_status == "processed":
return "processed"
elif transient.processing_status == "completed" or transient.processing_status == "blocked":
return "ready"
else:
global_valid_status = TaskRegister.objects.filter(task__name="Validate local photometry",transient=transient)
local_valid_status = TaskRegister.objects.filter(task__name="Validate global photometry",transient=transient)
if not len(global_valid_status) or not len(local_valid_status):
return "not ready"

global_valid_status = TaskRegister.objects.filter(task__name="Validate local photometry",transient=transient)[0].status.message
local_valid_status = TaskRegister.objects.filter(task__name="Validate global photometry",transient=transient)[0].status.message
if global_valid_status != "not processed" and local_valid_status != "not processed":
return "ready"
else:
return "not ready"


class TaskRunner(ABC):
"""
Abstract base class for a TaskRunner.
Expand Down
11 changes: 3 additions & 8 deletions app/host/trim_images.py → app/host/crop_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,21 @@

from host.models import Cutout
from host.models import Aperture
from host.models import Transient
from host.host_utils import get_local_aperture_size
from host.cutouts import download_and_save_cutouts
from host.object_store import ObjectStore
from django.conf import settings


def trim_images(transient):
def crop_images(transient):

cutouts = Cutout.objects.filter(transient=transient) # ,filter__name='DES_r')
for c in cutouts:
if c.fits.name and os.path.exists(c.fits.name):
trim_image(c)
crop_image(c)

transient = Transient.objects.get(pk=transient.pk)
transient.image_trim_status = "processed"
transient.save()


def trim_image(cutout):
def crop_image(cutout):
transient = cutout.transient
# Download FITS file local file cache
s3 = ObjectStore()
Expand Down
1 change: 0 additions & 1 deletion app/host/debug_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ def rerun_failed_task(task_register):
GlobalAperturePhotometry(task_register.transient.name),
ValidateLocalPhotometry(task_register.transient.name),
ValidateGlobalPhotometry(task_register.transient.name),
TransientInformation(task_register.transient.name),
HostInformation(task_register.transient.name),
GlobalHostSEDFitting(task_register.transient.name),
LocalHostSEDFitting(task_register.transient.name),
Expand Down
2 changes: 0 additions & 2 deletions app/host/fixtures/test/setup_test_transient.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
tns_prefix: AT
ra_deg: 120.027325
dec_deg: 2.1707639
image_trim_status: "not ready"
- model: host.Transient
fields:
name: 2022testtwo
Expand All @@ -15,4 +14,3 @@
tns_prefix: AT
ra_deg: 123.027325
dec_deg: 2.1707639
image_trim_status: "not ready"
1 change: 0 additions & 1 deletion app/host/fixtures/test/test_2010H_onefilter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
spectroscopic_class: SN Ia
milkyway_dust_reddening: 0.026
tasks_initialized: true
image_trim_status: "not ready"
- model: host.TaskRegister
pk: 21
fields:
Expand Down
29 changes: 19 additions & 10 deletions app/host/host_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,16 @@ def check_global_contamination(global_aperture_phot, aperture_primary):
return is_contam


def select_best_cutout(transient_name):
cutouts = Cutout.objects.filter(transient__name__exact=transient_name).filter(~Q(fits=""))
# Choose the cutout from the available filters using the priority define in select_cutout_aperture()
cutout = None
cutout_set = select_cutout_aperture(cutouts).filter(~Q(fits=""))
if len(cutout_set):
cutout = cutout_set[0]
return cutout


def select_cutout_aperture(cutouts, choice=0):
"""
Select cutout for aperture by searching through the available filters.
Expand Down Expand Up @@ -444,17 +454,16 @@ def select_cutout_aperture(cutouts, choice=0):


def select_aperture(transient):
'''Select the best Aperture object for the input transient.
Returns a QuerySet of Aperture objects.'''
# Find all cutouts that have an associated FITS image file
cutouts = Cutout.objects.filter(transient=transient).filter(~Q(fits=""))
if len(cutouts):
cutout_for_aperture = select_cutout_aperture(cutouts)
if len(cutouts) and len(cutout_for_aperture):
global_aperture = Aperture.objects.filter(
type__exact="global", transient=transient, cutout=cutout_for_aperture[0]
)
else:
global_aperture = Aperture.objects.none()

return global_aperture
# If no cutout images are found, return an empty Aperture set
if not len(cutouts):
return Aperture.objects.none()
cutout_for_aperture = select_cutout_aperture(cutouts)
if len(cutout_for_aperture):
return Aperture.objects.filter(type__exact="global", transient=transient, cutout=cutout_for_aperture[0])


def estimate_background(image, filter_name=None):
Expand Down
50 changes: 50 additions & 0 deletions app/host/migrations/0039_remove_transient_image_trim_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Generated by Django 5.1.14 on 2025-12-17 22:41

from django.db import migrations


def add_new_tasks(apps, schema_editor):
Task = apps.get_model("host", "Task")
for name in [
'Crop transient images',
'Generate thumbnail',
'Generate thumbnail final',
]:
Task(name=name).save()


def migrate_trim_status(apps, schema_editor):
Transient = apps.get_model("host", "Transient")
TaskRegister = apps.get_model("host", "TaskRegister")
Task = apps.get_model("host", "Task")
Status = apps.get_model("host", "Status")

task = Task.objects.get(name__exact="Crop transient images")
processed_status = Status.objects.get(message__exact="processed")
not_processed_status = Status.objects.get(message__exact="not processed")
for transient in Transient.objects.all():
task_register, created = TaskRegister.objects.get_or_create(
transient=transient,
task=task,
status=processed_status if transient.image_trim_status == "processed" else not_processed_status,
)
if not created:
print(f'''WARNING: Unexpected TaskRegister object already exists for "{task.name}" '''
f'''with status "{task_register.status.message}".''')


class Migration(migrations.Migration):

dependencies = [
('host', '0038_alter_transient_name'),
]

operations = [
migrations.RunPython(add_new_tasks),
migrations.RunPython(migrate_trim_status),
migrations.RemoveField(
model_name='transient',
name='image_trim_status',
),
]

24 changes: 24 additions & 0 deletions app/host/migrations/0040_remove_transient_information.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from django.db import migrations


def remove_registered_tasks(apps, schema_editor):
TaskRegister = apps.get_model("host", "TaskRegister")
for registered_task in TaskRegister.objects.filter(task__name="Transient information"):
registered_task.delete()


def remove_task(apps, schema_editor):
Task = apps.get_model("host", "Task")
Task.objects.get(name='Transient information').delete()


class Migration(migrations.Migration):

dependencies = [
('host', '0039_remove_transient_image_trim_status'),
]

operations = [
migrations.RunPython(remove_registered_tasks),
migrations.RunPython(remove_task),
]
3 changes: 0 additions & 3 deletions app/host/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,6 @@ class Transient(SkyObject):
photometric_class = models.CharField(max_length=20, null=True, blank=True)
milkyway_dust_reddening = models.FloatField(null=True, blank=True)
processing_status = models.CharField(max_length=20, default="processing")
# trim images to save disk space
# status options will be "not ready", "ready", and "processed"
image_trim_status = models.CharField(max_length=20, default="not ready")
added_by = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)
progress = models.IntegerField(default=0)
software_version = models.CharField(max_length=50, blank=True, null=True)
Expand Down
3 changes: 1 addition & 2 deletions app/host/plotting_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ def generate_plot(fig, image_data):
""")
fig.x_range.js_on_change('end', hide_loading_indicator)
plot_image(image_data, fig)

script, div = components(fig)
return {"bokeh_cutout_script": script, "bokeh_cutout_div": div}

Expand All @@ -167,8 +166,8 @@ def generate_empty_plot(title):

# Load image data from FITS file
local_fits_path = cutout.fits.name
s3 = ObjectStore()
if not os.path.isfile(local_fits_path):
s3 = ObjectStore()
object_key = os.path.join(settings.S3_BASE_PATH, local_fits_path.strip('/'))
if s3.object_exists(object_key):
# Download FITS file local file cache
Expand Down
14 changes: 7 additions & 7 deletions app/host/prospector.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,6 @@
from host.log import get_logger
logger = get_logger(__name__)

try:
all_filters = [filt for filt in Filter.objects.all().select_related()]
trans_curves = [f.transmission_curve() for f in all_filters]
except (ProgrammingError, ValueError) as err:
logger.error(err)
pass


# add redshift scaling to agebins, such that
# t_max = t_univ
Expand Down Expand Up @@ -124,6 +117,13 @@ def build_obs(transient, aperture_type, use_mag_offset=True):

filters, flux_maggies, flux_maggies_error = [], [], []

try:
all_filters = [filt for filt in Filter.objects.all().select_related()]
trans_curves = [f.transmission_curve() for f in all_filters]
except (ProgrammingError, ValueError) as err:
logger.error(err)
pass

for filter, trans_curve in zip(all_filters, trans_curves):
try:
if filter.name in filter_names:
Expand Down
2 changes: 0 additions & 2 deletions app/host/slurm/run_single_transient.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
"Host MWEBV",
"Cutout download",
"Host information",
"Transient information",
"Global aperture construction",
"Global aperture photometry",
"Validate global photometry",
Expand All @@ -46,7 +45,6 @@
host.transient_tasks.MWEBV_Host(),
host.transient_tasks.ImageDownload(),
host.transient_tasks.HostInformation(),
host.transient_tasks.TransientInformation(),
host.transient_tasks.GlobalApertureConstruction(),
host.transient_tasks.GlobalAperturePhotometry(),
host.transient_tasks.ValidateGlobalPhotometry(),
Expand Down
33 changes: 0 additions & 33 deletions app/host/system_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from host.base_tasks import task_time_limit
from host.workflow import reprocess_transient
from host.workflow import transient_workflow
from host.trim_images import trim_images
from host.host_utils import inspect_worker_tasks
from host.host_utils import wait_for_free_space
from host.host_utils import reset_workflow_if_not_processing
Expand Down Expand Up @@ -256,38 +255,6 @@ def retrigger_incomplete_workflows():
RetriggerIncompleteWorkflows().run_process()


class TrimTransientImages(SystemTaskRunner):
def run_process(self):
"""
Updates the processing status for all transients.
"""
transients = Transient.objects.filter(image_trim_status="ready")

for transient in transients:
trim_images(transient)

@property
def task_name(self):
return "Trim transient images"

@property
def task_frequency_seconds(self):
return 3600

@property
def task_initially_enabled(self):
return True


# Periodic tasks
@shared_task(
time_limit=task_time_limit,
soft_time_limit=task_soft_time_limit,
)
def trim_transient_images():
TrimTransientImages().run_process()


@shared_task(
time_limit=task_time_limit,
soft_time_limit=task_soft_time_limit,
Expand Down
2 changes: 0 additions & 2 deletions app/host/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from host.system_tasks import InitializeTransientTasks
from host.system_tasks import SnapshotTaskRegister
from host.system_tasks import TNSDataIngestion
from host.system_tasks import TrimTransientImages
from host.system_tasks import RetriggerIncompleteWorkflows
from host.system_tasks import UsageLogRoller
from host.system_tasks import GarbageCollector
Expand All @@ -29,7 +28,6 @@
InitializeTransientTasks(),
SnapshotTaskRegister(),
IngestMissedTNSTransients(),
TrimTransientImages(),
RetriggerIncompleteWorkflows(),
GarbageCollector(),
UsageLogRoller(),
Expand Down
3 changes: 2 additions & 1 deletion app/host/templates/host/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
margin: 2rem;
margin-left: auto;
margin-right: auto;
display: none;
}

@keyframes spin {
Expand Down Expand Up @@ -204,7 +205,7 @@
</footer>

<!-- Bootstrap -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.3/dist/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.1.3/dist/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<!-- Bokeh -->
Expand Down
Loading