From 44ca1a0cb1063d63adef4142310e86b44b804131 Mon Sep 17 00:00:00 2001 From: Craig Pellegrino Date: Mon, 3 Aug 2026 16:34:13 -0400 Subject: [PATCH 1/2] DRAFT: first attempt at linking obs table rows to data archives --- poetry.lock | 16 ++++++---- pyproject.toml | 3 +- tom_across/tables.py | 70 ++++++++++++++++++++++++++++++++++++++++++++ tom_across/utils.py | 22 +++++++++++++- 4 files changed, 103 insertions(+), 8 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4e95e00..3081219 100644 --- a/poetry.lock +++ b/poetry.lock @@ -36,15 +36,13 @@ urllib3 = ">=2.1.0,<3.0.0" [[package]] name = "across-tools" -version = "1.6.1" +version = "1.6.2.dev1" description = "" optional = false python-versions = ">=3.10" groups = ["main"] -files = [ - {file = "across_tools-1.6.1-py3-none-any.whl", hash = "sha256:e794fae9d81610d2abe6fe9f9b4dbc94bec9a09ed87f1a2fdc06a47f0ddf23b3"}, - {file = "across_tools-1.6.1.tar.gz", hash = "sha256:5400c890b5c42eac4259cd6827c01c12cc55129952c2fee739e846e14eefdd51"}, -] +files = [] +develop = false [package.dependencies] astropy = ">=5" @@ -62,6 +60,12 @@ shapely = ">=2.0.4" spacetrack = ">=1.0.0" spiceypy = ">=6.0.0" +[package.source] +type = "git" +url = "https://github.com/NASA-ACROSS/across-tools" +reference = "170-add-functionality-to-link-observations-to-archival-data-pages" +resolved_reference = "8b75f4a598f76f884408391008fb88d09d0efb17" + [[package]] name = "alerce" version = "2.3.0" @@ -4149,4 +4153,4 @@ type = ["pytest-mypy (>=1.0.1) ; platform_python_implementation != \"PyPy\""] [metadata] lock-version = "2.1" python-versions = ">=3.10.0,<3.14" -content-hash = "a267435baaf4e9f2d907c16815188f34c216cb54e760c19058f7d2be1801505a" +content-hash = "2afcf6956fdf6f53b5753003c8d005772efb12206299aa6ab2718a00b12bead5" diff --git a/pyproject.toml b/pyproject.toml index 5c73ae8..22e25a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,8 @@ dynamic = ["version"] requires-python = ">=3.10.0,<3.14" dependencies = [ "tomtoolkit >=3.0.0,<4.0", - "across-client" + "across-client", + "across-tools @ git+https://github.com/NASA-ACROSS/across-tools@170-add-functionality-to-link-observations-to-archival-data-pages" ] [tool.poetry] diff --git a/tom_across/tables.py b/tom_across/tables.py index 5d5ffac..04f8031 100644 --- a/tom_across/tables.py +++ b/tom_across/tables.py @@ -1,8 +1,13 @@ import django_tables2 as tables from tom_common.htmx_table import HTMXTable from django.urls import reverse +from django.utils.html import format_html import logging +from tom_across.utils import get_across_instrument_ids, get_across_observatory_telescope_name_map + +from across.tools.archive_resolver import HEASARCArchiveResolver, MASTArchiveResolver + logger = logging.getLogger(__name__) class ObservationTable(HTMXTable): telescope = tables.Column() @@ -12,6 +17,7 @@ class ObservationTable(HTMXTable): type = tables.Column() filter_name = tables.Column() wavelength_range = tables.Column() + external_observation_id = tables.Column() selection = None @@ -43,3 +49,67 @@ def render_wavelength_range(self, value): return f"{rendered_min} - {rendered_max}" except (ValueError, TypeError): return value + + def render_external_observation_id(self, value): + """ + Render the external observation ID as a hyperlink to the corresponding archive page. + + Args: + value (tuple): A tuple containing the instrument ID and the external observation ID. + + Returns: + str: The external observation ID, as an href link to the corresponding archive page if resolvable, + or just the ID if not. + """ + if not value: + return "" + try: + inst_id_dict = get_across_instrument_ids() + inst_tele_name = inst_id_dict[value[0]][1] + + obs_tele_name_dict = get_across_observatory_telescope_name_map() + name_to_try = None + for obs_name, tele_names in obs_tele_name_dict.items(): + if inst_tele_name in tele_names: + name_to_try = obs_name + break + + if not name_to_try: + return value[1] + + if name_to_try in ["HST", "JWST"]: + resolver = MASTArchiveResolver( + external_observation_id=value[1], + mission=name_to_try + ) + + elif name_to_try in [ + "Chandra", "IXPE", "NICER", "NuSTAR", "XMM-Newton", "Swift", "XRISM" + ]: + name_table_dict = { + "Chandra": "chanmastr", + "IXPE": "ixmaster", + "NICER": "nicermastr", + "NuSTAR": "numaster", + "Swift": "swiftmastr", + "XMM-Newton": "xmmmaster", + "XRISM": "xrismmastr", + } + resolver = HEASARCArchiveResolver( + external_observation_id=value[1], + heasarc_table=name_table_dict[name_to_try] + ) + else: + return value[1] + + resolver.construct_archive_url() + if resolver.archive_url: + return format_html( + f'{value[1]}' + ) + else: + logger.warning(f"Could not resolve archive URL for observation ID: {value}") + return value[1] + except Exception as e: + logger.error(f"Error resolving archive URL for observation ID {value}: {e}") + return value[1] diff --git a/tom_across/utils.py b/tom_across/utils.py index 3e5ffc0..b701c19 100644 --- a/tom_across/utils.py +++ b/tom_across/utils.py @@ -194,7 +194,8 @@ def observation_rows(target, start_date=None, end_date=None, wavelength_type=Non 'date': obs.date_range.end, 'type': getattr(obs.type, 'value', obs.type), 'filter_name': band, - 'wavelength_range': (min_band, max_band) + 'wavelength_range': (min_band, max_band), + 'external_observation_id': (obs.instrument_id, obs.external_observation_id) }) except ServiceException as e: @@ -219,4 +220,23 @@ def get_across_instrument_ids(): cache.set(cache_key, data, timeout=24 * 60 * 60) # Cache for 24 hours + return data + +def get_across_observatory_telescope_name_map(): + """ + Build a dictionary of ACROSS observatory names and their corresponding telescope names. + Cached for 24 hours, refreshed on cache miss. + """ + cache_key = "across_observatory_telescope_name_map" + data = cache.get(cache_key) + + if data is None: + print('GETTING OBSERVATORY TELESCOPE NAMES FROM ACROSS') + observatories = client.observatory.get_many() + data = {} + for obs in observatories: + data[obs.short_name] = [tele.name for tele in obs.telescopes] + + cache.set(cache_key, data, timeout=24 * 60 * 60) # Cache for 24 hours + return data \ No newline at end of file From 4787eb792039bab15235a22968a2a072026f323f Mon Sep 17 00:00:00 2001 From: Craig Pellegrino Date: Tue, 4 Aug 2026 14:33:48 -0400 Subject: [PATCH 2/2] update dependency, fix for JWST program IDs --- poetry.lock | 2 +- tom_across/tables.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3081219..ddf99a2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -64,7 +64,7 @@ spiceypy = ">=6.0.0" type = "git" url = "https://github.com/NASA-ACROSS/across-tools" reference = "170-add-functionality-to-link-observations-to-archival-data-pages" -resolved_reference = "8b75f4a598f76f884408391008fb88d09d0efb17" +resolved_reference = "75df1a6868845b9027451efbc3585787b95a34d9" [[package]] name = "alerce" diff --git a/tom_across/tables.py b/tom_across/tables.py index 04f8031..702f28f 100644 --- a/tom_across/tables.py +++ b/tom_across/tables.py @@ -79,7 +79,7 @@ def render_external_observation_id(self, value): if name_to_try in ["HST", "JWST"]: resolver = MASTArchiveResolver( - external_observation_id=value[1], + external_observation_id=value[1].split(":")[0], mission=name_to_try )