Skip to content

Commit af98b9e

Browse files
authored
GOATS-845 GOATS-723: Add GPP program ID field and GPP URL generation for GEM observations. (#488)
- Introduced `gpp_program_id` field in `GEMObservationForm`. - Updated `observationrecord_detail.html` to use `gpp_url` for the GEM facility. - Implemented GPP URL generation logic in `ObservationRecordDetailView`. - If the GPP URL can be generated, it will be displayed as an enabled button linking to the GPP page for the observation.
1 parent e50a53a commit af98b9e

5 files changed

Lines changed: 59 additions & 8 deletions

File tree

docs/changes/488.new.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added the link to Gemini Explore for observations with GPP-style IDs on the observation detail page.

src/goats_tom/facilities/gemini.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747

4848
ocs_client = OCSClient()
4949

50+
51+
5052
class GEMObservationForm(BaseRoboticObservationForm):
5153
gpp_id = forms.CharField(required=True)
5254
observation_id = forms.CharField(required=True)
@@ -56,6 +58,7 @@ class GEMObservationForm(BaseRoboticObservationForm):
5658
cloud_extinction = forms.CharField(required=False)
5759
sky_background = forms.CharField(required=False)
5860
water_vapor = forms.CharField(required=False)
61+
gpp_program_id = forms.CharField(required=False)
5962

6063
# All FIELD_MAP keys are relative to `observing_parameters`, which is passed
6164
# directly as the `data` dict to this form. So do NOT prefix paths with
@@ -71,6 +74,7 @@ class GEMObservationForm(BaseRoboticObservationForm):
7174
"water_vapor": "constraintSet.waterVapor",
7275
"target_id": "target_id",
7376
"facility": "facility",
77+
"gpp_program_id": "program.id",
7478
}
7579

7680
FIELD_MAP = {}

src/goats_tom/templates/tom_observations/observationrecord_detail.html

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,15 @@ <h2>{{ object.observation_id }} @{{ object.facility }}</h2>
1616
{% if editable %}
1717
<p>{% update_observation_id_form object %}</p>
1818
{% endif %}
19-
{% if object.url %}
2019
<p class="my-auto pb-3">
21-
<a class="btn btn-outline-primary {% if object.facility == 'GEM' %}disabled{% endif %}" href="{{ object.url }}" target="_blank">View at Observatory »</a>
20+
<a
21+
class="btn btn-outline-primary {% if not gpp_url %}disabled{% endif %}"
22+
href="{{ gpp_url|default:'#' }}"
23+
target="_blank"
24+
>
25+
View at Observatory »
26+
</a>
2227
</p>
23-
{% endif %}
2428
<p class="my-auto"><strong>Created:</strong> {{ object.created }} <strong>Modified:</strong> {{ object.modified }}</p>
2529
<p class="my-auto"><strong>Status:</strong> {{ object.status }}</p>
2630
</div>

src/goats_tom/templatetags/gemini.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
# Standard library imports.
2-
3-
# Related third party imports.
4-
5-
# Local application/library specific imports.
61
from typing import Any
72

83
from django import template

src/goats_tom/views/observation_record_detail.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
__all__ = ["ObservationRecordDetailView"]
2+
import logging
3+
import re
4+
from typing import Any
5+
26
from django.urls import reverse
37
from django.views.generic import DetailView
48
from tom_dataproducts.forms import AddProductToGroupForm, DataProductUploadForm
@@ -10,6 +14,26 @@
1014
ObservationRecordDetailView as BaseObservationRecordDetailView,
1115
)
1216

17+
logger = logging.getLogger(__name__)
18+
19+
20+
def _is_gpp_id(obs_id: str) -> bool:
21+
"""
22+
Return True if the observation ID is a GPP-style ID, otherwise False.
23+
24+
Parameters
25+
----------
26+
obs_id : str
27+
The observation ID to check.
28+
29+
Returns
30+
-------
31+
bool
32+
True if it is a GPP ID (e.g., G-2026A-0166-Q-0001), False otherwise.
33+
"""
34+
pattern = re.compile(r"^G-(?![NS]-)")
35+
return bool(pattern.match(obs_id))
36+
1337

1438
class ObservationRecordDetailView(BaseObservationRecordDetailView):
1539
"""View to override creating thumbnails."""
@@ -39,4 +63,27 @@ def get_context_data(self, *args, **kwargs):
3963
)
4064
context["data_product_form"] = data_product_upload_form
4165
context["observation_id"] = observation_record.observation_id
66+
# Add GPP URL if applicable
67+
context["gpp_url"] = self._get_gpp_url(observation_record)
4268
return context
69+
70+
@staticmethod
71+
def _get_gpp_url(observation_record: Any) -> str | None:
72+
"""Return the Explore URL if this is a GPP Gemini observation."""
73+
if (
74+
not _is_gpp_id(observation_record.observation_id)
75+
or observation_record.facility != "GEM"
76+
):
77+
return None
78+
79+
try:
80+
program_id = observation_record.parameters.get("gpp_program_id")
81+
obs_id = observation_record.parameters.get("gpp_id")
82+
if program_id and obs_id:
83+
return f"https://explore.gemini.edu/{program_id}/observation/{obs_id}"
84+
except Exception:
85+
logger.exception(
86+
"Failed to build GPP URL for observation %s",
87+
observation_record.observation_id,
88+
)
89+
return None

0 commit comments

Comments
 (0)