Skip to content

Commit e080ea2

Browse files
GOATS-1305: update code to support breaking changes in latest gpp client version (#644)
* GOATS-1305: Update code to support breaking changes in latest GPP client version * GOATS-1305: Remove stale settings patch from finder chart tests * GOATS-1305: Pin gpp-client to 26.5.0 to match conda environment * Add tests * GOATS-1305: Adapt observation views to Pydantic workflow state result
1 parent 171d434 commit e080ea2

49 files changed

Lines changed: 3266 additions & 1544 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ dependencies = [
3333
"djangorestframework>=3.16.1,<4",
3434
"dramatiq-abort>=1.2.1",
3535
"dramatiq[redis,watch]>=1.18.0,<2",
36-
"gpp-client==26.4.0",
36+
"gpp-client==26.5.0",
3737
"marshmallow==3.26.2",
3838
"marshmallow-jsonapi>=0.24.0",
3939
"numpydoc>=1.9.0,<2",

src/goats_tom/api_views/gpp/finder_chart.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import logging
22

33
from asgiref.sync import async_to_sync
4-
from django.conf import settings
54
from django.core.cache import cache
65
from gpp_client import GPPClient
76
from rest_framework import permissions, status
@@ -74,7 +73,7 @@ def _run_with_client(self, *, token: str, coro):
7473
"""
7574

7675
async def _runner():
77-
client = GPPClient(env=settings.GPP_ENV, token=token)
76+
client = GPPClient(token=token)
7877
try:
7978
return await coro(client)
8079
finally:

src/goats_tom/api_views/gpp/gpp.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
__all__ = ["GPPViewSet"]
66

77
from asgiref.sync import async_to_sync
8-
from django.conf import settings
98
from gpp_client import GPPClient
109
from rest_framework import permissions, status, viewsets
1110
from rest_framework.decorators import action
@@ -36,8 +35,8 @@ def ping(self, request: Request) -> Response:
3635
)
3736

3837
credentials = request.user.gpplogin
39-
client = GPPClient(token=credentials.token, env=settings.GPP_ENV)
40-
reachable, error = async_to_sync(client.is_reachable)()
38+
client = GPPClient(token=credentials.token)
39+
reachable, error = async_to_sync(client.ping)()
4140

4241
if reachable:
4342
return Response({"detail": "Successfully connected to GPP."})

src/goats_tom/api_views/gpp/observations.py

Lines changed: 85 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
from typing import Any, List
1010

1111
from asgiref.sync import async_to_sync
12-
from django.conf import settings
13-
from gpp_client import GPPClient, GPPDirector
14-
from gpp_client.api.enums import AttachmentType
15-
from gpp_client.api.input_types import (
12+
from gpp_client import GPPClient
13+
from gpp_client.generated.enums import AttachmentType
14+
from gpp_client.generated.input_types import (
15+
CloneObservationInput,
1616
TargetEnvironmentInput,
1717
)
1818
from rest_framework import permissions, status
@@ -74,6 +74,12 @@ class StageMessage:
7474
message: str
7575

7676

77+
def _format_workflow_state(workflow_state) -> str:
78+
state = getattr(workflow_state, "state", None)
79+
value = getattr(state, "value", None)
80+
return value or "unknown"
81+
82+
7783
def build_failure_response(
7884
stage: Stage,
7985
error: Exception | str,
@@ -187,9 +193,11 @@ def _process_finder_charts(
187193
f"Failed to delete finder chart '{attachment_id}': {e}"
188194
) from e
189195
try:
190-
attachment_data = async_to_sync(client.attachment.get_all_by_observation)(
191-
observation_id=observation_id,
192-
observation_reference=None,
196+
attachment_result = async_to_sync(
197+
client.attachment.get_all_by_observation_id
198+
)(observation_id=observation_id)
199+
attachment_data = (
200+
attachment_result.model_dump(by_alias=True).get("observation") or {}
193201
)
194202
except Exception as e:
195203
raise ValueError(
@@ -293,36 +301,36 @@ def list(self, request: Request, *args, **kwargs) -> Response:
293301

294302
try:
295303
# Setup client to communicate with GPP.
296-
client = GPPClient(env=settings.GPP_ENV, token=credentials.token)
297-
director = GPPDirector(client)
304+
client = GPPClient(token=credentials.token)
298305
if program_id is not None:
299306
logger.debug(
300307
"Retrieving GPP observations for program ID: %s", program_id
301308
)
302-
payload = async_to_sync(director.goats.observation.get_all)(
309+
payload = async_to_sync(client.goats.get_observations_by_program_id)(
303310
program_id=program_id
304311
)
312+
data = payload.model_dump(by_alias=True)["observations"]
305313
# Filter the observations into too and normal categories.
306-
matches = payload.get("matches", [])
314+
matches = data.get("matches", [])
307315
too_obs = [o for o in matches if self.is_too(o)]
308316
normal_obs = [o for o in matches if not self.is_too(o)]
309317

310-
# Build the custom payload response.
318+
# Build the custom data response.
311319
return Response(
312320
{
313321
"matches": {
314322
"too": {"count": len(too_obs), "results": too_obs},
315323
"normal": {"count": len(normal_obs), "results": normal_obs},
316324
},
317-
"hasMore": payload.get("hasMore", False),
325+
"hasMore": data.get("hasMore", False),
318326
}
319327
)
320328
else:
321329
logger.debug(
322330
"Retrieving all GPP observations for user: %s", request.user
323331
)
324332
payload = async_to_sync(client.observation.get_all)()
325-
return Response(payload)
333+
return Response(payload.model_dump(by_alias=True))
326334
except Exception as e:
327335
logger.exception("Error retrieving GPP observations")
328336
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
@@ -388,11 +396,11 @@ def retrieve(self, request: Request, *args, **kwargs) -> Response:
388396

389397
# Setup client to communicate with GPP.
390398
try:
391-
client = GPPClient(env=settings.GPP_ENV, token=credentials.token)
392-
observation = async_to_sync(client.observation.get_by_id)(
399+
client = GPPClient(token=credentials.token)
400+
data = async_to_sync(client.observation.get_by_id)(
393401
observation_id=observation_id
394402
)
395-
return Response(observation)
403+
return Response(data.model_dump(by_alias=True)["observation"])
396404
except Exception as e:
397405
logger.exception("Error retrieving GPP observation")
398406
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
@@ -508,7 +516,8 @@ def update_only(self, request: Request, *args, **kwargs) -> Response:
508516
A DRF Response object containing the details of the updated observation.
509517
"""
510518
messages: list[StageMessage] = []
511-
data: dict[str, Any] = {}
519+
updated_target_id: str | None = None
520+
updated_observation_id: str | None = None
512521

513522
logger.info("Updating observation on GPP")
514523

@@ -549,7 +558,7 @@ def update_only(self, request: Request, *args, **kwargs) -> Response:
549558
client = None
550559
try:
551560
# Setup client to communicate with GPP.
552-
client = GPPClient(env=settings.GPP_ENV, token=credentials.token)
561+
client = GPPClient(token=credentials.token)
553562

554563
# Validate and extract required IDs for observation update.
555564
context_serializer = ContextSerializer(data=normalized_data)
@@ -598,16 +607,17 @@ def update_only(self, request: Request, *args, **kwargs) -> Response:
598607
logger.debug("Updating sidereal target in GPP")
599608
try:
600609
update_target_result = async_to_sync(client.target.update_by_id)(
601-
target_id=gpp_target_id, properties=target_properties
610+
gpp_target_id, properties=target_properties
602611
)
603-
updated_target_id = update_target_result.get("id")
612+
update_target_dump = update_target_result.model_dump(by_alias=True)
613+
targets = update_target_dump.get("updateTargets", {}).get("targets", [])
614+
updated_target_id = targets[0].get("id") if targets else None
604615

605616
if updated_target_id is None:
606617
raise ValueError(
607618
"Failed to retrieve updated target ID from update result."
608619
)
609620

610-
data["updatedTargetId"] = updated_target_id
611621
messages.append(
612622
StageMessage(
613623
stage=Stage.UPDATE_TARGET,
@@ -629,9 +639,9 @@ def update_only(self, request: Request, *args, **kwargs) -> Response:
629639
# Update observation.
630640
logger.debug("Updating observation in GPP")
631641
try:
632-
finder_charts = normalized_data.get("finderCharts", None)
642+
finder_charts = normalized_data.get("finderCharts") or {}
633643

634-
if finder_charts:
644+
if finder_charts.get("toAdd") or finder_charts.get("toDelete"):
635645
finder_chart_ids = self._process_finder_charts(
636646
client=client,
637647
observation_id=gpp_observation_id,
@@ -645,14 +655,21 @@ def update_only(self, request: Request, *args, **kwargs) -> Response:
645655
update_observation_result = async_to_sync(client.observation.update_by_id)(
646656
observation_id=gpp_observation_id, properties=observation_properties
647657
)
648-
updated_observation_id = update_observation_result.get("id")
658+
update_observation_dump = update_observation_result.model_dump(
659+
by_alias=True
660+
)
661+
observations_list = update_observation_dump.get(
662+
"updateObservations", {}
663+
).get("observations", [])
664+
updated_observation_id = (
665+
observations_list[0].get("id") if observations_list else None
666+
)
649667

650668
if updated_observation_id is None:
651669
raise ValueError(
652670
"Failed to retrieve updated observation ID from update result."
653671
)
654672

655-
data["updatedObservationId"] = updated_observation_id
656673
messages.append(
657674
StageMessage(
658675
stage=Stage.UPDATE_OBSERVATION,
@@ -689,7 +706,8 @@ def update_only(self, request: Request, *args, **kwargs) -> Response:
689706
StageMessage(
690707
stage=Stage.UPDATE_WORKFLOW_STATE,
691708
status=MessageStatus.SUCCESS,
692-
message=f"Workflow state set to {new_workflow_state['state']}.",
709+
message="Workflow state set to "
710+
f"{_format_workflow_state(new_workflow_state)}.",
693711
)
694712
)
695713

@@ -702,13 +720,19 @@ def update_only(self, request: Request, *args, **kwargs) -> Response:
702720
message=str(e),
703721
)
704722
)
705-
706723
if client is not None:
707724
try:
708725
async_to_sync(client.close)()
709726
except Exception:
710727
logger.debug("Failed to close GPP client.", exc_info=True)
711-
return self._build_structured_response(messages=messages, data=data)
728+
729+
return self._build_structured_response(
730+
messages=messages,
731+
data={
732+
"updatedTargetId": updated_target_id,
733+
"updatedObservationId": updated_observation_id,
734+
},
735+
)
712736

713737
@action(detail=False, methods=["post"], url_path="create-and-save")
714738
def create_and_save_observation(
@@ -732,7 +756,9 @@ def create_and_save_observation(
732756
A DRF Response object containing the details of the created observation.
733757
"""
734758
messages: list[StageMessage] = []
735-
data: dict[str, Any] = {}
759+
new_target_id: str | None = None
760+
new_observation_id: str | None = None
761+
goats_observation: dict[str, Any] | None = None
736762

737763
logger.info(
738764
"Creating observation on GPP and saving observation to GOATS database"
@@ -775,7 +801,7 @@ def create_and_save_observation(
775801
client = None
776802
try:
777803
# Setup client to communicate with GPP.
778-
client = GPPClient(env=settings.GPP_ENV, token=credentials.token)
804+
client = GPPClient(token=credentials.token)
779805

780806
# Validate and extract required IDs for observation creation.
781807
context_serializer = ContextSerializer(data=normalized_data)
@@ -823,14 +849,16 @@ def create_and_save_observation(
823849
logger.debug("Creating sidereal target in GPP")
824850
try:
825851
clone_target_result = async_to_sync(client.target.clone)(
826-
target_id=gpp_target_id, properties=target_properties
852+
gpp_target_id, properties=target_properties
853+
)
854+
clone_target_dump = clone_target_result.model_dump(by_alias=True)
855+
new_target_id = (
856+
clone_target_dump.get("cloneTarget", {}).get("newTarget", {}).get("id")
827857
)
828-
new_target_id = clone_target_result.get("newTarget", {}).get("id")
829858

830859
if new_target_id is None:
831860
raise ValueError("Failed to retrieve new target ID from clone result.")
832861

833-
data["newTargetId"] = new_target_id
834862
messages.append(
835863
StageMessage(
836864
stage=Stage.CREATE_TARGET,
@@ -849,8 +877,8 @@ def create_and_save_observation(
849877
asterism=[new_target_id]
850878
)
851879

852-
finder_charts = normalized_data.get("finderCharts", None)
853-
if finder_charts:
880+
finder_charts = normalized_data.get("finderCharts") or {}
881+
if finder_charts.get("toAdd") or finder_charts.get("toDelete"):
854882
finder_chart_ids = self._process_finder_charts(
855883
client=client,
856884
observation_id=gpp_observation_id,
@@ -859,18 +887,24 @@ def create_and_save_observation(
859887
)
860888
observation_properties.attachments = finder_chart_ids
861889

890+
clone_input = CloneObservationInput(
891+
observation_id=gpp_observation_id,
892+
set_=observation_properties,
893+
)
862894
clone_observation_result = async_to_sync(client.observation.clone)(
863-
observation_id=gpp_observation_id, properties=observation_properties
895+
input=clone_input,
896+
)
897+
clone_observation_dump = clone_observation_result.model_dump(by_alias=True)
898+
new_observation = clone_observation_dump.get("cloneObservation", {}).get(
899+
"newObservation", {}
864900
)
865-
new_observation = clone_observation_result.get("newObservation", {})
866901
new_observation_id = new_observation.get("id")
867902

868903
if new_observation_id is None:
869904
raise ValueError(
870905
"Failed to retrieve new observation ID from clone result."
871906
)
872907

873-
data["newObservationId"] = new_observation_id
874908
messages.append(
875909
StageMessage(
876910
stage=Stage.CREATE_OBSERVATION,
@@ -905,7 +939,8 @@ def create_and_save_observation(
905939
StageMessage(
906940
stage=Stage.UPDATE_WORKFLOW_STATE,
907941
status=MessageStatus.SUCCESS,
908-
message=f"Workflow state set to {new_workflow_state['state']}.",
942+
message=f"Workflow state set to "
943+
f"{_format_workflow_state(new_workflow_state)}.",
909944
)
910945
)
911946

@@ -940,7 +975,7 @@ def create_and_save_observation(
940975
raise ValueError(tom_response.data)
941976

942977
else:
943-
data["goatsObservation"] = tom_response.data
978+
goats_observation = tom_response.data
944979
messages.append(
945980
StageMessage(
946981
stage=Stage.GOATS_OBSERVATION_SAVE,
@@ -958,12 +993,20 @@ def create_and_save_observation(
958993
)
959994
)
960995

996+
response_data: dict[str, Any] = {
997+
"newTargetId": new_target_id,
998+
"newObservationId": new_observation_id,
999+
}
1000+
if goats_observation is not None:
1001+
response_data["goatsObservation"] = goats_observation
1002+
9611003
if client is not None:
9621004
try:
9631005
async_to_sync(client.close)()
9641006
except Exception:
9651007
logger.debug("Failed to close GPP client.", exc_info=True)
966-
return self._build_structured_response(messages=messages, data=data)
1008+
1009+
return self._build_structured_response(messages=messages, data=response_data)
9671010

9681011
def _create_goats_observation(
9691012
self,

0 commit comments

Comments
 (0)