From f11a524016d1df7e9e6e22623ac25d41f7c95de6 Mon Sep 17 00:00:00 2001 From: Rob Norris Date: Fri, 25 Oct 2024 10:53:52 -0500 Subject: [PATCH] remove previous user state bits --- .../lucuma/odb/graphql/OdbSchema.graphql | 146 ---- .../db/migration/V0924__obs_workflow.sql | 28 + .../lucuma/odb/graphql/BaseMapping.scala | 2 - .../graphql/binding/ObsStatusBinding.scala | 9 - .../binding/OcsActiveStatusBinding.scala | 9 - .../input/ObservationPropertiesInput.scala | 26 - .../odb/graphql/input/WhereObservation.scala | 15 +- .../odb/graphql/mapping/LeafMappings.scala | 2 - .../graphql/mapping/ObservationMapping.scala | 18 - .../odb/graphql/table/ObservationView.scala | 5 +- .../odb/logic/TimeEstimateService.scala | 3 +- .../odb/service/GeneratorParamsService.scala | 17 +- .../odb/service/ObservationService.scala | 29 - .../service/ObservationWorkflowService.scala | 35 +- .../main/scala/lucuma/odb/util/Codecs.scala | 6 - .../odb/graphql/feature/calibrations.scala | 2 +- .../odb/graphql/issue/shortcut/2772.scala | 4 +- .../graphql/mutation/cloneObservation.scala | 178 +++-- .../graphql/mutation/createObservation.scala | 229 +++--- .../graphql/mutation/updateObservations.scala | 151 ++-- .../query/ObservingModeSetupOperations.scala | 27 +- .../odb/graphql/query/executionFailures.scala | 6 +- .../query/observationValidations.scala | 690 ------------------ .../graphql/query/observation_workflow.scala | 2 +- .../graphql/query/programPlannedTime.scala | 189 +++-- 25 files changed, 400 insertions(+), 1428 deletions(-) create mode 100644 modules/service/src/main/resources/db/migration/V0924__obs_workflow.sql delete mode 100644 modules/service/src/main/scala/lucuma/odb/graphql/binding/ObsStatusBinding.scala delete mode 100644 modules/service/src/main/scala/lucuma/odb/graphql/binding/OcsActiveStatusBinding.scala delete mode 100644 modules/service/src/test/scala/lucuma/odb/graphql/query/observationValidations.scala diff --git a/modules/schema/src/main/resources/lucuma/odb/graphql/OdbSchema.graphql b/modules/schema/src/main/resources/lucuma/odb/graphql/OdbSchema.graphql index e835bd787..aed28d29e 100644 --- a/modules/schema/src/main/resources/lucuma/odb/graphql/OdbSchema.graphql +++ b/modules/schema/src/main/resources/lucuma/odb/graphql/OdbSchema.graphql @@ -3183,16 +3183,6 @@ input ObservationPropertiesInput { """ subtitle: NonEmptyString - """ - The observation status will default to New if not specified when an observation is created and may be edited but not deleted - """ - status: ObsStatus - - """ - The observation active status will default to Active if not specified when an observation is created and may be edited but not deleted - """ - activeStatus: ObsActiveStatus - """ The science band to assign to this observation. Set to `null` to remove the science band. @@ -3253,11 +3243,6 @@ input ObservationPropertiesInput { Set the notes for thhe observer """ observerNotes: NonEmptyString - - """ - Sets the "for review" bit. - """ - forReview: Boolean } @@ -8496,16 +8481,6 @@ type Observation { """ subtitle: NonEmptyString - """ - Observation status - """ - status: ObsStatus! @deprecated - - """ - Observation operational status - """ - activeStatus: ObsActiveStatus! @deprecated - """ Observations are associated with a science band once time has been allocated to a program. @@ -8583,11 +8558,6 @@ type Observation { """ itc: Itc! - """ - A list of observation validation problems - """ - validations: [ObservationValidation!]! @deprecated - """ Enclosing group, if any. """ @@ -8618,12 +8588,6 @@ type Observation { """ configurationRequests: [ConfigurationRequest!]! - """ - This flag is set to true prior to Phase II, and must be reset to false by the PI prior to observation. The - intent is to prompt PIs to review their accepted observations. - """ - forReview: Boolean! @deprecated - workflow: ObservationWorkflow! @@ -12553,26 +12517,11 @@ input WhereObservation { """ subtitle: WhereOptionString - """ - Matches the observation status. - """ - status: WhereOrderObsStatus - - """ - Matches the observation active status. - """ - activeStatus: WhereOrderObsActiveStatus - """ Matches the observation science band. """ scienceBand: WhereOptionOrderScienceBand - """ - Matches the "for review" bit. - """ - forReview: WhereBoolean - } """ @@ -13398,101 +13347,6 @@ input WhereOrderLong { LTE: Long } -""" -Filters on equality or order comparisons of the property. All supplied -criteria must match, but usually only one is selected. E.g., 'GT = 2' -for an integer property will match when the value is 3 or more. -""" -input WhereOrderObsActiveStatus { - """ - Matches if the property is exactly the supplied value. - """ - EQ: ObsActiveStatus - - """ - Matches if the property is not the supplied value. - """ - NEQ: ObsActiveStatus - - """ - Matches if the property value is any of the supplied options. - """ - IN: [ObsActiveStatus!] - - """ - Matches if the property value is none of the supplied values. - """ - NIN: [ObsActiveStatus!] - - """ - Matches if the property is ordered after (>) the supplied value. - """ - GT: ObsActiveStatus - - """ - Matches if the property is ordered before (<) the supplied value. - """ - LT: ObsActiveStatus - - """ - Matches if the property is ordered after or equal (>=) the supplied value. - """ - GTE: ObsActiveStatus - - """ - Matches if the property is ordered before or equal (<=) the supplied value. - """ - LTE: ObsActiveStatus -} - -""" -Filters on equality or order comparisons of the property. All supplied -criteria must match, but usually only one is selected. E.g., 'GT = 2' -for an integer property will match when the value is 3 or more. - -""" -input WhereOrderObsStatus { - """ - Matches if the property is exactly the supplied value. - """ - EQ: ObsStatus - - """ - Matches if the property is not the supplied value. - """ - NEQ: ObsStatus - - """ - Matches if the property value is any of the supplied options. - """ - IN: [ObsStatus!] - - """ - Matches if the property value is none of the supplied values. - """ - NIN: [ObsStatus!] - - """ - Matches if the property is ordered after (>) the supplied value. - """ - GT: ObsStatus - - """ - Matches if the property is ordered before (<) the supplied value. - """ - LT: ObsStatus - - """ - Matches if the property is ordered after or equal (>=) the supplied value. - """ - GTE: ObsStatus - - """ - Matches if the property is ordered before or equal (<=) the supplied value. - """ - LTE: ObsStatus -} - """ Filters on equality or order comparisons of the property. All supplied criteria must match, but usually only one is selected. E.g., 'GT = 2' diff --git a/modules/service/src/main/resources/db/migration/V0924__obs_workflow.sql b/modules/service/src/main/resources/db/migration/V0924__obs_workflow.sql new file mode 100644 index 000000000..4f717a2b2 --- /dev/null +++ b/modules/service/src/main/resources/db/migration/V0924__obs_workflow.sql @@ -0,0 +1,28 @@ + +-- Remove the observation view so we can drop underlying columns. +DROP VIEW v_observation; + +-- Remove state bits that are no longer in use; add workflow state column. +ALTER TABLE t_observation +DROP c_status, +DROP c_active_status, +DROP c_for_review; + +-- Re-create v_observation +CREATE OR REPLACE VIEW v_observation AS + SELECT o.*, + CASE WHEN c_explicit_ra IS NOT NULL THEN c_observation_id END AS c_explicit_base_id, + CASE WHEN c_air_mass_min IS NOT NULL THEN c_observation_id END AS c_air_mass_id, + CASE WHEN c_hour_angle_min IS NOT NULL THEN c_observation_id END AS c_hour_angle_id, + CASE WHEN c_observing_mode_type IS NOT NULL THEN c_observation_id END AS c_observing_mode_id, + CASE WHEN c_spec_wavelength IS NOT NULL THEN c_observation_id END AS c_spec_wavelength_id, + CASE WHEN c_spec_signal_to_noise_at IS NOT NULL THEN c_observation_id END AS c_spec_signal_to_noise_at_id, + CASE WHEN c_spec_wavelength_coverage IS NOT NULL THEN c_observation_id END AS c_spec_wavelength_coverage_id, + CASE WHEN c_spec_focal_plane_angle IS NOT NULL THEN c_observation_id END AS c_spec_focal_plane_angle_id, + CASE WHEN c_observation_duration IS NOT NULL THEN c_observation_id END AS c_observation_duration_id, + c.c_active_start::timestamp + (c.c_active_end::timestamp - c.c_active_start::timestamp) * 0.5 AS c_reference_time + FROM t_observation o + LEFT JOIN t_proposal p on p.c_program_id = o.c_program_id + LEFT JOIN t_cfp c on p.c_cfp_id = c.c_cfp_id +; + diff --git a/modules/service/src/main/scala/lucuma/odb/graphql/BaseMapping.scala b/modules/service/src/main/scala/lucuma/odb/graphql/BaseMapping.scala index b3b04a67f..8694c938a 100644 --- a/modules/service/src/main/scala/lucuma/odb/graphql/BaseMapping.scala +++ b/modules/service/src/main/scala/lucuma/odb/graphql/BaseMapping.scala @@ -189,7 +189,6 @@ trait BaseMapping[F[_]] lazy val OffsetPType = schema.ref("OffsetP") lazy val OffsetQType = schema.ref("OffsetQ") lazy val OffsetType = schema.ref("Offset") - lazy val ObsActiveStatusType = schema.ref("ObsActiveStatus") lazy val ObsAttachmentFileExtType = schema.ref("ObsAttachmentFileExt") lazy val ObsAttachmentIdType = schema.ref("ObsAttachmentId") lazy val ObsAttachmentType = schema.ref("ObsAttachment") @@ -205,7 +204,6 @@ trait BaseMapping[F[_]] lazy val ObservationReferenceLabelType = schema.ref("ObservationReferenceLabel") lazy val ObservationSelectResultType = schema.ref("ObservationSelectResult") lazy val ObserveClassType = schema.ref("ObserveClass") - lazy val ObsStatusType = schema.ref("ObsStatus") lazy val ParallaxType = schema.ref("Parallax") lazy val PartnerMetaType = schema.ref("PartnerMeta") lazy val PartnerSplitType = schema.ref("PartnerSplit") diff --git a/modules/service/src/main/scala/lucuma/odb/graphql/binding/ObsStatusBinding.scala b/modules/service/src/main/scala/lucuma/odb/graphql/binding/ObsStatusBinding.scala deleted file mode 100644 index 5bce60d8e..000000000 --- a/modules/service/src/main/scala/lucuma/odb/graphql/binding/ObsStatusBinding.scala +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA) -// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause - -package lucuma.odb.graphql.binding - -import lucuma.core.enums.ObsStatus - -val ObsStatusBinding: Matcher[ObsStatus] = - enumeratedBinding diff --git a/modules/service/src/main/scala/lucuma/odb/graphql/binding/OcsActiveStatusBinding.scala b/modules/service/src/main/scala/lucuma/odb/graphql/binding/OcsActiveStatusBinding.scala deleted file mode 100644 index 9df236226..000000000 --- a/modules/service/src/main/scala/lucuma/odb/graphql/binding/OcsActiveStatusBinding.scala +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA) -// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause - -package lucuma.odb.graphql.binding - -import lucuma.core.enums.ObsActiveStatus - -val ObsActiveStatusBinding: Matcher[ObsActiveStatus] = - enumeratedBinding diff --git a/modules/service/src/main/scala/lucuma/odb/graphql/input/ObservationPropertiesInput.scala b/modules/service/src/main/scala/lucuma/odb/graphql/input/ObservationPropertiesInput.scala index a77dd7633..7e7873199 100644 --- a/modules/service/src/main/scala/lucuma/odb/graphql/input/ObservationPropertiesInput.scala +++ b/modules/service/src/main/scala/lucuma/odb/graphql/input/ObservationPropertiesInput.scala @@ -10,8 +10,6 @@ import cats.syntax.all.* import eu.timepit.refined.types.numeric.NonNegShort import eu.timepit.refined.types.string.NonEmptyString import grackle.Result -import lucuma.core.enums.ObsActiveStatus -import lucuma.core.enums.ObsStatus import lucuma.core.enums.ScienceBand import lucuma.core.model.Group import lucuma.core.model.ObsAttachment @@ -38,8 +36,6 @@ object ObservationPropertiesInput { final case class Create( subtitle: Option[NonEmptyString], - status: Option[ObsStatus], - activeStatus: Option[ObsActiveStatus], scienceBand: Option[ScienceBand], posAngleConstraint: Option[PosAngleConstraintInput], targetEnvironment: Option[TargetEnvironmentInput.Create], @@ -52,7 +48,6 @@ object ObservationPropertiesInput { group: Option[Group.Id], groupIndex: Option[NonNegShort], observerNotes: Option[NonEmptyString], - forReview: Option[Boolean], ) extends AsterismInput object Create { @@ -63,8 +58,6 @@ object ObservationPropertiesInput { val Default: Create = Create( subtitle = Option.empty, - status = ObsStatus.New.some, - activeStatus = ObsActiveStatus.Active.some, scienceBand = None, posAngleConstraint = None, targetEnvironment = None, @@ -77,15 +70,12 @@ object ObservationPropertiesInput { group = None, groupIndex = None, observerNotes = None, - forReview = None, ) val Binding: Matcher[Create] = ObjectFieldsBinding.rmap { case List( NonEmptyStringBinding.Option("subtitle", rSubtitle), - ObsStatusBinding.Option("status", rObsStatus), - ObsActiveStatusBinding.Option("activeStatus", rObsActiveStatus), ScienceBandBinding.Option("scienceBand", rScienceBand), PosAngleConstraintInput.Binding.Option("posAngleConstraint", rPosAngleConstraint), TargetEnvironmentInput.Create.Binding.Option("targetEnvironment", rTargetEnvironment), @@ -98,11 +88,8 @@ object ObservationPropertiesInput { GroupIdBinding.Option("groupId", rGroupId), NonNegShortBinding.Option("groupIndex", rGroupIndex), NonEmptyStringBinding.Option("observerNotes", rObserverNotes), - BooleanBinding.Option("forReview", rForReview) ) => (rSubtitle, - rObsStatus, - rObsActiveStatus, rScienceBand, rPosAngleConstraint, rTargetEnvironment, @@ -115,7 +102,6 @@ object ObservationPropertiesInput { rGroupId, rGroupIndex, rObserverNotes, - rForReview, ).parMapN(Create.apply) } @@ -123,8 +109,6 @@ object ObservationPropertiesInput { final case class Edit( subtitle: Nullable[NonEmptyString], - status: Option[ObsStatus], - activeStatus: Option[ObsActiveStatus], scienceBand: Nullable[ScienceBand], posAngleConstraint: Option[PosAngleConstraintInput], targetEnvironment: Option[TargetEnvironmentInput.Edit], @@ -137,7 +121,6 @@ object ObservationPropertiesInput { group: Nullable[Group.Id], groupIndex: Option[NonNegShort], observerNotes: Nullable[NonEmptyString], - forReview: Option[Boolean], ) extends AsterismInput object Edit { @@ -145,8 +128,6 @@ object ObservationPropertiesInput { val Empty: Edit = Edit( subtitle = Nullable.Absent, - status = None, - activeStatus = None, scienceBand = Nullable.Absent, posAngleConstraint = None, targetEnvironment = None, @@ -159,15 +140,12 @@ object ObservationPropertiesInput { group = Nullable.Absent, groupIndex = None, observerNotes = Nullable.Absent, - forReview = None, ) val Binding: Matcher[Edit] = ObjectFieldsBinding.rmap { case List( NonEmptyStringBinding.Nullable("subtitle", rSubtitle), - ObsStatusBinding.Option("status", rObsStatus), - ObsActiveStatusBinding.Option("activeStatus", rObsActiveStatus), ScienceBandBinding.Nullable("scienceBand", rScienceBand), PosAngleConstraintInput.Binding.Option("posAngleConstraint", rPosAngleConstraint), TargetEnvironmentInput.Edit.Binding.Option("targetEnvironment", rTargetEnvironment), @@ -180,11 +158,8 @@ object ObservationPropertiesInput { GroupIdBinding.Nullable("groupId", rGroupId), NonNegShortBinding.NonNullable("groupIndex", rGroupIndex), NonEmptyStringBinding.Nullable("observerNotes", rObserverNotes), - BooleanBinding.Option("forReview", rForReview) ) => (rSubtitle, - rObsStatus, - rObsActiveStatus, rScienceBand, rPosAngleConstraint, rTargetEnvironment, @@ -197,7 +172,6 @@ object ObservationPropertiesInput { rGroupId, rGroupIndex, rObserverNotes, - rForReview, ).parMapN(apply) } } diff --git a/modules/service/src/main/scala/lucuma/odb/graphql/input/WhereObservation.scala b/modules/service/src/main/scala/lucuma/odb/graphql/input/WhereObservation.scala index 13a9c49da..9ac6efdc8 100644 --- a/modules/service/src/main/scala/lucuma/odb/graphql/input/WhereObservation.scala +++ b/modules/service/src/main/scala/lucuma/odb/graphql/input/WhereObservation.scala @@ -9,8 +9,6 @@ import cats.syntax.parallel.* import grackle.Path import grackle.Predicate import grackle.Predicate.* -import lucuma.core.enums.ObsActiveStatus -import lucuma.core.enums.ObsStatus import lucuma.core.enums.ScienceBand import lucuma.core.model.Observation import lucuma.odb.graphql.binding.* @@ -22,10 +20,7 @@ object WhereObservation { val WhereOrderObservationIdBinding = WhereOrder.binding[Observation.Id](path / "id", ObservationIdBinding) val WhereReferenceBinding = WhereObservationReference.binding(path / "reference") val WhereProgramBinding = WhereProgram.binding(path / "program") - val StatusBinding = WhereOrder.binding(path / "status", enumeratedBinding[ObsStatus]) - val ActiveStatusBinding = WhereOrder.binding(path / "activeStatus", enumeratedBinding[ObsActiveStatus]) val ScienceBandBinding = WhereOptionOrder.binding(path / "scienceBand", enumeratedBinding[ScienceBand]) - val ForReviewBinding = WhereOptionBoolean.binding(path / "forReview", BooleanBinding) lazy val WhereObservationBinding = binding(path) ObjectFieldsBinding.rmap { @@ -37,13 +32,10 @@ object WhereObservation { WhereReferenceBinding.Option("reference", rRef), WhereProgramBinding.Option("program", rProgram), SubtitleBinding.Option("subtitle", rSubtitle), - StatusBinding.Option("status", rStatus), - ActiveStatusBinding.Option("activeStatus", rActiveStatus), ScienceBandBinding.Option("scienceBand", rScienceBand), - ForReviewBinding.Option("forReview", rForReview), ) => - (rAND, rOR, rNOT, rId, rRef, rProgram, rSubtitle, rStatus, rActiveStatus, rScienceBand, rForReview).parMapN { - (AND, OR, NOT, id, ref, program, subtitle, status, activeStatus, scienceBand, forReview) => + (rAND, rOR, rNOT, rId, rRef, rProgram, rSubtitle, rScienceBand).parMapN { + (AND, OR, NOT, id, ref, program, subtitle, scienceBand) => and(List( AND.map(and), OR.map(or), @@ -52,10 +44,7 @@ object WhereObservation { ref, program, subtitle, - status, - activeStatus, scienceBand, - forReview ).flatten) } } diff --git a/modules/service/src/main/scala/lucuma/odb/graphql/mapping/LeafMappings.scala b/modules/service/src/main/scala/lucuma/odb/graphql/mapping/LeafMappings.scala index f49c54e62..6e5783175 100644 --- a/modules/service/src/main/scala/lucuma/odb/graphql/mapping/LeafMappings.scala +++ b/modules/service/src/main/scala/lucuma/odb/graphql/mapping/LeafMappings.scala @@ -142,7 +142,6 @@ trait LeafMappings[F[_]] extends BaseMapping[F] { LeafMapping[NonEmptyString](NonEmptyStringType), LeafMapping[NonNegInt](NonNegIntType), LeafMapping[NonNegShort](NonNegShortType), - LeafMapping[ObsActiveStatus](ObsActiveStatusType), LeafMapping[ObsAttachment.Id](ObsAttachmentIdType), LeafMapping[Tag](ObsAttachmentTypeType), LeafMapping[ObservingModeType](ObservingModeTypeType), @@ -150,7 +149,6 @@ trait LeafMappings[F[_]] extends BaseMapping[F] { LeafMapping[ObservationExecutionState](ObservationExecutionStateType), LeafMapping[ObservationReference](ObservationReferenceLabelType), LeafMapping[ObserveClass](ObserveClassType), - LeafMapping[ObsStatus](ObsStatusType), LeafMapping[Partner](PartnerType), LeafMapping[PartnerLinkType](PartnerLinkTypeType), LeafMapping[PosAngleConstraintMode](PosAngleConstraintModeType), diff --git a/modules/service/src/main/scala/lucuma/odb/graphql/mapping/ObservationMapping.scala b/modules/service/src/main/scala/lucuma/odb/graphql/mapping/ObservationMapping.scala index 93ffbfd39..85dc45099 100644 --- a/modules/service/src/main/scala/lucuma/odb/graphql/mapping/ObservationMapping.scala +++ b/modules/service/src/main/scala/lucuma/odb/graphql/mapping/ObservationMapping.scala @@ -19,7 +19,6 @@ import grackle.syntax.* import lucuma.core.model.ConfigurationRequest import lucuma.core.model.ObsAttachment import lucuma.core.model.Observation -import lucuma.core.model.ObservationValidation import lucuma.core.model.ObservationWorkflow import lucuma.core.model.Program import lucuma.itc.client.ItcClient @@ -59,8 +58,6 @@ trait ObservationMapping[F[_]] SqlField("index", ObservationView.ObservationIndex), SqlField("title", ObservationView.Title), SqlField("subtitle", ObservationView.Subtitle), - SqlField("status", ObservationView.Status), - SqlField("activeStatus", ObservationView.ActiveStatus), SqlField("scienceBand", ObservationView.ScienceBand), SqlField("observationTime", ObservationView.ObservationTime), SqlObject("observationDuration"), @@ -76,7 +73,6 @@ trait ObservationMapping[F[_]] SqlField("instrument", ObservationView.Instrument), SqlObject("program", Join(ObservationView.ProgramId, ProgramTable.Id)), EffectField("itc", itcQueryHandler, List("id", "programId")), - EffectField("validations", validationsQueryHandler, List("id", "programId")), SqlObject("execution"), SqlField("groupId", ObservationView.GroupId), SqlField("groupIndex", ObservationView.GroupIndex), @@ -85,7 +81,6 @@ trait ObservationMapping[F[_]] SqlObject("configuration"), EffectField("configurationRequests", configurationRequestsQueryHandler, List("id", "programId")), EffectField("workflow", workflowQueryHandler, List("id", "programId")), - SqlField("forReview", ObservationView.ForReview), ) lazy val ObservationElaborator: PartialFunction[(TypeRef, String, List[Binding]), Elab[Unit]] = { @@ -135,19 +130,6 @@ trait ObservationMapping[F[_]] effectHandler(readEnv, calculate) } - def validationsQueryHandler: EffectHandler[F] = { - val readEnv: Env => Result[Unit] = _ => ().success - - val calculate: (Program.Id, Observation.Id, Unit) => F[Result[List[ObservationValidation]]] = - (pid, oid, _) => - services.useTransactionally { - observationWorkflowService - .observationValidations(oid, itcClient) - } - - effectHandler(readEnv, calculate) - } - lazy val configurationRequestsQueryHandler: EffectHandler[F] = { val readEnv: Env => Result[Unit] = _ => ().success diff --git a/modules/service/src/main/scala/lucuma/odb/graphql/table/ObservationView.scala b/modules/service/src/main/scala/lucuma/odb/graphql/table/ObservationView.scala index 16babd13b..6f83658d6 100644 --- a/modules/service/src/main/scala/lucuma/odb/graphql/table/ObservationView.scala +++ b/modules/service/src/main/scala/lucuma/odb/graphql/table/ObservationView.scala @@ -19,8 +19,6 @@ trait ObservationView[F[_]] extends BaseMapping[F] { val Title: ColumnRef = col("c_title", text_nonempty) val Subtitle: ColumnRef = col("c_subtitle", text_nonempty.opt) val Instrument: ColumnRef = col("c_instrument", instrument.opt) - val Status: ColumnRef = col("c_status", obs_status) - val ActiveStatus: ColumnRef = col("c_active_status", obs_active_status) val ScienceBand: ColumnRef = col("c_science_band", science_band.opt) val ObservationTime: ColumnRef = col("c_observation_time", core_timestamp.opt) val AsterismGroup: ColumnRef = col("c_asterism_group", jsonb) @@ -28,8 +26,7 @@ trait ObservationView[F[_]] extends BaseMapping[F] { val GroupIndex: ColumnRef = col("c_group_index", int2_nonneg) val CalibrationRole: ColumnRef = col("c_calibration_role", calibration_role.opt) val ObserverNotes: ColumnRef = col("c_observer_notes", text_nonempty.opt) - val ReferenceTime: ColumnRef = col("c_reference_time", core_timestamp.opt) - val ForReview: ColumnRef = col("c_for_review", bool) + val ReferenceTime: ColumnRef = col("c_reference_time", core_timestamp.opt) object PlannedTime { val Pi = col("c_pts_pi", time_span) diff --git a/modules/service/src/main/scala/lucuma/odb/logic/TimeEstimateService.scala b/modules/service/src/main/scala/lucuma/odb/logic/TimeEstimateService.scala index 75fad7682..cab553f15 100644 --- a/modules/service/src/main/scala/lucuma/odb/logic/TimeEstimateService.scala +++ b/modules/service/src/main/scala/lucuma/odb/logic/TimeEstimateService.scala @@ -15,7 +15,6 @@ import cats.syntax.functor.* import cats.syntax.functorFilter.* import cats.syntax.traverse.* import eu.timepit.refined.types.numeric.NonNegShort -import lucuma.core.enums.ObsStatus import lucuma.core.model.Group import lucuma.core.model.Observation import lucuma.core.model.Program @@ -76,7 +75,7 @@ object TimeEstimateService { itcClient: ItcClient[F] )(using Services[F], Transaction[F]): F[Map[Observation.Id, ObservationData]] = for { - p <- generatorParamsService.selectAll(pid, ObsStatus.Included) + p <- generatorParamsService.selectAll(pid /*, ObsStatus.Included*/) pʹ = p.collect { case (oid, Right(gp)) => (oid, gp) } i <- itcService(itcClient).selectAll(pid, pʹ) d <- executionDigestService.selectAll(pid) diff --git a/modules/service/src/main/scala/lucuma/odb/service/GeneratorParamsService.scala b/modules/service/src/main/scala/lucuma/odb/service/GeneratorParamsService.scala index 265206bf7..bfd685fae 100644 --- a/modules/service/src/main/scala/lucuma/odb/service/GeneratorParamsService.scala +++ b/modules/service/src/main/scala/lucuma/odb/service/GeneratorParamsService.scala @@ -20,7 +20,6 @@ import cats.syntax.option.* import lucuma.core.enums.CalibrationRole import lucuma.core.enums.GmosNorthFilter import lucuma.core.enums.GmosSouthFilter -import lucuma.core.enums.ObsStatus import lucuma.core.enums.ObservingModeType import lucuma.core.math.RadialVelocity import lucuma.core.math.SignalToNoise @@ -72,7 +71,7 @@ trait GeneratorParamsService[F[_]] { def selectAll( programId: Program.Id, - minStatus: ObsStatus = ObsStatus.New, + // minStatus: ObsStatus = ObsStatus.New, selection: ObservationSelection = ObservationSelection.All )(using Transaction[F]): F[Map[Observation.Id, Either[Error, GeneratorParams]]] @@ -132,10 +131,10 @@ object GeneratorParamsService { override def selectAll( pid: Program.Id, - minStatus: ObsStatus, + // minStatus: ObsStatus, selection: ObservationSelection )(using Transaction[F]): F[Map[Observation.Id, Either[Error, GeneratorParams]]] = - doSelect(selectAllParams(pid, minStatus, selection)) + doSelect(selectAllParams(pid, /*minStatus,*/ selection)) private def doSelect( params: F[List[ParamsRow]] @@ -161,10 +160,10 @@ object GeneratorParamsService { private def selectAllParams( pid: Program.Id, - minStatus: ObsStatus, + // minStatus: ObsStatus, selection: ObservationSelection ): F[List[ParamsRow]] = - executeSelect(Statements.selectAllParams(user, pid, minStatus, selection)) + executeSelect(Statements.selectAllParams(user, pid, /*minStatus,*/ selection)) private def executeSelect(af: AppliedFragment): F[List[ParamsRow]] = session @@ -367,7 +366,7 @@ object GeneratorParamsService { def selectAllParams( user: User, programId: Program.Id, - minStatus: ObsStatus, + // minStatus: ObsStatus, selection: ObservationSelection ): AppliedFragment = { val selector = selection match @@ -384,8 +383,8 @@ object GeneratorParamsService { """(Void) |+| sql"""gp.c_program_id = $program_id""".apply(programId) |+| void""" AND ob.c_existence = 'present' """ |+| - sql""" AND ob.c_status >= $obs_status """.apply(minStatus) |+| - void""" AND ob.c_active_status = 'active' """ |+| + // sql""" AND ob.c_status >= $obs_status """.apply(minStatus) |+| + // void""" AND ob.c_active_status = 'active' """ |+| selector |+| existsUserAccess(user, programId).fold(AppliedFragment.empty) { af => void""" AND """ |+| af } } diff --git a/modules/service/src/main/scala/lucuma/odb/service/ObservationService.scala b/modules/service/src/main/scala/lucuma/odb/service/ObservationService.scala index 4c92cd451..f544c1a9d 100644 --- a/modules/service/src/main/scala/lucuma/odb/service/ObservationService.scala +++ b/modules/service/src/main/scala/lucuma/odb/service/ObservationService.scala @@ -20,8 +20,6 @@ import lucuma.core.enums.CloudExtinction import lucuma.core.enums.FocalPlane import lucuma.core.enums.ImageQuality import lucuma.core.enums.Instrument -import lucuma.core.enums.ObsActiveStatus -import lucuma.core.enums.ObsStatus import lucuma.core.enums.ObservingModeType import lucuma.core.enums.ScienceBand import lucuma.core.enums.ScienceMode @@ -70,7 +68,6 @@ import lucuma.odb.util.Codecs.group_id import lucuma.odb.util.Codecs.int2_nonneg import natchez.Trace import skunk.* -import skunk.codec.boolean.bool import skunk.exception.PostgresErrorException import skunk.implicits.* @@ -199,7 +196,6 @@ object ObservationService { forReview: Boolean, role: Option[CalibrationRole], proposalStatus: Tag, - activeStatus: ObsActiveStatus ) { def isMarkedReady: Boolean = false // TODO } @@ -276,8 +272,6 @@ object ObservationService { )(using Transaction[F]): F[Result[Unit]] = { val existenceOff = ObservationPropertiesInput.Edit( Nullable.Absent, - None, - None, Nullable.Absent, None, None, @@ -290,7 +284,6 @@ object ObservationService { Nullable.Null, None, Nullable.Absent, - None ) // delete targets, asterisms and observations @@ -601,8 +594,6 @@ object ObservationService { groupIndex, SET.subtitle, SET.existence.getOrElse(Existence.Default), - SET.status.getOrElse(ObsStatus.New), - SET.activeStatus.getOrElse(ObsActiveStatus.Active), SET.scienceBand, SET.posAngleConstraint.flatMap(_.mode).getOrElse(PosAngleConstraintMode.Unbounded), SET.posAngleConstraint.flatMap(_.angle).getOrElse(Angle.Angle0), @@ -622,8 +613,6 @@ object ObservationService { groupIndex: NonNegShort, subtitle: Option[NonEmptyString], existence: Existence, - status: ObsStatus, - activeState: ObsActiveStatus, scienceBand: Option[ScienceBand], posAngleConsMode: PosAngleConstraintMode, posAngle: Angle, @@ -645,8 +634,6 @@ object ObservationService { groupIndex , subtitle , existence , - status , - activeState , scienceBand , posAngleConsMode , posAngle , @@ -689,8 +676,6 @@ object ObservationService { NonNegShort , Option[NonEmptyString] , Existence , - ObsStatus , - ObsActiveStatus , Option[ScienceBand] , PosAngleConstraintMode , Angle , @@ -724,8 +709,6 @@ object ObservationService { c_group_index, c_subtitle, c_existence, - c_status, - c_active_status, c_science_band, c_pac_mode, c_pac_angle, @@ -758,8 +741,6 @@ object ObservationService { $int2_nonneg, ${text_nonempty.opt}, $existence, - $obs_status, - $obs_active_status, ${science_band.opt}, $pac_mode, $angle_µas, @@ -899,21 +880,15 @@ object ObservationService { def updates(SET: ObservationPropertiesInput.Edit): Result[Option[NonEmptyList[AppliedFragment]]] = { val upExistence = sql"c_existence = $existence" val upSubtitle = sql"c_subtitle = ${text_nonempty.opt}" - val upStatus = sql"c_status = $obs_status" - val upActive = sql"c_active_status = $obs_active_status" val upScienceBand = sql"c_science_band = ${science_band.opt}" val upObserverNotes = sql"c_observer_notes = ${text_nonempty.opt}" - val upForReview = sql"c_for_review = $bool" val ups: List[AppliedFragment] = List( SET.existence.map(upExistence), SET.subtitle.foldPresent(upSubtitle), - SET.status.map(upStatus), - SET.activeStatus.map(upActive), SET.scienceBand.foldPresent(upScienceBand), SET.observerNotes.foldPresent(upObserverNotes), - SET.forReview.map(upForReview), ).flatten val posAngleConstraint: List[AppliedFragment] = @@ -1007,8 +982,6 @@ object ObservationService { c_title, c_subtitle, c_instrument, - c_status, - c_active_status, c_science_band, c_observation_time, c_pts_pi, @@ -1044,8 +1017,6 @@ object ObservationService { c_title, c_subtitle, c_instrument, - 'new', - 'active', c_science_band, c_observation_time, c_pts_pi, diff --git a/modules/service/src/main/scala/lucuma/odb/service/ObservationWorkflowService.scala b/modules/service/src/main/scala/lucuma/odb/service/ObservationWorkflowService.scala index 7f74fe157..d87164072 100644 --- a/modules/service/src/main/scala/lucuma/odb/service/ObservationWorkflowService.scala +++ b/modules/service/src/main/scala/lucuma/odb/service/ObservationWorkflowService.scala @@ -12,7 +12,6 @@ import grackle.ResultT import lucuma.core.enums.CalibrationRole import lucuma.core.enums.ConfigurationRequestStatus import lucuma.core.enums.Instrument -import lucuma.core.enums.ObsActiveStatus import lucuma.core.enums.ObservationExecutionState import lucuma.core.enums.ObservationValidationCode import lucuma.core.enums.ObservationWorkflowState @@ -46,7 +45,6 @@ import lucuma.odb.syntax.instrument.* import lucuma.odb.util.Codecs.* import natchez.Trace import skunk.* -import skunk.codec.boolean.bool import skunk.implicits.* import java.time.Duration @@ -55,11 +53,6 @@ import Services.Syntax.* sealed trait ObservationWorkflowService[F[_]] { - def observationValidations( - oid: Observation.Id, - itcClient: ItcClient[F], - )(using Transaction[F]): F[Result[List[ObservationValidation]]] - def getWorkflow( oid: Observation.Id, commitHash: CommitHash, @@ -75,16 +68,11 @@ case class ObservationValidationInfo( instrument: Option[Instrument], ra: Option[RightAscension], dec: Option[Declination], - forReview: Boolean, role: Option[CalibrationRole], proposalStatus: Tag, - activeStatus: ObsActiveStatus, cfpid: Option[CallForProposals.Id], ) { - def isMarkedReady: Boolean = - false // TODO - /* Has the proposal been accepted? */ def isAccepted(using enums: Enums): Result[Boolean] = Result.fromOption( @@ -102,7 +90,6 @@ object ObservationWorkflowService { val AsterismOutOfRange = "Asterism out of Call for Proposals limits." val ExplicitBaseOutOfRange = "Explicit base out of Call for Proposals limits." - val ConfigurationForReview = "Observation must be reviewed prior to execution." def invalidInstrument(instr: Instrument): String = s"Instrument $instr not part of Call for Proposals." @@ -198,8 +185,7 @@ object ObservationWorkflowService { valInstr <- validateInstrument(cid, info.instrument) explicitBase = (info.ra, info.dec).tupled valRaDec <- validateRaDec(info, cid, explicitBase) - valForactivation = Option.when(info.forReview)(ObservationValidation.configuration(Messages.ConfigurationForReview)) - yield ObservationValidationMap.fromList(List(valInstr, valRaDec, valForactivation).flatten) + yield ObservationValidationMap.fromList(List(valInstr, valRaDec).flatten) private def validateInstrument(cid: CallForProposals.Id, optInstr: Option[Instrument]): F[Option[ObservationValidation]] = { // If there is no instrument in the observation, that will get caught with the generatorValidations @@ -293,12 +279,9 @@ object ObservationWorkflowService { ObservationValidationCode.ConfigurationRequestPending => Unapproved def userStatus(validationStatus: ValidationState): Option[UserState] = - info.activeStatus match - case ObsActiveStatus.Inactive => Some(Inactive) - case ObsActiveStatus.Active => - val ready = info.isMarkedReady || info.role.isDefined // calibrations are always ready - Option.when(ready && validationStatus == Defined)(Ready) - + if info.role.isDefined then Some(Ready) // Calibrations are immediately ready + else None // TODO + // Our final state is the execution state (if any), else the user state (if any), else the validation state, // with the one exception that user state Inactive overrides execution state Ongoing val state: ObservationWorkflowState = @@ -356,12 +339,6 @@ object ObservationWorkflowService { } - override def observationValidations( - oid: Observation.Id, - itcClient: ItcClient[F] - )(using Transaction[F]): F[Result[List[ObservationValidation]]] = - ResultT.liftF(obsInfo(oid)).flatMap(observationValidationsImpl(_, itcClient)).value - // split this off because it can be done togther in one transaction private def getObsInfoAndOtherStuff( oid: Observation.Id, @@ -408,17 +385,15 @@ object ObservationWorkflowService { o.c_instrument, o.c_explicit_ra, o.c_explicit_dec, - o.c_for_review, o.c_calibration_role, p.c_proposal_status, - o.c_active_status, x.c_cfp_id FROM t_observation o JOIN t_program p on p.c_program_id = o.c_program_id LEFT JOIN t_proposal x ON o.c_program_id = x.c_program_id WHERE c_observation_id = $observation_id """ - .query(program_id *: observation_id *: instrument.opt *: right_ascension.opt *: declination.opt *: bool *: calibration_role.opt *: tag *: obs_active_status *: cfp_id.opt) + .query(program_id *: observation_id *: instrument.opt *: right_ascension.opt *: declination.opt *: calibration_role.opt *: tag *: cfp_id.opt) .to[ObservationValidationInfo] diff --git a/modules/service/src/main/scala/lucuma/odb/util/Codecs.scala b/modules/service/src/main/scala/lucuma/odb/util/Codecs.scala index 5f496fc7c..53c2e28f0 100644 --- a/modules/service/src/main/scala/lucuma/odb/util/Codecs.scala +++ b/modules/service/src/main/scala/lucuma/odb/util/Codecs.scala @@ -323,18 +323,12 @@ trait Codecs { b => if (b) MosPreImaging.IsMosPreImaging else MosPreImaging.IsNotMosPreImaging )(_.toBoolean) - val obs_active_status: Codec[ObsActiveStatus] = - enumerated(Type("e_obs_active_status")) - val obs_attachment_id: Codec[ObsAttachment.Id] = gid[ObsAttachment.Id] val obs_class: Codec[ObserveClass] = enumerated(Type("e_obs_class")) - val obs_status: Codec[ObsStatus] = - enumerated(Type("e_obs_status")) - val observation_id: Codec[Observation.Id] = gid[Observation.Id] diff --git a/modules/service/src/test/scala/lucuma/odb/graphql/feature/calibrations.scala b/modules/service/src/test/scala/lucuma/odb/graphql/feature/calibrations.scala index 43c20aa50..0012f255a 100644 --- a/modules/service/src/test/scala/lucuma/odb/graphql/feature/calibrations.scala +++ b/modules/service/src/test/scala/lucuma/odb/graphql/feature/calibrations.scala @@ -915,7 +915,7 @@ class calibrations extends OdbSuite with SubscriptionUtils { """ :: b :: Nil case l => l }, - a.get.map(_.reverse.zip(expectedTargets).flatMap {case (cid, tn) => List( + a.get.map(_.zip(expectedTargets).flatMap {case (cid, tn) => List( json""" { "observationEdit" : { diff --git a/modules/service/src/test/scala/lucuma/odb/graphql/issue/shortcut/2772.scala b/modules/service/src/test/scala/lucuma/odb/graphql/issue/shortcut/2772.scala index 739c09562..95e77f4c7 100644 --- a/modules/service/src/test/scala/lucuma/odb/graphql/issue/shortcut/2772.scala +++ b/modules/service/src/test/scala/lucuma/odb/graphql/issue/shortcut/2772.scala @@ -124,9 +124,7 @@ class ShortCut_2772 extends ExecutionTestSupport { nanometers: 500 } } - }, - status: APPROVED, - activeStatus: ACTIVE + } } }) { observation { diff --git a/modules/service/src/test/scala/lucuma/odb/graphql/mutation/cloneObservation.scala b/modules/service/src/test/scala/lucuma/odb/graphql/mutation/cloneObservation.scala index 637590f9a..6eb1c9948 100644 --- a/modules/service/src/test/scala/lucuma/odb/graphql/mutation/cloneObservation.scala +++ b/modules/service/src/test/scala/lucuma/odb/graphql/mutation/cloneObservation.scala @@ -10,16 +10,10 @@ import eu.timepit.refined.types.numeric.PosInt import io.circe.Json import io.circe.literal.* import io.circe.syntax.* -import lucuma.core.enums.ObsActiveStatus -import lucuma.core.enums.ObsStatus import lucuma.core.enums.ObservingModeType -import lucuma.core.enums.ScienceBand -import lucuma.core.enums.TimeAccountingCategory import lucuma.core.model.Observation import lucuma.core.model.ObservationReference import lucuma.core.model.Target -import lucuma.core.syntax.timespan.* -import lucuma.odb.data.Existence class cloneObservation extends OdbSuite { val pi, pi2 = TestUsers.Standard.pi(nextId, nextId) @@ -228,96 +222,96 @@ class cloneObservation extends OdbSuite { } - test("cloned observation should always be present/new/active") { + // test("cloned observation should always be present/new/active") { - def updateFields(oid: Observation.Id): IO[Unit] = - expect( - user = pi, - query = s""" - mutation { - updateObservations(input: { - SET: { - scienceBand: ${ScienceBand.Band1.tag.toUpperCase} - existence: ${Existence.Deleted.tag.toUpperCase} - status: ${ObsStatus.Observed.tag.toUpperCase} - activeStatus: ${ObsActiveStatus.Inactive.tag.toUpperCase} - }, - WHERE: { - id: { EQ: ${oid.asJson} } - } - }) { - observations { - id - scienceBand - existence - status - activeStatus - } - } - } - """, - expected = Right( - json""" - { - "updateObservations" : { - "observations" : [ - { - "id" : $oid, - "scienceBand": "BAND1", - "existence" : "DELETED", - "status" : "OBSERVED", - "activeStatus" : "INACTIVE" - } - ] - } - } - """ - ) - ) + // def updateFields(oid: Observation.Id): IO[Unit] = + // expect( + // user = pi, + // query = s""" + // mutation { + // updateObservations(input: { + // SET: { + // scienceBand: ${ScienceBand.Band1.tag.toUpperCase} + // existence: ${Existence.Deleted.tag.toUpperCase} + // status: ${ObsStatus.Observed.tag.toUpperCase} + // activeStatus: ${ObsActiveStatus.Inactive.tag.toUpperCase} + // }, + // WHERE: { + // id: { EQ: ${oid.asJson} } + // } + // }) { + // observations { + // id + // scienceBand + // existence + // status + // activeStatus + // } + // } + // } + // """, + // expected = Right( + // json""" + // { + // "updateObservations" : { + // "observations" : [ + // { + // "id" : $oid, + // "scienceBand": "BAND1", + // "existence" : "DELETED", + // "status" : "OBSERVED", + // "activeStatus" : "INACTIVE" + // } + // ] + // } + // } + // """ + // ) + // ) - val setup = - for - pid <- createProgramAs(pi) - _ <- setOneAllocationAs(staff, pid, TimeAccountingCategory.US, ScienceBand.Band1, 1.hourTimeSpan) - oid <- createObservationAs(pi, pid) - _ <- updateFields(oid) - yield oid + // val setup = + // for + // pid <- createProgramAs(pi) + // _ <- setOneAllocationAs(staff, pid, TimeAccountingCategory.US, ScienceBand.Band1, 1.hourTimeSpan) + // oid <- createObservationAs(pi, pid) + // _ <- updateFields(oid) + // yield oid - setup.flatMap { oid => - expect( - user = pi, - query = s""" - mutation { - cloneObservation(input: { - observationId: "$oid" - }) { - newObservation { - scienceBand - existence - status - activeStatus - } - } - } - """, - expected = Right( - json""" - { - "cloneObservation" : { - "newObservation" : { - "scienceBand": "BAND1", - "existence" : "PRESENT", - "status" : "NEW", - "activeStatus" : "ACTIVE" - } - } - } - """ - ) - ) - } + // setup.flatMap { oid => + // expect( + // user = pi, + // query = s""" + // mutation { + // cloneObservation(input: { + // observationId: "$oid" + // }) { + // newObservation { + // scienceBand + // existence + // status + // activeStatus + // } + // } + // } + // """, + // expected = Right( + // json""" + // { + // "cloneObservation" : { + // "newObservation" : { + // "scienceBand": "BAND1", + // "existence" : "PRESENT", + // "status" : "NEW", + // "activeStatus" : "ACTIVE" + // } + // } + // } + // """ + // ) + // ) + // } - } + // } test("cloned observation should appear next to its source (top level)") { for diff --git a/modules/service/src/test/scala/lucuma/odb/graphql/mutation/createObservation.scala b/modules/service/src/test/scala/lucuma/odb/graphql/mutation/createObservation.scala index c53d3f22e..356b3f256 100644 --- a/modules/service/src/test/scala/lucuma/odb/graphql/mutation/createObservation.scala +++ b/modules/service/src/test/scala/lucuma/odb/graphql/mutation/createObservation.scala @@ -27,8 +27,6 @@ import lucuma.core.enums.GmosSouthGrating import lucuma.core.enums.GmosXBinning import lucuma.core.enums.GmosYBinning import lucuma.core.enums.ImageQuality -import lucuma.core.enums.ObsActiveStatus -import lucuma.core.enums.ObsStatus import lucuma.core.enums.Partner import lucuma.core.enums.ScienceBand import lucuma.core.enums.ScienceMode @@ -49,7 +47,6 @@ import lucuma.core.model.sequence.gmos.binning.northSpectralBinning import lucuma.core.syntax.timespan.* import lucuma.odb.data.PosAngleConstraintMode import lucuma.odb.graphql.input.AllocationInput -import lucuma.odb.service.ObservationService import scala.collection.immutable.SortedMap @@ -341,34 +338,34 @@ class createObservation extends OdbSuite { } } - test("[general] created observation should have specified status") { - createProgramAs(pi).flatMap { pid => - query(pi, - s""" - mutation { - createObservation(input: { - programId: ${pid.asJson} - SET: { - status: FOR_REVIEW - } - }) { - observation { - status - } - } - } - """).flatMap { js => - val get = js.hcursor - .downField("createObservation") - .downField("observation") - .downField("status") - .as[ObsStatus] - .leftMap(f => new RuntimeException(f.message)) - .liftTo[IO] - assertIO(get, ObsStatus.ForReview) - } - } - } + // test("[general] created observation should have specified status") { + // createProgramAs(pi).flatMap { pid => + // query(pi, + // s""" + // mutation { + // createObservation(input: { + // programId: ${pid.asJson} + // SET: { + // status: FOR_REVIEW + // } + // }) { + // observation { + // status + // } + // } + // } + // """).flatMap { js => + // val get = js.hcursor + // .downField("createObservation") + // .downField("observation") + // .downField("status") + // .as[ObsStatus] + // .leftMap(f => new RuntimeException(f.message)) + // .liftTo[IO] + // assertIO(get, ObsStatus.ForReview) + // } + // } + // } test("[general] created observation may have no science band") { createProgramAs(pi).flatMap { pid => @@ -452,63 +449,63 @@ class createObservation extends OdbSuite { } } - test("[general] created ready observation must have a science band") { - createProgramAs(pi) - .flatTap(pid => setOneAllocationAs(staff, pid, TimeAccountingCategory.US, ScienceBand.Band2, 1.hourTimeSpan)) - .flatMap { pid => - query(pi, - s""" - mutation { - createObservation(input: { - programId: ${pid.asJson} - SET: { - scienceBand: BAND2 - status: READY - } - }) { - observation { - scienceBand - status - } - } - } - """).flatMap { js => - val get = js.hcursor - .downField("createObservation") - .downField("observation") - .downField("scienceBand") - .as[Option[ScienceBand]] - .leftMap(f => new RuntimeException(f.message)) - .liftTo[IO] - assertIO(get, ScienceBand.Band2.some) - } - } - } - - test("[general] created ready observation must fail without a science band") { - createProgramAs(pi) - .flatTap(pid => setAllocationsAs(staff, pid, List(AllocationInput(TimeAccountingCategory.US, ScienceBand.Band1, 1.hourTimeSpan), AllocationInput(TimeAccountingCategory.CA, ScienceBand.Band2, 1.hourTimeSpan)))) - .flatMap { pid => - expect(pi, - s""" - mutation { - createObservation(input: { - programId: ${pid.asJson} - SET: { - status: READY - } - }) { - observation { - scienceBand - status - } - } - } - """, - List(ObservationService.MissingScienceBandConstraint.message).asLeft - ) - } - } + // test("[general] created ready observation must have a science band") { + // createProgramAs(pi) + // .flatTap(pid => setOneAllocationAs(staff, pid, TimeAccountingCategory.US, ScienceBand.Band2, 1.hourTimeSpan)) + // .flatMap { pid => + // query(pi, + // s""" + // mutation { + // createObservation(input: { + // programId: ${pid.asJson} + // SET: { + // scienceBand: BAND2 + // status: READY + // } + // }) { + // observation { + // scienceBand + // status + // } + // } + // } + // """).flatMap { js => + // val get = js.hcursor + // .downField("createObservation") + // .downField("observation") + // .downField("scienceBand") + // .as[Option[ScienceBand]] + // .leftMap(f => new RuntimeException(f.message)) + // .liftTo[IO] + // assertIO(get, ScienceBand.Band2.some) + // } + // } + // } + + // test("[general] created ready observation must fail without a science band") { + // createProgramAs(pi) + // .flatTap(pid => setAllocationsAs(staff, pid, List(AllocationInput(TimeAccountingCategory.US, ScienceBand.Band1, 1.hourTimeSpan), AllocationInput(TimeAccountingCategory.CA, ScienceBand.Band2, 1.hourTimeSpan)))) + // .flatMap { pid => + // expect(pi, + // s""" + // mutation { + // createObservation(input: { + // programId: ${pid.asJson} + // SET: { + // status: READY + // } + // }) { + // observation { + // scienceBand + // status + // } + // } + // } + // """, + // List(ObservationService.MissingScienceBandConstraint.message).asLeft + // ) + // } + // } test("[general] create observation with a single science band allocation") { createProgramAs(pi) @@ -574,34 +571,34 @@ class createObservation extends OdbSuite { } } - test("[general] created observation should have specified active status") { - createProgramAs(pi).flatMap { pid => - query(pi, - s""" - mutation { - createObservation(input: { - programId: ${pid.asJson} - SET: { - activeStatus: INACTIVE - } - }) { - observation { - activeStatus - } - } - } - """).flatMap { js => - val get = js.hcursor - .downField("createObservation") - .downField("observation") - .downField("activeStatus") - .as[ObsActiveStatus] - .leftMap(f => new RuntimeException(f.message)) - .liftTo[IO] - assertIO(get, ObsActiveStatus.Inactive) - } - } - } + // test("[general] created observation should have specified active status") { + // createProgramAs(pi).flatMap { pid => + // query(pi, + // s""" + // mutation { + // createObservation(input: { + // programId: ${pid.asJson} + // SET: { + // activeStatus: INACTIVE + // } + // }) { + // observation { + // activeStatus + // } + // } + // } + // """).flatMap { js => + // val get = js.hcursor + // .downField("createObservation") + // .downField("observation") + // .downField("activeStatus") + // .as[ObsActiveStatus] + // .leftMap(f => new RuntimeException(f.message)) + // .liftTo[IO] + // assertIO(get, ObsActiveStatus.Inactive) + // } + // } + // } test("[general] created observation has no explicit base by default") { createProgramAs(pi).flatMap { pid => diff --git a/modules/service/src/test/scala/lucuma/odb/graphql/mutation/updateObservations.scala b/modules/service/src/test/scala/lucuma/odb/graphql/mutation/updateObservations.scala index 4be01c2c6..74e6cb400 100644 --- a/modules/service/src/test/scala/lucuma/odb/graphql/mutation/updateObservations.scala +++ b/modules/service/src/test/scala/lucuma/odb/graphql/mutation/updateObservations.scala @@ -1778,102 +1778,61 @@ class updateObservations extends OdbSuite } yield () } - test("constraint set: ready but no science band") { - oneUpdateTest( - user = pi, - update = """ - status: READY - """, - query = """ - observations { - status - } - """, - expected = ObservationService.MissingScienceBandConstraint.message.asLeft - ) - } - - test("constraint set: science band / ready set together") { - for - pid <- createProgramAs(pi) - _ <- setOneAllocationAs(staff, pid, TimeAccountingCategory.US, ScienceBand.Band1, 1.hourTimeSpan) - oid <- createObservationAs(pi, pid) - _ <- expect( - user = pi, - query = s""" - mutation { - updateObservations(input: { - SET: { - status: READY - scienceBand: BAND1 - }, - WHERE: { - id: { EQ: "$oid" } - } - }) { - observations { - status - scienceBand - } - } - } - """, - expected = json""" - { - "updateObservations": { - "observations": [ - { - "status": "READY", - "scienceBand": "BAND1" - } - ] - } - } - """.asRight - ) - yield () - } - - - test("forReview: change value") { - for - pid <- createProgramAs(pi) - oid <- createObservationAs(pi, pid) // defaults to false - _ <- expect( - user = pi, - query = s""" - mutation { - updateObservations(input: { - SET: { - forReview: true - }, - WHERE: { - id: { EQ: "$oid" } - } - }) { - observations { - id - forReview - } - } - } - """, - expected = json""" - { - "updateObservations": { - "observations": [ - { - "id": $oid, - "forReview": true - } - ] - } - } - """.asRight - ) - yield () - } - + // test("constraint set: ready but no science band") { + // oneUpdateTest( + // user = pi, + // update = """ + // status: READY + // """, + // query = """ + // observations { + // status + // } + // """, + // expected = ObservationService.MissingScienceBandConstraint.message.asLeft + // ) + // } + + // test("constraint set: science band / ready set together") { + // for + // pid <- createProgramAs(pi) + // _ <- setOneAllocationAs(staff, pid, TimeAccountingCategory.US, ScienceBand.Band1, 1.hourTimeSpan) + // oid <- createObservationAs(pi, pid) + // _ <- expect( + // user = pi, + // query = s""" + // mutation { + // updateObservations(input: { + // SET: { + // status: READY + // scienceBand: BAND1 + // }, + // WHERE: { + // id: { EQ: "$oid" } + // } + // }) { + // observations { + // status + // scienceBand + // } + // } + // } + // """, + // expected = json""" + // { + // "updateObservations": { + // "observations": [ + // { + // "status": "READY", + // "scienceBand": "BAND1" + // } + // ] + // } + // } + // """.asRight + // ) + // yield () + // } } diff --git a/modules/service/src/test/scala/lucuma/odb/graphql/query/ObservingModeSetupOperations.scala b/modules/service/src/test/scala/lucuma/odb/graphql/query/ObservingModeSetupOperations.scala index ed0c69b45..527c2a0ff 100644 --- a/modules/service/src/test/scala/lucuma/odb/graphql/query/ObservingModeSetupOperations.scala +++ b/modules/service/src/test/scala/lucuma/odb/graphql/query/ObservingModeSetupOperations.scala @@ -6,14 +6,11 @@ package query import cats.effect.IO import io.circe.syntax.* -import lucuma.core.enums.ObsActiveStatus -import lucuma.core.enums.ObsStatus import lucuma.core.math.Angle import lucuma.core.model.Observation import lucuma.core.model.Program import lucuma.core.model.Target import lucuma.core.model.User -import lucuma.core.syntax.string.* import ObservingModeSetupOperations.* @@ -26,8 +23,6 @@ trait ObservingModeSetupOperations extends DatabaseOperations { this: OdbSuite = user: User, pid: Program.Id, tids: List[Target.Id], - status: ObsStatus = ObsStatus.Approved, - activeStatus: ObsActiveStatus = ObsActiveStatus.Active, offsetArcsec: Option[List[Int]] = None ): IO[Observation.Id] = createObservationWithModeAs( @@ -45,17 +40,13 @@ trait ObservingModeSetupOperations extends DatabaseOperations { this: OdbSuite = explicitYBin: TWO ${offsetArcsec.fold("")(formatExplicitOffsetsInput)} } - """, - status, - activeStatus + """ ) def createGmosSouthLongSlitObservationAs( user: User, pid: Program.Id, - tids: List[Target.Id], - status: ObsStatus = ObsStatus.Approved, - activeStatus: ObsActiveStatus = ObsActiveStatus.Active + tids: List[Target.Id] ): IO[Observation.Id] = createObservationWithModeAs( user, @@ -71,9 +62,7 @@ trait ObservingModeSetupOperations extends DatabaseOperations { this: OdbSuite = }, explicitYBin: TWO } - """, - status, - activeStatus + """ ) def createObservationWithModeAs( @@ -81,8 +70,6 @@ trait ObservingModeSetupOperations extends DatabaseOperations { this: OdbSuite = pid: Program.Id, tids: List[Target.Id], mode: String, - status: ObsStatus = ObsStatus.Approved, - activeStatus: ObsActiveStatus = ObsActiveStatus.Active ): IO[Observation.Id] = query( user = user, @@ -100,8 +87,6 @@ trait ObservingModeSetupOperations extends DatabaseOperations { this: OdbSuite = observingMode: { $mode }, - status: ${status.tag.toScreamingSnakeCase}, - activeStatus: ${activeStatus.tag.toScreamingSnakeCase} } }) { observation { @@ -117,9 +102,7 @@ trait ObservingModeSetupOperations extends DatabaseOperations { this: OdbSuite = def createObservationWithNoModeAs( user: User, pid: Program.Id, - tid: Target.Id, - status: ObsStatus = ObsStatus.Approved, - activeStatus: ObsActiveStatus = ObsActiveStatus.Active + tid: Target.Id, ): IO[Observation.Id] = query( user = user, @@ -134,8 +117,6 @@ trait ObservingModeSetupOperations extends DatabaseOperations { this: OdbSuite = asterism: ${List(tid).asJson} }, $ScienceRequirements, - status: ${status.tag.toScreamingSnakeCase}, - activeStatus: ${activeStatus.tag.toScreamingSnakeCase} } }) { observation { diff --git a/modules/service/src/test/scala/lucuma/odb/graphql/query/executionFailures.scala b/modules/service/src/test/scala/lucuma/odb/graphql/query/executionFailures.scala index 87f5bd95f..fe584fabb 100644 --- a/modules/service/src/test/scala/lucuma/odb/graphql/query/executionFailures.scala +++ b/modules/service/src/test/scala/lucuma/odb/graphql/query/executionFailures.scala @@ -10,8 +10,6 @@ import cats.syntax.option.* import io.circe.Json import io.circe.literal.* import lucuma.core.enums.CalibrationRole -import lucuma.core.enums.ObsActiveStatus -import lucuma.core.enums.ObsStatus import lucuma.core.model.Observation import lucuma.core.model.Program import lucuma.core.model.Target @@ -39,9 +37,7 @@ class executionFailures extends ExecutionTestSupport { fpu: LONG_SLIT_0_50, centralWavelength: { nanometers: 666 } } - """, - ObsStatus.Approved, - ObsActiveStatus.Active + """ ) val setup: IO[Observation.Id] = diff --git a/modules/service/src/test/scala/lucuma/odb/graphql/query/observationValidations.scala b/modules/service/src/test/scala/lucuma/odb/graphql/query/observationValidations.scala deleted file mode 100644 index 07f41cd00..000000000 --- a/modules/service/src/test/scala/lucuma/odb/graphql/query/observationValidations.scala +++ /dev/null @@ -1,690 +0,0 @@ -// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA) -// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause - -package lucuma.odb.graphql - -package query - -import cats.effect.IO -import cats.syntax.all.* -import clue.ResponseException -import io.circe.Json -import io.circe.literal.* -import io.circe.syntax.* -import lucuma.core.enums.CalibrationRole -import lucuma.core.enums.CallForProposalsType -import lucuma.core.enums.Instrument -import lucuma.core.enums.ScienceBand -import lucuma.core.enums.TimeAccountingCategory -import lucuma.core.model.CallForProposals -import lucuma.core.model.ConfigurationRequest -import lucuma.core.model.Observation -import lucuma.core.model.ObservationValidation -import lucuma.core.model.Target -import lucuma.core.model.User -import lucuma.core.syntax.string.* -import lucuma.core.syntax.timespan.* -import lucuma.core.util.TimeSpan -import lucuma.odb.graphql.input.AllocationInput -import lucuma.odb.graphql.mutation.UpdateConstraintSetOps -import lucuma.odb.service.ObservationService - -class observationValidations - extends OdbSuite - with ObservingModeSetupOperations - with UpdateConstraintSetOps { - - val pi = TestUsers.Standard.pi(1, 30) - val staff = TestUsers.Standard.staff(3, 103) - - override def validUsers: List[User] = List(pi, staff) - - // ra and dec values in degrees - val RaStart = 90 - val RaCenter = 180 - val RaEnd = 270 - val RaStartWrap = RaEnd - val RaCenterWrap = 1 - val RaEndWrap = RaStart - val DecStart = 0 - val DecCenter = 25 - val DecEnd = 45 - val Limits = (RaStart, RaEnd, DecStart, DecEnd) - val WrappedLimits = (RaStartWrap, RaEndWrap, DecStart, DecEnd) - - def validationQuery(oid: Observation.Id) = - s""" - query { - observation(observationId: "$oid") { - validations { - code - messages - } - } - } - """ - - def itcQuery(oid: Observation.Id) = - s""" - query { - observation(observationId: "$oid") { - itc { - science { - selected { - targetId - } - } - } - } - } - """ - - // Load up the cache with an ITC result - def computeItcResult(oid: Observation.Id): IO[Unit] = - query(pi, itcQuery(oid)).void - - def queryResult(obsVals: ObservationValidation*): Json = { - Json.obj( - "observation" -> - Json.obj("validations" -> obsVals.asJson) - ) - } - - // only use this if you have instruments, limits or both. Otherwise use createCallForProposalsAs(...) - def createCfp( - instruments: List[Instrument] = List.empty, - limits: Option[(Int, Int, Int, Int)] = none - ): IO[CallForProposals.Id] = - val inStr = - if (instruments.isEmpty) "" - else s"instruments: [${instruments.map(_.tag.toScreamingSnakeCase).mkString(",")}]\n" - val limitStr = limits.fold("") { (raStart, raEnd, decStart, decEnd) => - s""" - coordinateLimits: { - north: { - raStart: { degrees: $raStart } - raEnd: { degrees: $raEnd } - decStart: { degrees: $decStart } - decEnd: { degrees: $decEnd } - } - south: { - raStart: { degrees: $raStart } - raEnd: { degrees: $raEnd } - decStart: { degrees: $decStart } - decEnd: { degrees: $decEnd } - } - } - """ - } - createCallForProposalsAs(staff, other = (inStr + limitStr).some) - - def setTargetCoords(tid: Target.Id, raHours: Int, decHours: Int): IO[Unit] = - query( - user = pi, - query = s""" - mutation { - updateTargets(input: { - SET: { - sidereal: { - ra: { degrees: $raHours } - dec: { degrees: $decHours } - } - } - WHERE: { - id: { EQ: "$tid" } - } - }) { - targets { - id - } - } - } - """ - ).void - - def setObservationExplicitBase(oid: Observation.Id, raHours: Int, decHours: Int): IO[Unit] = - query( - user = pi, - query = s""" - mutation { - updateObservations(input: { - SET: { - targetEnvironment: { - explicitBase: { - ra: { degrees: $raHours } - dec: { degrees: $decHours } - } - } - }, - WHERE: { - id: { EQ: ${oid.asJson} } - } - }) { - observations { - id - } - } - } - """ - ).void - - test("no target") { - val setup: IO[Observation.Id] = - for { - pid <- createProgramAs(pi) - oid <- createObservationAs(pi, pid) - } yield oid - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult( - ObservationValidation.configuration(ObservationService.MissingDataMsg(none, "target")) - ).asRight - ) - } - } - - test("no observing mode") { - val setup: IO[Observation.Id] = - for { - pid <- createProgramAs(pi) - tid <- createTargetAs(pi, pid) - oid <- createObservationAs(pi, pid, tid) - } yield oid - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult( - ObservationValidation.configuration(ObservationService.MissingDataMsg(none, "observing mode")) - ).asRight - ) - } - } - - test("missing target info") { - val setup: IO[(Target.Id, Observation.Id)] = - for { - pid <- createProgramAs(pi) - tid <- createIncompleteTargetAs(pi, pid) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - } yield (tid, oid) - setup.flatMap { (_, oid) => - expect( - pi, - validationQuery(oid), - expected = queryResult( - ObservationValidation.configuration( - "Missing brightness measure, radial velocity" - ) - ).asRight - ) - } - } - - - test("otherwise ok, but no itc results in cache") { - val setup: IO[Observation.Id] = - for { - pid <- createProgramAs(pi) - tid <- createTargetWithProfileAs(pi, pid) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - } yield oid - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult( - ObservationValidation.itc("ITC results are not present.") - ).asRight - ) - } - } - - // temporary, until this is doable via graphql - def approveConfigurationRequestHack(req: ConfigurationRequest.Id): IO[Unit] = - import skunk.syntax.all.* - import lucuma.odb.util.Codecs.configuration_request_id - session.use: s => - s.prepareR(sql"update t_configuration_request set c_status = 'approved' where c_configuration_request_id = $configuration_request_id".command).use: ps => - ps.execute(req).void - - def denyConfigurationRequestHack(req: ConfigurationRequest.Id): IO[Unit] = - import skunk.syntax.all.* - import lucuma.odb.util.Codecs.configuration_request_id - session.use: s => - s.prepareR(sql"update t_configuration_request set c_status = 'denied' where c_configuration_request_id = $configuration_request_id".command).use: ps => - ps.execute(req).void - - test("no validations") { - val setup: IO[Observation.Id] = - for { - cfp <- createCallForProposalsAs(staff) - pid <- createProgramAs(pi) - _ <- addProposal(pi, pid, Some(cfp), None, "Foo") - tid <- createTargetWithProfileAs(pi, pid) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - _ <- createConfigurationRequestAs(pi, oid).flatMap(approveConfigurationRequestHack) - _ <- computeItcResult(oid) - } yield oid - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult().asRight - ) - } - } - - test("missing target info, invalid instrument") { - val setup: IO[(Target.Id, Observation.Id)] = - for { - pid <- createProgramAs(pi) - cid <- createCfp(List(Instrument.GmosSouth)) - _ <- addProposal(pi, pid, cid.some) - tid <- createIncompleteTargetAs(pi, pid) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - } yield (tid, oid) - setup.flatMap { (_, oid) => - expect( - pi, - validationQuery(oid), - expected = queryResult( - ObservationValidation.configuration( - "Missing brightness measure, radial velocity" - ), - ObservationValidation.callForProposals( - ObservationService.InvalidInstrumentMsg(Instrument.GmosNorth) - ) - ).asRight - ) - } - } - - test("valid instrument") { - val setup: IO[Observation.Id] = - for { - pid <- createProgramAs(pi) - cid <- createCfp(List(Instrument.GmosNorth)) - _ <- addProposal(pi, pid, cid.some) - tid <- createTargetWithProfileAs(pi, pid) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - _ <- createConfigurationRequestAs(pi, oid).flatMap(approveConfigurationRequestHack) - _ <- computeItcResult(oid) - } yield oid - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult().asRight - ) - } - } - - test("invalid instrument") { - val setup: IO[Observation.Id] = - for { - pid <- createProgramAs(pi) - cid <- createCfp(List(Instrument.GmosSouth)) - _ <- addProposal(pi, pid, cid.some) - tid <- createTargetWithProfileAs(pi, pid) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - } yield oid - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult( - ObservationValidation.callForProposals( - ObservationService.InvalidInstrumentMsg(Instrument.GmosNorth) - ) - ).asRight - ) - } - } - - test("explicit base within limits") { - val setup: IO[Observation.Id] = - for { - pid <- createProgramAs(pi) - cid <- createCfp(List(Instrument.GmosNorth), limits = Limits.some) - _ <- addProposal(pi, pid, cid.some) - tid <- createTargetWithProfileAs(pi, pid) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - _ <- computeItcResult(oid) - } yield oid - setup.flatMap { oid => - List((RaStart, DecStart), (RaEnd, DecEnd), (RaStart, DecEnd), (RaCenter, DecCenter)).traverse { (ra, dec) => - setObservationExplicitBase(oid, ra, dec) >> - createConfigurationRequestAs(pi, oid).flatMap(approveConfigurationRequestHack) >> - expect( - pi, - validationQuery(oid), - expected = queryResult().asRight - ) - } - } - } - - test("explicit base within limits, CfP with wrapped RA limits") { - val setup: IO[Observation.Id] = - for { - pid <- createProgramAs(pi) - cid <- createCfp(List(Instrument.GmosNorth), limits = WrappedLimits.some) - _ <- addProposal(pi, pid, cid.some) - tid <- createTargetWithProfileAs(pi, pid) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - _ <- computeItcResult(oid) - } yield oid - setup.flatMap { oid => - List((RaStartWrap, DecStart), (RaEndWrap, DecEnd), (RaStartWrap, DecEnd), (RaCenterWrap, DecCenter)).traverse { (ra, dec) => - setObservationExplicitBase(oid, ra, dec) >> - createConfigurationRequestAs(pi, oid).flatMap(approveConfigurationRequestHack) >> - expect( - pi, - validationQuery(oid), - expected = queryResult().asRight - ) - } - } - } - - test("explicit base outside limits") { - val setup: IO[Observation.Id] = - for { - pid <- createProgramAs(pi) - cid <- createCfp(List(Instrument.GmosNorth), limits = Limits.some) - _ <- addProposal(pi, pid, cid.some) - tid <- createTargetWithProfileAs(pi, pid) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - } yield oid - setup.flatMap { oid => - List((RaStart - 1, DecStart), (RaEnd, DecEnd + 1), (RaCenterWrap, DecCenter)).traverse { (ra, dec) => - setObservationExplicitBase(oid, ra, dec) >> - expect( - pi, - validationQuery(oid), - expected = queryResult( - ObservationValidation.callForProposals(ObservationService.ExplicitBaseOutOfRangeMsg) - ).asRight - ) - } - } - } - - test("explicit base outside limits, CfP with wrapped RA limits") { - val setup: IO[Observation.Id] = - for { - pid <- createProgramAs(pi) - cid <- createCfp(List(Instrument.GmosNorth), limits = WrappedLimits.some) - _ <- addProposal(pi, pid, cid.some) - tid <- createTargetWithProfileAs(pi, pid) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - } yield oid - setup.flatMap { oid => - List((RaStartWrap, DecStart - 1), (RaEndWrap + 1, DecEnd), (RaStartWrap - 1, DecEnd), (RaCenter, DecCenter)).traverse { (ra, dec) => - setObservationExplicitBase(oid, ra, dec) >> - expect( - pi, - validationQuery(oid), - expected = queryResult( - ObservationValidation.callForProposals(ObservationService.ExplicitBaseOutOfRangeMsg) - ).asRight - ) - } - } - } - - test("asterism within limits, valid instrument") { - val setup: IO[Observation.Id] = - for { - pid <- createProgramAs(pi) - cid <- createCfp(List(Instrument.GmosNorth), limits = Limits.some) - _ <- addProposal(pi, pid, cid.some) - tid <- createTargetWithProfileAs(pi, pid) - _ <- setTargetCoords(tid, RaCenter, DecCenter) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - _ <- createConfigurationRequestAs(pi, oid).flatMap(approveConfigurationRequestHack) - _ <- computeItcResult(oid) - } yield oid - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult().asRight - ) - } - } - - test("asterism outside limits, valid instrument") { - val setup: IO[Observation.Id] = - for { - pid <- createProgramAs(pi) - cid <- createCfp(List(Instrument.GmosNorth), limits = Limits.some) - _ <- addProposal(pi, pid, cid.some) - tid <- createTargetWithProfileAs(pi, pid) - _ <- setTargetCoords(tid, RaStart - 20, DecCenter) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - } yield oid - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult( - ObservationValidation.callForProposals(ObservationService.AsterismOutOfRangeMsg) - ).asRight - ) - } - } - - test("asterism outside limits, invalid instrument") { - val setup: IO[Observation.Id] = - for { - pid <- createProgramAs(pi) - cid <- createCfp(List(Instrument.GmosNorth), limits = Limits.some) - _ <- addProposal(pi, pid, cid.some) - tid <- createTargetWithProfileAs(pi, pid) - _ <- setTargetCoords(tid, RaStart - 20, DecCenter) - oid <- createGmosSouthLongSlitObservationAs(pi, pid, List(tid)) - } yield oid - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult( - ObservationValidation.callForProposals( - ObservationService.InvalidInstrumentMsg(Instrument.GmosSouth), - ObservationService.AsterismOutOfRangeMsg - ) - ).asRight - ) - } - } - - test("invalid band") { - val allocations = List( - AllocationInput(TimeAccountingCategory.US, ScienceBand.Band1, 1.hourTimeSpan), - AllocationInput(TimeAccountingCategory.CA, ScienceBand.Band2, 4.hourTimeSpan) - ) - - val setup: IO[Observation.Id] = - for { - pid <- createProgramAs(pi) - _ <- setAllocationsAs(staff, pid, allocations) - tid <- createTargetWithProfileAs(pi, pid) - oid <- createGmosSouthLongSlitObservationAs(pi, pid, List(tid)) - _ <- computeItcResult(oid) - _ <- setScienceBandAs(pi, oid, ScienceBand.Band1.some) - _ <- setAllocationsAs(staff, pid, allocations.tail).intercept[ResponseException[Json]].void - } yield oid - - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult( - ObservationValidation.configuration( - ObservationService.InvalidScienceBandMsg(ScienceBand.Band1) - ) - ).asRight - ) - } - } - - test("no configuration request checks if proposal is not approved") { - val setup: IO[Observation.Id] = - for { - cfp <- createCallForProposalsAs(staff) - pid <- createProgramAs(pi) - _ <- addProposal(pi, pid, Some(cfp), None, "Foo") - tid <- createTargetWithProfileAs(pi, pid) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - _ <- computeItcResult(oid) - } yield oid - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult().asRight // no warnings! - ) - } - } - - test("no configuration request") { - val setup: IO[Observation.Id] = - for { - cfp <- createCallForProposalsAs(staff) - pid <- createProgramAs(pi) - _ <- addProposal(pi, pid, Some(cfp), None, "Foo") - _ <- addPartnerSplits(pi, pid) - _ <- setProposalStatus(staff, pid, "ACCEPTED") - tid <- createTargetWithProfileAs(pi, pid) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - _ <- computeItcResult(oid) - } yield oid - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult( - ObservationValidation.configurationRequestNotRequested - ).asRight - ) - } - } - - test("unapproved configuration request") { - val setup: IO[Observation.Id] = - for { - cfp <- createCallForProposalsAs(staff) - pid <- createProgramAs(pi) - _ <- addProposal(pi, pid, Some(cfp), None, "Foo") - _ <- addPartnerSplits(pi, pid) - _ <- setProposalStatus(staff, pid, "ACCEPTED") - tid <- createTargetWithProfileAs(pi, pid) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - _ <- createConfigurationRequestAs(pi, oid) - _ <- computeItcResult(oid) - } yield oid - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult( - ObservationValidation.configurationRequestPending - ).asRight - ) - } - } - - test("denied configuration request") { - val setup: IO[Observation.Id] = - for { - cfp <- createCallForProposalsAs(staff) - pid <- createProgramAs(pi) - _ <- addProposal(pi, pid, Some(cfp), None, "Foo") - _ <- addPartnerSplits(pi, pid) - _ <- setProposalStatus(staff, pid, "ACCEPTED") - tid <- createTargetWithProfileAs(pi, pid) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - _ <- createConfigurationRequestAs(pi, oid).flatMap(denyConfigurationRequestHack) - _ <- computeItcResult(oid) - } yield oid - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult( - ObservationValidation.configurationRequestDenied - ).asRight - ) - } - } - - test("for review") { - def setForReview(oid: Observation.Id): IO[Unit] = - updateObservation( - user = pi, - oid = oid, - update = "forReview: true", - query = """ - observations { - id - forReview - } - """, - expected = Right(json""" - { - "updateObservations": { - "observations": [ - { - "id": $oid, - "forReview": true - } - ] - } - } - """) - ) - val setup: IO[Observation.Id] = - for { - cfp <- createCallForProposalsAs(staff) - pid <- createProgramAs(pi) - _ <- addProposal(pi, pid, Some(cfp), None, "Foo") - tid <- createTargetWithProfileAs(pi, pid) - oid <- createGmosNorthLongSlitObservationAs(pi, pid, List(tid)) - _ <- createConfigurationRequestAs(pi, oid).flatMap(approveConfigurationRequestHack) - _ <- computeItcResult(oid) - _ <- setForReview(oid) - } yield oid - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult( - ObservationValidation.configuration( - ObservationService.ConfigurationForReviewMsg - ) - ).asRight - ) - } - } - - test("calibrations are not validated") { - val setup: IO[Observation.Id] = - for { - pid <- createProgramAs(pi) - oid <- createObservationAs(pi, pid) // is missing target - _ <- setObservationCalibratioRole(oid, CalibrationRole.SpectroPhotometric.some) - } yield oid - setup.flatMap { oid => - expect( - pi, - validationQuery(oid), - expected = queryResult().asRight - ) - } - } - -} diff --git a/modules/service/src/test/scala/lucuma/odb/graphql/query/observation_workflow.scala b/modules/service/src/test/scala/lucuma/odb/graphql/query/observation_workflow.scala index 5e059b450..8101ce6ee 100644 --- a/modules/service/src/test/scala/lucuma/odb/graphql/query/observation_workflow.scala +++ b/modules/service/src/test/scala/lucuma/odb/graphql/query/observation_workflow.scala @@ -703,7 +703,7 @@ class observation_workflow } } - test("calibrations are not validated") { + test("calibrations are not validated and are immediately Ready") { val setup: IO[Observation.Id] = for { pid <- createProgramAs(pi) diff --git a/modules/service/src/test/scala/lucuma/odb/graphql/query/programPlannedTime.scala b/modules/service/src/test/scala/lucuma/odb/graphql/query/programPlannedTime.scala index beaa55528..5e59c490c 100644 --- a/modules/service/src/test/scala/lucuma/odb/graphql/query/programPlannedTime.scala +++ b/modules/service/src/test/scala/lucuma/odb/graphql/query/programPlannedTime.scala @@ -11,9 +11,6 @@ import eu.timepit.refined.types.numeric.PosInt import io.circe.Json import io.circe.literal.* import io.circe.syntax.* -import lucuma.core.enums.ObsActiveStatus.Inactive -import lucuma.core.enums.ObsStatus.Approved -import lucuma.core.enums.ObsStatus.New import lucuma.core.math.SignalToNoise import lucuma.core.model.Group import lucuma.core.model.Observation @@ -131,99 +128,99 @@ class programPlannedTime extends ExecutionTestSupport { } - test("program level: single complete, but 'New' observation") { - val setup: IO[Program.Id] = - for { - p <- createProgram - t <- createTargetWithProfileAs(user, p) - _ <- createGmosNorthLongSlitObservationAs(user, p, List(t), status = New) - } yield p - - setup.flatMap { pid => - expect( - user = user, - query = - s""" - query { - program(programId: "$pid") { - timeEstimateRange { - minimum { total { seconds } } - maximum { total { seconds } } - } - } - } - """, - expected = Right( - json""" - { - "program": { - "timeEstimateRange": { - "minimum": { - "total" : { - "seconds" : 0.000000 - } - }, - "maximum": { - "total" : { - "seconds" : 0.000000 - } - } - } - } - } - """ - ) - ) - } - - } - - test("program level: single complete, but 'Inactive' observation") { - val setup: IO[Program.Id] = - for { - p <- createProgram - t <- createTargetWithProfileAs(user, p) - _ <- createGmosNorthLongSlitObservationAs(user, p, List(t), status = Approved, activeStatus = Inactive) - } yield p - - setup.flatMap { pid => - expect( - user = user, - query = - s""" - query { - program(programId: "$pid") { - timeEstimateRange { - minimum { total { seconds } } - maximum { total { seconds } } - } - } - } - """, - expected = Right( - json""" - { - "program": { - "timeEstimateRange": { - "minimum": { - "total" : { - "seconds" : 0.000000 - } - }, - "maximum": { - "total" : { - "seconds" : 0.000000 - } - } - } - } - } - """ - ) - ) - } - - } + // test("program level: single complete, but 'New' observation") { + // val setup: IO[Program.Id] = + // for { + // p <- createProgram + // t <- createTargetWithProfileAs(user, p) + // _ <- createGmosNorthLongSlitObservationAs(user, p, List(t), status = New) + // } yield p + + // setup.flatMap { pid => + // expect( + // user = user, + // query = + // s""" + // query { + // program(programId: "$pid") { + // timeEstimateRange { + // minimum { total { seconds } } + // maximum { total { seconds } } + // } + // } + // } + // """, + // expected = Right( + // json""" + // { + // "program": { + // "timeEstimateRange": { + // "minimum": { + // "total" : { + // "seconds" : 0.000000 + // } + // }, + // "maximum": { + // "total" : { + // "seconds" : 0.000000 + // } + // } + // } + // } + // } + // """ + // ) + // ) + // } + + // } + + // test("program level: single complete, but 'Inactive' observation") { + // val setup: IO[Program.Id] = + // for { + // p <- createProgram + // t <- createTargetWithProfileAs(user, p) + // _ <- createGmosNorthLongSlitObservationAs(user, p, List(t), status = Approved, activeStatus = Inactive) + // } yield p + + // setup.flatMap { pid => + // expect( + // user = user, + // query = + // s""" + // query { + // program(programId: "$pid") { + // timeEstimateRange { + // minimum { total { seconds } } + // maximum { total { seconds } } + // } + // } + // } + // """, + // expected = Right( + // json""" + // { + // "program": { + // "timeEstimateRange": { + // "minimum": { + // "total" : { + // "seconds" : 0.000000 + // } + // }, + // "maximum": { + // "total" : { + // "seconds" : 0.000000 + // } + // } + // } + // } + // } + // """ + // ) + // ) + // } + + // } test("program level: two complete observations") { val setup: IO[Program.Id] =