From a174f640aee3866e95a9275ad92ede2f14b89ffe Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Wed, 1 Jul 2026 16:25:47 -0700 Subject: [PATCH 01/33] Model generation for CFHT Introduces the datamodel-codegen tool that can convert openapi specs into Pydantic models. This is used to generate models for cfht (after converting to OpenAPI v3 - an annoying side quest) so that theses api types don't need to be hand-written. The docs explicitly state that the API is under development so automating the conversion like this is definitely the way to go for now. --- .gitignore | 1 + Justfile | 4 + codegen/cfht/generate_models.sh | 20 + pyproject.toml | 1 + src/aeonlib/cfht/models.py | 1946 +++++++++++++++++++++++++++++++ src/aeonlib/conf.py | 4 + uv.lock | 177 +++ 7 files changed, 2153 insertions(+) create mode 100644 codegen/cfht/generate_models.sh create mode 100644 src/aeonlib/cfht/models.py diff --git a/.gitignore b/.gitignore index 222ff34..5f3138a 100644 --- a/.gitignore +++ b/.gitignore @@ -176,3 +176,4 @@ cython_debug/ # Direnv .envrc .zed/ +build/ diff --git a/Justfile b/Justfile index 07e305f..83e0a3f 100644 --- a/Justfile +++ b/Justfile @@ -26,6 +26,10 @@ update-blanco: update-saao: curl https://ocsio.saao.ac.za/api/instruments/ | codegen/lco/generator.py SAAO > src/aeonlib/ocs/saao/instruments.py +# Update the CFHT generated models via swagger +update-cfht: + sh codegen/cfht/generate_models.sh + # Update all generated instrument files update-all: update-lco update-soar update-saao update-blanco @echo "All updates completed" diff --git a/codegen/cfht/generate_models.sh b/codegen/cfht/generate_models.sh new file mode 100644 index 0000000..bf71622 --- /dev/null +++ b/codegen/cfht/generate_models.sh @@ -0,0 +1,20 @@ +#!/bin/env sh +set -euxo pipefail +# The CFHT API uses OAS 2.0 use the online Swagger converter to convert to OAS 3.0 +curl -L \ +"https://converter.swagger.io/api/convert?url=https://hou-stage.cfht.hawaii.edu/api-docs/piapi_openapiv2.swagger.json" \ +-o build/cfht.openapi3.json + +# Generate Pydantic models from the OpenAPI 3.0 spec +datamodel-codegen \ + --input "build/cfht.openapi3.json" \ + --input-file-type openapi \ + --openapi-scopes schemas \ + --output src/aeonlib/cfht/models.py \ + --output-model-type pydantic_v2.BaseModel \ + --use-annotated \ + --set-default-enum-member \ + --formatters builtin + +# Ignore some ruff rules that are not relevant for generated code +sed -i '1s/^/# ruff: noqa: E741\n/' src/aeonlib/cfht/models.py diff --git a/pyproject.toml b/pyproject.toml index cf81bd2..f853154 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ build-backend = "hatchling.build" codegen = [ "jinja2>=3.1.6", "textcase>=0.2.1", + "datamodel-code-generator>=0.66.3", ] dev = [ diff --git a/src/aeonlib/cfht/models.py b/src/aeonlib/cfht/models.py new file mode 100644 index 0000000..203b116 --- /dev/null +++ b/src/aeonlib/cfht/models.py @@ -0,0 +1,1946 @@ +# ruff: noqa: E741 +# generated by datamodel-codegen: +# filename: cfht.openapi3.json +# timestamp: 2026-07-01T23:20:38+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Annotated + +from pydantic import Base64Str, BaseModel, Field + + +class AllocationDataAgency(BaseModel): + letter: str | None = None + name: str | None = None + + +class CalibrationCalibrationMode(Enum): + no_calibration_mode = 'no_calibration_mode' + laser = 'laser' + phase = 'phase' + + +class CalibrationCalibrationPosition(Enum): + no_calibration_position = 'no_calibration_position' + zenith = 'zenith' + target = 'target' + + +class ConfigDithering(BaseModel): + isStaring: bool | None = None + + +class EspadonsStatusGuiderSeeingProcessingResult(BaseModel): + guiderSeeing: float | None = None + + +class ExposureDataAttribution(BaseModel): + runid: str | None = None + observing_block_label: Annotated[ + str | None, + Field( + title='If displaying the observing block to the user, this uniquely identifies it in the context\nof an observing group, i.e. OG3-2' + ), + ] = None + observing_block_label_int: Annotated[ + int | None, Field(title='Label of the observing block') + ] = None + observingBlockToken: Annotated[ + str | None, + Field( + description='Token of the observing block this exposure is attributed to.' + ), + ] = None + observingGroupLabel: Annotated[ + int | None, + Field( + description='Label of the observing group this exposure is attributed to.' + ), + ] = None + observingGroupToken: Annotated[ + str | None, + Field( + description='Token of the observing group this exposure is attributed to.' + ), + ] = None + observingComponentToken: Annotated[ + str | None, + Field( + description='Token of the observing component this exposure is attributed to.' + ), + ] = None + exposureNumber: Annotated[ + int | None, Field(description='exposure number this exposure is attributed to.') + ] = None + instrument_run: Annotated[ + str | None, Field(title='the queue run this exposure was taken under') + ] = None + attributingAgency: Annotated[str | None, Field(title='the agency id')] = None + tacRank: int | None = None + tacGrade: str | None = None + + +class ExposureDataEspadonsStatusExposureMeterProcessingResult(BaseModel): + snr: float | None = None + + +class ExposureDataSpirouStatusExposureMeterProcessingResult(BaseModel): + flux: float | None = None + snr: float | None = None + midExposureDateMillis: float | None = None + spemsnr: Annotated[float | None, Field(title='Calculated SNR')] = None + spemsnrc: float | None = None + + +class ExposureDataWenaStatus(BaseModel): + pass + + +class ExposureDataWircamStatus(BaseModel): + iqUncertainty: float | None = None + absorption: float | None = None + absorptionUncertainty: float | None = None + numberOfStarsIq: int | None = None + numberOfStarsAbsorption: int | None = None + absoluteSkyLevel: float | None = None + elongation: float | None = None + mdcoords: int | None = None + mdrepeat: int | None = None + actualFilter: str | None = None + iq: float | None = None + + +class ExposureStatusFile(BaseModel): + absolutePath: Annotated[ + str | None, + Field( + description='Absolute path to directory file is homed in. Starts with / and ends with /.' + ), + ] = None + baseName: Annotated[str | None, Field(title='Name of file without extension')] = ( + None + ) + ext: Annotated[str | None, Field(description='Name of extenion including .')] = None + + +class FindingChartDataApproval(BaseModel): + approvedAtMillis: str | None = None + approvedByUsertoken: str | None = None + + +class FixedTargetProperMotion(BaseModel): + raMas: Annotated[ + float | None, + Field( + description='Motion in the right ascension axis in milliarcseconds/year.' + ), + ] = None + decMas: Annotated[ + float | None, + Field(description='Motion in the declination axis in milliarcseconds/year.'), + ] = None + + +class InstrumentConfigurationDataEspadonsConfigurationDataOObservingMode(Enum): + Polarimetry = 'Polarimetry' + Spectroscopy_star_only = 'Spectroscopy_star_only' + Spectroscopy_star_sky = 'Spectroscopy_star_sky' + + +class InstrumentConfigurationDataMegacamConfigurationDataBinning(Enum): + one_one = 'one_one' + two_two = 'two_two' + three_three = 'three_three' + four_four = 'four_four' + + +class InstrumentConfigurationDataMegacamConfigurationDataFilter(Enum): + Unknown = 'Unknown' + u = 'u' + g = 'g' + i = 'i' + r = 'r' + z = 'z' + gri = 'gri' + CaHK = 'CaHK' + Ha = 'Ha' + HaOFF = 'HaOFF' + OIII = 'OIII' + OIIIOFF = 'OIIIOFF' + uS = 'uS' + gS = 'gS' + rS = 'rS' + iS = 'iS' + zS = 'zS' + N393S = 'N393S' + HaS = 'HaS' + HaOFFS = 'HaOFFS' + TiOS = 'TiOS' + CNS = 'CNS' + OIIIS = 'OIIIS' + PHGS = 'PHGS' + M4112 = 'M4112' + M4376 = 'M4376' + + +class InstrumentConfigurationDataSitelleConfigurationDataBinning(Enum): + one_one = 'one_one' + two_two = 'two_two' + three_three = 'three_three' + + +class InstrumentConfigurationDataSitelleConfigurationDataFilter(Enum): + UNKNOWN = 'UNKNOWN' + NONE = 'NONE' + C1 = 'C1' + C2 = 'C2' + C3 = 'C3' + C4 = 'C4' + SN1 = 'SN1' + SN2 = 'SN2' + SN3 = 'SN3' + SN4 = 'SN4' + SN5 = 'SN5' + SN6 = 'SN6' + + +class InstrumentConfigurationDataSpirouConfigurationDataOObservingMode(Enum): + Polarimetry = 'Polarimetry' + Star = 'Star' + + +class InstrumentConfigurationDataTracking(Enum): + Unknown = 'Unknown' + SID = 'SID' + NONSID = 'NONSID' + SID_NOG = 'SID_NOG' + NONSID_G = 'NONSID_G' + + +class InstrumentConfigurationDataWenaConfigurationData(BaseModel): + pass + + +class InstrumentConfigurationDataWircamConfigurationDataFilter(Enum): + UNKNOWN = 'UNKNOWN' + W = 'W' + H = 'H' + J = 'J' + Ks = 'Ks' + Y = 'Y' + H2 = 'H2' + KCont = 'KCont' + CH4On = 'CH4On' + CH4Off = 'CH4Off' + LowOH1 = 'LowOH1' + LowOH2 = 'LowOH2' + BrG = 'BrG' + CO = 'CO' + COnar = 'COnar' + + +class MMode(Enum): + Dark = 'Dark' + Fabry_Perot = 'Fabry_Perot' + Wave_Hc1 = 'Wave_Hc1' + Wave_Hc2 = 'Wave_Hc2' + FLAT = 'FLAT' + + +class MegacamConfigurationDataSnrMode(Enum): + NONE = 'NONE' + STANDARD = 'STANDARD' + CUMULATIVE = 'CUMULATIVE' + ZERO_POINT = 'ZERO_POINT' + + +class MegacamStatusElixirProcessingResult(BaseModel): + iq: float | None = None + skyBackground: float | None = None + snr: float | None = None + zeropoint: float | None = None + + +class ObservingBlockConditionRequisite(BaseModel): + requisiteObservingBlockIndex: Annotated[ + int | None, + Field( + description='This should match the index of the observing block in the\nread_observing_block list.' + ), + ] = None + waitPeriodMinMillis: Annotated[ + str | None, + Field( + description='Minimum time to wait after requisite observing block exposure has\nbeen taken.' + ), + ] = None + waitPeriodMillis: Annotated[ + str | None, + Field( + description='Optimal to wait after requisite observing block exposure has\nbeen taken.' + ), + ] = None + waitPeriodMaxMillis: Annotated[ + str | None, + Field( + description='Minimum time to wait after requisite observing block exposure has\nbeen taken.' + ), + ] = None + unbounded: Annotated[ + bool | None, + Field( + description='If this observation can happen any time after the requisite.' + ), + ] = None + + +class ObservingBlockConditionWindow(BaseModel): + observeAfter: Annotated[ + str | None, + Field( + description='Start of the observation window, in Unix time milliseconds.' + ), + ] = None + observeBefore: Annotated[ + str | None, + Field(description='End of the observation window, in Unix time milliseconds.'), + ] = None + + +class ObservingBlockObservingComponent(BaseModel): + targetToken: Annotated[ + str | None, + Field( + description='Unique identifier of the target used in this observing component.\n\nWhen creating or updating an observing group, this must be an\nexisting target, or on the map of the request to be created.' + ), + ] = None + observingTemplateToken: Annotated[ + str | None, + Field( + description='Unique identifier of the observing template used in this\nobserving component.\n\nWhen creating or updating an observing group, this must be an\nexisting observing template, or on the map of the request to be\ncreated.' + ), + ] = None + targetLabel: Annotated[ + int | None, + Field( + description='Label of the target used in this observing component.\nThis value is only set on the read_observing_block.\n\napi_visibility: read only' + ), + ] = None + observingTemplateLabel: Annotated[ + int | None, + Field( + description='Label of the observing template used in this observing component.\nThis value is only set on the read_observing_block.\n\napi_visibility: read only' + ), + ] = None + snrBlockToken: Annotated[ + str | None, + Field( + description='Unique identifier of the SNR block used in this observing\ncomponent, if any. Must be set if and only if the observing\ncomponent uses a MegaCam cumulative SNR mode observing template.\n\nIf this is set when creating or updating an observing group,\nthe observing component will be tied to the SNR block with this\ntoken if it already exists, otherwise a new SNR block will be\ncreated using this token.' + ), + ] = None + + +class ObservingGroupDataDitherScheme(Enum): + NONE = 'NONE' + STANDARD = 'STANDARD' + GROUP = 'GROUP' + + +class ObservingGroupDataObservingBlock(BaseModel): + observingComponent: Annotated[ + list[ObservingBlockObservingComponent] | None, + Field( + description='List of observing components that comprise this observing block.' + ), + ] = None + token: Annotated[ + str | None, + Field( + description='Unique identifier for the observing block.\n\napi_visibility: operations read only' + ), + ] = None + label: Annotated[ + int | None, + Field( + description='Index of the observing block within the program, set by the system\nautomatically. Used for reference in communications with QSO staff.\n\nstorage: transient\napi_visibility: read only' + ), + ] = None + + +class ObservingGroupDataObservingGroupState(Enum): + NOT_STARTED = 'NOT_STARTED' + OBSERVED = 'OBSERVED' + STARTED = 'STARTED' + VALIDATED = 'VALIDATED' + + +class PhotometryAperture(BaseModel): + optimalAperture: bool | None = None + fixedAperture: Annotated[ + float | None, Field(title='Radius of the aperture in arcsec') + ] = None + ninetySixPercentFluxAperture: bool | None = None + + +class ProgramDataComments(BaseModel): + question: str | None = None + comment: str | None = None + + +class ProgramDataContactInfo(BaseModel): + piFirstName: str | None = None + piLastName: str | None = None + piEmail: str | None = None + userToken: str | None = None + + +class ProgramDataProgramType(Enum): + REGULAR = 'REGULAR' + TARGET_OF_OPPORTUNITY = 'TARGET_OF_OPPORTUNITY' + CALIBRATION = 'CALIBRATION' + SNAPSHOT = 'SNAPSHOT' + + +class ProgramInfoPiInfo(BaseModel): + ldapUsername: str | None = None + emailAddress: str | None = None + firstName: str | None = None + lastName: str | None = None + legacyUsername: str | None = None + userToken: str | None = None + + +class RReadoutMode(Enum): + Normal = 'Normal' + Slow = 'Slow' + Fast = 'Fast' + + +class SitelleConfigurationDataCalibration(BaseModel): + calibrationPosition: CalibrationCalibrationPosition | None = ( + CalibrationCalibrationPosition.no_calibration_position + ) + calibrationMode: CalibrationCalibrationMode | None = ( + CalibrationCalibrationMode.no_calibration_mode + ) + + +class SitelleConfigurationDataResolutionMode(Enum): + no_resolution_mode = 'no_resolution_mode' + hires = 'hires' + medres = 'medres' + + +class SitelleStatusCamera(BaseModel): + iq: float | None = None + iqRms: float | None = None + pixelShiftX: float | None = None + pixelShiftY: float | None = None + numberOfStars: int | None = None + me: float | None = None + + +class SpirouConfigurationDataSkyObservation(Enum): + None_ = 'None' + Before = 'Before' + After = 'After' + BeforeAndAfter = 'BeforeAndAfter' + + +class SpirouStatusGuiderProcessingResult(BaseModel): + etime: float | None = None + isustate: str | None = None + sbdensP: float | None = None + holePositionOffsetX: float | None = None + holePositionOffsetY: float | None = None + starToNullPositionOffsetX: float | None = None + starToNullPositionOffsetY: float | None = None + magnitudeEstimate: float | None = None + seeingEstimate: float | None = None + sgisust: Annotated[str | None, Field(title='per 160676372')] = None + sggdst: str | None = None + sgfrmrt: float | None = None + sgetime: float | None = None + sgnullx: float | None = None + sgnully: float | None = None + sgwinx0: float | None = None + sgwiny0: float | None = None + sgwinx1: float | None = None + sgwiny1: float | None = None + sgoffx0: float | None = None + sgoffy0: float | None = None + sgoffx1: float | None = None + sgoffy1: float | None = None + sgholex: float | None = None + sgholey: float | None = None + sgcholex: float | None = None + sgcholey: float | None = None + sgeholex: float | None = None + sgeholey: float | None = None + sgcstarx: float | None = None + sgcstary: float | None = None + sgestarx: float | None = None + sgestary: float | None = None + sgcnoffx: float | None = None + sgcnoffy: float | None = None + sgcsee: float | None = None + sgesee: float | None = None + sgcmagn: float | None = None + sgemagn: float | None = None + sgssee: float | None = None + trgType: str | None = None + + +class SpirouStatusPipelineProcessingResult(BaseModel): + snr10: float | None = None + snr34: float | None = None + snr44: float | None = None + dprtype: str | None = None + ccfmask: float | None = None + ccfmacpp: float | None = None + ccfrv: float | None = None + ccfcontr: float | None = None + ccfrvc: float | None = None + ccffwhm: float | None = None + ccfmaskStr: str | None = None + + +class TargetDataMagnitude(BaseModel): + U: Annotated[float | None, Field(title='U-band magnitude')] = None + B: Annotated[float | None, Field(title='B-band magnitude')] = None + V: Annotated[ + float | None, + Field( + description='Required for targets observed with ESPaDonS.', + title='V-band magnitude', + ), + ] = None + R: Annotated[float | None, Field(title='R-band magnitude')] = None + I: Annotated[float | None, Field(title='I-band magnitude')] = None + G: Annotated[float | None, Field(title='G-band magnitude')] = None + J: Annotated[float | None, Field(title='J-band magnitude')] = None + H: Annotated[ + float | None, + Field( + description='Required for targets observed with SPIRou.', + title='H-band magnitude', + ), + ] = None + K: Annotated[float | None, Field(title='K-band magnitude')] = None + uu: Annotated[float | None, Field(title='u-band magnitude')] = None + gg: Annotated[float | None, Field(title='g-band magnitude')] = None + rr: Annotated[float | None, Field(title='r-band magnitude')] = None + ii: Annotated[float | None, Field(title='i-band magnitude')] = None + zz: Annotated[float | None, Field(title='z-band magnitude')] = None + AB: Annotated[ + float | None, + Field( + description='Required for targets observed with MegaCam.', + title='AB magnitude', + ), + ] = None + + +class TargetListRequestTargetType(Enum): + ALL = 'ALL' + FIXED = 'FIXED' + MOVING = 'MOVING' + + +class TargetTimeDataView(Enum): + REQUESTED = 'REQUESTED' + ACTUAL = 'ACTUAL' + + +class TimeAccountingDataTimeAccountingStatus(Enum): + NOT_STARTED = 'NOT_STARTED' + STARTED = 'STARTED' + OBSERVED = 'OBSERVED' + VALIDATED = 'VALIDATED' + + +class UpenaProcessingResulth2oMm(BaseModel): + rawDensity: float | None = None + atZenith: float | None = None + + +class HttpErrors(BaseModel): + messages: list[str] | None = None + + +class ObservingObservingGroupDeleteResponse(BaseModel): + success: bool | None = None + error: HttpErrors | None = None + + +class ObservingObservingTemplateDeleteResponse(BaseModel): + success: bool | None = None + error: HttpErrors | None = None + + +class ObservingPointingOffsetDeleteResponse(BaseModel): + success: bool | None = None + error: HttpErrors | None = None + + +class ObservingTargetDeleteResponse(BaseModel): + success: bool | None = None + error: HttpErrors | None = None + + +class ObservingTelescopePatternDeleteResponse(BaseModel): + success: bool | None = None + error: HttpErrors | None = None + + +class ObservingpersistenceTimeConstraint(BaseModel): + window: Annotated[ + list[ObservingBlockConditionWindow] | None, + Field( + description='Time windows during which the observation can be made.\n\nWindows can not overlap, and will always be stored in temporal order.' + ), + ] = None + + +class PersistenceCatalogIdentifier(BaseModel): + catalog: Annotated[str | None, Field(description='Catalog to reference.')] = None + identifier: Annotated[ + str | None, + Field( + description='Valid identifier in the catalog.\n\nNote: this ID will contain a catalog name, because spacing matters in catalogs, the full ID for an object is like\nM 4. Catalog is M, id is identifier could be 4, but to reference m4, you need the spaces.\n\nNB: These identity values can not be set in fits headers because they do not have to conform to\nthe length restrictions.' + ), + ] = None + + +class PersistenceExposureTime(BaseModel): + desiredSnr: float | None = None + exposureTimeMs: str | None = None + + +class PersistenceFindingChartData(BaseModel): + comment: str | None = None + url: str | None = None + token: str | None = None + staffApproval: FindingChartDataApproval | None = None + piApproval: FindingChartDataApproval | None = None + targetToken: str | None = None + + +class PersistenceITimeData(BaseModel): + exposureTimeMillis: Annotated[ + str | None, + Field(description='Time spent collecting photons (in milliseconds).'), + ] = None + overheadTimeMillis: Annotated[ + str | None, + Field( + description='All other times required for taking the exposure (in milliseconds).' + ), + ] = None + + +class PersistenceListOfIdentifiers(BaseModel): + catalogIdentifier: list[PersistenceCatalogIdentifier] | None = None + + +class PersistenceObservingBlockCondition(BaseModel): + preRequisite: Annotated[ + list[ObservingBlockConditionRequisite] | None, + Field( + description='Pre-requisite conditions before each observing block can be started.\nThe index of items in this list corresponds directly to the index of the\nobserving block on the observing group i.e. this list is one-to-one with\nread_observing_block.' + ), + ] = None + window: Annotated[ + list[ObservingBlockConditionWindow] | None, + Field( + description='Time windows during which the observing block can be observed.\n\nWindows can not overlap, and will always be stored in temporal order.' + ), + ] = None + + +class PersistenceObservingGroupPriority(Enum): + UNKNOWN_OG_PRIORITY = 'UNKNOWN_OG_PRIORITY' + HIGH = 'HIGH' + MEDIUM = 'MEDIUM' + LOW = 'LOW' + INACTIVE = 'INACTIVE' + + +class PersistenceRollupValue(BaseModel): + requested: str | None = None + started: str | None = None + observed: str | None = None + validated: str | None = None + + +class PersistenceSingleObservingGroup(BaseModel): + observingBlock: ObservingGroupDataObservingBlock | None = None + + +class PersistenceState(Enum): + UNOBSERVED = 'UNOBSERVED' + OBSERVED = 'OBSERVED' + PROCESSED = 'PROCESSED' + GRADED = 'GRADED' + VALIDATED = 'VALIDATED' + REJECTED = 'REJECTED' + + +class PersistenceStokesParameter(Enum): + Q = 'Q' + U = 'U' + V = 'V' + W = 'W' + I = 'I' + + +class PersistenceSubTotalsData(BaseModel): + etime: PersistenceRollupValue | None = None + overheadtime: PersistenceRollupValue | None = None + exposureCount: PersistenceRollupValue | None = None + childCount: PersistenceRollupValue | None = None + etimeConfigured: PersistenceRollupValue | None = None + overheadTimeConfigured: PersistenceRollupValue | None = None + completeness: Annotated[ + float | None, Field(description='Percentage completed for the entity.') + ] = None + + +class PersistenceTargetIdentifiers(BaseModel): + catalogIdentifier: Annotated[ + dict[str, PersistenceListOfIdentifiers] | None, + Field( + description='key: catalog name, catalog_identifier.key === catalog_identifier.get(key).catalog\nvalue: catalog identifier.', + title='All catalogs resolved by simbad for this target', + ), + ] = None + twoMass: PersistenceCatalogIdentifier | None = None + gaia1: PersistenceCatalogIdentifier | None = None + gaia2: PersistenceCatalogIdentifier | None = None + resolvedCatalogIdentifier: PersistenceCatalogIdentifier | None = None + + +class PersistenceTargetTimeData(BaseModel): + requestedIntegrationTime: PersistenceITimeData | None = None + actualIntegrationTime: PersistenceITimeData | None = None + view: TargetTimeDataView | None = TargetTimeDataView.REQUESTED + exposureCount: Annotated[ + int | None, + Field( + description='The total number of exposures represented by the values here.\n\nThis value is the same for both views.' + ), + ] = None + childCount: Annotated[ + int | None, + Field( + description='The total number of child entities represented by the values here.\n\nIf this is a program, these are observing groups.\nIf this is an observing group, these are observing blocks.\nIf this is an observing block, these are exposures.\n\nUseful for e.g. seeing the number of iterations configuredor completed on an Observing Group.\nThis value is the same for both views.' + ), + ] = None + + +class PersistenceTimeAccountingData(BaseModel): + configuredTime: PersistenceTargetTimeData | None = None + chargedTime: PersistenceTargetTimeData | None = None + validatedTimeOnSky: PersistenceTargetTimeData | None = None + timeOnSky: PersistenceTargetTimeData | None = None + timeAccountingStatus: TimeAccountingDataTimeAccountingStatus | None = ( + TimeAccountingDataTimeAccountingStatus.NOT_STARTED + ) + completionRatio: Annotated[ + float | None, + Field( + description='Entity completion ratio, representing how much of the time configured has been charged.\n\nEqual to total charged time (as requested) divided by total configured time. For programs, if the total configured time is greater than the time allocated to the program, we divide by the time allocated instead.\nNote that if a program has INACTIVE observing groups that already have charged time, this could result in an inflated value here.', + le=1.0, + ), + ] = None + + +class ProtobufAny(BaseModel): + typeUrl: str | None = None + value: Annotated[ + Base64Str | None, + Field( + pattern='^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$' + ), + ] = None + + +class RpcStatus(BaseModel): + code: int | None = None + message: str | None = None + details: list[ProtobufAny] | None = None + + +class TypesBigInteger(BaseModel): + value: str | None = None + + +class TypesDoubleMinMax(BaseModel): + min: float | None = None + max: float | None = None + + +class TypesDurationUnit(Enum): + DAYS = 'DAYS' + HOURS = 'HOURS' + MINUTES = 'MINUTES' + SECONDS = 'SECONDS' + + +class TypesExposureType(Enum): + OBJECT = 'OBJECT' + TARGET = 'TARGET' + SKY = 'SKY' + DARK = 'DARK' + FLAT = 'FLAT' + SNAP = 'SNAP' + BIAS = 'BIAS' + FOCUS = 'FOCUS' + ALIGN = 'ALIGN' + COMPARISON = 'COMPARISON' + ACQUIRE = 'ACQUIRE' + FABRY_PEROT = 'FABRY_PEROT' + TWILIGHT_FLATS = 'TWILIGHT_FLATS' + PHOTOMETRY = 'PHOTOMETRY' + SFOCUS = 'SFOCUS' + + +class TypesInstrumentType(Enum): + UNKNOWN = 'UNKNOWN' + CFH12K = 'CFH12K' + MEGACAM = 'MEGACAM' + WIRCAM = 'WIRCAM' + ESPADONS = 'ESPADONS' + SITELLE = 'SITELLE' + SPIROU = 'SPIROU' + WENAOKEAO = 'WENAOKEAO' + + +class TypesOffsetCoordinate(BaseModel): + raOffset: Annotated[ + float | None, + Field( + description='Offset in the Right Ascension axis in arcsec of degrees.', + ge=-1296000.0, + le=1296000.0, + title='Right Ascension Offset', + ), + ] = None + decOffset: Annotated[ + float | None, + Field( + description='Offset in the Declination axis in arcsec of degrees.', + ge=-324000.0, + le=324000.0, + title='Declination Offset', + ), + ] = None + exposureNumber: Annotated[ + float | None, + Field(description='Exposure number', le=99.0, title='Exposure number'), + ] = None + + +class TypesSkyCoordinate(BaseModel): + ra: Annotated[ + float | None, + Field( + description='Right Ascension in degrees.', le=360.0, title='Right Ascension' + ), + ] = None + dec: Annotated[ + float | None, + Field( + description='Declination in degrees.', + ge=-90.0, + le=90.0, + title='Declination', + ), + ] = None + + +class ConfigNodding(BaseModel): + startSequence: TypesExposureType | None = TypesExposureType.OBJECT + targetDb: float | None = None + + +class EspadonsStatusUpenaProcessingResult(BaseModel): + iq: float | None = None + h2oColumnDensityFromTelluricLines: float | None = None + radialVelocityCorrections: float | None = None + snrPerCcdBin: float | None = None + snrPerSpecBin: float | None = None + actualWavelength: float | None = None + snrPerCcdBinPolar: float | None = None + snrPerSpecBinPolar: float | None = None + h2oMmData: UpenaProcessingResulth2oMm | None = None + + +class ExposureDataEspadonsStatus(BaseModel): + instrMode: str | None = None + actualWavelength: float | None = None + snrExpMeter: float | None = None + snrWaveMeasInt: float | None = None + snrWaveMeasPol: float | None = None + snrMeasIntPerCcdBin: float | None = None + snrMeasIntPerSpecBin: float | None = None + snrMeasPolarCcdBin: float | None = None + snrMeasPolarSpecBin: float | None = None + trgType: str | None = None + guiderSeeing: float | None = None + guiderSeeingProcessingResults: ( + EspadonsStatusGuiderSeeingProcessingResult | None + ) = None + exposureMeterProcessingResults: ( + ExposureDataEspadonsStatusExposureMeterProcessingResult | None + ) = None + upenaProcessingResults: EspadonsStatusUpenaProcessingResult | None = None + observationType: str | None = None + + +class ExposureDataMegacamStatus(BaseModel): + iq: float | None = None + skybg: float | None = None + elixirEval: int | None = None + elixirSeval: str | None = None + snr: float | None = None + atmosphericTransmission: float | None = None + elixirZeropoint: float | None = None + actualFilter: str | None = None + elixirProcessingResult: MegacamStatusElixirProcessingResult | None = None + + +class ExposureDataSitelleStatus(BaseModel): + cameraA: SitelleStatusCamera | None = None + cameraB: SitelleStatusCamera | None = None + relativeExtinction: float | None = None + relativeExtinctionRms: float | None = None + actualFilter: str | None = None + + +class ExposureDataSpirouStatus(BaseModel): + acqtime: float | None = None + acqtime1: str | None = None + asicNum: str | None = None + scaId: str | None = None + muxtype: int | None = None + noutputs: int | None = None + nadcs: int | None = None + pddector: int | None = None + clkoff: int | None = None + warmtst: int | None = None + clocking: int | None = None + glbreset: int | None = None + frmode: int | None = None + camlink: int | None = None + expmode: int | None = None + nresets: int | None = None + frmtime: float | None = None + exptime: float | None = None + acqtype: int | None = None + datamode: int | None = None + datlevel: int | None = None + asicgain: int | None = None + nomgain: int | None = None + ampreset: int | None = None + ktcremov: int | None = None + srccur: int | None = None + ampinput: int | None = None + v4v3v2v1: str | None = None + units: str | None = None + tstation: str | None = None + hxrgver: str | None = None + mclk: float | None = None + inttime: float | None = None + frameno: int | None = None + sbAdcS: str | None = None + sbadc1P: float | None = None + sbadc2P: float | None = None + sbcaliP: str | None = None + sbdensP: float | None = None + sbrhb1P: str | None = None + sbrhb2P: str | None = None + sbetemP: str | None = None + sbInjT: float | None = None + sbPolT: float | None = None + sbCs1T: float | None = None + sbCs2T: float | None = None + sbCb1T: float | None = None + sbCb2T: float | None = None + sbiavl1s: str | None = None + sbiavl2s: str | None = None + sbetelS: str | None = None + sbCscS: str | None = None + sbcrefP: str | None = None + sbccasP: str | None = None + sbcdenP: float | None = None + sbagitS: str | None = None + sbcmirP: str | None = None + sbclhc1s: str | None = None + sbclhc1w: str | None = None + sbclhc1c: float | None = None + sbclhc2s: str | None = None + sbclhc2w: str | None = None + sbclhc2c: float | None = None + sbclwlS: str | None = None + sbclwlC: float | None = None + sbwlshS: str | None = None + sbctecS: str | None = None + sbctecct: float | None = None + sbctecht: float | None = None + sbccprit: float | None = None + sbcccuT: float | None = None + sbccprot: float | None = None + sbcfbiT: float | None = None + sbcwlsT: float | None = None + sbclsuat: float | None = None + sbcfpiT: float | None = None + sbcfpeT: float | None = None + sbcfpbP: float | None = None + sbvgaCp: float | None = None + sbvgaIp: float | None = None + sbvgbCp: float | None = None + sbvgbIp: float | None = None + sbmk00T: float | None = None + sbmk01T: float | None = None + sbmk02T: float | None = None + sbmk03T: float | None = None + sbmk04T: float | None = None + sbmk05T: float | None = None + sbmk06T: float | None = None + sbmk07T: float | None = None + sbls000t: float | None = None + sbls001t: float | None = None + sbls002t: float | None = None + sbls003t: float | None = None + sbls004t: float | None = None + sbls005t: float | None = None + sbls006t: float | None = None + sbls007t: float | None = None + sbls008t: float | None = None + sbls009t: float | None = None + sbls010t: float | None = None + sbls011t: float | None = None + sbls100t: float | None = None + sbls101t: float | None = None + sbls102t: float | None = None + sbls103t: float | None = None + sbls104t: float | None = None + sbls105t: float | None = None + sbls106t: float | None = None + sbls107t: float | None = None + sbls108t: float | None = None + sbls109t: float | None = None + sbls110t: float | None = None + sbls111t: float | None = None + sbl00iT: float | None = None + sbl00hV: float | None = None + sbl01iT: float | None = None + sbl01hV: float | None = None + sbl02iT: float | None = None + sbl02hV: float | None = None + sbl03iT: float | None = None + sbl03hV: float | None = None + sbl04iT: float | None = None + sbl04hV: float | None = None + sbl05iT: float | None = None + sbl05hV: float | None = None + sbl06iT: float | None = None + sbl06hV: float | None = None + sbl07iT: float | None = None + sbl07hV: float | None = None + sbl08iT: float | None = None + sbl08hV: float | None = None + sbl09iT: float | None = None + sbl09hV: float | None = None + sbl10iT: float | None = None + sbl10hV: float | None = None + sbl11iT: float | None = None + sbl11hV: float | None = None + sbl12iT: float | None = None + sbl12hV: float | None = None + sbl13iT: float | None = None + sbl13hV: float | None = None + seadc1P: float | None = None + seadc2P: float | None = None + sedens_p: int | None = None + sedens_p_float: float | None = None + seInjT: float | None = None + sePolT: float | None = None + seCs1T: float | None = None + seCs2T: float | None = None + seCb1T: float | None = None + seCb2T: float | None = None + sectecct: float | None = None + sectecht: float | None = None + seccprit: float | None = None + secccuT: float | None = None + seccprot: float | None = None + secfbiT: float | None = None + secwlsT: float | None = None + seclhc1c: float | None = None + seclhc2c: float | None = None + seclwlC: float | None = None + seclsuat: float | None = None + secfpiT: float | None = None + secfpeT: float | None = None + secfpbP: float | None = None + sevgaCp: float | None = None + sevgaIp: float | None = None + sevgbCp: float | None = None + sevgbIp: float | None = None + semk00T: float | None = None + semk01T: float | None = None + semk02T: float | None = None + semk03T: float | None = None + semk04T: float | None = None + semk05T: float | None = None + semk06T: float | None = None + semk07T: float | None = None + sels000t: float | None = None + sels001t: float | None = None + sels002t: float | None = None + sels003t: float | None = None + sels004t: float | None = None + sels005t: float | None = None + sels006t: float | None = None + sels007t: float | None = None + sels008t: float | None = None + sels009t: float | None = None + sels010t: float | None = None + sels011t: float | None = None + sels100t: float | None = None + sels101t: float | None = None + sels102t: float | None = None + sels103t: float | None = None + sels104t: float | None = None + sels105t: float | None = None + sels106t: float | None = None + sels107t: float | None = None + sels108t: float | None = None + sels109t: float | None = None + sels110t: float | None = None + sels111t: float | None = None + sel00iT: float | None = None + sel00hV: float | None = None + sel01iT: float | None = None + sel01hV: float | None = None + sel02iT: float | None = None + sel02hV: float | None = None + sel03iT: float | None = None + sel03hV: float | None = None + sel04iT: float | None = None + sel04hV: float | None = None + sel05iT: float | None = None + sel05hV: float | None = None + sel06iT: float | None = None + sel06hV: float | None = None + sel07iT: float | None = None + sel07hV: float | None = None + sel08iT: float | None = None + sel08hV: float | None = None + sel09iT: float | None = None + sel09hV: float | None = None + sel10iT: float | None = None + sel10hV: float | None = None + sel11iT: float | None = None + sel11hV: float | None = None + sel12iT: float | None = None + sel12hV: float | None = None + sel13iT: float | None = None + sel13hV: float | None = None + guiderProcessing: SpirouStatusGuiderProcessingResult | None = None + exposureMeterProcessing: ( + ExposureDataSpirouStatusExposureMeterProcessingResult | None + ) = None + pipelineProcessing: SpirouStatusPipelineProcessingResult | None = None + + +class InstrumentConfigurationDataEspadonsConfigurationData(BaseModel): + observingMode: ( + InstrumentConfigurationDataEspadonsConfigurationDataOObservingMode | None + ) = InstrumentConfigurationDataEspadonsConfigurationDataOObservingMode.Polarimetry + readoutMode: RReadoutMode | None = RReadoutMode.Normal + stokesParameter: PersistenceStokesParameter | None = PersistenceStokesParameter.Q + numberOfSequences: int | None = None + numberOfExposures: int | None = None + needs_snr: bool | None = None + snrWave: float | None = None + snr: float | None = None + + +class InstrumentConfigurationDataMegacamConfigurationDataPhotometry(BaseModel): + aperture: PhotometryAperture | None = None + psf: bool | None = None + + +class InstrumentConfigurationDataSitelleConfigurationData(BaseModel): + filter: InstrumentConfigurationDataSitelleConfigurationDataFilter | None = ( + InstrumentConfigurationDataSitelleConfigurationDataFilter.UNKNOWN + ) + binning: InstrumentConfigurationDataSitelleConfigurationDataBinning | None = ( + InstrumentConfigurationDataSitelleConfigurationDataBinning.one_one + ) + resolution: int | None = None + snrwavelength: Annotated[ + float | None, + Field(title='This should be on target too , sitell, espedons, spirou'), + ] = None + calibration: SitelleConfigurationDataCalibration | None = None + resolutionMode: SitelleConfigurationDataResolutionMode | None = ( + SitelleConfigurationDataResolutionMode.no_resolution_mode + ) + needsPrecision: bool | None = None + needsFluxCalibration: bool | None = None + needsTargetCalibration: bool | None = None + + +class InstrumentConfigurationDataSpirouConfigurationData(BaseModel): + templateName: Annotated[str | None, Field(title='Deprecated fields')] = None + observingMode: ( + InstrumentConfigurationDataSpirouConfigurationDataOObservingMode | None + ) = InstrumentConfigurationDataSpirouConfigurationDataOObservingMode.Polarimetry + stokesParameter: PersistenceStokesParameter | None = PersistenceStokesParameter.Q + mode: MMode | None = MMode.Dark + skyObservation: SpirouConfigurationDataSkyObservation | None = ( + SpirouConfigurationDataSkyObservation.None_ + ) + numberOfSequences: Annotated[ + int | None, + Field( + title='Each sequence is 1 exposure for Star mode and 4 exposures for ObservingMode' + ), + ] = None + useSnr: Annotated[ + bool | None, Field(title='when true - use SNR for stoping exposures') + ] = None + skyEtimeMillis: Annotated[ + str | None, Field(title='Used to override the etime for the sky_observation') + ] = None + + +class InstrumentConfigurationDataWircamConfigurationData(BaseModel): + filter: InstrumentConfigurationDataWircamConfigurationDataFilter | None = ( + InstrumentConfigurationDataWircamConfigurationDataFilter.UNKNOWN + ) + isMicrodithering: bool | None = None + exposuresPerDpPosition: Annotated[int | None, Field(title='Must always be > 0')] = ( + None + ) + accuratePointing: bool | None = None + defocus: float | None = None + isStaring: bool | None = None + + +class MovingTargetEphemeris(BaseModel): + mjd: Annotated[ + float | None, + Field( + description='Modified Julian date when the target is at this coordinate.' + ), + ] = None + coordinate: TypesSkyCoordinate | None = None + computedCoordinate: TypesSkyCoordinate | None = None + + +class TargetDataFixedTarget(BaseModel): + coordinate: TypesSkyCoordinate | None = None + properMotion: FixedTargetProperMotion | None = None + computedCoordinate: TypesSkyCoordinate | None = None + estimatedRadialVelocityKmps: Annotated[ + float | None, + Field( + description='Expected radial velocity of the target in kilometers/second.\n\nUsed for targets observed with SPIRou.' + ), + ] = None + + +class TargetDataLinkedTargetIdentifiers(BaseModel): + piConfirmedIdentifiers: PersistenceTargetIdentifiers | None = None + customTarget: Annotated[ + bool | None, + Field( + description='Used to indicate this is a target without known identifiers.' + ), + ] = None + + +class TargetDataMovingTarget(BaseModel): + ephemerisPoint: Annotated[ + list[MovingTargetEphemeris] | None, + Field( + description="The ephemerides that define the path of motion for the target.\n\nMust be provided using topocentric astrometric coordinates.\nDo not provide apparent coordinates.\nCFHT strongly recommends the use of JPL Horizons System for\ngenerating the proper coordinates. CFHT's observatory code is T14.\n\nAt least five distinct positions must be provided that span the\ndesired observing period. The first position should be well in\nadvance of the observation. The positions do not need to be\nuniformly sampled throughout the desired observing period.\n\nThe points will be interpolated to determine the position of the\ntarget at the time of observation, at which point there must be\nat least one ephemeris point defined in the past, and at least\nfour defined in the future." + ), + ] = None + + +class TelescopePatternDataConfig(BaseModel): + name: str | None = None + description: str | None = None + chargeModel: Annotated[ + str | None, Field(title='Used for calculating cost for this pattern') + ] = None + dithering: ConfigDithering | None = None + nodding: ConfigNodding | None = None + scale: float | None = None + sybasePatId: TypesBigInteger | None = None + isSystem: bool | None = None + + +class TelescopePatternDataOffset(BaseModel): + exposureNumber: Annotated[ + int | None, + Field( + description='TODO: deprecate, no need to have duplicate information here.' + ), + ] = None + offset: TypesOffsetCoordinate | None = None + type: TypesExposureType | None = TypesExposureType.OBJECT + + +class ObservingRollupFields(BaseModel): + iTime: PersistenceRollupValue | None = None + iTimeConfigured: PersistenceRollupValue | None = None + subTotals: PersistenceSubTotalsData | None = None + + +class PersistenceAllocationData(BaseModel): + runId: str | None = None + timeAllocatedMillis: str | None = None + tacRank: int | None = None + tacGrade: str | None = None + instrument: TypesInstrumentType | None = TypesInstrumentType.UNKNOWN + agency: AllocationDataAgency | None = None + cfhtRank: int | None = None + + +class PersistenceConstraint(BaseModel): + imageQuality: TypesDoubleMinMax | None = None + skyBackgroundMax: float | None = None + airmassMax: float | None = None + extinctionMax: float | None = None + moonDistanceArcLengthMin: float | None = None + photometric: bool | None = None + name: str | None = None + sybaseConsId: TypesBigInteger | None = None + h2oVaporMax: float | None = None + readSkyBackgroundName: str | None = None + skyBackgroundName: str | None = None + + +class PersistencePointingOffsetData(BaseModel): + token: str | None = None + name: str | None = None + offset: TypesOffsetCoordinate | None = None + label: int | None = None + version: int | None = None + userToken: Annotated[ + str | None, + Field( + title='this is the PI of the program that this entity will be recorded for\nif null should be derived using the program_data.contact_info.user_token\nif null, then a system ?? gross' + ), + ] = None + instrument: TypesInstrumentType | None = TypesInstrumentType.UNKNOWN + isSystem: bool | None = None + + +class PersistenceProgramData(BaseModel): + title: str | None = None + abstract: str | None = None + contactInfo: ProgramDataContactInfo | None = None + comment: list[ProgramDataComments] | None = None + sybasePiUserId: str | None = None + subTotals: PersistenceSubTotalsData | None = None + token: Annotated[ + str | None, + Field( + description='Unique identifier for the program. Used interchangeably with "runid".' + ), + ] = None + timeAllocation: list[PersistenceAllocationData] | None = None + releaseDateMillis: str | None = None + metadataReleaseDateMillis: str | None = None + commentField: str | None = None + programType: ProgramDataProgramType | None = ProgramDataProgramType.REGULAR + distributionHash: str | None = None + timeAccounting: PersistenceTimeAccountingData | None = None + operationsCommentField: str | None = None + lastDataAcquisitionNotificationDateMillis: str | None = None + + +class PersistenceTargetData(BaseModel): + token: Annotated[ + str | None, + Field( + description='Unique identifier for the target.\n\nWhen creating a new target, this will be inferred from the request data\nif left blank here.\n\nMust begin with the program token followed by a dash, e.g.\n20AE01-0123456789' + ), + ] = None + name: Annotated[ + str | None, + Field( + description='Name for the target entered by PI, or primary name as resolved by CDS.\n\nWill become OBJECT in the FITS header.\n\nRequired.\nLength limited to 39 characters.\nAllowed characters are letters, numbers, spaces, or any of:\n!@#$%^&*()_-+.,?/[]<>' + ), + ] = None + label: Annotated[ + int | None, + Field( + description='Index of the target within the program, set by the system automatically.\nUsed for reference to targets in communications with QSO staff.\n\nstorage: transient\napi_visibility: read only' + ), + ] = None + version: Annotated[ + int | None, + Field( + description='Increments when this object updates.\n\nstorage: transient\napi_visibility: read only, incremented by the server' + ), + ] = None + fixedTarget: TargetDataFixedTarget | None = None + movingTarget: TargetDataMovingTarget | None = None + magnitude: TargetDataMagnitude | None = None + temperatureEffective: Annotated[ + float | None, + Field( + description='The estimated/measured effective temperature, in Kelvin.\n\nRequired for targets observed with SPIRou or ESPaDOnS.\nNot used for MegaCam.' + ), + ] = None + standardStar: Annotated[ + bool | None, + Field( + description='Whether the star is a "standard" star, to be observed each time other\nscience targets are observed.\n\nNot used for MegaCam.' + ), + ] = None + linkedTargetIdentifiers: TargetDataLinkedTargetIdentifiers | None = None + findingChart: Annotated[ + list[PersistenceFindingChartData] | None, + Field( + description='A read-only view of the finding charts for this target.\nTo create or update finding charts, see the FindingChartService.\n\nstorage: transient\napi_visibility: read only' + ), + ] = None + pointingOffsetToken: Annotated[ + str | None, + Field( + description='The token of a pointing offset which should be used when observing\nthis target.\n\nTo view or manage pointing offsets, see the PointingOffsetService.' + ), + ] = None + pointingOffset: PersistencePointingOffsetData | None = None + + +class PersistenceTelescopePatternData(BaseModel): + offsets: Annotated[ + list[TelescopePatternDataOffset] | None, + Field( + title='Should only be set if manually specifying custom dithering patterns' + ), + ] = None + config: TelescopePatternDataConfig | None = None + token: str | None = None + label: int | None = None + version: int | None = None + userToken: str | None = None + instrument: TypesInstrumentType | None = TypesInstrumentType.UNKNOWN + isSystem: bool | None = None + name: str | None = None + offsetCoordinate: list[TypesOffsetCoordinate] | None = None + + +class TypesDuration(BaseModel): + millis: str | None = None + displayUnit: TypesDurationUnit | None = TypesDurationUnit.DAYS + + +class ExposureDataExposureStatus(BaseModel): + megacamStatus: ExposureDataMegacamStatus | None = None + wircamStatus: ExposureDataWircamStatus | None = None + espadonsStatus: ExposureDataEspadonsStatus | None = None + sitelleStatus: ExposureDataSitelleStatus | None = None + spirouStatus: ExposureDataSpirouStatus | None = None + wenaStatus: ExposureDataWenaStatus | None = None + exposureTime: PersistenceExposureTime | None = None + expDateMjd: float | None = None + queueName: str | None = None + expType: str | None = None + expStatus: PersistenceState | None = PersistenceState.UNOBSERVED + isPhotometric: bool | None = None + obsComment: str | None = None + seqComment: str | None = None + actualPointing: TypesSkyCoordinate | None = None + actualSky: float | None = None + actualAirmass: float | None = None + actualMoondist: float | None = None + observerComment: str | None = None + qcComment: str | None = None + file: ExposureStatusFile | None = None + creditedQcoordinator: str | None = None + creditedObserver: str | None = None + grade: int | None = None + readObsDateUtc: str | None = None + readObsDateHst: str | None = None + readActualSkyName: str | None = None + ra: Annotated[str | None, Field(title='Values from headers')] = None + dec: str | None = None + cmmtobs: str | None = None + cmmtseq: str | None = None + piName: str | None = None + nexp: str | None = None + cmpltexp: str | None = None + ha: str | None = None + readObsDateMillis: str | None = None + object: str | None = None + + +class InstrumentConfigurationDataMegacamConfigurationData(BaseModel): + filter: InstrumentConfigurationDataMegacamConfigurationDataFilter | None = ( + InstrumentConfigurationDataMegacamConfigurationDataFilter.Unknown + ) + binning: InstrumentConfigurationDataMegacamConfigurationDataBinning | None = ( + InstrumentConfigurationDataMegacamConfigurationDataBinning.one_one + ) + isDynamicExposureTime: Annotated[ + bool | None, + Field( + title="TODO: is this needed billy? Can we use presence of snr or not to decide?\nI THINK SO. We have the capability to calculate SNR on every exposure, but we aren't necessarily doing SNR mode observations with everything" + ), + ] = None + minimumExposures: int | None = None + minExptimeMs: str | None = None + maxExptimeMs: str | None = None + magAB: Annotated[ + float | None, + Field( + title='TODO: interesting field here because associated with the target, but in the legacy mp snr mode, the mag is carried with the IC! so have to go back to this :(' + ), + ] = None + snrMode: MegacamConfigurationDataSnrMode | None = ( + MegacamConfigurationDataSnrMode.NONE + ) + etcPhotometry: ( + InstrumentConfigurationDataMegacamConfigurationDataPhotometry | None + ) = None + + +class PhaseScheduleObservingGroupPhaseSchedule(BaseModel): + earliestObserveDateMillis: Annotated[ + str | None, + Field(description='The earliest moment to observe, in Unix time milliseconds.'), + ] = None + tolerance: TypesDuration | None = None + period: TypesDuration | None = None + periodTolerance: TypesDuration | None = None + + +class ObservingEphemeridesUpdateResponse(BaseModel): + success: bool | None = None + error: HttpErrors | None = None + entity: PersistenceTargetData | None = None + + +class ObservingPointingOffsetGetResponse(BaseModel): + success: bool | None = None + entity: PersistencePointingOffsetData | None = None + error: HttpErrors | None = None + + +class ObservingPointingOffsetListResponse(BaseModel): + success: bool | None = None + entity: list[PersistencePointingOffsetData] | None = None + error: HttpErrors | None = None + + +class ObservingPointingOffsetUpdateResponse(BaseModel): + success: bool | None = None + error: HttpErrors | None = None + entity: PersistencePointingOffsetData | None = None + + +class ObservingProgramInfo(BaseModel): + programData: PersistenceProgramData | None = None + rollupFields: ObservingRollupFields | None = None + timeAccounting: PersistenceTimeAccountingData | None = None + piInfo: ProgramInfoPiInfo | None = None + version: int | None = None + + +class ObservingTargetListResponse(BaseModel): + success: bool | None = None + entity: list[PersistenceTargetData] | None = None + error: HttpErrors | None = None + + +class ObservingTargetShowResponse(BaseModel): + success: bool | None = None + error: HttpErrors | None = None + entity: PersistenceTargetData | None = None + + +class ObservingTargetUpdateResponse(BaseModel): + success: bool | None = None + error: HttpErrors | None = None + entity: PersistenceTargetData | None = None + + +class ObservingTelescopePatternListResponse(BaseModel): + success: bool | None = None + telescope_pattern: list[PersistenceTelescopePatternData] | None = None + error: HttpErrors | None = None + + +class ObservingTelescopePatternUpdateResponse(BaseModel): + success: bool | None = None + error: HttpErrors | None = None + telescope_pattern: PersistenceTelescopePatternData | None = None + + +class ObservingUpdateProgramCommentResponse(BaseModel): + success: bool | None = None + programData: PersistenceProgramData | None = None + error: HttpErrors | None = None + + +class ObservingUpdateProgramCommentsResponse(BaseModel): + success: bool | None = None + programData: PersistenceProgramData | None = None + error: HttpErrors | None = None + + +class PersistenceInstrumentConfigurationData(BaseModel): + espadonsConfiguration: ( + InstrumentConfigurationDataEspadonsConfigurationData | None + ) = None + megacamConfiguration: InstrumentConfigurationDataMegacamConfigurationData | None = ( + None + ) + wircamConfiguration: InstrumentConfigurationDataWircamConfigurationData | None = ( + None + ) + sitelleConfiguration: InstrumentConfigurationDataSitelleConfigurationData | None = ( + None + ) + spirouConfiguration: InstrumentConfigurationDataSpirouConfigurationData | None = ( + None + ) + wenaConfiguration: InstrumentConfigurationDataWenaConfigurationData | None = None + sybaseIcId: TypesBigInteger | None = None + etimeOrSnr: PersistenceExposureTime | None = None + sybaseConsId: TypesBigInteger | None = None + tracking: InstrumentConfigurationDataTracking | None = ( + InstrumentConfigurationDataTracking.Unknown + ) + + +class PersistenceLegacyReel(BaseModel): + reelOgToken: Annotated[ + str | None, Field(description='Token of the linked observing group.') + ] = None + delay: TypesDuration | None = None + window: TypesDuration | None = None + reelOgLabel: Annotated[ + int | None, + Field( + description='Label of the observing group which must be observed before this one.\n\nstorage: transient\napi_visibility: read only' + ), + ] = None + + +class PersistenceObservingTemplateData(BaseModel): + instrumentConfiguration: PersistenceInstrumentConfigurationData | None = None + constraint: PersistenceConstraint | None = None + telescope_offset: PersistenceTelescopePatternData | None = None + chargeModel: Annotated[ + str | None, + Field( + description='Used for calculating cost for this pattern\nInvokes code which reads the ExposureInstructions and\nattributes a cost to it.' + ), + ] = None + itime: str | None = None + calculateEtime: PersistenceExposureTime | None = None + acquisitionTime: PersistenceExposureTime | None = None + token: str | None = None + associatedObservingBlocks: Annotated[ + list[TypesBigInteger] | None, + Field( + description='select ob.* from ob, prg, icseq where ob.prg_id = prg.id\nand prg.runid=\nand icseq.ob_id=ob.id and ob.con_id = \nand icseq.ic_id = ;', + title='When just creating an OT, this list is empty\nWhen this OT belongs to an OG, then any OBs in sybase that this OT is related too must be filled in.\nselect * from blah where (select id from prg where prg\nQuery to find related OBs is, select ob.* from ob, prg where ob.prg_id = prg.id and prg.runid=? instrument_configuration.sybase_ic_id, constraint.sybase_cons_id', + ), + ] = None + name: str | None = None + totalExposureCount: int | None = None + label: int | None = None + version: Annotated[ + int | None, + Field( + description='storage: transient\napi_visibility: read only, incremented by the server', + title='Increments when this object updates', + ), + ] = None + telescopePatternToken: str | None = None + telescopePatternScale: float | None = None + megacamCumulativeSnrSystemGenerated: bool | None = None + + +class PersistencePhaseScheduleObservingGroup(BaseModel): + observingBlock: ObservingGroupDataObservingBlock | None = None + phaseSchedule: PhaseScheduleObservingGroupPhaseSchedule | None = None + + +class PersistenceTargetMonitoringObservingGroupTimeConstraint(BaseModel): + numberOfIterations: Annotated[ + int | None, Field(description='How many observations to take.') + ] = None + minimumNumberOfIterations: Annotated[ + int | None, + Field( + description='Minimum number of observations which still provides science value.\n\nIf weather becomes an issue, this is used to determine if additional\nobservations should be tried, or the whole observing group aborted\nto prioritize other observations.\n\nCannot be greater than requested number of iterations.' + ), + ] = None + timeInterval: TypesDuration | None = None + timeIntervalToleranceMin: TypesDuration | None = None + timeIntervalToleranceMax: TypesDuration | None = None + t0Millis: Annotated[ + str | None, + Field( + description="Time the observing group should start being observed, in Unix time\nmilliseconds.\n\nIf omitted, CFHT will start observing at the QC's discretion." + ), + ] = None + withinSameCameraRun: Annotated[ + bool | None, + Field( + description='Whether all iterations need to occur within the same camera run.' + ), + ] = None + + +class ObservingListProgramsResponse(BaseModel): + success: bool | None = None + entity: list[ObservingProgramInfo] | None = None + error: HttpErrors | None = None + + +class ObservingObservingTemplateListResponse(BaseModel): + success: bool | None = None + entity: list[PersistenceObservingTemplateData] | None = None + error: HttpErrors | None = None + + +class ObservingObservingTemplateUpdateResponse(BaseModel): + success: bool | None = None + error: HttpErrors | None = None + observing_template: PersistenceObservingTemplateData | None = None + + +class PersistenceObservingGroupContext(BaseModel): + iterationCount: Annotated[ + int | None, + Field( + title='The iteration number this object was part of in the observing group' + ), + ] = None + iterationTotal: Annotated[ + int | None, + Field(title='The total number of iterations requested for the observing group'), + ] = None + iterationMinimum: Annotated[ + int | None, + Field( + title='Minimum number of iterations this OG needs to provide science value' + ), + ] = None + window: Annotated[ + list[ObservingBlockConditionWindow] | None, + Field(title='Sorted by from past -> future on the observe_after key'), + ] = None + observingGroupToken: str | None = None + observingGroupPriority: PersistenceObservingGroupPriority | None = ( + PersistenceObservingGroupPriority.UNKNOWN_OG_PRIORITY + ) + observingGroupState: ObservingGroupDataObservingGroupState | None = ( + ObservingGroupDataObservingGroupState.NOT_STARTED + ) + observingGroupLabel: int | None = None + observingGroupVersion: Annotated[ + int | None, + Field(description='Increments every time the observing group changes.'), + ] = None + legacyReel: PersistenceLegacyReel | None = None + single: bool | None = None + targetMonitoring: PersistenceTargetMonitoringObservingGroupTimeConstraint | None = ( + None + ) + phaseSchedule: PhaseScheduleObservingGroupPhaseSchedule | None = None + timeAccounting: PersistenceTimeAccountingData | None = None + exposureType: TypesExposureType | None = TypesExposureType.OBJECT + useGroupDitherScheme: bool | None = None + + +class PersistenceTargetMonitoringObservingGroup(BaseModel): + observingBlock: ObservingGroupDataObservingBlock | None = None + timeConstraint: PersistenceTargetMonitoringObservingGroupTimeConstraint | None = ( + None + ) + + +class PersistenceExposureData(BaseModel): + token: str | None = None + obsid: str | None = None + exposureNumber: int | None = None + iteration: int | None = None + parentExposureToken: str | None = None + exposureStatus: ExposureDataExposureStatus | None = None + observingTemplate: PersistenceObservingTemplateData | None = None + target_data: PersistenceTargetData | None = None + observingGroupContext: PersistenceObservingGroupContext | None = None + allDerivationsComplete: Annotated[ + bool | None, + Field( + description='When false, there is data which is still being calculated for this exposure.\n\nData can still be displayed, but the client should attempt to refresh the data.' + ), + ] = None + queueRunId: str | None = None + cameraRunId: str | None = None + prgRunId: str | None = None + observingComponentToken: str | None = None + subTotals: PersistenceSubTotalsData | None = None + attribution: ExposureDataAttribution | None = None + version: Annotated[ + int | None, + Field( + description='storage: transient\napi_visibility: read only, incremented by the server', + title='Increments when this object updates', + ), + ] = None + + +class PersistenceObservingGroupData(BaseModel): + token: Annotated[ + str | None, Field(description='Unique identifier for the observing group.') + ] = None + label: Annotated[ + int | None, + Field( + description='Index of the observing group within the program, set by the system\nautomatically. Used for reference in communications with QSO staff.\n\nstorage: transient\napi_visibility: read only' + ), + ] = None + ogPriority: PersistenceObservingGroupPriority | None = ( + PersistenceObservingGroupPriority.UNKNOWN_OG_PRIORITY + ) + singleObservingGroup: PersistenceSingleObservingGroup | None = None + targetMonitoringObservingGroup: ( + PersistenceTargetMonitoringObservingGroup | None + ) = None + phaseScheduleObservingGroup: PersistencePhaseScheduleObservingGroup | None = None + readObservingBlock: Annotated[ + list[ObservingGroupDataObservingBlock] | None, + Field( + description='The observing blocks that have been created based on the entered\nconfiguration.\n\napi_visibility: read only' + ), + ] = None + observingBlockCondition: Annotated[ + dict[str, PersistenceObservingBlockCondition] | None, + Field( + description='The requirements that have been set for observing each observing blocks\nbased on the entered configuration.\n\napi_visibility: read only' + ), + ] = None + minimumObservingBlockCount: Annotated[ + int | None, + Field( + description='This field is unused, please ignore.\n\napi_visibility: read only' + ), + ] = None + timeAccounting: PersistenceTimeAccountingData | None = None + state: ObservingGroupDataObservingGroupState | None = ( + ObservingGroupDataObservingGroupState.NOT_STARTED + ) + version: Annotated[ + int | None, + Field( + description='Increments when this object updates.\n\nstorage: transient\napi_visibility: read only, incremented by the server' + ), + ] = None + legacyReel: PersistenceLegacyReel | None = None + timeConstraint: ObservingpersistenceTimeConstraint | None = None + targetType: TypesExposureType | None = TypesExposureType.OBJECT + ditherScheme: ObservingGroupDataDitherScheme | None = ( + ObservingGroupDataDitherScheme.NONE + ) + lowSurfaceBrightness: Annotated[ + bool | None, + Field( + title='Indicates the observing group uses low surface brightness (LSB) mode.with' + ), + ] = None + + +class SnrBlockDataSnrBlockObservingComponents(BaseModel): + observingComponentToken: Annotated[ + str | None, Field(title='the observing component') + ] = None + observingGroupToken: Annotated[ + str | None, Field(title='should not be here, should be in the parent') + ] = None + isPrimary: Annotated[bool | None, Field(title='is it the original oc?')] = None + configuredExposures: int | None = None + exposures: list[PersistenceExposureData] | None = None + obLabel: int | None = None + targetToken: str | None = None + observingTemplateToken: str | None = None + + +class ObservingObservingGroupListResponse(BaseModel): + success: bool | None = None + entity: list[PersistenceObservingGroupData] | None = None + error: HttpErrors | None = None + + +class ObservingObservingGroupUpdateResponse(BaseModel): + success: bool | None = None + error: HttpErrors | None = None + entity: PersistenceObservingGroupData | None = None + + +class PersistenceSnrBlockData(BaseModel): + token: str | None = None + label: int | None = None + cumulativeSnr: Annotated[ + float | None, + Field( + title='the total snr achieved of all exposures\n of all observing components of this snr block' + ), + ] = None + carryForwardSnr: Annotated[ + float | None, + Field( + title='if the program is copied to a new program possibly for a new semester\n the cumulative snr will be moved to the carry_forward_snr and the\n carry forward snr will be counted in the cumulative snr of the new program' + ), + ] = None + observingComponents: list[SnrBlockDataSnrBlockObservingComponents] | None = None + version: int | None = None + requestedTargetMagnitude: Annotated[ + float | None, Field(title='this will come from the target') + ] = None + requestedCumulativeSnr: Annotated[ + float | None, Field(title='this will come from the OT') + ] = None + requestedMinimumExposures: int | None = None + totalExposures: Annotated[ + int | None, + Field(title='counting the total validated exposures for this OC (OT/TARGET)'), + ] = None + primaryObservingGroupToken: str | None = None + primaryObservingComponentToken: str | None = None + primaryObservingGroupLabel: int | None = None + totalConfiguredExposures: Annotated[ + int | None, Field(title='counting the total configured exposures for this OC') + ] = None + + +class ObservingAddSingleToSnrBlockResponse(BaseModel): + success: bool | None = None + error: HttpErrors | None = None + snrBlock: PersistenceSnrBlockData | None = None + observingGroup: PersistenceObservingGroupData | None = None + + +class ObservingSnrBlockListResponse(BaseModel): + success: bool | None = None + error: HttpErrors | None = None + entity: list[PersistenceSnrBlockData] | None = None diff --git a/src/aeonlib/conf.py b/src/aeonlib/conf.py index e02d58a..2ccdd45 100644 --- a/src/aeonlib/conf.py +++ b/src/aeonlib/conf.py @@ -39,5 +39,9 @@ class Settings(BaseSettings): salt_username: str = "" salt_password: str = "" + # Canada/France/Hawaii Telescope + cfht_api_root: str = "https://api-stage.cfht.hawaii.edu/" + cfht_access_token: str = "" + settings = Settings() diff --git a/uv.lock b/uv.lock index b7a3fd7..85a13b5 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,10 @@ version = 1 revision = 3 requires-python = ">=3.12" +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version < '3.14'", +] [[package]] name = "aeonlib" @@ -31,6 +35,7 @@ salt = [ [package.dev-dependencies] codegen = [ + { name = "datamodel-code-generator" }, { name = "jinja2" }, { name = "textcase" }, ] @@ -59,6 +64,7 @@ provides-extras = ["eso", "lt", "salt"] [package.metadata.requires-dev] codegen = [ + { name = "datamodel-code-generator", specifier = ">=0.66.3" }, { name = "jinja2", specifier = ">=3.1.6" }, { name = "textcase", specifier = ">=0.2.1" }, ] @@ -90,6 +96,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/16/9826f089383c593cdfc4a6e5aca94d9e91ae1692c57af82c3b2aa5e810f7/anyio-4.14.0-py3-none-any.whl", hash = "sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9", size = 123506, upload-time = "2026-06-15T22:00:47.595Z" }, ] +[[package]] +name = "argcomplete" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/c0/c8e94135e66fabf89a120d9b4b123fe6993506beca6c1938a74c24cfa5fd/argcomplete-3.7.0.tar.gz", hash = "sha256:afde224f753f874807b1dc1414e883ab8fe0cda9c04807b6047dcb8e1ac23913", size = 73284, upload-time = "2026-06-30T22:28:22.249Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/f6/5b8ec087cd9cfa9449491ec83f76fb6b7006b4dff57d2ba8aaab330fe8e4/argcomplete-3.7.0-py3-none-any.whl", hash = "sha256:d8f0f22d2a8a7caa383be1e22b6caf1ecaf0ebd10d8f83cc125e36540c95830c", size = 42575, upload-time = "2026-06-30T22:28:20.547Z" }, +] + [[package]] name = "astropy" version = "8.0.0" @@ -135,6 +150,38 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl", hash = "sha256:d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9", size = 109924, upload-time = "2026-06-07T16:44:21.566Z" }, ] +[[package]] +name = "black" +version = "26.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pytokens" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/37/5628dd55bf2b34257fc7603f0fe97c40e3aaf24265f416a9c85c95ca1436/black-26.5.1.tar.gz", hash = "sha256:dd321f668053961824bcc1be1cc1df748b2d7e4fa28086b08331e577b0100a73", size = 679439, upload-time = "2026-05-18T16:53:36.107Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/99/7744b906703228264ef73bdd534df88ec1ef3de45c4e78f6d31b9e32d0c9/black-26.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4ad6fa01f941920f54f2bbb35f3df7673428a0ef98a0b0840c2eaef3b110efa8", size = 2012518, upload-time = "2026-05-18T17:05:20.108Z" }, + { url = "https://files.pythonhosted.org/packages/b7/c0/c5a3b1636dfd09c42534f2b3cf33506814f6d3e066fb0879ffa16c1ae860/black-26.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3915f256e75a2d7cf88d8953d37f780455dc586cc72dee059c528fe77f581217", size = 1816016, upload-time = "2026-05-18T17:05:21.84Z" }, + { url = "https://files.pythonhosted.org/packages/1f/0e/36044316b65ca471d3bb6d3703fd06fb50c6b727c3562f6a5a3153634f88/black-26.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d98d4137277c75dfb898ec8d846c4fd68ba1e9cf77f95e2865c203dc18f4c3d", size = 1884150, upload-time = "2026-05-18T17:05:23.546Z" }, + { url = "https://files.pythonhosted.org/packages/b3/33/dafc5808c2af43672912111d7c3354af1615f7e2be3bed7a878461abbe4d/black-26.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:a1dca32d9f1784af512a13410ec204c6f7f0aa9797a111c42e1c03449821c264", size = 1486825, upload-time = "2026-05-18T17:05:25.004Z" }, + { url = "https://files.pythonhosted.org/packages/82/14/b965ee6ad2a311f28bdbf692def3ee9848d2ae289dab28b27657fcee3e78/black-26.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:1037d5ac7b7b310b2632ad867ec8d0e4c4819dcdb0b820f63135da746a24e418", size = 1288646, upload-time = "2026-05-18T17:05:26.477Z" }, + { url = "https://files.pythonhosted.org/packages/3f/5c/c384363980e11e25ca6b93205949bb331fbf35f4e0dbec376dfa6326cec8/black-26.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b36cf2ddf5566e205f6535f782a62194a184d33e175b64ae8c40b1737522be3", size = 2009020, upload-time = "2026-05-18T17:05:28.132Z" }, + { url = "https://files.pythonhosted.org/packages/0b/df/9f31c5e0babbfed77d505fc5d120beb98b21b33feaeded3924ea941fe360/black-26.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f7ea64ebfa01b50f693508fc39f875e264446d3b097088f84f203b9d09618a0", size = 1813335, upload-time = "2026-05-18T17:05:31.266Z" }, + { url = "https://files.pythonhosted.org/packages/fb/24/8e7b9a2fa61b0afd82209efe937557d180a1fa055bd7f6161eb9defc3719/black-26.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecb3e624844c798144e9bd986954e0adc81d8911a1f30f375e1252fe26e8c294", size = 1881614, upload-time = "2026-05-18T17:05:32.718Z" }, + { url = "https://files.pythonhosted.org/packages/49/ad/b4e0d9365ba8ac34f6bbab62a4b1b2dd5d618fac3fa1b8db968c844201b5/black-26.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:e1a26503279b6b310669fb0b219c39e4820b77e8189fe80f522bb511f247db0a", size = 1488925, upload-time = "2026-05-18T17:05:34.259Z" }, + { url = "https://files.pythonhosted.org/packages/a1/4b/652b859bf5df88a751c30451b09338f7fd26a77d1271c666992f836b7711/black-26.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c34b25da232ead53a6f335b76dbea124f4d152ad568b9080d6f944bc2b34b52", size = 1289883, upload-time = "2026-05-18T17:05:36.019Z" }, + { url = "https://files.pythonhosted.org/packages/a6/16/a8da8eb208c51c7f4ce74609a45d0dcc6d8a2141e45e81ee5289d1bb0d59/black-26.5.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e88976690a64b0af98312ca958415849cb42423423c5f2ee74af4b49a97a2168", size = 2004800, upload-time = "2026-05-18T17:05:38.182Z" }, + { url = "https://files.pythonhosted.org/packages/11/8a/a479296a19e383b70a725882a6cf3d786540601ff03cabbaaf1cce864c5a/black-26.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32d5ea7f6c8bdfa6e648326ebca1f02b0764e2a029edc6f8dce2627e19d468c3", size = 1815576, upload-time = "2026-05-18T17:05:40.309Z" }, + { url = "https://files.pythonhosted.org/packages/81/6b/cfaf3d39f25132c156a068f6b805576c9103a84086019507c70e1911ee7d/black-26.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ea8d16dc41655aa113cd64665e7219446cd7e4ff2248d7178eaa905190c86b18", size = 1877927, upload-time = "2026-05-18T17:05:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/66/76/302e313964bcff7e28df329d39f84f5270095730d85ff0acc260610a0d82/black-26.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:577f21094ea469ef92ec1adaf2c9441a226d2144d01a5be2fa823cecf6543e50", size = 1511860, upload-time = "2026-05-18T17:05:43.943Z" }, + { url = "https://files.pythonhosted.org/packages/27/4e/a3827e35e0e567f9f9ee59e2a0ab979267dca98718f25547ca8c6733afd4/black-26.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:ed1a20af114c301a0269bf01163d51dbef72737fd65f850001e7cbe7f3c7abae", size = 1316632, upload-time = "2026-05-18T17:05:45.521Z" }, + { url = "https://files.pythonhosted.org/packages/94/51/f975cae76d44274cc2868dc9040ac5d58d464784610234455b4e7b19c6ef/black-26.5.1-py3-none-any.whl", hash = "sha256:4ed7f7da04046d2e488437170797d3b4a4ad83906683bcb7dfc68b673bbce5e2", size = 213693, upload-time = "2026-05-18T16:53:33.964Z" }, +] + [[package]] name = "certifi" version = "2026.6.17" @@ -254,6 +301,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, ] +[[package]] +name = "click" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -307,6 +366,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/93/77/8df9eb486495979bccecd1062e2eaf435250e84437040295b57d09048b0b/cryptography-49.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b", size = 4967968, upload-time = "2026-06-12T20:02:12.524Z" }, ] +[[package]] +name = "datamodel-code-generator" +version = "0.66.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argcomplete" }, + { name = "black", marker = "sys_platform != 'emscripten'" }, + { name = "genson" }, + { name = "inflect" }, + { name = "isort", marker = "sys_platform != 'emscripten'" }, + { name = "jinja2" }, + { name = "pydantic" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/4f/78f654273af65bd65f55b1eb670b23e56a8d1b227129bc17d96c4803495d/datamodel_code_generator-0.66.3.tar.gz", hash = "sha256:739f36b42d8131359a82cb25b704444581114e14611344538a61465d1743c6ec", size = 1516025, upload-time = "2026-07-01T16:03:37.389Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/ef/78fcf2a043e4c541827f156c98034bc6f2d4f511ec750f58f6f757985f66/datamodel_code_generator-0.66.3-py3-none-any.whl", hash = "sha256:7c1b44910951efea0e109f64243acb9721f6f7cd45b15635df17bd0829b24244", size = 418011, upload-time = "2026-07-01T16:03:35.348Z" }, +] + [[package]] name = "defusedxml" version = "0.7.1" @@ -316,6 +394,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, ] +[[package]] +name = "genson" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/cf/2303c8ad276dcf5ee2ad6cf69c4338fd86ef0f471a5207b069adf7a393cf/genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37", size = 34919, upload-time = "2024-05-15T22:08:49.123Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/5c/e226de133afd8bb267ec27eead9ae3d784b95b39a287ed404caab39a5f50/genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7", size = 21470, upload-time = "2024-05-15T22:08:47.056Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -362,6 +449,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, ] +[[package]] +name = "inflect" +version = "7.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, + { name = "typeguard" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/c6/943357d44a21fd995723d07ccaddd78023eace03c1846049a2645d4324a3/inflect-7.5.0.tar.gz", hash = "sha256:faf19801c3742ed5a05a8ce388e0d8fe1a07f8d095c82201eb904f5d27ad571f", size = 73751, upload-time = "2024-12-28T17:11:18.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/eb/427ed2b20a38a4ee29f24dbe4ae2dafab198674fe9a85e3d6adf9e5f5f41/inflect-7.5.0-py3-none-any.whl", hash = "sha256:2aea70e5e70c35d8350b8097396ec155ffd68def678c7ff97f51aa69c1d92344", size = 35197, upload-time = "2024-12-28T17:11:15.931Z" }, +] + [[package]] name = "iniconfig" version = "2.3.0" @@ -371,6 +471,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] +[[package]] +name = "isort" +version = "8.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/7c/ec4ab396d31b3b395e2e999c8f46dec78c5e29209fac49d1f4dace04041d/isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d", size = 769592, upload-time = "2026-02-28T10:08:20.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" }, +] + [[package]] name = "jaraco-classes" version = "3.4.0" @@ -603,6 +712,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e8/3d/1087453384dbde46a8c7f9356eead2c58be8a7bf156bca40243377c85715/more_itertools-11.1.0-py3-none-any.whl", hash = "sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192", size = 72226, upload-time = "2026-05-22T14:14:28.824Z" }, ] +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + [[package]] name = "numpy" version = "2.4.6" @@ -686,6 +804,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, ] +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224, upload-time = "2026-05-28T03:32:53.587Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743, upload-time = "2026-05-28T03:32:52.175Z" }, +] + [[package]] name = "pluggy" version = "1.6.0" @@ -874,6 +1010,35 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, ] +[[package]] +name = "pytokens" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, + { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, + { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, + { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, + { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, + { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, + { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, + { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, + { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, + { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, + { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, + { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, + { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, +] + [[package]] name = "pywin32-ctypes" version = "0.2.3" @@ -1047,6 +1212,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2f/22/8063101427ecd3d2652aada4d21d0876b07a3dc789125bca2ee858fec3ed/time_machine-3.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:7f2fb6784b414edbe2c0b558bfaab0c251955ba27edd62946cce4a01675a992c", size = 17359, upload-time = "2025-12-17T23:33:01.54Z" }, ] +[[package]] +name = "typeguard" +version = "4.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/1c/dfba5c4633cafc4c701f237d2ba63b416805047fd6d96aab4cfc40969f98/typeguard-4.5.2.tar.gz", hash = "sha256:5a16dcac23502039299c97c8941651bc33d7ea8cc4b2f7d6bbb1b528f6eea423", size = 80240, upload-time = "2026-05-14T12:59:40.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/29/74eeb4d3f3ae61ca096b018ad486b3b3c74b17bec09ab4edab721cbefec3/typeguard-4.5.2-py3-none-any.whl", hash = "sha256:fcf9de18bd945cdb4c7b996e12b4c51ce83f92f191314a6d7cf1739586ec98cf", size = 36748, upload-time = "2026-05-14T12:59:39.473Z" }, +] + [[package]] name = "types-defusedxml" version = "0.7.0.20260504" From 022b85c0ff9d57ca7c33e836b198d921bed2440e Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Wed, 1 Jul 2026 17:37:20 -0700 Subject: [PATCH 02/33] Create build directory if it doesn't exist --- codegen/cfht/generate_models.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/codegen/cfht/generate_models.sh b/codegen/cfht/generate_models.sh index bf71622..9d3762a 100644 --- a/codegen/cfht/generate_models.sh +++ b/codegen/cfht/generate_models.sh @@ -3,6 +3,7 @@ set -euxo pipefail # The CFHT API uses OAS 2.0 use the online Swagger converter to convert to OAS 3.0 curl -L \ "https://converter.swagger.io/api/convert?url=https://hou-stage.cfht.hawaii.edu/api-docs/piapi_openapiv2.swagger.json" \ +--create-dirs \ -o build/cfht.openapi3.json # Generate Pydantic models from the OpenAPI 3.0 spec From fa09052af40d30961a98a71ed46b209058c9fb03 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Wed, 1 Jul 2026 17:40:14 -0700 Subject: [PATCH 03/33] Add auto generated file warning --- codegen/cfht/generate_models.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/cfht/generate_models.sh b/codegen/cfht/generate_models.sh index 9d3762a..5b7be31 100644 --- a/codegen/cfht/generate_models.sh +++ b/codegen/cfht/generate_models.sh @@ -18,4 +18,4 @@ datamodel-codegen \ --formatters builtin # Ignore some ruff rules that are not relevant for generated code -sed -i '1s/^/# ruff: noqa: E741\n/' src/aeonlib/cfht/models.py +sed -i '1s/^/# Auto generated file, do not edit\n# ruff: noqa: E741\n/' src/aeonlib/cfht/models.py From e383aaf604f62f0b81cfe32ef22ccbf4d3220235 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Wed, 1 Jul 2026 17:42:59 -0700 Subject: [PATCH 04/33] Use snake_case fields --- codegen/cfht/generate_models.sh | 1 + src/aeonlib/cfht/models.py | 1865 +++++++++++++++++++------------ 2 files changed, 1130 insertions(+), 736 deletions(-) diff --git a/codegen/cfht/generate_models.sh b/codegen/cfht/generate_models.sh index 5b7be31..644b77c 100644 --- a/codegen/cfht/generate_models.sh +++ b/codegen/cfht/generate_models.sh @@ -13,6 +13,7 @@ datamodel-codegen \ --openapi-scopes schemas \ --output src/aeonlib/cfht/models.py \ --output-model-type pydantic_v2.BaseModel \ + --snake-case-field \ --use-annotated \ --set-default-enum-member \ --formatters builtin diff --git a/src/aeonlib/cfht/models.py b/src/aeonlib/cfht/models.py index 203b116..b3b2a45 100644 --- a/src/aeonlib/cfht/models.py +++ b/src/aeonlib/cfht/models.py @@ -1,7 +1,8 @@ +# Auto generated file, do not edit # ruff: noqa: E741 # generated by datamodel-codegen: # filename: cfht.openapi3.json -# timestamp: 2026-07-01T23:20:38+00:00 +# timestamp: 2026-07-02T00:42:01+00:00 from __future__ import annotations @@ -29,11 +30,11 @@ class CalibrationCalibrationPosition(Enum): class ConfigDithering(BaseModel): - isStaring: bool | None = None + is_staring: Annotated[bool | None, Field(alias='isStaring')] = None class EspadonsStatusGuiderSeeingProcessingResult(BaseModel): - guiderSeeing: float | None = None + guider_seeing: Annotated[float | None, Field(alias='guiderSeeing')] = None class ExposureDataAttribution(BaseModel): @@ -47,39 +48,49 @@ class ExposureDataAttribution(BaseModel): observing_block_label_int: Annotated[ int | None, Field(title='Label of the observing block') ] = None - observingBlockToken: Annotated[ + observing_block_token: Annotated[ str | None, Field( - description='Token of the observing block this exposure is attributed to.' + alias='observingBlockToken', + description='Token of the observing block this exposure is attributed to.', ), ] = None - observingGroupLabel: Annotated[ + observing_group_label: Annotated[ int | None, Field( - description='Label of the observing group this exposure is attributed to.' + alias='observingGroupLabel', + description='Label of the observing group this exposure is attributed to.', ), ] = None - observingGroupToken: Annotated[ + observing_group_token: Annotated[ str | None, Field( - description='Token of the observing group this exposure is attributed to.' + alias='observingGroupToken', + description='Token of the observing group this exposure is attributed to.', ), ] = None - observingComponentToken: Annotated[ + observing_component_token: Annotated[ str | None, Field( - description='Token of the observing component this exposure is attributed to.' + alias='observingComponentToken', + description='Token of the observing component this exposure is attributed to.', ), ] = None - exposureNumber: Annotated[ - int | None, Field(description='exposure number this exposure is attributed to.') + exposure_number: Annotated[ + int | None, + Field( + alias='exposureNumber', + description='exposure number this exposure is attributed to.', + ), ] = None instrument_run: Annotated[ str | None, Field(title='the queue run this exposure was taken under') ] = None - attributingAgency: Annotated[str | None, Field(title='the agency id')] = None - tacRank: int | None = None - tacGrade: str | None = None + attributing_agency: Annotated[ + str | None, Field(alias='attributingAgency', title='the agency id') + ] = None + tac_rank: Annotated[int | None, Field(alias='tacRank')] = None + tac_grade: Annotated[str | None, Field(alias='tacGrade')] = None class ExposureDataEspadonsStatusExposureMeterProcessingResult(BaseModel): @@ -89,7 +100,9 @@ class ExposureDataEspadonsStatusExposureMeterProcessingResult(BaseModel): class ExposureDataSpirouStatusExposureMeterProcessingResult(BaseModel): flux: float | None = None snr: float | None = None - midExposureDateMillis: float | None = None + mid_exposure_date_millis: Annotated[ + float | None, Field(alias='midExposureDateMillis') + ] = None spemsnr: Annotated[float | None, Field(title='Calculated SNR')] = None spemsnrc: float | None = None @@ -99,54 +112,65 @@ class ExposureDataWenaStatus(BaseModel): class ExposureDataWircamStatus(BaseModel): - iqUncertainty: float | None = None + iq_uncertainty: Annotated[float | None, Field(alias='iqUncertainty')] = None absorption: float | None = None - absorptionUncertainty: float | None = None - numberOfStarsIq: int | None = None - numberOfStarsAbsorption: int | None = None - absoluteSkyLevel: float | None = None + absorption_uncertainty: Annotated[ + float | None, Field(alias='absorptionUncertainty') + ] = None + number_of_stars_iq: Annotated[int | None, Field(alias='numberOfStarsIq')] = None + number_of_stars_absorption: Annotated[ + int | None, Field(alias='numberOfStarsAbsorption') + ] = None + absolute_sky_level: Annotated[float | None, Field(alias='absoluteSkyLevel')] = None elongation: float | None = None mdcoords: int | None = None mdrepeat: int | None = None - actualFilter: str | None = None + actual_filter: Annotated[str | None, Field(alias='actualFilter')] = None iq: float | None = None class ExposureStatusFile(BaseModel): - absolutePath: Annotated[ + absolute_path: Annotated[ str | None, Field( - description='Absolute path to directory file is homed in. Starts with / and ends with /.' + alias='absolutePath', + description='Absolute path to directory file is homed in. Starts with / and ends with /.', ), ] = None - baseName: Annotated[str | None, Field(title='Name of file without extension')] = ( - None - ) + base_name: Annotated[ + str | None, Field(alias='baseName', title='Name of file without extension') + ] = None ext: Annotated[str | None, Field(description='Name of extenion including .')] = None class FindingChartDataApproval(BaseModel): - approvedAtMillis: str | None = None - approvedByUsertoken: str | None = None + approved_at_millis: Annotated[str | None, Field(alias='approvedAtMillis')] = None + approved_by_usertoken: Annotated[str | None, Field(alias='approvedByUsertoken')] = ( + None + ) class FixedTargetProperMotion(BaseModel): - raMas: Annotated[ + ra_mas: Annotated[ float | None, Field( - description='Motion in the right ascension axis in milliarcseconds/year.' + alias='raMas', + description='Motion in the right ascension axis in milliarcseconds/year.', ), ] = None - decMas: Annotated[ + dec_mas: Annotated[ float | None, - Field(description='Motion in the declination axis in milliarcseconds/year.'), + Field( + alias='decMas', + description='Motion in the declination axis in milliarcseconds/year.', + ), ] = None class InstrumentConfigurationDataEspadonsConfigurationDataOObservingMode(Enum): - Polarimetry = 'Polarimetry' - Spectroscopy_star_only = 'Spectroscopy_star_only' - Spectroscopy_star_sky = 'Spectroscopy_star_sky' + polarimetry = 'Polarimetry' + spectroscopy_star_only = 'Spectroscopy_star_only' + spectroscopy_star_sky = 'Spectroscopy_star_sky' class InstrumentConfigurationDataMegacamConfigurationDataBinning(Enum): @@ -157,32 +181,32 @@ class InstrumentConfigurationDataMegacamConfigurationDataBinning(Enum): class InstrumentConfigurationDataMegacamConfigurationDataFilter(Enum): - Unknown = 'Unknown' + unknown = 'Unknown' u = 'u' g = 'g' i = 'i' r = 'r' z = 'z' gri = 'gri' - CaHK = 'CaHK' - Ha = 'Ha' - HaOFF = 'HaOFF' - OIII = 'OIII' - OIIIOFF = 'OIIIOFF' - uS = 'uS' - gS = 'gS' - rS = 'rS' - iS = 'iS' - zS = 'zS' - N393S = 'N393S' - HaS = 'HaS' - HaOFFS = 'HaOFFS' - TiOS = 'TiOS' - CNS = 'CNS' - OIIIS = 'OIIIS' - PHGS = 'PHGS' - M4112 = 'M4112' - M4376 = 'M4376' + ca_hk = 'CaHK' + ha = 'Ha' + ha_off = 'HaOFF' + oiii = 'OIII' + oiiioff = 'OIIIOFF' + u_s = 'uS' + g_s = 'gS' + r_s = 'rS' + i_s = 'iS' + z_s = 'zS' + n393_s = 'N393S' + ha_s = 'HaS' + ha_offs = 'HaOFFS' + ti_os = 'TiOS' + cns = 'CNS' + oiiis = 'OIIIS' + phgs = 'PHGS' + m4112 = 'M4112' + m4376 = 'M4376' class InstrumentConfigurationDataSitelleConfigurationDataBinning(Enum): @@ -192,31 +216,31 @@ class InstrumentConfigurationDataSitelleConfigurationDataBinning(Enum): class InstrumentConfigurationDataSitelleConfigurationDataFilter(Enum): - UNKNOWN = 'UNKNOWN' - NONE = 'NONE' - C1 = 'C1' - C2 = 'C2' - C3 = 'C3' - C4 = 'C4' - SN1 = 'SN1' - SN2 = 'SN2' - SN3 = 'SN3' - SN4 = 'SN4' - SN5 = 'SN5' - SN6 = 'SN6' + unknown = 'UNKNOWN' + none = 'NONE' + c1 = 'C1' + c2 = 'C2' + c3 = 'C3' + c4 = 'C4' + sn1 = 'SN1' + sn2 = 'SN2' + sn3 = 'SN3' + sn4 = 'SN4' + sn5 = 'SN5' + sn6 = 'SN6' class InstrumentConfigurationDataSpirouConfigurationDataOObservingMode(Enum): - Polarimetry = 'Polarimetry' - Star = 'Star' + polarimetry = 'Polarimetry' + star = 'Star' class InstrumentConfigurationDataTracking(Enum): - Unknown = 'Unknown' - SID = 'SID' - NONSID = 'NONSID' - SID_NOG = 'SID_NOG' - NONSID_G = 'NONSID_G' + unknown = 'Unknown' + sid = 'SID' + nonsid = 'NONSID' + sid_nog = 'SID_NOG' + nonsid_g = 'NONSID_G' class InstrumentConfigurationDataWenaConfigurationData(BaseModel): @@ -224,68 +248,72 @@ class InstrumentConfigurationDataWenaConfigurationData(BaseModel): class InstrumentConfigurationDataWircamConfigurationDataFilter(Enum): - UNKNOWN = 'UNKNOWN' - W = 'W' - H = 'H' - J = 'J' - Ks = 'Ks' - Y = 'Y' - H2 = 'H2' - KCont = 'KCont' - CH4On = 'CH4On' - CH4Off = 'CH4Off' - LowOH1 = 'LowOH1' - LowOH2 = 'LowOH2' - BrG = 'BrG' - CO = 'CO' - COnar = 'COnar' + unknown = 'UNKNOWN' + w = 'W' + h = 'H' + j = 'J' + ks = 'Ks' + y = 'Y' + h2 = 'H2' + k_cont = 'KCont' + ch4_on = 'CH4On' + ch4_off = 'CH4Off' + low_oh1 = 'LowOH1' + low_oh2 = 'LowOH2' + br_g = 'BrG' + co = 'CO' + c_onar = 'COnar' class MMode(Enum): - Dark = 'Dark' - Fabry_Perot = 'Fabry_Perot' - Wave_Hc1 = 'Wave_Hc1' - Wave_Hc2 = 'Wave_Hc2' - FLAT = 'FLAT' + dark = 'Dark' + fabry_perot = 'Fabry_Perot' + wave_hc1 = 'Wave_Hc1' + wave_hc2 = 'Wave_Hc2' + flat = 'FLAT' class MegacamConfigurationDataSnrMode(Enum): - NONE = 'NONE' - STANDARD = 'STANDARD' - CUMULATIVE = 'CUMULATIVE' - ZERO_POINT = 'ZERO_POINT' + none = 'NONE' + standard = 'STANDARD' + cumulative = 'CUMULATIVE' + zero_point = 'ZERO_POINT' class MegacamStatusElixirProcessingResult(BaseModel): iq: float | None = None - skyBackground: float | None = None + sky_background: Annotated[float | None, Field(alias='skyBackground')] = None snr: float | None = None zeropoint: float | None = None class ObservingBlockConditionRequisite(BaseModel): - requisiteObservingBlockIndex: Annotated[ + requisite_observing_block_index: Annotated[ int | None, Field( - description='This should match the index of the observing block in the\nread_observing_block list.' + alias='requisiteObservingBlockIndex', + description='This should match the index of the observing block in the\nread_observing_block list.', ), ] = None - waitPeriodMinMillis: Annotated[ + wait_period_min_millis: Annotated[ str | None, Field( - description='Minimum time to wait after requisite observing block exposure has\nbeen taken.' + alias='waitPeriodMinMillis', + description='Minimum time to wait after requisite observing block exposure has\nbeen taken.', ), ] = None - waitPeriodMillis: Annotated[ + wait_period_millis: Annotated[ str | None, Field( - description='Optimal to wait after requisite observing block exposure has\nbeen taken.' + alias='waitPeriodMillis', + description='Optimal to wait after requisite observing block exposure has\nbeen taken.', ), ] = None - waitPeriodMaxMillis: Annotated[ + wait_period_max_millis: Annotated[ str | None, Field( - description='Minimum time to wait after requisite observing block exposure has\nbeen taken.' + alias='waitPeriodMaxMillis', + description='Minimum time to wait after requisite observing block exposure has\nbeen taken.', ), ] = None unbounded: Annotated[ @@ -297,62 +325,72 @@ class ObservingBlockConditionRequisite(BaseModel): class ObservingBlockConditionWindow(BaseModel): - observeAfter: Annotated[ + observe_after: Annotated[ str | None, Field( - description='Start of the observation window, in Unix time milliseconds.' + alias='observeAfter', + description='Start of the observation window, in Unix time milliseconds.', ), ] = None - observeBefore: Annotated[ + observe_before: Annotated[ str | None, - Field(description='End of the observation window, in Unix time milliseconds.'), + Field( + alias='observeBefore', + description='End of the observation window, in Unix time milliseconds.', + ), ] = None class ObservingBlockObservingComponent(BaseModel): - targetToken: Annotated[ + target_token: Annotated[ str | None, Field( - description='Unique identifier of the target used in this observing component.\n\nWhen creating or updating an observing group, this must be an\nexisting target, or on the map of the request to be created.' + alias='targetToken', + description='Unique identifier of the target used in this observing component.\n\nWhen creating or updating an observing group, this must be an\nexisting target, or on the map of the request to be created.', ), ] = None - observingTemplateToken: Annotated[ + observing_template_token: Annotated[ str | None, Field( - description='Unique identifier of the observing template used in this\nobserving component.\n\nWhen creating or updating an observing group, this must be an\nexisting observing template, or on the map of the request to be\ncreated.' + alias='observingTemplateToken', + description='Unique identifier of the observing template used in this\nobserving component.\n\nWhen creating or updating an observing group, this must be an\nexisting observing template, or on the map of the request to be\ncreated.', ), ] = None - targetLabel: Annotated[ + target_label: Annotated[ int | None, Field( - description='Label of the target used in this observing component.\nThis value is only set on the read_observing_block.\n\napi_visibility: read only' + alias='targetLabel', + description='Label of the target used in this observing component.\nThis value is only set on the read_observing_block.\n\napi_visibility: read only', ), ] = None - observingTemplateLabel: Annotated[ + observing_template_label: Annotated[ int | None, Field( - description='Label of the observing template used in this observing component.\nThis value is only set on the read_observing_block.\n\napi_visibility: read only' + alias='observingTemplateLabel', + description='Label of the observing template used in this observing component.\nThis value is only set on the read_observing_block.\n\napi_visibility: read only', ), ] = None - snrBlockToken: Annotated[ + snr_block_token: Annotated[ str | None, Field( - description='Unique identifier of the SNR block used in this observing\ncomponent, if any. Must be set if and only if the observing\ncomponent uses a MegaCam cumulative SNR mode observing template.\n\nIf this is set when creating or updating an observing group,\nthe observing component will be tied to the SNR block with this\ntoken if it already exists, otherwise a new SNR block will be\ncreated using this token.' + alias='snrBlockToken', + description='Unique identifier of the SNR block used in this observing\ncomponent, if any. Must be set if and only if the observing\ncomponent uses a MegaCam cumulative SNR mode observing template.\n\nIf this is set when creating or updating an observing group,\nthe observing component will be tied to the SNR block with this\ntoken if it already exists, otherwise a new SNR block will be\ncreated using this token.', ), ] = None class ObservingGroupDataDitherScheme(Enum): - NONE = 'NONE' - STANDARD = 'STANDARD' - GROUP = 'GROUP' + none = 'NONE' + standard = 'STANDARD' + group = 'GROUP' class ObservingGroupDataObservingBlock(BaseModel): - observingComponent: Annotated[ + observing_component: Annotated[ list[ObservingBlockObservingComponent] | None, Field( - description='List of observing components that comprise this observing block.' + alias='observingComponent', + description='List of observing components that comprise this observing block.', ), ] = None token: Annotated[ @@ -370,18 +408,21 @@ class ObservingGroupDataObservingBlock(BaseModel): class ObservingGroupDataObservingGroupState(Enum): - NOT_STARTED = 'NOT_STARTED' - OBSERVED = 'OBSERVED' - STARTED = 'STARTED' - VALIDATED = 'VALIDATED' + not_started = 'NOT_STARTED' + observed = 'OBSERVED' + started = 'STARTED' + validated = 'VALIDATED' class PhotometryAperture(BaseModel): - optimalAperture: bool | None = None - fixedAperture: Annotated[ - float | None, Field(title='Radius of the aperture in arcsec') + optimal_aperture: Annotated[bool | None, Field(alias='optimalAperture')] = None + fixed_aperture: Annotated[ + float | None, + Field(alias='fixedAperture', title='Radius of the aperture in arcsec'), + ] = None + ninety_six_percent_flux_aperture: Annotated[ + bool | None, Field(alias='ninetySixPercentFluxAperture') ] = None - ninetySixPercentFluxAperture: bool | None = None class ProgramDataComments(BaseModel): @@ -390,41 +431,41 @@ class ProgramDataComments(BaseModel): class ProgramDataContactInfo(BaseModel): - piFirstName: str | None = None - piLastName: str | None = None - piEmail: str | None = None - userToken: str | None = None + pi_first_name: Annotated[str | None, Field(alias='piFirstName')] = None + pi_last_name: Annotated[str | None, Field(alias='piLastName')] = None + pi_email: Annotated[str | None, Field(alias='piEmail')] = None + user_token: Annotated[str | None, Field(alias='userToken')] = None class ProgramDataProgramType(Enum): - REGULAR = 'REGULAR' - TARGET_OF_OPPORTUNITY = 'TARGET_OF_OPPORTUNITY' - CALIBRATION = 'CALIBRATION' - SNAPSHOT = 'SNAPSHOT' + regular = 'REGULAR' + target_of_opportunity = 'TARGET_OF_OPPORTUNITY' + calibration = 'CALIBRATION' + snapshot = 'SNAPSHOT' class ProgramInfoPiInfo(BaseModel): - ldapUsername: str | None = None - emailAddress: str | None = None - firstName: str | None = None - lastName: str | None = None - legacyUsername: str | None = None - userToken: str | None = None + ldap_username: Annotated[str | None, Field(alias='ldapUsername')] = None + email_address: Annotated[str | None, Field(alias='emailAddress')] = None + first_name: Annotated[str | None, Field(alias='firstName')] = None + last_name: Annotated[str | None, Field(alias='lastName')] = None + legacy_username: Annotated[str | None, Field(alias='legacyUsername')] = None + user_token: Annotated[str | None, Field(alias='userToken')] = None class RReadoutMode(Enum): - Normal = 'Normal' - Slow = 'Slow' - Fast = 'Fast' + normal = 'Normal' + slow = 'Slow' + fast = 'Fast' class SitelleConfigurationDataCalibration(BaseModel): - calibrationPosition: CalibrationCalibrationPosition | None = ( - CalibrationCalibrationPosition.no_calibration_position - ) - calibrationMode: CalibrationCalibrationMode | None = ( - CalibrationCalibrationMode.no_calibration_mode - ) + calibration_position: Annotated[ + CalibrationCalibrationPosition | None, Field(alias='calibrationPosition') + ] = CalibrationCalibrationPosition.no_calibration_position + calibration_mode: Annotated[ + CalibrationCalibrationMode | None, Field(alias='calibrationMode') + ] = CalibrationCalibrationMode.no_calibration_mode class SitelleConfigurationDataResolutionMode(Enum): @@ -435,30 +476,38 @@ class SitelleConfigurationDataResolutionMode(Enum): class SitelleStatusCamera(BaseModel): iq: float | None = None - iqRms: float | None = None - pixelShiftX: float | None = None - pixelShiftY: float | None = None - numberOfStars: int | None = None + iq_rms: Annotated[float | None, Field(alias='iqRms')] = None + pixel_shift_x: Annotated[float | None, Field(alias='pixelShiftX')] = None + pixel_shift_y: Annotated[float | None, Field(alias='pixelShiftY')] = None + number_of_stars: Annotated[int | None, Field(alias='numberOfStars')] = None me: float | None = None class SpirouConfigurationDataSkyObservation(Enum): - None_ = 'None' - Before = 'Before' - After = 'After' - BeforeAndAfter = 'BeforeAndAfter' + none = 'None' + before = 'Before' + after = 'After' + before_and_after = 'BeforeAndAfter' class SpirouStatusGuiderProcessingResult(BaseModel): etime: float | None = None isustate: str | None = None - sbdensP: float | None = None - holePositionOffsetX: float | None = None - holePositionOffsetY: float | None = None - starToNullPositionOffsetX: float | None = None - starToNullPositionOffsetY: float | None = None - magnitudeEstimate: float | None = None - seeingEstimate: float | None = None + sbdens_p: Annotated[float | None, Field(alias='sbdensP')] = None + hole_position_offset_x: Annotated[float | None, Field(alias='holePositionOffsetX')] = ( + None + ) + hole_position_offset_y: Annotated[float | None, Field(alias='holePositionOffsetY')] = ( + None + ) + star_to_null_position_offset_x: Annotated[ + float | None, Field(alias='starToNullPositionOffsetX') + ] = None + star_to_null_position_offset_y: Annotated[ + float | None, Field(alias='starToNullPositionOffsetY') + ] = None + magnitude_estimate: Annotated[float | None, Field(alias='magnitudeEstimate')] = None + seeing_estimate: Annotated[float | None, Field(alias='seeingEstimate')] = None sgisust: Annotated[str | None, Field(title='per 160676372')] = None sggdst: str | None = None sgfrmrt: float | None = None @@ -490,7 +539,7 @@ class SpirouStatusGuiderProcessingResult(BaseModel): sgcmagn: float | None = None sgemagn: float | None = None sgssee: float | None = None - trgType: str | None = None + trg_type: Annotated[str | None, Field(alias='trgType')] = None class SpirouStatusPipelineProcessingResult(BaseModel): @@ -504,39 +553,42 @@ class SpirouStatusPipelineProcessingResult(BaseModel): ccfcontr: float | None = None ccfrvc: float | None = None ccffwhm: float | None = None - ccfmaskStr: str | None = None + ccfmask_str: Annotated[str | None, Field(alias='ccfmaskStr')] = None class TargetDataMagnitude(BaseModel): - U: Annotated[float | None, Field(title='U-band magnitude')] = None - B: Annotated[float | None, Field(title='B-band magnitude')] = None - V: Annotated[ + u: Annotated[float | None, Field(alias='U', title='U-band magnitude')] = None + b: Annotated[float | None, Field(alias='B', title='B-band magnitude')] = None + v: Annotated[ float | None, Field( + alias='V', description='Required for targets observed with ESPaDonS.', title='V-band magnitude', ), ] = None - R: Annotated[float | None, Field(title='R-band magnitude')] = None - I: Annotated[float | None, Field(title='I-band magnitude')] = None - G: Annotated[float | None, Field(title='G-band magnitude')] = None - J: Annotated[float | None, Field(title='J-band magnitude')] = None - H: Annotated[ + r: Annotated[float | None, Field(alias='R', title='R-band magnitude')] = None + i: Annotated[float | None, Field(alias='I', title='I-band magnitude')] = None + g: Annotated[float | None, Field(alias='G', title='G-band magnitude')] = None + j: Annotated[float | None, Field(alias='J', title='J-band magnitude')] = None + h: Annotated[ float | None, Field( + alias='H', description='Required for targets observed with SPIRou.', title='H-band magnitude', ), ] = None - K: Annotated[float | None, Field(title='K-band magnitude')] = None + k: Annotated[float | None, Field(alias='K', title='K-band magnitude')] = None uu: Annotated[float | None, Field(title='u-band magnitude')] = None gg: Annotated[float | None, Field(title='g-band magnitude')] = None rr: Annotated[float | None, Field(title='r-band magnitude')] = None ii: Annotated[float | None, Field(title='i-band magnitude')] = None zz: Annotated[float | None, Field(title='z-band magnitude')] = None - AB: Annotated[ + ab: Annotated[ float | None, Field( + alias='AB', description='Required for targets observed with MegaCam.', title='AB magnitude', ), @@ -544,26 +596,26 @@ class TargetDataMagnitude(BaseModel): class TargetListRequestTargetType(Enum): - ALL = 'ALL' - FIXED = 'FIXED' - MOVING = 'MOVING' + all = 'ALL' + fixed = 'FIXED' + moving = 'MOVING' class TargetTimeDataView(Enum): - REQUESTED = 'REQUESTED' - ACTUAL = 'ACTUAL' + requested = 'REQUESTED' + actual = 'ACTUAL' class TimeAccountingDataTimeAccountingStatus(Enum): - NOT_STARTED = 'NOT_STARTED' - STARTED = 'STARTED' - OBSERVED = 'OBSERVED' - VALIDATED = 'VALIDATED' + not_started = 'NOT_STARTED' + started = 'STARTED' + observed = 'OBSERVED' + validated = 'VALIDATED' class UpenaProcessingResulth2oMm(BaseModel): - rawDensity: float | None = None - atZenith: float | None = None + raw_density: Annotated[float | None, Field(alias='rawDensity')] = None + at_zenith: Annotated[float | None, Field(alias='atZenith')] = None class HttpErrors(BaseModel): @@ -615,41 +667,52 @@ class PersistenceCatalogIdentifier(BaseModel): class PersistenceExposureTime(BaseModel): - desiredSnr: float | None = None - exposureTimeMs: str | None = None + desired_snr: Annotated[float | None, Field(alias='desiredSnr')] = None + exposure_time_ms: Annotated[str | None, Field(alias='exposureTimeMs')] = None class PersistenceFindingChartData(BaseModel): comment: str | None = None url: str | None = None token: str | None = None - staffApproval: FindingChartDataApproval | None = None - piApproval: FindingChartDataApproval | None = None - targetToken: str | None = None + staff_approval: Annotated[ + FindingChartDataApproval | None, Field(alias='staffApproval') + ] = None + pi_approval: Annotated[FindingChartDataApproval | None, Field(alias='piApproval')] = ( + None + ) + target_token: Annotated[str | None, Field(alias='targetToken')] = None class PersistenceITimeData(BaseModel): - exposureTimeMillis: Annotated[ + exposure_time_millis: Annotated[ str | None, - Field(description='Time spent collecting photons (in milliseconds).'), + Field( + alias='exposureTimeMillis', + description='Time spent collecting photons (in milliseconds).', + ), ] = None - overheadTimeMillis: Annotated[ + overhead_time_millis: Annotated[ str | None, Field( - description='All other times required for taking the exposure (in milliseconds).' + alias='overheadTimeMillis', + description='All other times required for taking the exposure (in milliseconds).', ), ] = None class PersistenceListOfIdentifiers(BaseModel): - catalogIdentifier: list[PersistenceCatalogIdentifier] | None = None + catalog_identifier: Annotated[ + list[PersistenceCatalogIdentifier] | None, Field(alias='catalogIdentifier') + ] = None class PersistenceObservingBlockCondition(BaseModel): - preRequisite: Annotated[ + pre_requisite: Annotated[ list[ObservingBlockConditionRequisite] | None, Field( - description='Pre-requisite conditions before each observing block can be started.\nThe index of items in this list corresponds directly to the index of the\nobserving block on the observing group i.e. this list is one-to-one with\nread_observing_block.' + alias='preRequisite', + description='Pre-requisite conditions before each observing block can be started.\nThe index of items in this list corresponds directly to the index of the\nobserving block on the observing group i.e. this list is one-to-one with\nread_observing_block.', ), ] = None window: Annotated[ @@ -661,11 +724,11 @@ class PersistenceObservingBlockCondition(BaseModel): class PersistenceObservingGroupPriority(Enum): - UNKNOWN_OG_PRIORITY = 'UNKNOWN_OG_PRIORITY' - HIGH = 'HIGH' - MEDIUM = 'MEDIUM' - LOW = 'LOW' - INACTIVE = 'INACTIVE' + unknown_og_priority = 'UNKNOWN_OG_PRIORITY' + high = 'HIGH' + medium = 'MEDIUM' + low = 'LOW' + inactive = 'INACTIVE' class PersistenceRollupValue(BaseModel): @@ -676,81 +739,112 @@ class PersistenceRollupValue(BaseModel): class PersistenceSingleObservingGroup(BaseModel): - observingBlock: ObservingGroupDataObservingBlock | None = None + observing_block: Annotated[ + ObservingGroupDataObservingBlock | None, Field(alias='observingBlock') + ] = None class PersistenceState(Enum): - UNOBSERVED = 'UNOBSERVED' - OBSERVED = 'OBSERVED' - PROCESSED = 'PROCESSED' - GRADED = 'GRADED' - VALIDATED = 'VALIDATED' - REJECTED = 'REJECTED' + unobserved = 'UNOBSERVED' + observed = 'OBSERVED' + processed = 'PROCESSED' + graded = 'GRADED' + validated = 'VALIDATED' + rejected = 'REJECTED' class PersistenceStokesParameter(Enum): - Q = 'Q' - U = 'U' - V = 'V' - W = 'W' - I = 'I' + q = 'Q' + u = 'U' + v = 'V' + w = 'W' + i = 'I' class PersistenceSubTotalsData(BaseModel): etime: PersistenceRollupValue | None = None overheadtime: PersistenceRollupValue | None = None - exposureCount: PersistenceRollupValue | None = None - childCount: PersistenceRollupValue | None = None - etimeConfigured: PersistenceRollupValue | None = None - overheadTimeConfigured: PersistenceRollupValue | None = None + exposure_count: Annotated[ + PersistenceRollupValue | None, Field(alias='exposureCount') + ] = None + child_count: Annotated[PersistenceRollupValue | None, Field(alias='childCount')] = ( + None + ) + etime_configured: Annotated[ + PersistenceRollupValue | None, Field(alias='etimeConfigured') + ] = None + overhead_time_configured: Annotated[ + PersistenceRollupValue | None, Field(alias='overheadTimeConfigured') + ] = None completeness: Annotated[ float | None, Field(description='Percentage completed for the entity.') ] = None class PersistenceTargetIdentifiers(BaseModel): - catalogIdentifier: Annotated[ + catalog_identifier: Annotated[ dict[str, PersistenceListOfIdentifiers] | None, Field( + alias='catalogIdentifier', description='key: catalog name, catalog_identifier.key === catalog_identifier.get(key).catalog\nvalue: catalog identifier.', title='All catalogs resolved by simbad for this target', ), ] = None - twoMass: PersistenceCatalogIdentifier | None = None + two_mass: Annotated[PersistenceCatalogIdentifier | None, Field(alias='twoMass')] = ( + None + ) gaia1: PersistenceCatalogIdentifier | None = None gaia2: PersistenceCatalogIdentifier | None = None - resolvedCatalogIdentifier: PersistenceCatalogIdentifier | None = None + resolved_catalog_identifier: Annotated[ + PersistenceCatalogIdentifier | None, Field(alias='resolvedCatalogIdentifier') + ] = None class PersistenceTargetTimeData(BaseModel): - requestedIntegrationTime: PersistenceITimeData | None = None - actualIntegrationTime: PersistenceITimeData | None = None - view: TargetTimeDataView | None = TargetTimeDataView.REQUESTED - exposureCount: Annotated[ + requested_integration_time: Annotated[ + PersistenceITimeData | None, Field(alias='requestedIntegrationTime') + ] = None + actual_integration_time: Annotated[ + PersistenceITimeData | None, Field(alias='actualIntegrationTime') + ] = None + view: TargetTimeDataView | None = TargetTimeDataView.requested + exposure_count: Annotated[ int | None, Field( - description='The total number of exposures represented by the values here.\n\nThis value is the same for both views.' + alias='exposureCount', + description='The total number of exposures represented by the values here.\n\nThis value is the same for both views.', ), ] = None - childCount: Annotated[ + child_count: Annotated[ int | None, Field( - description='The total number of child entities represented by the values here.\n\nIf this is a program, these are observing groups.\nIf this is an observing group, these are observing blocks.\nIf this is an observing block, these are exposures.\n\nUseful for e.g. seeing the number of iterations configuredor completed on an Observing Group.\nThis value is the same for both views.' + alias='childCount', + description='The total number of child entities represented by the values here.\n\nIf this is a program, these are observing groups.\nIf this is an observing group, these are observing blocks.\nIf this is an observing block, these are exposures.\n\nUseful for e.g. seeing the number of iterations configuredor completed on an Observing Group.\nThis value is the same for both views.', ), ] = None class PersistenceTimeAccountingData(BaseModel): - configuredTime: PersistenceTargetTimeData | None = None - chargedTime: PersistenceTargetTimeData | None = None - validatedTimeOnSky: PersistenceTargetTimeData | None = None - timeOnSky: PersistenceTargetTimeData | None = None - timeAccountingStatus: TimeAccountingDataTimeAccountingStatus | None = ( - TimeAccountingDataTimeAccountingStatus.NOT_STARTED + configured_time: Annotated[ + PersistenceTargetTimeData | None, Field(alias='configuredTime') + ] = None + charged_time: Annotated[ + PersistenceTargetTimeData | None, Field(alias='chargedTime') + ] = None + validated_time_on_sky: Annotated[ + PersistenceTargetTimeData | None, Field(alias='validatedTimeOnSky') + ] = None + time_on_sky: Annotated[PersistenceTargetTimeData | None, Field(alias='timeOnSky')] = ( + None ) - completionRatio: Annotated[ + time_accounting_status: Annotated[ + TimeAccountingDataTimeAccountingStatus | None, + Field(alias='timeAccountingStatus'), + ] = TimeAccountingDataTimeAccountingStatus.not_started + completion_ratio: Annotated[ float | None, Field( + alias='completionRatio', description='Entity completion ratio, representing how much of the time configured has been charged.\n\nEqual to total charged time (as requested) divided by total configured time. For programs, if the total configured time is greater than the time allocated to the program, we divide by the time allocated instead.\nNote that if a program has INACTIVE observing groups that already have charged time, this could result in an inflated value here.', le=1.0, ), @@ -758,7 +852,7 @@ class PersistenceTimeAccountingData(BaseModel): class ProtobufAny(BaseModel): - typeUrl: str | None = None + type_url: Annotated[str | None, Field(alias='typeUrl')] = None value: Annotated[ Base64Str | None, Field( @@ -783,63 +877,70 @@ class TypesDoubleMinMax(BaseModel): class TypesDurationUnit(Enum): - DAYS = 'DAYS' - HOURS = 'HOURS' - MINUTES = 'MINUTES' - SECONDS = 'SECONDS' + days = 'DAYS' + hours = 'HOURS' + minutes = 'MINUTES' + seconds = 'SECONDS' class TypesExposureType(Enum): - OBJECT = 'OBJECT' - TARGET = 'TARGET' - SKY = 'SKY' - DARK = 'DARK' - FLAT = 'FLAT' - SNAP = 'SNAP' - BIAS = 'BIAS' - FOCUS = 'FOCUS' - ALIGN = 'ALIGN' - COMPARISON = 'COMPARISON' - ACQUIRE = 'ACQUIRE' - FABRY_PEROT = 'FABRY_PEROT' - TWILIGHT_FLATS = 'TWILIGHT_FLATS' - PHOTOMETRY = 'PHOTOMETRY' - SFOCUS = 'SFOCUS' + object = 'OBJECT' + target = 'TARGET' + sky = 'SKY' + dark = 'DARK' + flat = 'FLAT' + snap = 'SNAP' + bias = 'BIAS' + focus = 'FOCUS' + align = 'ALIGN' + comparison = 'COMPARISON' + acquire = 'ACQUIRE' + fabry_perot = 'FABRY_PEROT' + twilight_flats = 'TWILIGHT_FLATS' + photometry = 'PHOTOMETRY' + sfocus = 'SFOCUS' class TypesInstrumentType(Enum): - UNKNOWN = 'UNKNOWN' - CFH12K = 'CFH12K' - MEGACAM = 'MEGACAM' - WIRCAM = 'WIRCAM' - ESPADONS = 'ESPADONS' - SITELLE = 'SITELLE' - SPIROU = 'SPIROU' - WENAOKEAO = 'WENAOKEAO' + unknown = 'UNKNOWN' + cfh12_k = 'CFH12K' + megacam = 'MEGACAM' + wircam = 'WIRCAM' + espadons = 'ESPADONS' + sitelle = 'SITELLE' + spirou = 'SPIROU' + wenaokeao = 'WENAOKEAO' class TypesOffsetCoordinate(BaseModel): - raOffset: Annotated[ + ra_offset: Annotated[ float | None, Field( + alias='raOffset', description='Offset in the Right Ascension axis in arcsec of degrees.', ge=-1296000.0, le=1296000.0, title='Right Ascension Offset', ), ] = None - decOffset: Annotated[ + dec_offset: Annotated[ float | None, Field( + alias='decOffset', description='Offset in the Declination axis in arcsec of degrees.', ge=-324000.0, le=324000.0, title='Declination Offset', ), ] = None - exposureNumber: Annotated[ + exposure_number: Annotated[ float | None, - Field(description='Exposure number', le=99.0, title='Exposure number'), + Field( + alias='exposureNumber', + description='Exposure number', + le=99.0, + title='Exposure number', + ), ] = None @@ -862,69 +963,103 @@ class TypesSkyCoordinate(BaseModel): class ConfigNodding(BaseModel): - startSequence: TypesExposureType | None = TypesExposureType.OBJECT - targetDb: float | None = None + start_sequence: Annotated[ + TypesExposureType | None, Field(alias='startSequence') + ] = TypesExposureType.object + target_db: Annotated[float | None, Field(alias='targetDb')] = None class EspadonsStatusUpenaProcessingResult(BaseModel): iq: float | None = None - h2oColumnDensityFromTelluricLines: float | None = None - radialVelocityCorrections: float | None = None - snrPerCcdBin: float | None = None - snrPerSpecBin: float | None = None - actualWavelength: float | None = None - snrPerCcdBinPolar: float | None = None - snrPerSpecBinPolar: float | None = None - h2oMmData: UpenaProcessingResulth2oMm | None = None + h2o_column_density_from_telluric_lines: Annotated[ + float | None, Field(alias='h2oColumnDensityFromTelluricLines') + ] = None + radial_velocity_corrections: Annotated[ + float | None, Field(alias='radialVelocityCorrections') + ] = None + snr_per_ccd_bin: Annotated[float | None, Field(alias='snrPerCcdBin')] = None + snr_per_spec_bin: Annotated[float | None, Field(alias='snrPerSpecBin')] = None + actual_wavelength: Annotated[float | None, Field(alias='actualWavelength')] = None + snr_per_ccd_bin_polar: Annotated[float | None, Field(alias='snrPerCcdBinPolar')] = ( + None + ) + snr_per_spec_bin_polar: Annotated[float | None, Field(alias='snrPerSpecBinPolar')] = ( + None + ) + h2o_mm_data: Annotated[UpenaProcessingResulth2oMm | None, Field(alias='h2oMmData')] = ( + None + ) class ExposureDataEspadonsStatus(BaseModel): - instrMode: str | None = None - actualWavelength: float | None = None - snrExpMeter: float | None = None - snrWaveMeasInt: float | None = None - snrWaveMeasPol: float | None = None - snrMeasIntPerCcdBin: float | None = None - snrMeasIntPerSpecBin: float | None = None - snrMeasPolarCcdBin: float | None = None - snrMeasPolarSpecBin: float | None = None - trgType: str | None = None - guiderSeeing: float | None = None - guiderSeeingProcessingResults: ( - EspadonsStatusGuiderSeeingProcessingResult | None - ) = None - exposureMeterProcessingResults: ( - ExposureDataEspadonsStatusExposureMeterProcessingResult | None - ) = None - upenaProcessingResults: EspadonsStatusUpenaProcessingResult | None = None - observationType: str | None = None + instr_mode: Annotated[str | None, Field(alias='instrMode')] = None + actual_wavelength: Annotated[float | None, Field(alias='actualWavelength')] = None + snr_exp_meter: Annotated[float | None, Field(alias='snrExpMeter')] = None + snr_wave_meas_int: Annotated[float | None, Field(alias='snrWaveMeasInt')] = None + snr_wave_meas_pol: Annotated[float | None, Field(alias='snrWaveMeasPol')] = None + snr_meas_int_per_ccd_bin: Annotated[ + float | None, Field(alias='snrMeasIntPerCcdBin') + ] = None + snr_meas_int_per_spec_bin: Annotated[ + float | None, Field(alias='snrMeasIntPerSpecBin') + ] = None + snr_meas_polar_ccd_bin: Annotated[float | None, Field(alias='snrMeasPolarCcdBin')] = ( + None + ) + snr_meas_polar_spec_bin: Annotated[float | None, Field(alias='snrMeasPolarSpecBin')] = ( + None + ) + trg_type: Annotated[str | None, Field(alias='trgType')] = None + guider_seeing: Annotated[float | None, Field(alias='guiderSeeing')] = None + guider_seeing_processing_results: Annotated[ + EspadonsStatusGuiderSeeingProcessingResult | None, + Field(alias='guiderSeeingProcessingResults'), + ] = None + exposure_meter_processing_results: Annotated[ + ExposureDataEspadonsStatusExposureMeterProcessingResult | None, + Field(alias='exposureMeterProcessingResults'), + ] = None + upena_processing_results: Annotated[ + EspadonsStatusUpenaProcessingResult | None, + Field(alias='upenaProcessingResults'), + ] = None + observation_type: Annotated[str | None, Field(alias='observationType')] = None class ExposureDataMegacamStatus(BaseModel): iq: float | None = None skybg: float | None = None - elixirEval: int | None = None - elixirSeval: str | None = None + elixir_eval: Annotated[int | None, Field(alias='elixirEval')] = None + elixir_seval: Annotated[str | None, Field(alias='elixirSeval')] = None snr: float | None = None - atmosphericTransmission: float | None = None - elixirZeropoint: float | None = None - actualFilter: str | None = None - elixirProcessingResult: MegacamStatusElixirProcessingResult | None = None + atmospheric_transmission: Annotated[ + float | None, Field(alias='atmosphericTransmission') + ] = None + elixir_zeropoint: Annotated[float | None, Field(alias='elixirZeropoint')] = None + actual_filter: Annotated[str | None, Field(alias='actualFilter')] = None + elixir_processing_result: Annotated[ + MegacamStatusElixirProcessingResult | None, + Field(alias='elixirProcessingResult'), + ] = None class ExposureDataSitelleStatus(BaseModel): - cameraA: SitelleStatusCamera | None = None - cameraB: SitelleStatusCamera | None = None - relativeExtinction: float | None = None - relativeExtinctionRms: float | None = None - actualFilter: str | None = None + camera_a: Annotated[SitelleStatusCamera | None, Field(alias='cameraA')] = None + camera_b: Annotated[SitelleStatusCamera | None, Field(alias='cameraB')] = None + relative_extinction: Annotated[float | None, Field(alias='relativeExtinction')] = ( + None + ) + relative_extinction_rms: Annotated[ + float | None, Field(alias='relativeExtinctionRms') + ] = None + actual_filter: Annotated[str | None, Field(alias='actualFilter')] = None class ExposureDataSpirouStatus(BaseModel): acqtime: float | None = None acqtime1: str | None = None - asicNum: str | None = None - scaId: str | None = None + asic_num: Annotated[str | None, Field(alias='asicNum')] = None + sca_id: Annotated[str | None, Field(alias='scaId')] = None muxtype: int | None = None noutputs: int | None = None nadcs: int | None = None @@ -955,62 +1090,62 @@ class ExposureDataSpirouStatus(BaseModel): mclk: float | None = None inttime: float | None = None frameno: int | None = None - sbAdcS: str | None = None - sbadc1P: float | None = None - sbadc2P: float | None = None - sbcaliP: str | None = None - sbdensP: float | None = None - sbrhb1P: str | None = None - sbrhb2P: str | None = None - sbetemP: str | None = None - sbInjT: float | None = None - sbPolT: float | None = None - sbCs1T: float | None = None - sbCs2T: float | None = None - sbCb1T: float | None = None - sbCb2T: float | None = None + sb_adc_s: Annotated[str | None, Field(alias='sbAdcS')] = None + sbadc1_p: Annotated[float | None, Field(alias='sbadc1P')] = None + sbadc2_p: Annotated[float | None, Field(alias='sbadc2P')] = None + sbcali_p: Annotated[str | None, Field(alias='sbcaliP')] = None + sbdens_p: Annotated[float | None, Field(alias='sbdensP')] = None + sbrhb1_p: Annotated[str | None, Field(alias='sbrhb1P')] = None + sbrhb2_p: Annotated[str | None, Field(alias='sbrhb2P')] = None + sbetem_p: Annotated[str | None, Field(alias='sbetemP')] = None + sb_inj_t: Annotated[float | None, Field(alias='sbInjT')] = None + sb_pol_t: Annotated[float | None, Field(alias='sbPolT')] = None + sb_cs1_t: Annotated[float | None, Field(alias='sbCs1T')] = None + sb_cs2_t: Annotated[float | None, Field(alias='sbCs2T')] = None + sb_cb1_t: Annotated[float | None, Field(alias='sbCb1T')] = None + sb_cb2_t: Annotated[float | None, Field(alias='sbCb2T')] = None sbiavl1s: str | None = None sbiavl2s: str | None = None - sbetelS: str | None = None - sbCscS: str | None = None - sbcrefP: str | None = None - sbccasP: str | None = None - sbcdenP: float | None = None - sbagitS: str | None = None - sbcmirP: str | None = None + sbetel_s: Annotated[str | None, Field(alias='sbetelS')] = None + sb_csc_s: Annotated[str | None, Field(alias='sbCscS')] = None + sbcref_p: Annotated[str | None, Field(alias='sbcrefP')] = None + sbccas_p: Annotated[str | None, Field(alias='sbccasP')] = None + sbcden_p: Annotated[float | None, Field(alias='sbcdenP')] = None + sbagit_s: Annotated[str | None, Field(alias='sbagitS')] = None + sbcmir_p: Annotated[str | None, Field(alias='sbcmirP')] = None sbclhc1s: str | None = None sbclhc1w: str | None = None sbclhc1c: float | None = None sbclhc2s: str | None = None sbclhc2w: str | None = None sbclhc2c: float | None = None - sbclwlS: str | None = None - sbclwlC: float | None = None - sbwlshS: str | None = None - sbctecS: str | None = None + sbclwl_s: Annotated[str | None, Field(alias='sbclwlS')] = None + sbclwl_c: Annotated[float | None, Field(alias='sbclwlC')] = None + sbwlsh_s: Annotated[str | None, Field(alias='sbwlshS')] = None + sbctec_s: Annotated[str | None, Field(alias='sbctecS')] = None sbctecct: float | None = None sbctecht: float | None = None sbccprit: float | None = None - sbcccuT: float | None = None + sbcccu_t: Annotated[float | None, Field(alias='sbcccuT')] = None sbccprot: float | None = None - sbcfbiT: float | None = None - sbcwlsT: float | None = None + sbcfbi_t: Annotated[float | None, Field(alias='sbcfbiT')] = None + sbcwls_t: Annotated[float | None, Field(alias='sbcwlsT')] = None sbclsuat: float | None = None - sbcfpiT: float | None = None - sbcfpeT: float | None = None - sbcfpbP: float | None = None - sbvgaCp: float | None = None - sbvgaIp: float | None = None - sbvgbCp: float | None = None - sbvgbIp: float | None = None - sbmk00T: float | None = None - sbmk01T: float | None = None - sbmk02T: float | None = None - sbmk03T: float | None = None - sbmk04T: float | None = None - sbmk05T: float | None = None - sbmk06T: float | None = None - sbmk07T: float | None = None + sbcfpi_t: Annotated[float | None, Field(alias='sbcfpiT')] = None + sbcfpe_t: Annotated[float | None, Field(alias='sbcfpeT')] = None + sbcfpb_p: Annotated[float | None, Field(alias='sbcfpbP')] = None + sbvga_cp: Annotated[float | None, Field(alias='sbvgaCp')] = None + sbvga_ip: Annotated[float | None, Field(alias='sbvgaIp')] = None + sbvgb_cp: Annotated[float | None, Field(alias='sbvgbCp')] = None + sbvgb_ip: Annotated[float | None, Field(alias='sbvgbIp')] = None + sbmk00_t: Annotated[float | None, Field(alias='sbmk00T')] = None + sbmk01_t: Annotated[float | None, Field(alias='sbmk01T')] = None + sbmk02_t: Annotated[float | None, Field(alias='sbmk02T')] = None + sbmk03_t: Annotated[float | None, Field(alias='sbmk03T')] = None + sbmk04_t: Annotated[float | None, Field(alias='sbmk04T')] = None + sbmk05_t: Annotated[float | None, Field(alias='sbmk05T')] = None + sbmk06_t: Annotated[float | None, Field(alias='sbmk06T')] = None + sbmk07_t: Annotated[float | None, Field(alias='sbmk07T')] = None sbls000t: float | None = None sbls001t: float | None = None sbls002t: float | None = None @@ -1035,70 +1170,70 @@ class ExposureDataSpirouStatus(BaseModel): sbls109t: float | None = None sbls110t: float | None = None sbls111t: float | None = None - sbl00iT: float | None = None - sbl00hV: float | None = None - sbl01iT: float | None = None - sbl01hV: float | None = None - sbl02iT: float | None = None - sbl02hV: float | None = None - sbl03iT: float | None = None - sbl03hV: float | None = None - sbl04iT: float | None = None - sbl04hV: float | None = None - sbl05iT: float | None = None - sbl05hV: float | None = None - sbl06iT: float | None = None - sbl06hV: float | None = None - sbl07iT: float | None = None - sbl07hV: float | None = None - sbl08iT: float | None = None - sbl08hV: float | None = None - sbl09iT: float | None = None - sbl09hV: float | None = None - sbl10iT: float | None = None - sbl10hV: float | None = None - sbl11iT: float | None = None - sbl11hV: float | None = None - sbl12iT: float | None = None - sbl12hV: float | None = None - sbl13iT: float | None = None - sbl13hV: float | None = None - seadc1P: float | None = None - seadc2P: float | None = None + sbl00i_t: Annotated[float | None, Field(alias='sbl00iT')] = None + sbl00h_v: Annotated[float | None, Field(alias='sbl00hV')] = None + sbl01i_t: Annotated[float | None, Field(alias='sbl01iT')] = None + sbl01h_v: Annotated[float | None, Field(alias='sbl01hV')] = None + sbl02i_t: Annotated[float | None, Field(alias='sbl02iT')] = None + sbl02h_v: Annotated[float | None, Field(alias='sbl02hV')] = None + sbl03i_t: Annotated[float | None, Field(alias='sbl03iT')] = None + sbl03h_v: Annotated[float | None, Field(alias='sbl03hV')] = None + sbl04i_t: Annotated[float | None, Field(alias='sbl04iT')] = None + sbl04h_v: Annotated[float | None, Field(alias='sbl04hV')] = None + sbl05i_t: Annotated[float | None, Field(alias='sbl05iT')] = None + sbl05h_v: Annotated[float | None, Field(alias='sbl05hV')] = None + sbl06i_t: Annotated[float | None, Field(alias='sbl06iT')] = None + sbl06h_v: Annotated[float | None, Field(alias='sbl06hV')] = None + sbl07i_t: Annotated[float | None, Field(alias='sbl07iT')] = None + sbl07h_v: Annotated[float | None, Field(alias='sbl07hV')] = None + sbl08i_t: Annotated[float | None, Field(alias='sbl08iT')] = None + sbl08h_v: Annotated[float | None, Field(alias='sbl08hV')] = None + sbl09i_t: Annotated[float | None, Field(alias='sbl09iT')] = None + sbl09h_v: Annotated[float | None, Field(alias='sbl09hV')] = None + sbl10i_t: Annotated[float | None, Field(alias='sbl10iT')] = None + sbl10h_v: Annotated[float | None, Field(alias='sbl10hV')] = None + sbl11i_t: Annotated[float | None, Field(alias='sbl11iT')] = None + sbl11h_v: Annotated[float | None, Field(alias='sbl11hV')] = None + sbl12i_t: Annotated[float | None, Field(alias='sbl12iT')] = None + sbl12h_v: Annotated[float | None, Field(alias='sbl12hV')] = None + sbl13i_t: Annotated[float | None, Field(alias='sbl13iT')] = None + sbl13h_v: Annotated[float | None, Field(alias='sbl13hV')] = None + seadc1_p: Annotated[float | None, Field(alias='seadc1P')] = None + seadc2_p: Annotated[float | None, Field(alias='seadc2P')] = None sedens_p: int | None = None sedens_p_float: float | None = None - seInjT: float | None = None - sePolT: float | None = None - seCs1T: float | None = None - seCs2T: float | None = None - seCb1T: float | None = None - seCb2T: float | None = None + se_inj_t: Annotated[float | None, Field(alias='seInjT')] = None + se_pol_t: Annotated[float | None, Field(alias='sePolT')] = None + se_cs1_t: Annotated[float | None, Field(alias='seCs1T')] = None + se_cs2_t: Annotated[float | None, Field(alias='seCs2T')] = None + se_cb1_t: Annotated[float | None, Field(alias='seCb1T')] = None + se_cb2_t: Annotated[float | None, Field(alias='seCb2T')] = None sectecct: float | None = None sectecht: float | None = None seccprit: float | None = None - secccuT: float | None = None + secccu_t: Annotated[float | None, Field(alias='secccuT')] = None seccprot: float | None = None - secfbiT: float | None = None - secwlsT: float | None = None + secfbi_t: Annotated[float | None, Field(alias='secfbiT')] = None + secwls_t: Annotated[float | None, Field(alias='secwlsT')] = None seclhc1c: float | None = None seclhc2c: float | None = None - seclwlC: float | None = None + seclwl_c: Annotated[float | None, Field(alias='seclwlC')] = None seclsuat: float | None = None - secfpiT: float | None = None - secfpeT: float | None = None - secfpbP: float | None = None - sevgaCp: float | None = None - sevgaIp: float | None = None - sevgbCp: float | None = None - sevgbIp: float | None = None - semk00T: float | None = None - semk01T: float | None = None - semk02T: float | None = None - semk03T: float | None = None - semk04T: float | None = None - semk05T: float | None = None - semk06T: float | None = None - semk07T: float | None = None + secfpi_t: Annotated[float | None, Field(alias='secfpiT')] = None + secfpe_t: Annotated[float | None, Field(alias='secfpeT')] = None + secfpb_p: Annotated[float | None, Field(alias='secfpbP')] = None + sevga_cp: Annotated[float | None, Field(alias='sevgaCp')] = None + sevga_ip: Annotated[float | None, Field(alias='sevgaIp')] = None + sevgb_cp: Annotated[float | None, Field(alias='sevgbCp')] = None + sevgb_ip: Annotated[float | None, Field(alias='sevgbIp')] = None + semk00_t: Annotated[float | None, Field(alias='semk00T')] = None + semk01_t: Annotated[float | None, Field(alias='semk01T')] = None + semk02_t: Annotated[float | None, Field(alias='semk02T')] = None + semk03_t: Annotated[float | None, Field(alias='semk03T')] = None + semk04_t: Annotated[float | None, Field(alias='semk04T')] = None + semk05_t: Annotated[float | None, Field(alias='semk05T')] = None + semk06_t: Annotated[float | None, Field(alias='semk06T')] = None + semk07_t: Annotated[float | None, Field(alias='semk07T')] = None sels000t: float | None = None sels001t: float | None = None sels002t: float | None = None @@ -1123,51 +1258,61 @@ class ExposureDataSpirouStatus(BaseModel): sels109t: float | None = None sels110t: float | None = None sels111t: float | None = None - sel00iT: float | None = None - sel00hV: float | None = None - sel01iT: float | None = None - sel01hV: float | None = None - sel02iT: float | None = None - sel02hV: float | None = None - sel03iT: float | None = None - sel03hV: float | None = None - sel04iT: float | None = None - sel04hV: float | None = None - sel05iT: float | None = None - sel05hV: float | None = None - sel06iT: float | None = None - sel06hV: float | None = None - sel07iT: float | None = None - sel07hV: float | None = None - sel08iT: float | None = None - sel08hV: float | None = None - sel09iT: float | None = None - sel09hV: float | None = None - sel10iT: float | None = None - sel10hV: float | None = None - sel11iT: float | None = None - sel11hV: float | None = None - sel12iT: float | None = None - sel12hV: float | None = None - sel13iT: float | None = None - sel13hV: float | None = None - guiderProcessing: SpirouStatusGuiderProcessingResult | None = None - exposureMeterProcessing: ( - ExposureDataSpirouStatusExposureMeterProcessingResult | None - ) = None - pipelineProcessing: SpirouStatusPipelineProcessingResult | None = None + sel00i_t: Annotated[float | None, Field(alias='sel00iT')] = None + sel00h_v: Annotated[float | None, Field(alias='sel00hV')] = None + sel01i_t: Annotated[float | None, Field(alias='sel01iT')] = None + sel01h_v: Annotated[float | None, Field(alias='sel01hV')] = None + sel02i_t: Annotated[float | None, Field(alias='sel02iT')] = None + sel02h_v: Annotated[float | None, Field(alias='sel02hV')] = None + sel03i_t: Annotated[float | None, Field(alias='sel03iT')] = None + sel03h_v: Annotated[float | None, Field(alias='sel03hV')] = None + sel04i_t: Annotated[float | None, Field(alias='sel04iT')] = None + sel04h_v: Annotated[float | None, Field(alias='sel04hV')] = None + sel05i_t: Annotated[float | None, Field(alias='sel05iT')] = None + sel05h_v: Annotated[float | None, Field(alias='sel05hV')] = None + sel06i_t: Annotated[float | None, Field(alias='sel06iT')] = None + sel06h_v: Annotated[float | None, Field(alias='sel06hV')] = None + sel07i_t: Annotated[float | None, Field(alias='sel07iT')] = None + sel07h_v: Annotated[float | None, Field(alias='sel07hV')] = None + sel08i_t: Annotated[float | None, Field(alias='sel08iT')] = None + sel08h_v: Annotated[float | None, Field(alias='sel08hV')] = None + sel09i_t: Annotated[float | None, Field(alias='sel09iT')] = None + sel09h_v: Annotated[float | None, Field(alias='sel09hV')] = None + sel10i_t: Annotated[float | None, Field(alias='sel10iT')] = None + sel10h_v: Annotated[float | None, Field(alias='sel10hV')] = None + sel11i_t: Annotated[float | None, Field(alias='sel11iT')] = None + sel11h_v: Annotated[float | None, Field(alias='sel11hV')] = None + sel12i_t: Annotated[float | None, Field(alias='sel12iT')] = None + sel12h_v: Annotated[float | None, Field(alias='sel12hV')] = None + sel13i_t: Annotated[float | None, Field(alias='sel13iT')] = None + sel13h_v: Annotated[float | None, Field(alias='sel13hV')] = None + guider_processing: Annotated[ + SpirouStatusGuiderProcessingResult | None, Field(alias='guiderProcessing') + ] = None + exposure_meter_processing: Annotated[ + ExposureDataSpirouStatusExposureMeterProcessingResult | None, + Field(alias='exposureMeterProcessing'), + ] = None + pipeline_processing: Annotated[ + SpirouStatusPipelineProcessingResult | None, Field(alias='pipelineProcessing') + ] = None class InstrumentConfigurationDataEspadonsConfigurationData(BaseModel): - observingMode: ( - InstrumentConfigurationDataEspadonsConfigurationDataOObservingMode | None - ) = InstrumentConfigurationDataEspadonsConfigurationDataOObservingMode.Polarimetry - readoutMode: RReadoutMode | None = RReadoutMode.Normal - stokesParameter: PersistenceStokesParameter | None = PersistenceStokesParameter.Q - numberOfSequences: int | None = None - numberOfExposures: int | None = None + observing_mode: Annotated[ + InstrumentConfigurationDataEspadonsConfigurationDataOObservingMode | None, + Field(alias='observingMode'), + ] = InstrumentConfigurationDataEspadonsConfigurationDataOObservingMode.polarimetry + readout_mode: Annotated[RReadoutMode | None, Field(alias='readoutMode')] = ( + RReadoutMode.normal + ) + stokes_parameter: Annotated[ + PersistenceStokesParameter | None, Field(alias='stokesParameter') + ] = PersistenceStokesParameter.q + number_of_sequences: Annotated[int | None, Field(alias='numberOfSequences')] = None + number_of_exposures: Annotated[int | None, Field(alias='numberOfExposures')] = None needs_snr: bool | None = None - snrWave: float | None = None + snr_wave: Annotated[float | None, Field(alias='snrWave')] = None snr: float | None = None @@ -1178,7 +1323,7 @@ class InstrumentConfigurationDataMegacamConfigurationDataPhotometry(BaseModel): class InstrumentConfigurationDataSitelleConfigurationData(BaseModel): filter: InstrumentConfigurationDataSitelleConfigurationDataFilter | None = ( - InstrumentConfigurationDataSitelleConfigurationDataFilter.UNKNOWN + InstrumentConfigurationDataSitelleConfigurationDataFilter.unknown ) binning: InstrumentConfigurationDataSitelleConfigurationDataBinning | None = ( InstrumentConfigurationDataSitelleConfigurationDataBinning.one_one @@ -1189,49 +1334,64 @@ class InstrumentConfigurationDataSitelleConfigurationData(BaseModel): Field(title='This should be on target too , sitell, espedons, spirou'), ] = None calibration: SitelleConfigurationDataCalibration | None = None - resolutionMode: SitelleConfigurationDataResolutionMode | None = ( - SitelleConfigurationDataResolutionMode.no_resolution_mode + resolution_mode: Annotated[ + SitelleConfigurationDataResolutionMode | None, Field(alias='resolutionMode') + ] = SitelleConfigurationDataResolutionMode.no_resolution_mode + needs_precision: Annotated[bool | None, Field(alias='needsPrecision')] = None + needs_flux_calibration: Annotated[bool | None, Field(alias='needsFluxCalibration')] = ( + None ) - needsPrecision: bool | None = None - needsFluxCalibration: bool | None = None - needsTargetCalibration: bool | None = None + needs_target_calibration: Annotated[ + bool | None, Field(alias='needsTargetCalibration') + ] = None class InstrumentConfigurationDataSpirouConfigurationData(BaseModel): - templateName: Annotated[str | None, Field(title='Deprecated fields')] = None - observingMode: ( - InstrumentConfigurationDataSpirouConfigurationDataOObservingMode | None - ) = InstrumentConfigurationDataSpirouConfigurationDataOObservingMode.Polarimetry - stokesParameter: PersistenceStokesParameter | None = PersistenceStokesParameter.Q - mode: MMode | None = MMode.Dark - skyObservation: SpirouConfigurationDataSkyObservation | None = ( - SpirouConfigurationDataSkyObservation.None_ - ) - numberOfSequences: Annotated[ + template_name: Annotated[ + str | None, Field(alias='templateName', title='Deprecated fields') + ] = None + observing_mode: Annotated[ + InstrumentConfigurationDataSpirouConfigurationDataOObservingMode | None, + Field(alias='observingMode'), + ] = InstrumentConfigurationDataSpirouConfigurationDataOObservingMode.polarimetry + stokes_parameter: Annotated[ + PersistenceStokesParameter | None, Field(alias='stokesParameter') + ] = PersistenceStokesParameter.q + mode: MMode | None = MMode.dark + sky_observation: Annotated[ + SpirouConfigurationDataSkyObservation | None, Field(alias='skyObservation') + ] = SpirouConfigurationDataSkyObservation.none + number_of_sequences: Annotated[ int | None, Field( - title='Each sequence is 1 exposure for Star mode and 4 exposures for ObservingMode' + alias='numberOfSequences', + title='Each sequence is 1 exposure for Star mode and 4 exposures for ObservingMode', ), ] = None - useSnr: Annotated[ - bool | None, Field(title='when true - use SNR for stoping exposures') + use_snr: Annotated[ + bool | None, + Field(alias='useSnr', title='when true - use SNR for stoping exposures'), ] = None - skyEtimeMillis: Annotated[ - str | None, Field(title='Used to override the etime for the sky_observation') + sky_etime_millis: Annotated[ + str | None, + Field( + alias='skyEtimeMillis', + title='Used to override the etime for the sky_observation', + ), ] = None class InstrumentConfigurationDataWircamConfigurationData(BaseModel): filter: InstrumentConfigurationDataWircamConfigurationDataFilter | None = ( - InstrumentConfigurationDataWircamConfigurationDataFilter.UNKNOWN + InstrumentConfigurationDataWircamConfigurationDataFilter.unknown ) - isMicrodithering: bool | None = None - exposuresPerDpPosition: Annotated[int | None, Field(title='Must always be > 0')] = ( - None - ) - accuratePointing: bool | None = None + is_microdithering: Annotated[bool | None, Field(alias='isMicrodithering')] = None + exposures_per_dp_position: Annotated[ + int | None, Field(alias='exposuresPerDpPosition', title='Must always be > 0') + ] = None + accurate_pointing: Annotated[bool | None, Field(alias='accuratePointing')] = None defocus: float | None = None - isStaring: bool | None = None + is_staring: Annotated[bool | None, Field(alias='isStaring')] = None class MovingTargetEphemeris(BaseModel): @@ -1242,36 +1402,47 @@ class MovingTargetEphemeris(BaseModel): ), ] = None coordinate: TypesSkyCoordinate | None = None - computedCoordinate: TypesSkyCoordinate | None = None + computed_coordinate: Annotated[ + TypesSkyCoordinate | None, Field(alias='computedCoordinate') + ] = None class TargetDataFixedTarget(BaseModel): coordinate: TypesSkyCoordinate | None = None - properMotion: FixedTargetProperMotion | None = None - computedCoordinate: TypesSkyCoordinate | None = None - estimatedRadialVelocityKmps: Annotated[ + proper_motion: Annotated[ + FixedTargetProperMotion | None, Field(alias='properMotion') + ] = None + computed_coordinate: Annotated[ + TypesSkyCoordinate | None, Field(alias='computedCoordinate') + ] = None + estimated_radial_velocity_kmps: Annotated[ float | None, Field( - description='Expected radial velocity of the target in kilometers/second.\n\nUsed for targets observed with SPIRou.' + alias='estimatedRadialVelocityKmps', + description='Expected radial velocity of the target in kilometers/second.\n\nUsed for targets observed with SPIRou.', ), ] = None class TargetDataLinkedTargetIdentifiers(BaseModel): - piConfirmedIdentifiers: PersistenceTargetIdentifiers | None = None - customTarget: Annotated[ + pi_confirmed_identifiers: Annotated[ + PersistenceTargetIdentifiers | None, Field(alias='piConfirmedIdentifiers') + ] = None + custom_target: Annotated[ bool | None, Field( - description='Used to indicate this is a target without known identifiers.' + alias='customTarget', + description='Used to indicate this is a target without known identifiers.', ), ] = None class TargetDataMovingTarget(BaseModel): - ephemerisPoint: Annotated[ + ephemeris_point: Annotated[ list[MovingTargetEphemeris] | None, Field( - description="The ephemerides that define the path of motion for the target.\n\nMust be provided using topocentric astrometric coordinates.\nDo not provide apparent coordinates.\nCFHT strongly recommends the use of JPL Horizons System for\ngenerating the proper coordinates. CFHT's observatory code is T14.\n\nAt least five distinct positions must be provided that span the\ndesired observing period. The first position should be well in\nadvance of the observation. The positions do not need to be\nuniformly sampled throughout the desired observing period.\n\nThe points will be interpolated to determine the position of the\ntarget at the time of observation, at which point there must be\nat least one ephemeris point defined in the past, and at least\nfour defined in the future." + alias='ephemerisPoint', + description="The ephemerides that define the path of motion for the target.\n\nMust be provided using topocentric astrometric coordinates.\nDo not provide apparent coordinates.\nCFHT strongly recommends the use of JPL Horizons System for\ngenerating the proper coordinates. CFHT's observatory code is T14.\n\nAt least five distinct positions must be provided that span the\ndesired observing period. The first position should be well in\nadvance of the observation. The positions do not need to be\nuniformly sampled throughout the desired observing period.\n\nThe points will be interpolated to determine the position of the\ntarget at the time of observation, at which point there must be\nat least one ephemeris point defined in the past, and at least\nfour defined in the future.", ), ] = None @@ -1279,55 +1450,71 @@ class TargetDataMovingTarget(BaseModel): class TelescopePatternDataConfig(BaseModel): name: str | None = None description: str | None = None - chargeModel: Annotated[ - str | None, Field(title='Used for calculating cost for this pattern') + charge_model: Annotated[ + str | None, + Field(alias='chargeModel', title='Used for calculating cost for this pattern'), ] = None dithering: ConfigDithering | None = None nodding: ConfigNodding | None = None scale: float | None = None - sybasePatId: TypesBigInteger | None = None - isSystem: bool | None = None + sybase_pat_id: Annotated[TypesBigInteger | None, Field(alias='sybasePatId')] = None + is_system: Annotated[bool | None, Field(alias='isSystem')] = None class TelescopePatternDataOffset(BaseModel): - exposureNumber: Annotated[ + exposure_number: Annotated[ int | None, Field( - description='TODO: deprecate, no need to have duplicate information here.' + alias='exposureNumber', + description='TODO: deprecate, no need to have duplicate information here.', ), ] = None offset: TypesOffsetCoordinate | None = None - type: TypesExposureType | None = TypesExposureType.OBJECT + type: TypesExposureType | None = TypesExposureType.object class ObservingRollupFields(BaseModel): - iTime: PersistenceRollupValue | None = None - iTimeConfigured: PersistenceRollupValue | None = None - subTotals: PersistenceSubTotalsData | None = None + i_time: Annotated[PersistenceRollupValue | None, Field(alias='iTime')] = None + i_time_configured: Annotated[ + PersistenceRollupValue | None, Field(alias='iTimeConfigured') + ] = None + sub_totals: Annotated[PersistenceSubTotalsData | None, Field(alias='subTotals')] = ( + None + ) class PersistenceAllocationData(BaseModel): - runId: str | None = None - timeAllocatedMillis: str | None = None - tacRank: int | None = None - tacGrade: str | None = None - instrument: TypesInstrumentType | None = TypesInstrumentType.UNKNOWN + run_id: Annotated[str | None, Field(alias='runId')] = None + time_allocated_millis: Annotated[str | None, Field(alias='timeAllocatedMillis')] = ( + None + ) + tac_rank: Annotated[int | None, Field(alias='tacRank')] = None + tac_grade: Annotated[str | None, Field(alias='tacGrade')] = None + instrument: TypesInstrumentType | None = TypesInstrumentType.unknown agency: AllocationDataAgency | None = None - cfhtRank: int | None = None + cfht_rank: Annotated[int | None, Field(alias='cfhtRank')] = None class PersistenceConstraint(BaseModel): - imageQuality: TypesDoubleMinMax | None = None - skyBackgroundMax: float | None = None - airmassMax: float | None = None - extinctionMax: float | None = None - moonDistanceArcLengthMin: float | None = None + image_quality: Annotated[TypesDoubleMinMax | None, Field(alias='imageQuality')] = ( + None + ) + sky_background_max: Annotated[float | None, Field(alias='skyBackgroundMax')] = None + airmass_max: Annotated[float | None, Field(alias='airmassMax')] = None + extinction_max: Annotated[float | None, Field(alias='extinctionMax')] = None + moon_distance_arc_length_min: Annotated[ + float | None, Field(alias='moonDistanceArcLengthMin') + ] = None photometric: bool | None = None name: str | None = None - sybaseConsId: TypesBigInteger | None = None - h2oVaporMax: float | None = None - readSkyBackgroundName: str | None = None - skyBackgroundName: str | None = None + sybase_cons_id: Annotated[TypesBigInteger | None, Field(alias='sybaseConsId')] = ( + None + ) + h2o_vapor_max: Annotated[float | None, Field(alias='h2oVaporMax')] = None + read_sky_background_name: Annotated[ + str | None, Field(alias='readSkyBackgroundName') + ] = None + sky_background_name: Annotated[str | None, Field(alias='skyBackgroundName')] = None class PersistencePointingOffsetData(BaseModel): @@ -1336,38 +1523,55 @@ class PersistencePointingOffsetData(BaseModel): offset: TypesOffsetCoordinate | None = None label: int | None = None version: int | None = None - userToken: Annotated[ + user_token: Annotated[ str | None, Field( - title='this is the PI of the program that this entity will be recorded for\nif null should be derived using the program_data.contact_info.user_token\nif null, then a system ?? gross' + alias='userToken', + title='this is the PI of the program that this entity will be recorded for\nif null should be derived using the program_data.contact_info.user_token\nif null, then a system ?? gross', ), ] = None - instrument: TypesInstrumentType | None = TypesInstrumentType.UNKNOWN - isSystem: bool | None = None + instrument: TypesInstrumentType | None = TypesInstrumentType.unknown + is_system: Annotated[bool | None, Field(alias='isSystem')] = None class PersistenceProgramData(BaseModel): title: str | None = None abstract: str | None = None - contactInfo: ProgramDataContactInfo | None = None + contact_info: Annotated[ProgramDataContactInfo | None, Field(alias='contactInfo')] = ( + None + ) comment: list[ProgramDataComments] | None = None - sybasePiUserId: str | None = None - subTotals: PersistenceSubTotalsData | None = None + sybase_pi_user_id: Annotated[str | None, Field(alias='sybasePiUserId')] = None + sub_totals: Annotated[PersistenceSubTotalsData | None, Field(alias='subTotals')] = ( + None + ) token: Annotated[ str | None, Field( description='Unique identifier for the program. Used interchangeably with "runid".' ), ] = None - timeAllocation: list[PersistenceAllocationData] | None = None - releaseDateMillis: str | None = None - metadataReleaseDateMillis: str | None = None - commentField: str | None = None - programType: ProgramDataProgramType | None = ProgramDataProgramType.REGULAR - distributionHash: str | None = None - timeAccounting: PersistenceTimeAccountingData | None = None - operationsCommentField: str | None = None - lastDataAcquisitionNotificationDateMillis: str | None = None + time_allocation: Annotated[ + list[PersistenceAllocationData] | None, Field(alias='timeAllocation') + ] = None + release_date_millis: Annotated[str | None, Field(alias='releaseDateMillis')] = None + metadata_release_date_millis: Annotated[ + str | None, Field(alias='metadataReleaseDateMillis') + ] = None + comment_field: Annotated[str | None, Field(alias='commentField')] = None + program_type: Annotated[ + ProgramDataProgramType | None, Field(alias='programType') + ] = ProgramDataProgramType.regular + distribution_hash: Annotated[str | None, Field(alias='distributionHash')] = None + time_accounting: Annotated[ + PersistenceTimeAccountingData | None, Field(alias='timeAccounting') + ] = None + operations_comment_field: Annotated[ + str | None, Field(alias='operationsCommentField') + ] = None + last_data_acquisition_notification_date_millis: Annotated[ + str | None, Field(alias='lastDataAcquisitionNotificationDateMillis') + ] = None class PersistenceTargetData(BaseModel): @@ -1395,35 +1599,47 @@ class PersistenceTargetData(BaseModel): description='Increments when this object updates.\n\nstorage: transient\napi_visibility: read only, incremented by the server' ), ] = None - fixedTarget: TargetDataFixedTarget | None = None - movingTarget: TargetDataMovingTarget | None = None + fixed_target: Annotated[TargetDataFixedTarget | None, Field(alias='fixedTarget')] = ( + None + ) + moving_target: Annotated[TargetDataMovingTarget | None, Field(alias='movingTarget')] = ( + None + ) magnitude: TargetDataMagnitude | None = None - temperatureEffective: Annotated[ + temperature_effective: Annotated[ float | None, Field( - description='The estimated/measured effective temperature, in Kelvin.\n\nRequired for targets observed with SPIRou or ESPaDOnS.\nNot used for MegaCam.' + alias='temperatureEffective', + description='The estimated/measured effective temperature, in Kelvin.\n\nRequired for targets observed with SPIRou or ESPaDOnS.\nNot used for MegaCam.', ), ] = None - standardStar: Annotated[ + standard_star: Annotated[ bool | None, Field( - description='Whether the star is a "standard" star, to be observed each time other\nscience targets are observed.\n\nNot used for MegaCam.' + alias='standardStar', + description='Whether the star is a "standard" star, to be observed each time other\nscience targets are observed.\n\nNot used for MegaCam.', ), ] = None - linkedTargetIdentifiers: TargetDataLinkedTargetIdentifiers | None = None - findingChart: Annotated[ + linked_target_identifiers: Annotated[ + TargetDataLinkedTargetIdentifiers | None, Field(alias='linkedTargetIdentifiers') + ] = None + finding_chart: Annotated[ list[PersistenceFindingChartData] | None, Field( - description='A read-only view of the finding charts for this target.\nTo create or update finding charts, see the FindingChartService.\n\nstorage: transient\napi_visibility: read only' + alias='findingChart', + description='A read-only view of the finding charts for this target.\nTo create or update finding charts, see the FindingChartService.\n\nstorage: transient\napi_visibility: read only', ), ] = None - pointingOffsetToken: Annotated[ + pointing_offset_token: Annotated[ str | None, Field( - description='The token of a pointing offset which should be used when observing\nthis target.\n\nTo view or manage pointing offsets, see the PointingOffsetService.' + alias='pointingOffsetToken', + description='The token of a pointing offset which should be used when observing\nthis target.\n\nTo view or manage pointing offsets, see the PointingOffsetService.', ), ] = None - pointingOffset: PersistencePointingOffsetData | None = None + pointing_offset: Annotated[ + PersistencePointingOffsetData | None, Field(alias='pointingOffset') + ] = None class PersistenceTelescopePatternData(BaseModel): @@ -1437,96 +1653,128 @@ class PersistenceTelescopePatternData(BaseModel): token: str | None = None label: int | None = None version: int | None = None - userToken: str | None = None - instrument: TypesInstrumentType | None = TypesInstrumentType.UNKNOWN - isSystem: bool | None = None + user_token: Annotated[str | None, Field(alias='userToken')] = None + instrument: TypesInstrumentType | None = TypesInstrumentType.unknown + is_system: Annotated[bool | None, Field(alias='isSystem')] = None name: str | None = None - offsetCoordinate: list[TypesOffsetCoordinate] | None = None + offset_coordinate: Annotated[ + list[TypesOffsetCoordinate] | None, Field(alias='offsetCoordinate') + ] = None class TypesDuration(BaseModel): millis: str | None = None - displayUnit: TypesDurationUnit | None = TypesDurationUnit.DAYS + display_unit: Annotated[ + TypesDurationUnit | None, Field(alias='displayUnit') + ] = TypesDurationUnit.days class ExposureDataExposureStatus(BaseModel): - megacamStatus: ExposureDataMegacamStatus | None = None - wircamStatus: ExposureDataWircamStatus | None = None - espadonsStatus: ExposureDataEspadonsStatus | None = None - sitelleStatus: ExposureDataSitelleStatus | None = None - spirouStatus: ExposureDataSpirouStatus | None = None - wenaStatus: ExposureDataWenaStatus | None = None - exposureTime: PersistenceExposureTime | None = None - expDateMjd: float | None = None - queueName: str | None = None - expType: str | None = None - expStatus: PersistenceState | None = PersistenceState.UNOBSERVED - isPhotometric: bool | None = None - obsComment: str | None = None - seqComment: str | None = None - actualPointing: TypesSkyCoordinate | None = None - actualSky: float | None = None - actualAirmass: float | None = None - actualMoondist: float | None = None - observerComment: str | None = None - qcComment: str | None = None + megacam_status: Annotated[ + ExposureDataMegacamStatus | None, Field(alias='megacamStatus') + ] = None + wircam_status: Annotated[ + ExposureDataWircamStatus | None, Field(alias='wircamStatus') + ] = None + espadons_status: Annotated[ + ExposureDataEspadonsStatus | None, Field(alias='espadonsStatus') + ] = None + sitelle_status: Annotated[ + ExposureDataSitelleStatus | None, Field(alias='sitelleStatus') + ] = None + spirou_status: Annotated[ + ExposureDataSpirouStatus | None, Field(alias='spirouStatus') + ] = None + wena_status: Annotated[ExposureDataWenaStatus | None, Field(alias='wenaStatus')] = ( + None + ) + exposure_time: Annotated[ + PersistenceExposureTime | None, Field(alias='exposureTime') + ] = None + exp_date_mjd: Annotated[float | None, Field(alias='expDateMjd')] = None + queue_name: Annotated[str | None, Field(alias='queueName')] = None + exp_type: Annotated[str | None, Field(alias='expType')] = None + exp_status: Annotated[ + PersistenceState | None, Field(alias='expStatus') + ] = PersistenceState.unobserved + is_photometric: Annotated[bool | None, Field(alias='isPhotometric')] = None + obs_comment: Annotated[str | None, Field(alias='obsComment')] = None + seq_comment: Annotated[str | None, Field(alias='seqComment')] = None + actual_pointing: Annotated[TypesSkyCoordinate | None, Field(alias='actualPointing')] = ( + None + ) + actual_sky: Annotated[float | None, Field(alias='actualSky')] = None + actual_airmass: Annotated[float | None, Field(alias='actualAirmass')] = None + actual_moondist: Annotated[float | None, Field(alias='actualMoondist')] = None + observer_comment: Annotated[str | None, Field(alias='observerComment')] = None + qc_comment: Annotated[str | None, Field(alias='qcComment')] = None file: ExposureStatusFile | None = None - creditedQcoordinator: str | None = None - creditedObserver: str | None = None + credited_qcoordinator: Annotated[str | None, Field(alias='creditedQcoordinator')] = ( + None + ) + credited_observer: Annotated[str | None, Field(alias='creditedObserver')] = None grade: int | None = None - readObsDateUtc: str | None = None - readObsDateHst: str | None = None - readActualSkyName: str | None = None + read_obs_date_utc: Annotated[str | None, Field(alias='readObsDateUtc')] = None + read_obs_date_hst: Annotated[str | None, Field(alias='readObsDateHst')] = None + read_actual_sky_name: Annotated[str | None, Field(alias='readActualSkyName')] = None ra: Annotated[str | None, Field(title='Values from headers')] = None dec: str | None = None cmmtobs: str | None = None cmmtseq: str | None = None - piName: str | None = None + pi_name: Annotated[str | None, Field(alias='piName')] = None nexp: str | None = None cmpltexp: str | None = None ha: str | None = None - readObsDateMillis: str | None = None + read_obs_date_millis: Annotated[str | None, Field(alias='readObsDateMillis')] = None object: str | None = None class InstrumentConfigurationDataMegacamConfigurationData(BaseModel): filter: InstrumentConfigurationDataMegacamConfigurationDataFilter | None = ( - InstrumentConfigurationDataMegacamConfigurationDataFilter.Unknown + InstrumentConfigurationDataMegacamConfigurationDataFilter.unknown ) binning: InstrumentConfigurationDataMegacamConfigurationDataBinning | None = ( InstrumentConfigurationDataMegacamConfigurationDataBinning.one_one ) - isDynamicExposureTime: Annotated[ + is_dynamic_exposure_time: Annotated[ bool | None, Field( - title="TODO: is this needed billy? Can we use presence of snr or not to decide?\nI THINK SO. We have the capability to calculate SNR on every exposure, but we aren't necessarily doing SNR mode observations with everything" + alias='isDynamicExposureTime', + title="TODO: is this needed billy? Can we use presence of snr or not to decide?\nI THINK SO. We have the capability to calculate SNR on every exposure, but we aren't necessarily doing SNR mode observations with everything", ), ] = None - minimumExposures: int | None = None - minExptimeMs: str | None = None - maxExptimeMs: str | None = None - magAB: Annotated[ + minimum_exposures: Annotated[int | None, Field(alias='minimumExposures')] = None + min_exptime_ms: Annotated[str | None, Field(alias='minExptimeMs')] = None + max_exptime_ms: Annotated[str | None, Field(alias='maxExptimeMs')] = None + mag_ab: Annotated[ float | None, Field( - title='TODO: interesting field here because associated with the target, but in the legacy mp snr mode, the mag is carried with the IC! so have to go back to this :(' + alias='magAB', + title='TODO: interesting field here because associated with the target, but in the legacy mp snr mode, the mag is carried with the IC! so have to go back to this :(', ), ] = None - snrMode: MegacamConfigurationDataSnrMode | None = ( - MegacamConfigurationDataSnrMode.NONE - ) - etcPhotometry: ( - InstrumentConfigurationDataMegacamConfigurationDataPhotometry | None - ) = None + snr_mode: Annotated[ + MegacamConfigurationDataSnrMode | None, Field(alias='snrMode') + ] = MegacamConfigurationDataSnrMode.none + etc_photometry: Annotated[ + InstrumentConfigurationDataMegacamConfigurationDataPhotometry | None, + Field(alias='etcPhotometry'), + ] = None class PhaseScheduleObservingGroupPhaseSchedule(BaseModel): - earliestObserveDateMillis: Annotated[ + earliest_observe_date_millis: Annotated[ str | None, - Field(description='The earliest moment to observe, in Unix time milliseconds.'), + Field( + alias='earliestObserveDateMillis', + description='The earliest moment to observe, in Unix time milliseconds.', + ), ] = None tolerance: TypesDuration | None = None period: TypesDuration | None = None - periodTolerance: TypesDuration | None = None + period_tolerance: Annotated[TypesDuration | None, Field(alias='periodTolerance')] = ( + None + ) class ObservingEphemeridesUpdateResponse(BaseModel): @@ -1554,10 +1802,16 @@ class ObservingPointingOffsetUpdateResponse(BaseModel): class ObservingProgramInfo(BaseModel): - programData: PersistenceProgramData | None = None - rollupFields: ObservingRollupFields | None = None - timeAccounting: PersistenceTimeAccountingData | None = None - piInfo: ProgramInfoPiInfo | None = None + program_data: Annotated[PersistenceProgramData | None, Field(alias='programData')] = ( + None + ) + rollup_fields: Annotated[ObservingRollupFields | None, Field(alias='rollupFields')] = ( + None + ) + time_accounting: Annotated[ + PersistenceTimeAccountingData | None, Field(alias='timeAccounting') + ] = None + pi_info: Annotated[ProgramInfoPiInfo | None, Field(alias='piInfo')] = None version: int | None = None @@ -1593,78 +1847,107 @@ class ObservingTelescopePatternUpdateResponse(BaseModel): class ObservingUpdateProgramCommentResponse(BaseModel): success: bool | None = None - programData: PersistenceProgramData | None = None + program_data: Annotated[PersistenceProgramData | None, Field(alias='programData')] = ( + None + ) error: HttpErrors | None = None class ObservingUpdateProgramCommentsResponse(BaseModel): success: bool | None = None - programData: PersistenceProgramData | None = None + program_data: Annotated[PersistenceProgramData | None, Field(alias='programData')] = ( + None + ) error: HttpErrors | None = None class PersistenceInstrumentConfigurationData(BaseModel): - espadonsConfiguration: ( - InstrumentConfigurationDataEspadonsConfigurationData | None - ) = None - megacamConfiguration: InstrumentConfigurationDataMegacamConfigurationData | None = ( - None - ) - wircamConfiguration: InstrumentConfigurationDataWircamConfigurationData | None = ( - None - ) - sitelleConfiguration: InstrumentConfigurationDataSitelleConfigurationData | None = ( + espadons_configuration: Annotated[ + InstrumentConfigurationDataEspadonsConfigurationData | None, + Field(alias='espadonsConfiguration'), + ] = None + megacam_configuration: Annotated[ + InstrumentConfigurationDataMegacamConfigurationData | None, + Field(alias='megacamConfiguration'), + ] = None + wircam_configuration: Annotated[ + InstrumentConfigurationDataWircamConfigurationData | None, + Field(alias='wircamConfiguration'), + ] = None + sitelle_configuration: Annotated[ + InstrumentConfigurationDataSitelleConfigurationData | None, + Field(alias='sitelleConfiguration'), + ] = None + spirou_configuration: Annotated[ + InstrumentConfigurationDataSpirouConfigurationData | None, + Field(alias='spirouConfiguration'), + ] = None + wena_configuration: Annotated[ + InstrumentConfigurationDataWenaConfigurationData | None, + Field(alias='wenaConfiguration'), + ] = None + sybase_ic_id: Annotated[TypesBigInteger | None, Field(alias='sybaseIcId')] = None + etime_or_snr: Annotated[PersistenceExposureTime | None, Field(alias='etimeOrSnr')] = ( None ) - spirouConfiguration: InstrumentConfigurationDataSpirouConfigurationData | None = ( + sybase_cons_id: Annotated[TypesBigInteger | None, Field(alias='sybaseConsId')] = ( None ) - wenaConfiguration: InstrumentConfigurationDataWenaConfigurationData | None = None - sybaseIcId: TypesBigInteger | None = None - etimeOrSnr: PersistenceExposureTime | None = None - sybaseConsId: TypesBigInteger | None = None tracking: InstrumentConfigurationDataTracking | None = ( - InstrumentConfigurationDataTracking.Unknown + InstrumentConfigurationDataTracking.unknown ) class PersistenceLegacyReel(BaseModel): - reelOgToken: Annotated[ - str | None, Field(description='Token of the linked observing group.') + reel_og_token: Annotated[ + str | None, + Field(alias='reelOgToken', description='Token of the linked observing group.'), ] = None delay: TypesDuration | None = None window: TypesDuration | None = None - reelOgLabel: Annotated[ + reel_og_label: Annotated[ int | None, Field( - description='Label of the observing group which must be observed before this one.\n\nstorage: transient\napi_visibility: read only' + alias='reelOgLabel', + description='Label of the observing group which must be observed before this one.\n\nstorage: transient\napi_visibility: read only', ), ] = None class PersistenceObservingTemplateData(BaseModel): - instrumentConfiguration: PersistenceInstrumentConfigurationData | None = None + instrument_configuration: Annotated[ + PersistenceInstrumentConfigurationData | None, + Field(alias='instrumentConfiguration'), + ] = None constraint: PersistenceConstraint | None = None telescope_offset: PersistenceTelescopePatternData | None = None - chargeModel: Annotated[ + charge_model: Annotated[ str | None, Field( - description='Used for calculating cost for this pattern\nInvokes code which reads the ExposureInstructions and\nattributes a cost to it.' + alias='chargeModel', + description='Used for calculating cost for this pattern\nInvokes code which reads the ExposureInstructions and\nattributes a cost to it.', ), ] = None itime: str | None = None - calculateEtime: PersistenceExposureTime | None = None - acquisitionTime: PersistenceExposureTime | None = None + calculate_etime: Annotated[ + PersistenceExposureTime | None, Field(alias='calculateEtime') + ] = None + acquisition_time: Annotated[ + PersistenceExposureTime | None, Field(alias='acquisitionTime') + ] = None token: str | None = None - associatedObservingBlocks: Annotated[ + associated_observing_blocks: Annotated[ list[TypesBigInteger] | None, Field( + alias='associatedObservingBlocks', description='select ob.* from ob, prg, icseq where ob.prg_id = prg.id\nand prg.runid=\nand icseq.ob_id=ob.id and ob.con_id = \nand icseq.ic_id = ;', title='When just creating an OT, this list is empty\nWhen this OT belongs to an OG, then any OBs in sybase that this OT is related too must be filled in.\nselect * from blah where (select id from prg where prg\nQuery to find related OBs is, select ob.* from ob, prg where ob.prg_id = prg.id and prg.runid=? instrument_configuration.sybase_ic_id, constraint.sybase_cons_id', ), ] = None name: str | None = None - totalExposureCount: int | None = None + total_exposure_count: Annotated[int | None, Field(alias='totalExposureCount')] = ( + None + ) label: int | None = None version: Annotated[ int | None, @@ -1673,39 +1956,57 @@ class PersistenceObservingTemplateData(BaseModel): title='Increments when this object updates', ), ] = None - telescopePatternToken: str | None = None - telescopePatternScale: float | None = None - megacamCumulativeSnrSystemGenerated: bool | None = None + telescope_pattern_token: Annotated[str | None, Field(alias='telescopePatternToken')] = ( + None + ) + telescope_pattern_scale: Annotated[ + float | None, Field(alias='telescopePatternScale') + ] = None + megacam_cumulative_snr_system_generated: Annotated[ + bool | None, Field(alias='megacamCumulativeSnrSystemGenerated') + ] = None class PersistencePhaseScheduleObservingGroup(BaseModel): - observingBlock: ObservingGroupDataObservingBlock | None = None - phaseSchedule: PhaseScheduleObservingGroupPhaseSchedule | None = None + observing_block: Annotated[ + ObservingGroupDataObservingBlock | None, Field(alias='observingBlock') + ] = None + phase_schedule: Annotated[ + PhaseScheduleObservingGroupPhaseSchedule | None, Field(alias='phaseSchedule') + ] = None class PersistenceTargetMonitoringObservingGroupTimeConstraint(BaseModel): - numberOfIterations: Annotated[ - int | None, Field(description='How many observations to take.') + number_of_iterations: Annotated[ + int | None, + Field(alias='numberOfIterations', description='How many observations to take.'), ] = None - minimumNumberOfIterations: Annotated[ + minimum_number_of_iterations: Annotated[ int | None, Field( - description='Minimum number of observations which still provides science value.\n\nIf weather becomes an issue, this is used to determine if additional\nobservations should be tried, or the whole observing group aborted\nto prioritize other observations.\n\nCannot be greater than requested number of iterations.' + alias='minimumNumberOfIterations', + description='Minimum number of observations which still provides science value.\n\nIf weather becomes an issue, this is used to determine if additional\nobservations should be tried, or the whole observing group aborted\nto prioritize other observations.\n\nCannot be greater than requested number of iterations.', ), ] = None - timeInterval: TypesDuration | None = None - timeIntervalToleranceMin: TypesDuration | None = None - timeIntervalToleranceMax: TypesDuration | None = None - t0Millis: Annotated[ + time_interval: Annotated[TypesDuration | None, Field(alias='timeInterval')] = None + time_interval_tolerance_min: Annotated[ + TypesDuration | None, Field(alias='timeIntervalToleranceMin') + ] = None + time_interval_tolerance_max: Annotated[ + TypesDuration | None, Field(alias='timeIntervalToleranceMax') + ] = None + t0_millis: Annotated[ str | None, Field( - description="Time the observing group should start being observed, in Unix time\nmilliseconds.\n\nIf omitted, CFHT will start observing at the QC's discretion." + alias='t0Millis', + description="Time the observing group should start being observed, in Unix time\nmilliseconds.\n\nIf omitted, CFHT will start observing at the QC's discretion.", ), ] = None - withinSameCameraRun: Annotated[ + within_same_camera_run: Annotated[ bool | None, Field( - description='Whether all iterations need to occur within the same camera run.' + alias='withinSameCameraRun', + description='Whether all iterations need to occur within the same camera run.', ), ] = None @@ -1729,77 +2030,116 @@ class ObservingObservingTemplateUpdateResponse(BaseModel): class PersistenceObservingGroupContext(BaseModel): - iterationCount: Annotated[ + iteration_count: Annotated[ int | None, Field( - title='The iteration number this object was part of in the observing group' + alias='iterationCount', + title='The iteration number this object was part of in the observing group', ), ] = None - iterationTotal: Annotated[ + iteration_total: Annotated[ int | None, - Field(title='The total number of iterations requested for the observing group'), + Field( + alias='iterationTotal', + title='The total number of iterations requested for the observing group', + ), ] = None - iterationMinimum: Annotated[ + iteration_minimum: Annotated[ int | None, Field( - title='Minimum number of iterations this OG needs to provide science value' + alias='iterationMinimum', + title='Minimum number of iterations this OG needs to provide science value', ), ] = None window: Annotated[ list[ObservingBlockConditionWindow] | None, Field(title='Sorted by from past -> future on the observe_after key'), ] = None - observingGroupToken: str | None = None - observingGroupPriority: PersistenceObservingGroupPriority | None = ( - PersistenceObservingGroupPriority.UNKNOWN_OG_PRIORITY + observing_group_token: Annotated[str | None, Field(alias='observingGroupToken')] = ( + None ) - observingGroupState: ObservingGroupDataObservingGroupState | None = ( - ObservingGroupDataObservingGroupState.NOT_STARTED + observing_group_priority: Annotated[ + PersistenceObservingGroupPriority | None, Field(alias='observingGroupPriority') + ] = PersistenceObservingGroupPriority.unknown_og_priority + observing_group_state: Annotated[ + ObservingGroupDataObservingGroupState | None, Field(alias='observingGroupState') + ] = ObservingGroupDataObservingGroupState.not_started + observing_group_label: Annotated[int | None, Field(alias='observingGroupLabel')] = ( + None ) - observingGroupLabel: int | None = None - observingGroupVersion: Annotated[ + observing_group_version: Annotated[ int | None, - Field(description='Increments every time the observing group changes.'), + Field( + alias='observingGroupVersion', + description='Increments every time the observing group changes.', + ), ] = None - legacyReel: PersistenceLegacyReel | None = None + legacy_reel: Annotated[PersistenceLegacyReel | None, Field(alias='legacyReel')] = ( + None + ) single: bool | None = None - targetMonitoring: PersistenceTargetMonitoringObservingGroupTimeConstraint | None = ( + target_monitoring: Annotated[ + PersistenceTargetMonitoringObservingGroupTimeConstraint | None, + Field(alias='targetMonitoring'), + ] = None + phase_schedule: Annotated[ + PhaseScheduleObservingGroupPhaseSchedule | None, Field(alias='phaseSchedule') + ] = None + time_accounting: Annotated[ + PersistenceTimeAccountingData | None, Field(alias='timeAccounting') + ] = None + exposure_type: Annotated[ + TypesExposureType | None, Field(alias='exposureType') + ] = TypesExposureType.object + use_group_dither_scheme: Annotated[bool | None, Field(alias='useGroupDitherScheme')] = ( None ) - phaseSchedule: PhaseScheduleObservingGroupPhaseSchedule | None = None - timeAccounting: PersistenceTimeAccountingData | None = None - exposureType: TypesExposureType | None = TypesExposureType.OBJECT - useGroupDitherScheme: bool | None = None class PersistenceTargetMonitoringObservingGroup(BaseModel): - observingBlock: ObservingGroupDataObservingBlock | None = None - timeConstraint: PersistenceTargetMonitoringObservingGroupTimeConstraint | None = ( - None - ) + observing_block: Annotated[ + ObservingGroupDataObservingBlock | None, Field(alias='observingBlock') + ] = None + time_constraint: Annotated[ + PersistenceTargetMonitoringObservingGroupTimeConstraint | None, + Field(alias='timeConstraint'), + ] = None class PersistenceExposureData(BaseModel): token: str | None = None obsid: str | None = None - exposureNumber: int | None = None + exposure_number: Annotated[int | None, Field(alias='exposureNumber')] = None iteration: int | None = None - parentExposureToken: str | None = None - exposureStatus: ExposureDataExposureStatus | None = None - observingTemplate: PersistenceObservingTemplateData | None = None + parent_exposure_token: Annotated[str | None, Field(alias='parentExposureToken')] = ( + None + ) + exposure_status: Annotated[ + ExposureDataExposureStatus | None, Field(alias='exposureStatus') + ] = None + observing_template: Annotated[ + PersistenceObservingTemplateData | None, Field(alias='observingTemplate') + ] = None target_data: PersistenceTargetData | None = None - observingGroupContext: PersistenceObservingGroupContext | None = None - allDerivationsComplete: Annotated[ + observing_group_context: Annotated[ + PersistenceObservingGroupContext | None, Field(alias='observingGroupContext') + ] = None + all_derivations_complete: Annotated[ bool | None, Field( - description='When false, there is data which is still being calculated for this exposure.\n\nData can still be displayed, but the client should attempt to refresh the data.' + alias='allDerivationsComplete', + description='When false, there is data which is still being calculated for this exposure.\n\nData can still be displayed, but the client should attempt to refresh the data.', ), ] = None - queueRunId: str | None = None - cameraRunId: str | None = None - prgRunId: str | None = None - observingComponentToken: str | None = None - subTotals: PersistenceSubTotalsData | None = None + queue_run_id: Annotated[str | None, Field(alias='queueRunId')] = None + camera_run_id: Annotated[str | None, Field(alias='cameraRunId')] = None + prg_run_id: Annotated[str | None, Field(alias='prgRunId')] = None + observing_component_token: Annotated[ + str | None, Field(alias='observingComponentToken') + ] = None + sub_totals: Annotated[PersistenceSubTotalsData | None, Field(alias='subTotals')] = ( + None + ) attribution: ExposureDataAttribution | None = None version: Annotated[ int | None, @@ -1820,35 +2160,46 @@ class PersistenceObservingGroupData(BaseModel): description='Index of the observing group within the program, set by the system\nautomatically. Used for reference in communications with QSO staff.\n\nstorage: transient\napi_visibility: read only' ), ] = None - ogPriority: PersistenceObservingGroupPriority | None = ( - PersistenceObservingGroupPriority.UNKNOWN_OG_PRIORITY - ) - singleObservingGroup: PersistenceSingleObservingGroup | None = None - targetMonitoringObservingGroup: ( - PersistenceTargetMonitoringObservingGroup | None - ) = None - phaseScheduleObservingGroup: PersistencePhaseScheduleObservingGroup | None = None - readObservingBlock: Annotated[ + og_priority: Annotated[ + PersistenceObservingGroupPriority | None, Field(alias='ogPriority') + ] = PersistenceObservingGroupPriority.unknown_og_priority + single_observing_group: Annotated[ + PersistenceSingleObservingGroup | None, Field(alias='singleObservingGroup') + ] = None + target_monitoring_observing_group: Annotated[ + PersistenceTargetMonitoringObservingGroup | None, + Field(alias='targetMonitoringObservingGroup'), + ] = None + phase_schedule_observing_group: Annotated[ + PersistencePhaseScheduleObservingGroup | None, + Field(alias='phaseScheduleObservingGroup'), + ] = None + read_observing_block: Annotated[ list[ObservingGroupDataObservingBlock] | None, Field( - description='The observing blocks that have been created based on the entered\nconfiguration.\n\napi_visibility: read only' + alias='readObservingBlock', + description='The observing blocks that have been created based on the entered\nconfiguration.\n\napi_visibility: read only', ), ] = None - observingBlockCondition: Annotated[ + observing_block_condition: Annotated[ dict[str, PersistenceObservingBlockCondition] | None, Field( - description='The requirements that have been set for observing each observing blocks\nbased on the entered configuration.\n\napi_visibility: read only' + alias='observingBlockCondition', + description='The requirements that have been set for observing each observing blocks\nbased on the entered configuration.\n\napi_visibility: read only', ), ] = None - minimumObservingBlockCount: Annotated[ + minimum_observing_block_count: Annotated[ int | None, Field( - description='This field is unused, please ignore.\n\napi_visibility: read only' + alias='minimumObservingBlockCount', + description='This field is unused, please ignore.\n\napi_visibility: read only', ), ] = None - timeAccounting: PersistenceTimeAccountingData | None = None + time_accounting: Annotated[ + PersistenceTimeAccountingData | None, Field(alias='timeAccounting') + ] = None state: ObservingGroupDataObservingGroupState | None = ( - ObservingGroupDataObservingGroupState.NOT_STARTED + ObservingGroupDataObservingGroupState.not_started ) version: Annotated[ int | None, @@ -1856,33 +2207,51 @@ class PersistenceObservingGroupData(BaseModel): description='Increments when this object updates.\n\nstorage: transient\napi_visibility: read only, incremented by the server' ), ] = None - legacyReel: PersistenceLegacyReel | None = None - timeConstraint: ObservingpersistenceTimeConstraint | None = None - targetType: TypesExposureType | None = TypesExposureType.OBJECT - ditherScheme: ObservingGroupDataDitherScheme | None = ( - ObservingGroupDataDitherScheme.NONE + legacy_reel: Annotated[PersistenceLegacyReel | None, Field(alias='legacyReel')] = ( + None ) - lowSurfaceBrightness: Annotated[ + time_constraint: Annotated[ + ObservingpersistenceTimeConstraint | None, Field(alias='timeConstraint') + ] = None + target_type: Annotated[ + TypesExposureType | None, Field(alias='targetType') + ] = TypesExposureType.object + dither_scheme: Annotated[ + ObservingGroupDataDitherScheme | None, Field(alias='ditherScheme') + ] = ObservingGroupDataDitherScheme.none + low_surface_brightness: Annotated[ bool | None, Field( - title='Indicates the observing group uses low surface brightness (LSB) mode.with' + alias='lowSurfaceBrightness', + title='Indicates the observing group uses low surface brightness (LSB) mode.with', ), ] = None class SnrBlockDataSnrBlockObservingComponents(BaseModel): - observingComponentToken: Annotated[ - str | None, Field(title='the observing component') + observing_component_token: Annotated[ + str | None, + Field(alias='observingComponentToken', title='the observing component'), ] = None - observingGroupToken: Annotated[ - str | None, Field(title='should not be here, should be in the parent') + observing_group_token: Annotated[ + str | None, + Field( + alias='observingGroupToken', + title='should not be here, should be in the parent', + ), + ] = None + is_primary: Annotated[ + bool | None, Field(alias='isPrimary', title='is it the original oc?') ] = None - isPrimary: Annotated[bool | None, Field(title='is it the original oc?')] = None - configuredExposures: int | None = None + configured_exposures: Annotated[int | None, Field(alias='configuredExposures')] = ( + None + ) exposures: list[PersistenceExposureData] | None = None - obLabel: int | None = None - targetToken: str | None = None - observingTemplateToken: str | None = None + ob_label: Annotated[int | None, Field(alias='obLabel')] = None + target_token: Annotated[str | None, Field(alias='targetToken')] = None + observing_template_token: Annotated[ + str | None, Field(alias='observingTemplateToken') + ] = None class ObservingObservingGroupListResponse(BaseModel): @@ -1900,44 +2269,68 @@ class ObservingObservingGroupUpdateResponse(BaseModel): class PersistenceSnrBlockData(BaseModel): token: str | None = None label: int | None = None - cumulativeSnr: Annotated[ + cumulative_snr: Annotated[ float | None, Field( - title='the total snr achieved of all exposures\n of all observing components of this snr block' + alias='cumulativeSnr', + title='the total snr achieved of all exposures\n of all observing components of this snr block', ), ] = None - carryForwardSnr: Annotated[ + carry_forward_snr: Annotated[ float | None, Field( - title='if the program is copied to a new program possibly for a new semester\n the cumulative snr will be moved to the carry_forward_snr and the\n carry forward snr will be counted in the cumulative snr of the new program' + alias='carryForwardSnr', + title='if the program is copied to a new program possibly for a new semester\n the cumulative snr will be moved to the carry_forward_snr and the\n carry forward snr will be counted in the cumulative snr of the new program', ), ] = None - observingComponents: list[SnrBlockDataSnrBlockObservingComponents] | None = None + observing_components: Annotated[ + list[SnrBlockDataSnrBlockObservingComponents] | None, + Field(alias='observingComponents'), + ] = None version: int | None = None - requestedTargetMagnitude: Annotated[ - float | None, Field(title='this will come from the target') + requested_target_magnitude: Annotated[ + float | None, + Field(alias='requestedTargetMagnitude', title='this will come from the target'), + ] = None + requested_cumulative_snr: Annotated[ + float | None, + Field(alias='requestedCumulativeSnr', title='this will come from the OT'), ] = None - requestedCumulativeSnr: Annotated[ - float | None, Field(title='this will come from the OT') + requested_minimum_exposures: Annotated[ + int | None, Field(alias='requestedMinimumExposures') ] = None - requestedMinimumExposures: int | None = None - totalExposures: Annotated[ + total_exposures: Annotated[ int | None, - Field(title='counting the total validated exposures for this OC (OT/TARGET)'), + Field( + alias='totalExposures', + title='counting the total validated exposures for this OC (OT/TARGET)', + ), + ] = None + primary_observing_group_token: Annotated[ + str | None, Field(alias='primaryObservingGroupToken') ] = None - primaryObservingGroupToken: str | None = None - primaryObservingComponentToken: str | None = None - primaryObservingGroupLabel: int | None = None - totalConfiguredExposures: Annotated[ - int | None, Field(title='counting the total configured exposures for this OC') + primary_observing_component_token: Annotated[ + str | None, Field(alias='primaryObservingComponentToken') + ] = None + primary_observing_group_label: Annotated[ + int | None, Field(alias='primaryObservingGroupLabel') + ] = None + total_configured_exposures: Annotated[ + int | None, + Field( + alias='totalConfiguredExposures', + title='counting the total configured exposures for this OC', + ), ] = None class ObservingAddSingleToSnrBlockResponse(BaseModel): success: bool | None = None error: HttpErrors | None = None - snrBlock: PersistenceSnrBlockData | None = None - observingGroup: PersistenceObservingGroupData | None = None + snr_block: Annotated[PersistenceSnrBlockData | None, Field(alias='snrBlock')] = None + observing_group: Annotated[ + PersistenceObservingGroupData | None, Field(alias='observingGroup') + ] = None class ObservingSnrBlockListResponse(BaseModel): From 4212aac28d593d5b1d2072e4d9838cdfe3f76438 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Wed, 1 Jul 2026 17:44:30 -0700 Subject: [PATCH 05/33] Formatting --- codegen/cfht/generate_models.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/codegen/cfht/generate_models.sh b/codegen/cfht/generate_models.sh index 644b77c..fa3df94 100644 --- a/codegen/cfht/generate_models.sh +++ b/codegen/cfht/generate_models.sh @@ -2,9 +2,9 @@ set -euxo pipefail # The CFHT API uses OAS 2.0 use the online Swagger converter to convert to OAS 3.0 curl -L \ -"https://converter.swagger.io/api/convert?url=https://hou-stage.cfht.hawaii.edu/api-docs/piapi_openapiv2.swagger.json" \ ---create-dirs \ --o build/cfht.openapi3.json + "https://converter.swagger.io/api/convert?url=https://hou-stage.cfht.hawaii.edu/api-docs/piapi_openapiv2.swagger.json" \ + --create-dirs \ + -o build/cfht.openapi3.json # Generate Pydantic models from the OpenAPI 3.0 spec datamodel-codegen \ From af01e28ac9e36050cd39c643f4d80b1bb40e4459 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Wed, 22 Jul 2026 16:15:33 -0700 Subject: [PATCH 06/33] WIP cfht model generation --- Justfile | 2 +- codegen/cfht/generate_models.sh | 22 -- codegen/cfht/generator.py | 99 +++++ src/aeonlib/cfht/base_model.py | 10 + src/aeonlib/cfht/facility.py | 39 ++ src/aeonlib/cfht/models.py | 619 +++++++++++++++++++++++++------- tests/cfht/test_online.py | 16 + 7 files changed, 645 insertions(+), 162 deletions(-) delete mode 100644 codegen/cfht/generate_models.sh create mode 100755 codegen/cfht/generator.py create mode 100644 src/aeonlib/cfht/base_model.py create mode 100644 src/aeonlib/cfht/facility.py create mode 100644 tests/cfht/test_online.py diff --git a/Justfile b/Justfile index 83e0a3f..32c847a 100644 --- a/Justfile +++ b/Justfile @@ -28,7 +28,7 @@ update-saao: # Update the CFHT generated models via swagger update-cfht: - sh codegen/cfht/generate_models.sh + codegen/cfht/generator.py > src/aeonlib/cfht/models.py # Update all generated instrument files update-all: update-lco update-soar update-saao update-blanco diff --git a/codegen/cfht/generate_models.sh b/codegen/cfht/generate_models.sh deleted file mode 100644 index fa3df94..0000000 --- a/codegen/cfht/generate_models.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/env sh -set -euxo pipefail -# The CFHT API uses OAS 2.0 use the online Swagger converter to convert to OAS 3.0 -curl -L \ - "https://converter.swagger.io/api/convert?url=https://hou-stage.cfht.hawaii.edu/api-docs/piapi_openapiv2.swagger.json" \ - --create-dirs \ - -o build/cfht.openapi3.json - -# Generate Pydantic models from the OpenAPI 3.0 spec -datamodel-codegen \ - --input "build/cfht.openapi3.json" \ - --input-file-type openapi \ - --openapi-scopes schemas \ - --output src/aeonlib/cfht/models.py \ - --output-model-type pydantic_v2.BaseModel \ - --snake-case-field \ - --use-annotated \ - --set-default-enum-member \ - --formatters builtin - -# Ignore some ruff rules that are not relevant for generated code -sed -i '1s/^/# Auto generated file, do not edit\n# ruff: noqa: E741\n/' src/aeonlib/cfht/models.py diff --git a/codegen/cfht/generator.py b/codegen/cfht/generator.py new file mode 100755 index 0000000..832cc38 --- /dev/null +++ b/codegen/cfht/generator.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +import sys +from typing import Any + +import httpx +from datamodel_code_generator import ( + DataModelType, + Formatter, + GenerateConfig, + InputFileType, + OpenAPIScope, + TargetPydanticVersion, + generate, +) +from datamodel_code_generator import ( + Error as DataModelCodegenError, +) + +SWAGGER_V2_URL = ( + "https://hou-stage.cfht.hawaii.edu/api-docs/piapi_openapiv2.swagger.json" +) +SWAGGER_CONVERTER_URL = "https://converter.swagger.io/api/convert" + +GENERATED_HEADER = "# Auto generated file, do not edit\n# ruff: noqa: E741\n" + + +def fetch_converted_openapi(swagger_v2_url: str) -> dict[str, Any]: + response = httpx.get( + SWAGGER_CONVERTER_URL, + params={"url": swagger_v2_url}, + timeout=120.0, + follow_redirects=True, + ) + response.raise_for_status() + return response.json() + + +def normalize_string_int64_to_integer(node: Any) -> None: + """ + The CFHT spec marks some integer-like fields as: + {"type": "string", "format": "int64"} + but the API returns true integers. Normalize schema to integers so that + the generated pydantic models can be used without schema validation errors. + """ + + if isinstance(node, dict): + if node.get("type") == "string" and node.get("format") == "int64": + node["type"] = "integer" + + for value in node.values(): + normalize_string_int64_to_integer(value) + + elif isinstance(node, list): + for item in node: + normalize_string_int64_to_integer(item) + + +def generate_models(openapi_document: dict[str, Any]) -> str: + config = GenerateConfig( + input_file_type=InputFileType.OpenAPI, + input_filename="cfht.openapi3.json", + openapi_scopes=[OpenAPIScope.Schemas], + output_model_type=DataModelType.PydanticV2BaseModel, + base_class="aeonlib.cfht.base_model.CFHTBaseModel", + target_pydantic_version=TargetPydanticVersion.V2_11, + use_annotated=True, + field_constraints=True, + set_default_enum_member=True, + snake_case_field=True, + allow_population_by_field_name=True, + formatters=[Formatter.BUILTIN], + ) + + result = generate(openapi_document, config=config) + if not isinstance(result, str): + raise RuntimeError( + "Expected string output from datamodel-code-generator" + f"got {type(result).__name__}." + ) + + return GENERATED_HEADER + result + "\n" + + +def main() -> int: + document = fetch_converted_openapi(SWAGGER_V2_URL) + normalize_string_int64_to_integer(document) + + generated = generate_models(document) + _ = sys.stdout.write(generated) + + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (httpx.HTTPError, DataModelCodegenError, OSError, RuntimeError) as exc: + print(f"CFHT model generation failed: {exc}", file=sys.stderr) + raise SystemExit(1) diff --git a/src/aeonlib/cfht/base_model.py b/src/aeonlib/cfht/base_model.py new file mode 100644 index 0000000..2b47a2d --- /dev/null +++ b/src/aeonlib/cfht/base_model.py @@ -0,0 +1,10 @@ +from pydantic import BaseModel, ConfigDict + + +class CFHTBaseModel(BaseModel): + """Base model for CFHT generated schemas. + This allows us to configure all derived classes + if necessary + """ + + model_config = ConfigDict(coerce_numbers_to_str=False) diff --git a/src/aeonlib/cfht/facility.py b/src/aeonlib/cfht/facility.py new file mode 100644 index 0000000..590b3f1 --- /dev/null +++ b/src/aeonlib/cfht/facility.py @@ -0,0 +1,39 @@ +from typing import Literal + +import httpx + +from aeonlib.conf import settings + +from .models import ObservingProgramInfo + +INSTRUMENTS = Literal["SPIROU", "ESPADONS", "MEGACAM"] + + +class CFHTFacility: + """CFHT Facility class""" + + def __init__(self): + base_url = settings.cfht_api_root + if not base_url: + raise ValueError("AEON_CFHT_API_ROOT is not set") + access_token = settings.cfht_access_token + if not access_token: + raise ValueError("AEON_CFHT_ACCESS_TOKEN token is not set") + headers = { + "Authorization": f"Bearer {access_token}", + } + self._client = httpx.Client(base_url=base_url, headers=headers) + + def programs(self) -> list[ObservingProgramInfo]: + """Get the list of observing programs""" + response = self._client.get("/programs/") + import json + + print(json.dumps(response.json(), indent=4)) + response.raise_for_status() + + payload = response.json() + return [ + ObservingProgramInfo.model_validate(program) + for program in payload.get("entity", []) + ] diff --git a/src/aeonlib/cfht/models.py b/src/aeonlib/cfht/models.py index b3b2a45..f0b9433 100644 --- a/src/aeonlib/cfht/models.py +++ b/src/aeonlib/cfht/models.py @@ -2,17 +2,21 @@ # ruff: noqa: E741 # generated by datamodel-codegen: # filename: cfht.openapi3.json -# timestamp: 2026-07-02T00:42:01+00:00 +# timestamp: 2026-07-22T22:53:53+00:00 from __future__ import annotations from enum import Enum from typing import Annotated -from pydantic import Base64Str, BaseModel, Field +from aeonlib.cfht.base_model import CFHTBaseModel +from pydantic import Base64Str, ConfigDict, Field -class AllocationDataAgency(BaseModel): +class AllocationDataAgency(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) letter: str | None = None name: str | None = None @@ -29,15 +33,24 @@ class CalibrationCalibrationPosition(Enum): target = 'target' -class ConfigDithering(BaseModel): +class ConfigDithering(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) is_staring: Annotated[bool | None, Field(alias='isStaring')] = None -class EspadonsStatusGuiderSeeingProcessingResult(BaseModel): +class EspadonsStatusGuiderSeeingProcessingResult(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) guider_seeing: Annotated[float | None, Field(alias='guiderSeeing')] = None -class ExposureDataAttribution(BaseModel): +class ExposureDataAttribution(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) runid: str | None = None observing_block_label: Annotated[ str | None, @@ -93,11 +106,17 @@ class ExposureDataAttribution(BaseModel): tac_grade: Annotated[str | None, Field(alias='tacGrade')] = None -class ExposureDataEspadonsStatusExposureMeterProcessingResult(BaseModel): +class ExposureDataEspadonsStatusExposureMeterProcessingResult(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) snr: float | None = None -class ExposureDataSpirouStatusExposureMeterProcessingResult(BaseModel): +class ExposureDataSpirouStatusExposureMeterProcessingResult(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) flux: float | None = None snr: float | None = None mid_exposure_date_millis: Annotated[ @@ -107,11 +126,16 @@ class ExposureDataSpirouStatusExposureMeterProcessingResult(BaseModel): spemsnrc: float | None = None -class ExposureDataWenaStatus(BaseModel): - pass +class ExposureDataWenaStatus(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) -class ExposureDataWircamStatus(BaseModel): +class ExposureDataWircamStatus(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) iq_uncertainty: Annotated[float | None, Field(alias='iqUncertainty')] = None absorption: float | None = None absorption_uncertainty: Annotated[ @@ -129,7 +153,10 @@ class ExposureDataWircamStatus(BaseModel): iq: float | None = None -class ExposureStatusFile(BaseModel): +class ExposureStatusFile(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) absolute_path: Annotated[ str | None, Field( @@ -143,14 +170,20 @@ class ExposureStatusFile(BaseModel): ext: Annotated[str | None, Field(description='Name of extenion including .')] = None -class FindingChartDataApproval(BaseModel): - approved_at_millis: Annotated[str | None, Field(alias='approvedAtMillis')] = None +class FindingChartDataApproval(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + approved_at_millis: Annotated[int | None, Field(alias='approvedAtMillis')] = None approved_by_usertoken: Annotated[str | None, Field(alias='approvedByUsertoken')] = ( None ) -class FixedTargetProperMotion(BaseModel): +class FixedTargetProperMotion(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) ra_mas: Annotated[ float | None, Field( @@ -243,8 +276,10 @@ class InstrumentConfigurationDataTracking(Enum): nonsid_g = 'NONSID_G' -class InstrumentConfigurationDataWenaConfigurationData(BaseModel): - pass +class InstrumentConfigurationDataWenaConfigurationData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) class InstrumentConfigurationDataWircamConfigurationDataFilter(Enum): @@ -280,14 +315,20 @@ class MegacamConfigurationDataSnrMode(Enum): zero_point = 'ZERO_POINT' -class MegacamStatusElixirProcessingResult(BaseModel): +class MegacamStatusElixirProcessingResult(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) iq: float | None = None sky_background: Annotated[float | None, Field(alias='skyBackground')] = None snr: float | None = None zeropoint: float | None = None -class ObservingBlockConditionRequisite(BaseModel): +class ObservingBlockConditionRequisite(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) requisite_observing_block_index: Annotated[ int | None, Field( @@ -296,21 +337,21 @@ class ObservingBlockConditionRequisite(BaseModel): ), ] = None wait_period_min_millis: Annotated[ - str | None, + int | None, Field( alias='waitPeriodMinMillis', description='Minimum time to wait after requisite observing block exposure has\nbeen taken.', ), ] = None wait_period_millis: Annotated[ - str | None, + int | None, Field( alias='waitPeriodMillis', description='Optimal to wait after requisite observing block exposure has\nbeen taken.', ), ] = None wait_period_max_millis: Annotated[ - str | None, + int | None, Field( alias='waitPeriodMaxMillis', description='Minimum time to wait after requisite observing block exposure has\nbeen taken.', @@ -324,16 +365,19 @@ class ObservingBlockConditionRequisite(BaseModel): ] = None -class ObservingBlockConditionWindow(BaseModel): +class ObservingBlockConditionWindow(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) observe_after: Annotated[ - str | None, + int | None, Field( alias='observeAfter', description='Start of the observation window, in Unix time milliseconds.', ), ] = None observe_before: Annotated[ - str | None, + int | None, Field( alias='observeBefore', description='End of the observation window, in Unix time milliseconds.', @@ -341,7 +385,10 @@ class ObservingBlockConditionWindow(BaseModel): ] = None -class ObservingBlockObservingComponent(BaseModel): +class ObservingBlockObservingComponent(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) target_token: Annotated[ str | None, Field( @@ -385,7 +432,10 @@ class ObservingGroupDataDitherScheme(Enum): group = 'GROUP' -class ObservingGroupDataObservingBlock(BaseModel): +class ObservingGroupDataObservingBlock(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) observing_component: Annotated[ list[ObservingBlockObservingComponent] | None, Field( @@ -414,7 +464,10 @@ class ObservingGroupDataObservingGroupState(Enum): validated = 'VALIDATED' -class PhotometryAperture(BaseModel): +class PhotometryAperture(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) optimal_aperture: Annotated[bool | None, Field(alias='optimalAperture')] = None fixed_aperture: Annotated[ float | None, @@ -425,12 +478,18 @@ class PhotometryAperture(BaseModel): ] = None -class ProgramDataComments(BaseModel): +class ProgramDataComments(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) question: str | None = None comment: str | None = None -class ProgramDataContactInfo(BaseModel): +class ProgramDataContactInfo(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) pi_first_name: Annotated[str | None, Field(alias='piFirstName')] = None pi_last_name: Annotated[str | None, Field(alias='piLastName')] = None pi_email: Annotated[str | None, Field(alias='piEmail')] = None @@ -444,7 +503,10 @@ class ProgramDataProgramType(Enum): snapshot = 'SNAPSHOT' -class ProgramInfoPiInfo(BaseModel): +class ProgramInfoPiInfo(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) ldap_username: Annotated[str | None, Field(alias='ldapUsername')] = None email_address: Annotated[str | None, Field(alias='emailAddress')] = None first_name: Annotated[str | None, Field(alias='firstName')] = None @@ -459,7 +521,10 @@ class RReadoutMode(Enum): fast = 'Fast' -class SitelleConfigurationDataCalibration(BaseModel): +class SitelleConfigurationDataCalibration(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) calibration_position: Annotated[ CalibrationCalibrationPosition | None, Field(alias='calibrationPosition') ] = CalibrationCalibrationPosition.no_calibration_position @@ -474,7 +539,10 @@ class SitelleConfigurationDataResolutionMode(Enum): medres = 'medres' -class SitelleStatusCamera(BaseModel): +class SitelleStatusCamera(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) iq: float | None = None iq_rms: Annotated[float | None, Field(alias='iqRms')] = None pixel_shift_x: Annotated[float | None, Field(alias='pixelShiftX')] = None @@ -490,7 +558,10 @@ class SpirouConfigurationDataSkyObservation(Enum): before_and_after = 'BeforeAndAfter' -class SpirouStatusGuiderProcessingResult(BaseModel): +class SpirouStatusGuiderProcessingResult(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) etime: float | None = None isustate: str | None = None sbdens_p: Annotated[float | None, Field(alias='sbdensP')] = None @@ -542,7 +613,10 @@ class SpirouStatusGuiderProcessingResult(BaseModel): trg_type: Annotated[str | None, Field(alias='trgType')] = None -class SpirouStatusPipelineProcessingResult(BaseModel): +class SpirouStatusPipelineProcessingResult(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) snr10: float | None = None snr34: float | None = None snr44: float | None = None @@ -556,7 +630,10 @@ class SpirouStatusPipelineProcessingResult(BaseModel): ccfmask_str: Annotated[str | None, Field(alias='ccfmaskStr')] = None -class TargetDataMagnitude(BaseModel): +class TargetDataMagnitude(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) u: Annotated[float | None, Field(alias='U', title='U-band magnitude')] = None b: Annotated[float | None, Field(alias='B', title='B-band magnitude')] = None v: Annotated[ @@ -613,41 +690,65 @@ class TimeAccountingDataTimeAccountingStatus(Enum): validated = 'VALIDATED' -class UpenaProcessingResulth2oMm(BaseModel): +class UpenaProcessingResulth2oMm(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) raw_density: Annotated[float | None, Field(alias='rawDensity')] = None at_zenith: Annotated[float | None, Field(alias='atZenith')] = None -class HttpErrors(BaseModel): +class HttpErrors(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) messages: list[str] | None = None -class ObservingObservingGroupDeleteResponse(BaseModel): +class ObservingObservingGroupDeleteResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None error: HttpErrors | None = None -class ObservingObservingTemplateDeleteResponse(BaseModel): +class ObservingObservingTemplateDeleteResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None error: HttpErrors | None = None -class ObservingPointingOffsetDeleteResponse(BaseModel): +class ObservingPointingOffsetDeleteResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None error: HttpErrors | None = None -class ObservingTargetDeleteResponse(BaseModel): +class ObservingTargetDeleteResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None error: HttpErrors | None = None -class ObservingTelescopePatternDeleteResponse(BaseModel): +class ObservingTelescopePatternDeleteResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None error: HttpErrors | None = None -class ObservingpersistenceTimeConstraint(BaseModel): +class ObservingpersistenceTimeConstraint(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) window: Annotated[ list[ObservingBlockConditionWindow] | None, Field( @@ -656,7 +757,10 @@ class ObservingpersistenceTimeConstraint(BaseModel): ] = None -class PersistenceCatalogIdentifier(BaseModel): +class PersistenceCatalogIdentifier(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) catalog: Annotated[str | None, Field(description='Catalog to reference.')] = None identifier: Annotated[ str | None, @@ -666,12 +770,26 @@ class PersistenceCatalogIdentifier(BaseModel): ] = None -class PersistenceExposureTime(BaseModel): +class PersistenceDynamicExposureTimeLimit(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + minimum_multiplier: Annotated[float | None, Field(alias='minimumMultiplier')] = None + maximum_multiplier: Annotated[float | None, Field(alias='maximumMultiplier')] = None + + +class PersistenceExposureTime(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) desired_snr: Annotated[float | None, Field(alias='desiredSnr')] = None - exposure_time_ms: Annotated[str | None, Field(alias='exposureTimeMs')] = None + exposure_time_ms: Annotated[int | None, Field(alias='exposureTimeMs')] = None -class PersistenceFindingChartData(BaseModel): +class PersistenceFindingChartData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) comment: str | None = None url: str | None = None token: str | None = None @@ -684,16 +802,19 @@ class PersistenceFindingChartData(BaseModel): target_token: Annotated[str | None, Field(alias='targetToken')] = None -class PersistenceITimeData(BaseModel): +class PersistenceITimeData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) exposure_time_millis: Annotated[ - str | None, + int | None, Field( alias='exposureTimeMillis', description='Time spent collecting photons (in milliseconds).', ), ] = None overhead_time_millis: Annotated[ - str | None, + int | None, Field( alias='overheadTimeMillis', description='All other times required for taking the exposure (in milliseconds).', @@ -701,13 +822,19 @@ class PersistenceITimeData(BaseModel): ] = None -class PersistenceListOfIdentifiers(BaseModel): +class PersistenceListOfIdentifiers(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) catalog_identifier: Annotated[ list[PersistenceCatalogIdentifier] | None, Field(alias='catalogIdentifier') ] = None -class PersistenceObservingBlockCondition(BaseModel): +class PersistenceObservingBlockCondition(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) pre_requisite: Annotated[ list[ObservingBlockConditionRequisite] | None, Field( @@ -731,14 +858,20 @@ class PersistenceObservingGroupPriority(Enum): inactive = 'INACTIVE' -class PersistenceRollupValue(BaseModel): - requested: str | None = None - started: str | None = None - observed: str | None = None - validated: str | None = None +class PersistenceRollupValue(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + requested: int | None = None + started: int | None = None + observed: int | None = None + validated: int | None = None -class PersistenceSingleObservingGroup(BaseModel): +class PersistenceSingleObservingGroup(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) observing_block: Annotated[ ObservingGroupDataObservingBlock | None, Field(alias='observingBlock') ] = None @@ -761,7 +894,10 @@ class PersistenceStokesParameter(Enum): i = 'I' -class PersistenceSubTotalsData(BaseModel): +class PersistenceSubTotalsData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) etime: PersistenceRollupValue | None = None overheadtime: PersistenceRollupValue | None = None exposure_count: Annotated[ @@ -781,7 +917,10 @@ class PersistenceSubTotalsData(BaseModel): ] = None -class PersistenceTargetIdentifiers(BaseModel): +class PersistenceTargetIdentifiers(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) catalog_identifier: Annotated[ dict[str, PersistenceListOfIdentifiers] | None, Field( @@ -800,7 +939,10 @@ class PersistenceTargetIdentifiers(BaseModel): ] = None -class PersistenceTargetTimeData(BaseModel): +class PersistenceTargetTimeData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) requested_integration_time: Annotated[ PersistenceITimeData | None, Field(alias='requestedIntegrationTime') ] = None @@ -824,7 +966,10 @@ class PersistenceTargetTimeData(BaseModel): ] = None -class PersistenceTimeAccountingData(BaseModel): +class PersistenceTimeAccountingData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) configured_time: Annotated[ PersistenceTargetTimeData | None, Field(alias='configuredTime') ] = None @@ -851,7 +996,10 @@ class PersistenceTimeAccountingData(BaseModel): ] = None -class ProtobufAny(BaseModel): +class ProtobufAny(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) type_url: Annotated[str | None, Field(alias='typeUrl')] = None value: Annotated[ Base64Str | None, @@ -861,17 +1009,26 @@ class ProtobufAny(BaseModel): ] = None -class RpcStatus(BaseModel): +class RpcStatus(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) code: int | None = None message: str | None = None details: list[ProtobufAny] | None = None -class TypesBigInteger(BaseModel): +class TypesBigInteger(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) value: str | None = None -class TypesDoubleMinMax(BaseModel): +class TypesDoubleMinMax(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) min: float | None = None max: float | None = None @@ -912,7 +1069,10 @@ class TypesInstrumentType(Enum): wenaokeao = 'WENAOKEAO' -class TypesOffsetCoordinate(BaseModel): +class TypesOffsetCoordinate(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) ra_offset: Annotated[ float | None, Field( @@ -944,7 +1104,10 @@ class TypesOffsetCoordinate(BaseModel): ] = None -class TypesSkyCoordinate(BaseModel): +class TypesSkyCoordinate(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) ra: Annotated[ float | None, Field( @@ -962,14 +1125,20 @@ class TypesSkyCoordinate(BaseModel): ] = None -class ConfigNodding(BaseModel): +class ConfigNodding(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) start_sequence: Annotated[ TypesExposureType | None, Field(alias='startSequence') ] = TypesExposureType.object target_db: Annotated[float | None, Field(alias='targetDb')] = None -class EspadonsStatusUpenaProcessingResult(BaseModel): +class EspadonsStatusUpenaProcessingResult(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) iq: float | None = None h2o_column_density_from_telluric_lines: Annotated[ float | None, Field(alias='h2oColumnDensityFromTelluricLines') @@ -991,7 +1160,10 @@ class EspadonsStatusUpenaProcessingResult(BaseModel): ) -class ExposureDataEspadonsStatus(BaseModel): +class ExposureDataEspadonsStatus(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) instr_mode: Annotated[str | None, Field(alias='instrMode')] = None actual_wavelength: Annotated[float | None, Field(alias='actualWavelength')] = None snr_exp_meter: Annotated[float | None, Field(alias='snrExpMeter')] = None @@ -1026,7 +1198,10 @@ class ExposureDataEspadonsStatus(BaseModel): observation_type: Annotated[str | None, Field(alias='observationType')] = None -class ExposureDataMegacamStatus(BaseModel): +class ExposureDataMegacamStatus(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) iq: float | None = None skybg: float | None = None elixir_eval: Annotated[int | None, Field(alias='elixirEval')] = None @@ -1043,7 +1218,10 @@ class ExposureDataMegacamStatus(BaseModel): ] = None -class ExposureDataSitelleStatus(BaseModel): +class ExposureDataSitelleStatus(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) camera_a: Annotated[SitelleStatusCamera | None, Field(alias='cameraA')] = None camera_b: Annotated[SitelleStatusCamera | None, Field(alias='cameraB')] = None relative_extinction: Annotated[float | None, Field(alias='relativeExtinction')] = ( @@ -1055,7 +1233,10 @@ class ExposureDataSitelleStatus(BaseModel): actual_filter: Annotated[str | None, Field(alias='actualFilter')] = None -class ExposureDataSpirouStatus(BaseModel): +class ExposureDataSpirouStatus(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) acqtime: float | None = None acqtime1: str | None = None asic_num: Annotated[str | None, Field(alias='asicNum')] = None @@ -1298,7 +1479,10 @@ class ExposureDataSpirouStatus(BaseModel): ] = None -class InstrumentConfigurationDataEspadonsConfigurationData(BaseModel): +class InstrumentConfigurationDataEspadonsConfigurationData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) observing_mode: Annotated[ InstrumentConfigurationDataEspadonsConfigurationDataOObservingMode | None, Field(alias='observingMode'), @@ -1316,12 +1500,18 @@ class InstrumentConfigurationDataEspadonsConfigurationData(BaseModel): snr: float | None = None -class InstrumentConfigurationDataMegacamConfigurationDataPhotometry(BaseModel): +class InstrumentConfigurationDataMegacamConfigurationDataPhotometry(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) aperture: PhotometryAperture | None = None psf: bool | None = None -class InstrumentConfigurationDataSitelleConfigurationData(BaseModel): +class InstrumentConfigurationDataSitelleConfigurationData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) filter: InstrumentConfigurationDataSitelleConfigurationDataFilter | None = ( InstrumentConfigurationDataSitelleConfigurationDataFilter.unknown ) @@ -1346,7 +1536,10 @@ class InstrumentConfigurationDataSitelleConfigurationData(BaseModel): ] = None -class InstrumentConfigurationDataSpirouConfigurationData(BaseModel): +class InstrumentConfigurationDataSpirouConfigurationData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) template_name: Annotated[ str | None, Field(alias='templateName', title='Deprecated fields') ] = None @@ -1373,7 +1566,7 @@ class InstrumentConfigurationDataSpirouConfigurationData(BaseModel): Field(alias='useSnr', title='when true - use SNR for stoping exposures'), ] = None sky_etime_millis: Annotated[ - str | None, + int | None, Field( alias='skyEtimeMillis', title='Used to override the etime for the sky_observation', @@ -1381,7 +1574,10 @@ class InstrumentConfigurationDataSpirouConfigurationData(BaseModel): ] = None -class InstrumentConfigurationDataWircamConfigurationData(BaseModel): +class InstrumentConfigurationDataWircamConfigurationData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) filter: InstrumentConfigurationDataWircamConfigurationDataFilter | None = ( InstrumentConfigurationDataWircamConfigurationDataFilter.unknown ) @@ -1394,7 +1590,10 @@ class InstrumentConfigurationDataWircamConfigurationData(BaseModel): is_staring: Annotated[bool | None, Field(alias='isStaring')] = None -class MovingTargetEphemeris(BaseModel): +class MovingTargetEphemeris(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) mjd: Annotated[ float | None, Field( @@ -1407,7 +1606,10 @@ class MovingTargetEphemeris(BaseModel): ] = None -class TargetDataFixedTarget(BaseModel): +class TargetDataFixedTarget(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) coordinate: TypesSkyCoordinate | None = None proper_motion: Annotated[ FixedTargetProperMotion | None, Field(alias='properMotion') @@ -1424,7 +1626,10 @@ class TargetDataFixedTarget(BaseModel): ] = None -class TargetDataLinkedTargetIdentifiers(BaseModel): +class TargetDataLinkedTargetIdentifiers(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) pi_confirmed_identifiers: Annotated[ PersistenceTargetIdentifiers | None, Field(alias='piConfirmedIdentifiers') ] = None @@ -1437,7 +1642,10 @@ class TargetDataLinkedTargetIdentifiers(BaseModel): ] = None -class TargetDataMovingTarget(BaseModel): +class TargetDataMovingTarget(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) ephemeris_point: Annotated[ list[MovingTargetEphemeris] | None, Field( @@ -1447,7 +1655,10 @@ class TargetDataMovingTarget(BaseModel): ] = None -class TelescopePatternDataConfig(BaseModel): +class TelescopePatternDataConfig(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) name: str | None = None description: str | None = None charge_model: Annotated[ @@ -1461,7 +1672,10 @@ class TelescopePatternDataConfig(BaseModel): is_system: Annotated[bool | None, Field(alias='isSystem')] = None -class TelescopePatternDataOffset(BaseModel): +class TelescopePatternDataOffset(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) exposure_number: Annotated[ int | None, Field( @@ -1473,7 +1687,10 @@ class TelescopePatternDataOffset(BaseModel): type: TypesExposureType | None = TypesExposureType.object -class ObservingRollupFields(BaseModel): +class ObservingRollupFields(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) i_time: Annotated[PersistenceRollupValue | None, Field(alias='iTime')] = None i_time_configured: Annotated[ PersistenceRollupValue | None, Field(alias='iTimeConfigured') @@ -1483,9 +1700,12 @@ class ObservingRollupFields(BaseModel): ) -class PersistenceAllocationData(BaseModel): +class PersistenceAllocationData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) run_id: Annotated[str | None, Field(alias='runId')] = None - time_allocated_millis: Annotated[str | None, Field(alias='timeAllocatedMillis')] = ( + time_allocated_millis: Annotated[int | None, Field(alias='timeAllocatedMillis')] = ( None ) tac_rank: Annotated[int | None, Field(alias='tacRank')] = None @@ -1495,7 +1715,10 @@ class PersistenceAllocationData(BaseModel): cfht_rank: Annotated[int | None, Field(alias='cfhtRank')] = None -class PersistenceConstraint(BaseModel): +class PersistenceConstraint(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) image_quality: Annotated[TypesDoubleMinMax | None, Field(alias='imageQuality')] = ( None ) @@ -1517,7 +1740,10 @@ class PersistenceConstraint(BaseModel): sky_background_name: Annotated[str | None, Field(alias='skyBackgroundName')] = None -class PersistencePointingOffsetData(BaseModel): +class PersistencePointingOffsetData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) token: str | None = None name: str | None = None offset: TypesOffsetCoordinate | None = None @@ -1534,7 +1760,10 @@ class PersistencePointingOffsetData(BaseModel): is_system: Annotated[bool | None, Field(alias='isSystem')] = None -class PersistenceProgramData(BaseModel): +class PersistenceProgramData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) title: str | None = None abstract: str | None = None contact_info: Annotated[ProgramDataContactInfo | None, Field(alias='contactInfo')] = ( @@ -1554,9 +1783,9 @@ class PersistenceProgramData(BaseModel): time_allocation: Annotated[ list[PersistenceAllocationData] | None, Field(alias='timeAllocation') ] = None - release_date_millis: Annotated[str | None, Field(alias='releaseDateMillis')] = None + release_date_millis: Annotated[int | None, Field(alias='releaseDateMillis')] = None metadata_release_date_millis: Annotated[ - str | None, Field(alias='metadataReleaseDateMillis') + int | None, Field(alias='metadataReleaseDateMillis') ] = None comment_field: Annotated[str | None, Field(alias='commentField')] = None program_type: Annotated[ @@ -1570,11 +1799,18 @@ class PersistenceProgramData(BaseModel): str | None, Field(alias='operationsCommentField') ] = None last_data_acquisition_notification_date_millis: Annotated[ - str | None, Field(alias='lastDataAcquisitionNotificationDateMillis') + int | None, Field(alias='lastDataAcquisitionNotificationDateMillis') + ] = None + dynamic_exposure_time_limit: Annotated[ + PersistenceDynamicExposureTimeLimit | None, + Field(alias='dynamicExposureTimeLimit'), ] = None -class PersistenceTargetData(BaseModel): +class PersistenceTargetData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) token: Annotated[ str | None, Field( @@ -1642,7 +1878,10 @@ class PersistenceTargetData(BaseModel): ] = None -class PersistenceTelescopePatternData(BaseModel): +class PersistenceTelescopePatternData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) offsets: Annotated[ list[TelescopePatternDataOffset] | None, Field( @@ -1662,14 +1901,20 @@ class PersistenceTelescopePatternData(BaseModel): ] = None -class TypesDuration(BaseModel): - millis: str | None = None +class TypesDuration(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + millis: int | None = None display_unit: Annotated[ TypesDurationUnit | None, Field(alias='displayUnit') ] = TypesDurationUnit.days -class ExposureDataExposureStatus(BaseModel): +class ExposureDataExposureStatus(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) megacam_status: Annotated[ ExposureDataMegacamStatus | None, Field(alias='megacamStatus') ] = None @@ -1725,11 +1970,14 @@ class ExposureDataExposureStatus(BaseModel): nexp: str | None = None cmpltexp: str | None = None ha: str | None = None - read_obs_date_millis: Annotated[str | None, Field(alias='readObsDateMillis')] = None + read_obs_date_millis: Annotated[int | None, Field(alias='readObsDateMillis')] = None object: str | None = None -class InstrumentConfigurationDataMegacamConfigurationData(BaseModel): +class InstrumentConfigurationDataMegacamConfigurationData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) filter: InstrumentConfigurationDataMegacamConfigurationDataFilter | None = ( InstrumentConfigurationDataMegacamConfigurationDataFilter.unknown ) @@ -1744,8 +1992,8 @@ class InstrumentConfigurationDataMegacamConfigurationData(BaseModel): ), ] = None minimum_exposures: Annotated[int | None, Field(alias='minimumExposures')] = None - min_exptime_ms: Annotated[str | None, Field(alias='minExptimeMs')] = None - max_exptime_ms: Annotated[str | None, Field(alias='maxExptimeMs')] = None + min_exptime_ms: Annotated[int | None, Field(alias='minExptimeMs')] = None + max_exptime_ms: Annotated[int | None, Field(alias='maxExptimeMs')] = None mag_ab: Annotated[ float | None, Field( @@ -1762,9 +2010,12 @@ class InstrumentConfigurationDataMegacamConfigurationData(BaseModel): ] = None -class PhaseScheduleObservingGroupPhaseSchedule(BaseModel): +class PhaseScheduleObservingGroupPhaseSchedule(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) earliest_observe_date_millis: Annotated[ - str | None, + int | None, Field( alias='earliestObserveDateMillis', description='The earliest moment to observe, in Unix time milliseconds.', @@ -1777,31 +2028,46 @@ class PhaseScheduleObservingGroupPhaseSchedule(BaseModel): ) -class ObservingEphemeridesUpdateResponse(BaseModel): +class ObservingEphemeridesUpdateResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None error: HttpErrors | None = None entity: PersistenceTargetData | None = None -class ObservingPointingOffsetGetResponse(BaseModel): +class ObservingPointingOffsetGetResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None entity: PersistencePointingOffsetData | None = None error: HttpErrors | None = None -class ObservingPointingOffsetListResponse(BaseModel): +class ObservingPointingOffsetListResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None entity: list[PersistencePointingOffsetData] | None = None error: HttpErrors | None = None -class ObservingPointingOffsetUpdateResponse(BaseModel): +class ObservingPointingOffsetUpdateResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None error: HttpErrors | None = None entity: PersistencePointingOffsetData | None = None -class ObservingProgramInfo(BaseModel): +class ObservingProgramInfo(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) program_data: Annotated[PersistenceProgramData | None, Field(alias='programData')] = ( None ) @@ -1815,37 +2081,55 @@ class ObservingProgramInfo(BaseModel): version: int | None = None -class ObservingTargetListResponse(BaseModel): +class ObservingTargetListResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None entity: list[PersistenceTargetData] | None = None error: HttpErrors | None = None -class ObservingTargetShowResponse(BaseModel): +class ObservingTargetShowResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None error: HttpErrors | None = None entity: PersistenceTargetData | None = None -class ObservingTargetUpdateResponse(BaseModel): +class ObservingTargetUpdateResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None error: HttpErrors | None = None entity: PersistenceTargetData | None = None -class ObservingTelescopePatternListResponse(BaseModel): +class ObservingTelescopePatternListResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None telescope_pattern: list[PersistenceTelescopePatternData] | None = None error: HttpErrors | None = None -class ObservingTelescopePatternUpdateResponse(BaseModel): +class ObservingTelescopePatternUpdateResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None error: HttpErrors | None = None telescope_pattern: PersistenceTelescopePatternData | None = None -class ObservingUpdateProgramCommentResponse(BaseModel): +class ObservingUpdateProgramCommentResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None program_data: Annotated[PersistenceProgramData | None, Field(alias='programData')] = ( None @@ -1853,7 +2137,10 @@ class ObservingUpdateProgramCommentResponse(BaseModel): error: HttpErrors | None = None -class ObservingUpdateProgramCommentsResponse(BaseModel): +class ObservingUpdateProgramCommentsResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None program_data: Annotated[PersistenceProgramData | None, Field(alias='programData')] = ( None @@ -1861,7 +2148,10 @@ class ObservingUpdateProgramCommentsResponse(BaseModel): error: HttpErrors | None = None -class PersistenceInstrumentConfigurationData(BaseModel): +class PersistenceInstrumentConfigurationData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) espadons_configuration: Annotated[ InstrumentConfigurationDataEspadonsConfigurationData | None, Field(alias='espadonsConfiguration'), @@ -1898,7 +2188,10 @@ class PersistenceInstrumentConfigurationData(BaseModel): ) -class PersistenceLegacyReel(BaseModel): +class PersistenceLegacyReel(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) reel_og_token: Annotated[ str | None, Field(alias='reelOgToken', description='Token of the linked observing group.'), @@ -1914,7 +2207,10 @@ class PersistenceLegacyReel(BaseModel): ] = None -class PersistenceObservingTemplateData(BaseModel): +class PersistenceObservingTemplateData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) instrument_configuration: Annotated[ PersistenceInstrumentConfigurationData | None, Field(alias='instrumentConfiguration'), @@ -1928,7 +2224,7 @@ class PersistenceObservingTemplateData(BaseModel): description='Used for calculating cost for this pattern\nInvokes code which reads the ExposureInstructions and\nattributes a cost to it.', ), ] = None - itime: str | None = None + itime: int | None = None calculate_etime: Annotated[ PersistenceExposureTime | None, Field(alias='calculateEtime') ] = None @@ -1967,7 +2263,10 @@ class PersistenceObservingTemplateData(BaseModel): ] = None -class PersistencePhaseScheduleObservingGroup(BaseModel): +class PersistencePhaseScheduleObservingGroup(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) observing_block: Annotated[ ObservingGroupDataObservingBlock | None, Field(alias='observingBlock') ] = None @@ -1976,7 +2275,10 @@ class PersistencePhaseScheduleObservingGroup(BaseModel): ] = None -class PersistenceTargetMonitoringObservingGroupTimeConstraint(BaseModel): +class PersistenceTargetMonitoringObservingGroupTimeConstraint(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) number_of_iterations: Annotated[ int | None, Field(alias='numberOfIterations', description='How many observations to take.'), @@ -1996,7 +2298,7 @@ class PersistenceTargetMonitoringObservingGroupTimeConstraint(BaseModel): TypesDuration | None, Field(alias='timeIntervalToleranceMax') ] = None t0_millis: Annotated[ - str | None, + int | None, Field( alias='t0Millis', description="Time the observing group should start being observed, in Unix time\nmilliseconds.\n\nIf omitted, CFHT will start observing at the QC's discretion.", @@ -2011,25 +2313,37 @@ class PersistenceTargetMonitoringObservingGroupTimeConstraint(BaseModel): ] = None -class ObservingListProgramsResponse(BaseModel): +class ObservingListProgramsResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None entity: list[ObservingProgramInfo] | None = None error: HttpErrors | None = None -class ObservingObservingTemplateListResponse(BaseModel): +class ObservingObservingTemplateListResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None entity: list[PersistenceObservingTemplateData] | None = None error: HttpErrors | None = None -class ObservingObservingTemplateUpdateResponse(BaseModel): +class ObservingObservingTemplateUpdateResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None error: HttpErrors | None = None observing_template: PersistenceObservingTemplateData | None = None -class PersistenceObservingGroupContext(BaseModel): +class PersistenceObservingGroupContext(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) iteration_count: Annotated[ int | None, Field( @@ -2096,7 +2410,10 @@ class PersistenceObservingGroupContext(BaseModel): ) -class PersistenceTargetMonitoringObservingGroup(BaseModel): +class PersistenceTargetMonitoringObservingGroup(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) observing_block: Annotated[ ObservingGroupDataObservingBlock | None, Field(alias='observingBlock') ] = None @@ -2106,7 +2423,10 @@ class PersistenceTargetMonitoringObservingGroup(BaseModel): ] = None -class PersistenceExposureData(BaseModel): +class PersistenceExposureData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) token: str | None = None obsid: str | None = None exposure_number: Annotated[int | None, Field(alias='exposureNumber')] = None @@ -2150,7 +2470,10 @@ class PersistenceExposureData(BaseModel): ] = None -class PersistenceObservingGroupData(BaseModel): +class PersistenceObservingGroupData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) token: Annotated[ str | None, Field(description='Unique identifier for the observing group.') ] = None @@ -2228,7 +2551,10 @@ class PersistenceObservingGroupData(BaseModel): ] = None -class SnrBlockDataSnrBlockObservingComponents(BaseModel): +class SnrBlockDataSnrBlockObservingComponents(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) observing_component_token: Annotated[ str | None, Field(alias='observingComponentToken', title='the observing component'), @@ -2254,19 +2580,28 @@ class SnrBlockDataSnrBlockObservingComponents(BaseModel): ] = None -class ObservingObservingGroupListResponse(BaseModel): +class ObservingObservingGroupListResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None entity: list[PersistenceObservingGroupData] | None = None error: HttpErrors | None = None -class ObservingObservingGroupUpdateResponse(BaseModel): +class ObservingObservingGroupUpdateResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None error: HttpErrors | None = None entity: PersistenceObservingGroupData | None = None -class PersistenceSnrBlockData(BaseModel): +class PersistenceSnrBlockData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) token: str | None = None label: int | None = None cumulative_snr: Annotated[ @@ -2324,7 +2659,10 @@ class PersistenceSnrBlockData(BaseModel): ] = None -class ObservingAddSingleToSnrBlockResponse(BaseModel): +class ObservingAddSingleToSnrBlockResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None error: HttpErrors | None = None snr_block: Annotated[PersistenceSnrBlockData | None, Field(alias='snrBlock')] = None @@ -2333,7 +2671,10 @@ class ObservingAddSingleToSnrBlockResponse(BaseModel): ] = None -class ObservingSnrBlockListResponse(BaseModel): +class ObservingSnrBlockListResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) success: bool | None = None error: HttpErrors | None = None entity: list[PersistenceSnrBlockData] | None = None diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py new file mode 100644 index 0000000..bf40bee --- /dev/null +++ b/tests/cfht/test_online.py @@ -0,0 +1,16 @@ +import pytest + +from aeonlib.cfht.facility import CFHTFacility + +pytestmark = pytest.mark.online + + +def test_target_api(): + facility = CFHTFacility() + programs = facility.programs() + print(programs) + for program in programs: + print(program.program_data) + # assert program.program_data + # assert program.program_data.token + assert False From b8cadcf863d155be57164b054a0333d7ddf9be29 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 23 Jul 2026 10:00:05 -0700 Subject: [PATCH 07/33] first working online programs test --- src/aeonlib/cfht/facility.py | 3 --- tests/cfht/test_online.py | 10 +++------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/aeonlib/cfht/facility.py b/src/aeonlib/cfht/facility.py index 590b3f1..78208c9 100644 --- a/src/aeonlib/cfht/facility.py +++ b/src/aeonlib/cfht/facility.py @@ -27,9 +27,6 @@ def __init__(self): def programs(self) -> list[ObservingProgramInfo]: """Get the list of observing programs""" response = self._client.get("/programs/") - import json - - print(json.dumps(response.json(), indent=4)) response.raise_for_status() payload = response.json() diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index bf40bee..05eb134 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -5,12 +5,8 @@ pytestmark = pytest.mark.online -def test_target_api(): +def test_programs(): facility = CFHTFacility() programs = facility.programs() - print(programs) - for program in programs: - print(program.program_data) - # assert program.program_data - # assert program.program_data.token - assert False + assert programs[0].pi_info + assert programs[0].pi_info.first_name == "AEON" From 1cadaf2f0edad39c3f49a088485ad3fc93359e35 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 23 Jul 2026 10:21:10 -0700 Subject: [PATCH 08/33] Use OAS3 version of spec --- codegen/cfht/generator_oas3.py | 64 + src/aeonlib/cfht/facility.py | 7 +- src/aeonlib/cfht/models.py | 3054 +++++++++++++++----------------- tests/cfht/test_online.py | 2 + 4 files changed, 1475 insertions(+), 1652 deletions(-) create mode 100644 codegen/cfht/generator_oas3.py diff --git a/codegen/cfht/generator_oas3.py b/codegen/cfht/generator_oas3.py new file mode 100644 index 0000000..50d3195 --- /dev/null +++ b/codegen/cfht/generator_oas3.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +import sys +from pathlib import Path +from typing import Any + +import yaml +from datamodel_code_generator import ( + DataModelType, + Formatter, + GenerateConfig, + InputFileType, + OpenAPIScope, + TargetPydanticVersion, + generate, +) +from datamodel_code_generator import ( + Error as DataModelCodegenError, +) + +OPENAPI_SPEC_PATH = Path(__file__).with_name("cfhtopenapi.yaml") +GENERATED_HEADER = "# Auto generated file, do not edit\n# ruff: noqa: E741\n" + + +def generate_models(openapi_document: dict[str, Any]) -> str: + config = GenerateConfig( + input_file_type=InputFileType.OpenAPI, + input_filename="cfhtopenapi.yaml", + openapi_scopes=[OpenAPIScope.Schemas], + output_model_type=DataModelType.PydanticV2BaseModel, + base_class="aeonlib.cfht.base_model.CFHTBaseModel", + target_pydantic_version=TargetPydanticVersion.V2_11, + use_annotated=True, + field_constraints=True, + set_default_enum_member=True, + snake_case_field=True, + allow_population_by_field_name=True, + formatters=[Formatter.BUILTIN], + ) + + result = generate(openapi_document, config=config) + if not isinstance(result, str): + raise RuntimeError( + "Expected string output from datamodel-code-generator " + f"got {type(result).__name__}." + ) + + return GENERATED_HEADER + result + "\n" + + +def main() -> int: + with OPENAPI_SPEC_PATH.open(encoding="utf-8") as f: + document = yaml.safe_load(f) + generated = generate_models(document) + _ = sys.stdout.write(generated) + + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (yaml.YAMLError, DataModelCodegenError, OSError, RuntimeError) as exc: + print(f"CFHT model generation failed: {exc}", file=sys.stderr) + raise SystemExit(1) diff --git a/src/aeonlib/cfht/facility.py b/src/aeonlib/cfht/facility.py index 78208c9..cf01d00 100644 --- a/src/aeonlib/cfht/facility.py +++ b/src/aeonlib/cfht/facility.py @@ -4,7 +4,7 @@ from aeonlib.conf import settings -from .models import ObservingProgramInfo +from .models import ProgramInfo INSTRUMENTS = Literal["SPIROU", "ESPADONS", "MEGACAM"] @@ -24,13 +24,12 @@ def __init__(self): } self._client = httpx.Client(base_url=base_url, headers=headers) - def programs(self) -> list[ObservingProgramInfo]: + def programs(self) -> list[ProgramInfo]: """Get the list of observing programs""" response = self._client.get("/programs/") response.raise_for_status() payload = response.json() return [ - ObservingProgramInfo.model_validate(program) - for program in payload.get("entity", []) + ProgramInfo.model_validate(program) for program in payload.get("entity", []) ] diff --git a/src/aeonlib/cfht/models.py b/src/aeonlib/cfht/models.py index f0b9433..d29ecf1 100644 --- a/src/aeonlib/cfht/models.py +++ b/src/aeonlib/cfht/models.py @@ -1,8 +1,8 @@ # Auto generated file, do not edit # ruff: noqa: E741 # generated by datamodel-codegen: -# filename: cfht.openapi3.json -# timestamp: 2026-07-22T22:53:53+00:00 +# filename: cfhtopenapi.yaml +# timestamp: 2026-07-23T17:17:02+00:00 from __future__ import annotations @@ -10,7 +10,39 @@ from typing import Annotated from aeonlib.cfht.base_model import CFHTBaseModel -from pydantic import Base64Str, ConfigDict, Field +from pydantic import ConfigDict, Field + + +class AddSingleToSnrBlockRequest(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + runid: Annotated[ + str | None, Field(description='Unique identifier of the science program.') + ] = None + snr_block_token: Annotated[ + str | None, + Field( + description='Unique identifier of an existing SNR block to create the new observing group for.' + ), + ] = None + observing_group_token: Annotated[ + str | None, + Field( + description='Unique identifier of the observing group. If an observing group target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789' + ), + ] = None + + +class Instrument(Enum): + unknown = 'UNKNOWN' + cfh12_k = 'CFH12K' + megacam = 'MEGACAM' + wircam = 'WIRCAM' + espadons = 'ESPADONS' + sitelle = 'SITELLE' + spirou = 'SPIROU' + wenaokeao = 'WENAOKEAO' class AllocationDataAgency(CFHTBaseModel): @@ -21,30 +53,121 @@ class AllocationDataAgency(CFHTBaseModel): name: str | None = None -class CalibrationCalibrationMode(Enum): - no_calibration_mode = 'no_calibration_mode' - laser = 'laser' - phase = 'phase' +class BigInteger(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + value: str | None = None -class CalibrationCalibrationPosition(Enum): - no_calibration_position = 'no_calibration_position' - zenith = 'zenith' - target = 'target' +class CatalogIdentifier(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + catalog: Annotated[str | None, Field(description='Catalog to reference.')] = None + identifier: Annotated[ + str | None, + Field( + description='Valid identifier in the catalog. Note: this ID will contain a catalog name, because spacing matters in catalogs, the full ID for an object is like M 4. Catalog is M, id is identifier could be 4, but to reference m4, you need the spaces. NB: These identity values can not be set in fits headers because they do not have to conform to the length restrictions.' + ), + ] = None class ConfigDithering(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - is_staring: Annotated[bool | None, Field(alias='isStaring')] = None + is_staring: bool | None = None + + +class StartSequence(Enum): + object = 'OBJECT' + target = 'TARGET' + sky = 'SKY' + dark = 'DARK' + flat = 'FLAT' + snap = 'SNAP' + bias = 'BIAS' + focus = 'FOCUS' + align = 'ALIGN' + comparison = 'COMPARISON' + acquire = 'ACQUIRE' + fabry_perot = 'FABRY_PEROT' + twilight_flats = 'TWILIGHT_FLATS' + photometry = 'PHOTOMETRY' + sfocus = 'SFOCUS' + + +class ConfigNodding(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + start_sequence: StartSequence | None = None + target_db: float | None = None + + +class DoubleMinMax(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + min: float | None = None + max: float | None = None + + +class DoubleValue(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + value: Annotated[float | None, Field(description='The double value.')] = None + + +class DisplayUnit(Enum): + days = 'DAYS' + hours = 'HOURS' + minutes = 'MINUTES' + seconds = 'SECONDS' + + +class Duration(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + millis: int | None = None + display_unit: Annotated[ + DisplayUnit | None, + Field( + description='Only needed for rendering UI, should display the duration using the units specified. If unset, assume DAYS' + ), + ] = None + + +class DynamicExposureTimeLimit(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + minimum_multiplier: float | None = None + maximum_multiplier: float | None = None + + +class Errors(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + message: list[str] | None = None + + +class EspadonsStatusExposureMeterProcessingResult(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + snr: float | None = None class EspadonsStatusGuiderSeeingProcessingResult(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - guider_seeing: Annotated[float | None, Field(alias='guiderSeeing')] = None + guider_seeing: float | None = None class ExposureDataAttribution(CFHTBaseModel): @@ -52,78 +175,57 @@ class ExposureDataAttribution(CFHTBaseModel): validate_by_name=True, ) runid: str | None = None - observing_block_label: Annotated[ + observing_block_display_label: Annotated[ str | None, Field( - title='If displaying the observing block to the user, this uniquely identifies it in the context\nof an observing group, i.e. OG3-2' + description='If displaying the observing block to the user, this uniquely identifies it in the context of an observing group, i.e. OG3-2' ), ] = None - observing_block_label_int: Annotated[ - int | None, Field(title='Label of the observing block') + observing_block_label: Annotated[ + int | None, Field(description='Label of the observing block') ] = None observing_block_token: Annotated[ str | None, Field( - alias='observingBlockToken', - description='Token of the observing block this exposure is attributed to.', + description='Token of the observing block this exposure is attributed to.' ), ] = None observing_group_label: Annotated[ int | None, Field( - alias='observingGroupLabel', - description='Label of the observing group this exposure is attributed to.', + description='Label of the observing group this exposure is attributed to.' ), ] = None observing_group_token: Annotated[ str | None, Field( - alias='observingGroupToken', - description='Token of the observing group this exposure is attributed to.', + description='Token of the observing group this exposure is attributed to.' ), ] = None observing_component_token: Annotated[ str | None, Field( - alias='observingComponentToken', - description='Token of the observing component this exposure is attributed to.', + description='Token of the observing component this exposure is attributed to.' ), ] = None exposure_number: Annotated[ - int | None, - Field( - alias='exposureNumber', - description='exposure number this exposure is attributed to.', - ), - ] = None - instrument_run: Annotated[ - str | None, Field(title='the queue run this exposure was taken under') + int | None, Field(description='exposure number this exposure is attributed to.') ] = None - attributing_agency: Annotated[ - str | None, Field(alias='attributingAgency', title='the agency id') + queue_run: Annotated[ + str | None, Field(description='the queue run this exposure was taken under') ] = None - tac_rank: Annotated[int | None, Field(alias='tacRank')] = None - tac_grade: Annotated[str | None, Field(alias='tacGrade')] = None - - -class ExposureDataEspadonsStatusExposureMeterProcessingResult(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - snr: float | None = None + attributing_agency: Annotated[str | None, Field(description='the agency id')] = None + tac_rank: int | None = None + tac_grade: str | None = None -class ExposureDataSpirouStatusExposureMeterProcessingResult(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - flux: float | None = None - snr: float | None = None - mid_exposure_date_millis: Annotated[ - float | None, Field(alias='midExposureDateMillis') - ] = None - spemsnr: Annotated[float | None, Field(title='Calculated SNR')] = None - spemsnrc: float | None = None +class ExpStatus(Enum): + unobserved = 'UNOBSERVED' + observed = 'OBSERVED' + processed = 'PROCESSED' + graded = 'GRADED' + validated = 'VALIDATED' + rejected = 'REJECTED' class ExposureDataWenaStatus(CFHTBaseModel): @@ -136,20 +238,16 @@ class ExposureDataWircamStatus(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - iq_uncertainty: Annotated[float | None, Field(alias='iqUncertainty')] = None + iq_uncertainty: float | None = None absorption: float | None = None - absorption_uncertainty: Annotated[ - float | None, Field(alias='absorptionUncertainty') - ] = None - number_of_stars_iq: Annotated[int | None, Field(alias='numberOfStarsIq')] = None - number_of_stars_absorption: Annotated[ - int | None, Field(alias='numberOfStarsAbsorption') - ] = None - absolute_sky_level: Annotated[float | None, Field(alias='absoluteSkyLevel')] = None + absorption_uncertainty: float | None = None + number_of_stars_iq: int | None = None + number_of_stars_absorption: int | None = None + absolute_sky_level: float | None = None elongation: float | None = None mdcoords: int | None = None mdrepeat: int | None = None - actual_filter: Annotated[str | None, Field(alias='actualFilter')] = None + actual_filter: str | None = None iq: float | None = None @@ -160,24 +258,29 @@ class ExposureStatusFile(CFHTBaseModel): absolute_path: Annotated[ str | None, Field( - alias='absolutePath', - description='Absolute path to directory file is homed in. Starts with / and ends with /.', + description='Absolute path to directory file is homed in. Starts with / and ends with /.' ), ] = None base_name: Annotated[ - str | None, Field(alias='baseName', title='Name of file without extension') + str | None, Field(description='Name of file without extension') ] = None ext: Annotated[str | None, Field(description='Name of extenion including .')] = None -class FindingChartDataApproval(CFHTBaseModel): +class ExposureTime(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - approved_at_millis: Annotated[int | None, Field(alias='approvedAtMillis')] = None - approved_by_usertoken: Annotated[str | None, Field(alias='approvedByUsertoken')] = ( - None + desired_snr: float | None = None + exposure_time_ms: int | None = None + + +class FindingChartDataApproval(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, ) + approved_at_millis: int | None = None + approved_by_usertoken: str | None = None class FixedTargetProperMotion(CFHTBaseModel): @@ -187,33 +290,77 @@ class FixedTargetProperMotion(CFHTBaseModel): ra_mas: Annotated[ float | None, Field( - alias='raMas', - description='Motion in the right ascension axis in milliarcseconds/year.', + description='Motion in the right ascension axis in milliarcseconds/year.' ), ] = None dec_mas: Annotated[ float | None, - Field( - alias='decMas', - description='Motion in the declination axis in milliarcseconds/year.', - ), + Field(description='Motion in the declination axis in milliarcseconds/year.'), + ] = None + + +class GoogleProtobufAny(CFHTBaseModel): + model_config = ConfigDict( + extra='allow', + validate_by_name=True, + ) + field_type: Annotated[ + str | None, + Field(alias='@type', description='The type of the serialized message.'), ] = None -class InstrumentConfigurationDataEspadonsConfigurationDataOObservingMode(Enum): +class ITimeData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + exposure_time_millis: int | None = None + overhead_time_millis: int | None = None + + +class Tracking(Enum): + unknown = 'Unknown' + sid = 'SID' + nonsid = 'NONSID' + sid_nog = 'SID_NOG' + nonsid_g = 'NONSID_G' + + +class ObservingMode(Enum): polarimetry = 'Polarimetry' spectroscopy_star_only = 'Spectroscopy_star_only' spectroscopy_star_sky = 'Spectroscopy_star_sky' -class InstrumentConfigurationDataMegacamConfigurationDataBinning(Enum): - one_one = 'one_one' - two_two = 'two_two' - three_three = 'three_three' - four_four = 'four_four' +class ReadoutMode(Enum): + normal = 'Normal' + slow = 'Slow' + fast = 'Fast' + + +class StokesParameter(Enum): + q = 'Q' + u = 'U' + v = 'V' + w = 'W' + i = 'I' + + +class InstrumentConfigurationDataEspadonsConfigurationData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + observing_mode: ObservingMode | None = None + readout_mode: ReadoutMode | None = None + stokes_parameter: StokesParameter | None = None + number_of_sequences: int | None = None + number_of_exposures: int | None = None + use_snr: bool | None = None + snr_wave: float | None = None + snr: float | None = None -class InstrumentConfigurationDataMegacamConfigurationDataFilter(Enum): +class Filter(Enum): unknown = 'Unknown' u = 'u' g = 'g' @@ -242,13 +389,21 @@ class InstrumentConfigurationDataMegacamConfigurationDataFilter(Enum): m4376 = 'M4376' -class InstrumentConfigurationDataSitelleConfigurationDataBinning(Enum): +class Binning(Enum): one_one = 'one_one' two_two = 'two_two' three_three = 'three_three' + four_four = 'four_four' + + +class SnrMode(Enum): + none = 'NONE' + standard = 'STANDARD' + cumulative = 'CUMULATIVE' + zero_point = 'ZERO_POINT' -class InstrumentConfigurationDataSitelleConfigurationDataFilter(Enum): +class Filter1(Enum): unknown = 'UNKNOWN' none = 'NONE' c1 = 'C1' @@ -263,17 +418,36 @@ class InstrumentConfigurationDataSitelleConfigurationDataFilter(Enum): sn6 = 'SN6' -class InstrumentConfigurationDataSpirouConfigurationDataOObservingMode(Enum): +class Binning1(Enum): + one_one = 'one_one' + two_two = 'two_two' + three_three = 'three_three' + + +class ResolutionMode(Enum): + no_resolution_mode = 'no_resolution_mode' + hires = 'hires' + medres = 'medres' + + +class ObservingMode1(Enum): polarimetry = 'Polarimetry' star = 'Star' -class InstrumentConfigurationDataTracking(Enum): - unknown = 'Unknown' - sid = 'SID' - nonsid = 'NONSID' - sid_nog = 'SID_NOG' - nonsid_g = 'NONSID_G' +class Mode(Enum): + dark = 'Dark' + fabry_perot = 'Fabry_Perot' + wave_hc1 = 'Wave_Hc1' + wave_hc2 = 'Wave_Hc2' + flat = 'FLAT' + + +class SkyObservation(Enum): + none = 'None' + before = 'Before' + after = 'After' + before_and_after = 'BeforeAndAfter' class InstrumentConfigurationDataWenaConfigurationData(CFHTBaseModel): @@ -282,7 +456,7 @@ class InstrumentConfigurationDataWenaConfigurationData(CFHTBaseModel): ) -class InstrumentConfigurationDataWircamConfigurationDataFilter(Enum): +class Filter2(Enum): unknown = 'UNKNOWN' w = 'W' h = 'H' @@ -300,61 +474,94 @@ class InstrumentConfigurationDataWircamConfigurationDataFilter(Enum): c_onar = 'COnar' -class MMode(Enum): - dark = 'Dark' - fabry_perot = 'Fabry_Perot' - wave_hc1 = 'Wave_Hc1' - wave_hc2 = 'Wave_Hc2' - flat = 'FLAT' +class InstrumentConfigurationDataWircamConfigurationData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + filter: Filter2 | None = None + is_microdithering: bool | None = None + exposures_per_dp_position: Annotated[ + int | None, Field(description='Must always be > 0') + ] = None + accurate_pointing: bool | None = None + defocus: float | None = None + is_staring: bool | None = None -class MegacamConfigurationDataSnrMode(Enum): - none = 'NONE' - standard = 'STANDARD' - cumulative = 'CUMULATIVE' - zero_point = 'ZERO_POINT' +class Int32Value(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + value: Annotated[int | None, Field(description='The int32 value.')] = None -class MegacamStatusElixirProcessingResult(CFHTBaseModel): +class Int64Value(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - iq: float | None = None - sky_background: Annotated[float | None, Field(alias='skyBackground')] = None - snr: float | None = None - zeropoint: float | None = None + value: Annotated[int | None, Field(description='The int64 value.')] = None -class ObservingBlockConditionRequisite(CFHTBaseModel): +class LegacyReel(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - requisite_observing_block_index: Annotated[ - int | None, - Field( - alias='requisiteObservingBlockIndex', - description='This should match the index of the observing block in the\nread_observing_block list.', - ), + reel_og_token: Annotated[ + str | None, Field(description='Token of the linked observing group.') ] = None - wait_period_min_millis: Annotated[ + delay: Duration | None = None + window: Duration | None = None + reel_og_label: Annotated[ int | None, Field( - alias='waitPeriodMinMillis', - description='Minimum time to wait after requisite observing block exposure has\nbeen taken.', + description='Label of the observing group which must be observed before this one. storage: transient api_visibility: read only' ), ] = None - wait_period_millis: Annotated[ - int | None, - Field( - alias='waitPeriodMillis', - description='Optimal to wait after requisite observing block exposure has\nbeen taken.', - ), + + +class ListOfIdentifiers(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + catalog_identifier: list[CatalogIdentifier] | None = None + + +class MegacamStatusElixirProcessingResult(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + iq: float | None = None + sky_background: float | None = None + snr: float | None = None + zeropoint: float | None = None + + +class ObservingBlockConditionRequisite(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + requisite_observing_block_index: Annotated[ + int | None, + Field( + description='This should match the index of the observing block in the read_observing_block list.' + ), + ] = None + wait_period_min_millis: Annotated[ + int | None, + Field( + description='Minimum time to wait after requisite observing block exposure has been taken.' + ), + ] = None + wait_period_millis: Annotated[ + int | None, + Field( + description='Optimal to wait after requisite observing block exposure has been taken.' + ), ] = None wait_period_max_millis: Annotated[ int | None, Field( - alias='waitPeriodMaxMillis', - description='Minimum time to wait after requisite observing block exposure has\nbeen taken.', + description='Minimum time to wait after requisite observing block exposure has been taken.' ), ] = None unbounded: Annotated[ @@ -372,16 +579,12 @@ class ObservingBlockConditionWindow(CFHTBaseModel): observe_after: Annotated[ int | None, Field( - alias='observeAfter', - description='Start of the observation window, in Unix time milliseconds.', + description='Start of the observation window, in Unix time milliseconds.' ), ] = None observe_before: Annotated[ int | None, - Field( - alias='observeBefore', - description='End of the observation window, in Unix time milliseconds.', - ), + Field(description='End of the observation window, in Unix time milliseconds.'), ] = None @@ -392,41 +595,102 @@ class ObservingBlockObservingComponent(CFHTBaseModel): target_token: Annotated[ str | None, Field( - alias='targetToken', - description='Unique identifier of the target used in this observing component.\n\nWhen creating or updating an observing group, this must be an\nexisting target, or on the map of the request to be created.', + description='Unique identifier of the target used in this observing component. When creating or updating an observing group, this must be an existing target, or on the map of the request to be created.' ), ] = None observing_template_token: Annotated[ str | None, Field( - alias='observingTemplateToken', - description='Unique identifier of the observing template used in this\nobserving component.\n\nWhen creating or updating an observing group, this must be an\nexisting observing template, or on the map of the request to be\ncreated.', + description='Unique identifier of the observing template used in this observing component. When creating or updating an observing group, this must be an existing observing template, or on the map of the request to be created.' ), ] = None target_label: Annotated[ int | None, Field( - alias='targetLabel', - description='Label of the target used in this observing component.\nThis value is only set on the read_observing_block.\n\napi_visibility: read only', + description='Label of the target used in this observing component. This value is only set on the read_observing_block. api_visibility: read only' ), ] = None observing_template_label: Annotated[ int | None, Field( - alias='observingTemplateLabel', - description='Label of the observing template used in this observing component.\nThis value is only set on the read_observing_block.\n\napi_visibility: read only', + description='Label of the observing template used in this observing component. This value is only set on the read_observing_block. api_visibility: read only' ), ] = None snr_block_token: Annotated[ str | None, Field( - alias='snrBlockToken', - description='Unique identifier of the SNR block used in this observing\ncomponent, if any. Must be set if and only if the observing\ncomponent uses a MegaCam cumulative SNR mode observing template.\n\nIf this is set when creating or updating an observing group,\nthe observing component will be tied to the SNR block with this\ntoken if it already exists, otherwise a new SNR block will be\ncreated using this token.', + description='Unique identifier of the SNR block used in this observing component, if any. Must be set if and only if the observing component uses a MegaCam cumulative SNR mode observing template. If this is set when creating or updating an observing group, the observing component will be tied to the SNR block with this token if it already exists, otherwise a new SNR block will be created using this token.' ), ] = None -class ObservingGroupDataDitherScheme(Enum): +class ObservingGroupPriority(Enum): + unknown_og_priority = 'UNKNOWN_OG_PRIORITY' + high = 'HIGH' + medium = 'MEDIUM' + low = 'LOW' + inactive = 'INACTIVE' + + +class ObservingGroupState(Enum): + not_started = 'NOT_STARTED' + observed = 'OBSERVED' + started = 'STARTED' + validated = 'VALIDATED' + + +class ExposureType(Enum): + object = 'OBJECT' + target = 'TARGET' + sky = 'SKY' + dark = 'DARK' + flat = 'FLAT' + snap = 'SNAP' + bias = 'BIAS' + focus = 'FOCUS' + align = 'ALIGN' + comparison = 'COMPARISON' + acquire = 'ACQUIRE' + fabry_perot = 'FABRY_PEROT' + twilight_flats = 'TWILIGHT_FLATS' + photometry = 'PHOTOMETRY' + sfocus = 'SFOCUS' + + +class OgPriority(Enum): + unknown_og_priority = 'UNKNOWN_OG_PRIORITY' + high = 'HIGH' + medium = 'MEDIUM' + low = 'LOW' + inactive = 'INACTIVE' + + +class State(Enum): + not_started = 'NOT_STARTED' + observed = 'OBSERVED' + started = 'STARTED' + validated = 'VALIDATED' + + +class TargetType(Enum): + object = 'OBJECT' + target = 'TARGET' + sky = 'SKY' + dark = 'DARK' + flat = 'FLAT' + snap = 'SNAP' + bias = 'BIAS' + focus = 'FOCUS' + align = 'ALIGN' + comparison = 'COMPARISON' + acquire = 'ACQUIRE' + fabry_perot = 'FABRY_PEROT' + twilight_flats = 'TWILIGHT_FLATS' + photometry = 'PHOTOMETRY' + sfocus = 'SFOCUS' + + +class DitherScheme(Enum): none = 'NONE' standard = 'STANDARD' group = 'GROUP' @@ -439,104 +703,216 @@ class ObservingGroupDataObservingBlock(CFHTBaseModel): observing_component: Annotated[ list[ObservingBlockObservingComponent] | None, Field( - alias='observingComponent', - description='List of observing components that comprise this observing block.', + description='List of observing components that comprise this observing block.' ), ] = None token: Annotated[ str | None, Field( - description='Unique identifier for the observing block.\n\napi_visibility: operations read only' + description='Unique identifier for the observing block. api_visibility: operations read only' ), ] = None label: Annotated[ int | None, Field( - description='Index of the observing block within the program, set by the system\nautomatically. Used for reference in communications with QSO staff.\n\nstorage: transient\napi_visibility: read only' + description='Index of the observing block within the program, set by the system automatically. Used for reference in communications with QSO staff. storage: transient api_visibility: read only' ), ] = None -class ObservingGroupDataObservingGroupState(Enum): - not_started = 'NOT_STARTED' - observed = 'OBSERVED' - started = 'STARTED' - validated = 'VALIDATED' +class ObservingGroupDeleteResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + success: bool | None = None + error: Errors | None = None + + +class ObservingTemplateDeleteResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + success: bool | None = None + error: Errors | None = None + + +class OffsetCoordinate(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + ra_offset: Annotated[float | None, Field(description='Right ascension, in degrees')] = ( + None + ) + dec_offset: Annotated[float | None, Field(description='Declination, in degrees')] = ( + None + ) + exposure_number: int | None = None + + +class PhaseScheduleObservingGroupPhaseSchedule(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + earliest_observe_date_millis: Annotated[ + int | None, + Field(description='The earliest moment to observe, in Unix time milliseconds.'), + ] = None + tolerance: Duration | None = None + period: Duration | None = None + period_tolerance: Duration | None = None class PhotometryAperture(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - optimal_aperture: Annotated[bool | None, Field(alias='optimalAperture')] = None + optimal_aperture: bool | None = None fixed_aperture: Annotated[ - float | None, - Field(alias='fixedAperture', title='Radius of the aperture in arcsec'), + float | None, Field(description='Radius of the aperture in arcsec') + ] = None + ninety_six_percent_flux_aperture: bool | None = None + + +class PointingOffsetData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + token: str | None = None + name: str | None = None + offset: OffsetCoordinate | None = None + label: int | None = None + version: int | None = None + user_token: Annotated[ + str | None, + Field( + description='this is the PI of the program that this entity will be recorded forif null should be derived using the program_data.contact_info.user_tokenif null, then a system ?? gross' + ), ] = None - ninety_six_percent_flux_aperture: Annotated[ - bool | None, Field(alias='ninetySixPercentFluxAperture') + instrument: Annotated[ + Instrument | None, Field(description='the instrument for the pointing') ] = None + is_system: bool | None = None -class ProgramDataComments(CFHTBaseModel): +class PointingOffsetDeleteResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - question: str | None = None - comment: str | None = None + success: bool | None = None + error: Errors | None = None -class ProgramDataContactInfo(CFHTBaseModel): +class PointingOffsetGetResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + success: bool | None = None + entity: PointingOffsetData | None = None + error: Errors | None = None + + +class PointingOffsetListResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + success: bool | None = None + entity: list[PointingOffsetData] | None = None + error: Errors | None = None + + +class PointingOffsetUpdateRequest(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - pi_first_name: Annotated[str | None, Field(alias='piFirstName')] = None - pi_last_name: Annotated[str | None, Field(alias='piLastName')] = None - pi_email: Annotated[str | None, Field(alias='piEmail')] = None - user_token: Annotated[str | None, Field(alias='userToken')] = None + runid: Annotated[ + str | None, Field(description='Unique identifier your science program.') + ] = None + token: str | None = None + lock_version: Int32Value | None = None + entity: PointingOffsetData | None = None + + +class PointingOffsetUpdateResponse(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + success: bool | None = None + error: Errors | None = None + entity: PointingOffsetData | None = None -class ProgramDataProgramType(Enum): +class ProgramType(Enum): regular = 'REGULAR' target_of_opportunity = 'TARGET_OF_OPPORTUNITY' calibration = 'CALIBRATION' snapshot = 'SNAPSHOT' +class ProgramDataComments(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + question: str | None = None + comment: str | None = None + + +class ProgramDataContactInfo(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + pi_first_name: str | None = None + pi_last_name: str | None = None + pi_email: str | None = None + user_token: str | None = None + + class ProgramInfoPiInfo(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - ldap_username: Annotated[str | None, Field(alias='ldapUsername')] = None - email_address: Annotated[str | None, Field(alias='emailAddress')] = None - first_name: Annotated[str | None, Field(alias='firstName')] = None - last_name: Annotated[str | None, Field(alias='lastName')] = None - legacy_username: Annotated[str | None, Field(alias='legacyUsername')] = None - user_token: Annotated[str | None, Field(alias='userToken')] = None + ldap_username: str | None = None + email_address: str | None = None + first_name: str | None = None + last_name: str | None = None + legacy_username: str | None = None + user_token: str | None = None -class RReadoutMode(Enum): - normal = 'Normal' - slow = 'Slow' - fast = 'Fast' +class RollupValue(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + requested: int | None = None + started: int | None = None + observed: int | None = None + validated: int | None = None -class SitelleConfigurationDataCalibration(CFHTBaseModel): +class SingleObservingGroup(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - calibration_position: Annotated[ - CalibrationCalibrationPosition | None, Field(alias='calibrationPosition') - ] = CalibrationCalibrationPosition.no_calibration_position - calibration_mode: Annotated[ - CalibrationCalibrationMode | None, Field(alias='calibrationMode') - ] = CalibrationCalibrationMode.no_calibration_mode + observing_block: ObservingGroupDataObservingBlock | None = None -class SitelleConfigurationDataResolutionMode(Enum): - no_resolution_mode = 'no_resolution_mode' - hires = 'hires' - medres = 'medres' +class CalibrationPosition(Enum): + no_calibration_position = 'no_calibration_position' + zenith = 'zenith' + target = 'target' + + +class CalibrationMode(Enum): + no_calibration_mode = 'no_calibration_mode' + laser = 'laser' + phase = 'phase' + + +class SitelleConfigurationDataCalibration(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + calibration_position: CalibrationPosition | None = None + calibration_mode: CalibrationMode | None = None class SitelleStatusCamera(CFHTBaseModel): @@ -544,18 +920,30 @@ class SitelleStatusCamera(CFHTBaseModel): validate_by_name=True, ) iq: float | None = None - iq_rms: Annotated[float | None, Field(alias='iqRms')] = None - pixel_shift_x: Annotated[float | None, Field(alias='pixelShiftX')] = None - pixel_shift_y: Annotated[float | None, Field(alias='pixelShiftY')] = None - number_of_stars: Annotated[int | None, Field(alias='numberOfStars')] = None + iq_rms: float | None = None + pixel_shift_x: float | None = None + pixel_shift_y: float | None = None + number_of_stars: int | None = None me: float | None = None -class SpirouConfigurationDataSkyObservation(Enum): - none = 'None' - before = 'Before' - after = 'After' - before_and_after = 'BeforeAndAfter' +class SkyCoordinate(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + ra: Annotated[float | None, Field(description='Right Ascension, in degrees')] = None + dec: Annotated[float | None, Field(description='Declination, in degrees')] = None + + +class SpirouStatusExposureMeterProcessingResult(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + flux: float | None = None + snr: float | None = None + mid_exposure_date_millis: float | None = None + spemsnr: Annotated[float | None, Field(description='Calculated SNR')] = None + spemsnrc: float | None = None class SpirouStatusGuiderProcessingResult(CFHTBaseModel): @@ -564,22 +952,14 @@ class SpirouStatusGuiderProcessingResult(CFHTBaseModel): ) etime: float | None = None isustate: str | None = None - sbdens_p: Annotated[float | None, Field(alias='sbdensP')] = None - hole_position_offset_x: Annotated[float | None, Field(alias='holePositionOffsetX')] = ( - None - ) - hole_position_offset_y: Annotated[float | None, Field(alias='holePositionOffsetY')] = ( - None - ) - star_to_null_position_offset_x: Annotated[ - float | None, Field(alias='starToNullPositionOffsetX') - ] = None - star_to_null_position_offset_y: Annotated[ - float | None, Field(alias='starToNullPositionOffsetY') - ] = None - magnitude_estimate: Annotated[float | None, Field(alias='magnitudeEstimate')] = None - seeing_estimate: Annotated[float | None, Field(alias='seeingEstimate')] = None - sgisust: Annotated[str | None, Field(title='per 160676372')] = None + sbdens_p: float | None = None + hole_position_offset_x: float | None = None + hole_position_offset_y: float | None = None + star_to_null_position_offset_x: float | None = None + star_to_null_position_offset_y: float | None = None + magnitude_estimate: float | None = None + seeing_estimate: float | None = None + sgisust: Annotated[str | None, Field(description='per 160676372')] = None sggdst: str | None = None sgfrmrt: float | None = None sgetime: float | None = None @@ -610,7 +990,7 @@ class SpirouStatusGuiderProcessingResult(CFHTBaseModel): sgcmagn: float | None = None sgemagn: float | None = None sgssee: float | None = None - trg_type: Annotated[str | None, Field(alias='trgType')] = None + trg_type: str | None = None class SpirouStatusPipelineProcessingResult(CFHTBaseModel): @@ -627,512 +1007,301 @@ class SpirouStatusPipelineProcessingResult(CFHTBaseModel): ccfcontr: float | None = None ccfrvc: float | None = None ccffwhm: float | None = None - ccfmask_str: Annotated[str | None, Field(alias='ccfmaskStr')] = None + ccfmask_str: str | None = None -class TargetDataMagnitude(CFHTBaseModel): +class Status(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - u: Annotated[float | None, Field(alias='U', title='U-band magnitude')] = None - b: Annotated[float | None, Field(alias='B', title='B-band magnitude')] = None - v: Annotated[ - float | None, + code: Annotated[ + int | None, Field( - alias='V', - description='Required for targets observed with ESPaDonS.', - title='V-band magnitude', + description='The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code].' ), ] = None - r: Annotated[float | None, Field(alias='R', title='R-band magnitude')] = None - i: Annotated[float | None, Field(alias='I', title='I-band magnitude')] = None - g: Annotated[float | None, Field(alias='G', title='G-band magnitude')] = None - j: Annotated[float | None, Field(alias='J', title='J-band magnitude')] = None - h: Annotated[ - float | None, + message: Annotated[ + str | None, Field( - alias='H', - description='Required for targets observed with SPIRou.', - title='H-band magnitude', + description='A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client.' ), ] = None - k: Annotated[float | None, Field(alias='K', title='K-band magnitude')] = None - uu: Annotated[float | None, Field(title='u-band magnitude')] = None - gg: Annotated[float | None, Field(title='g-band magnitude')] = None - rr: Annotated[float | None, Field(title='r-band magnitude')] = None - ii: Annotated[float | None, Field(title='i-band magnitude')] = None - zz: Annotated[float | None, Field(title='z-band magnitude')] = None - ab: Annotated[ - float | None, + details: Annotated[ + list[GoogleProtobufAny] | None, Field( - alias='AB', - description='Required for targets observed with MegaCam.', - title='AB magnitude', + description='A list of messages that carry the error details. There is a common set of message types for APIs to use.' ), ] = None -class TargetListRequestTargetType(Enum): - all = 'ALL' - fixed = 'FIXED' - moving = 'MOVING' - - -class TargetTimeDataView(Enum): - requested = 'REQUESTED' - actual = 'ACTUAL' - - -class TimeAccountingDataTimeAccountingStatus(Enum): - not_started = 'NOT_STARTED' - started = 'STARTED' - observed = 'OBSERVED' - validated = 'VALIDATED' - - -class UpenaProcessingResulth2oMm(CFHTBaseModel): +class SubTotalsData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - raw_density: Annotated[float | None, Field(alias='rawDensity')] = None - at_zenith: Annotated[float | None, Field(alias='atZenith')] = None + etime: RollupValue | None = None + overheadtime: RollupValue | None = None + exposure_count: RollupValue | None = None + child_count: RollupValue | None = None + etime_configured: RollupValue | None = None + overhead_time_configured: RollupValue | None = None + completeness: Annotated[ + float | None, Field(description='Percentage completed for the entity.') + ] = None -class HttpErrors(CFHTBaseModel): +class TargetDataFixedTarget(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - messages: list[str] | None = None + coordinate: SkyCoordinate | None = None + proper_motion: FixedTargetProperMotion | None = None + computed_coordinate: SkyCoordinate | None = None + estimated_radial_velocity_kmps: DoubleValue | None = None -class ObservingObservingGroupDeleteResponse(CFHTBaseModel): +class TargetDataMagnitude(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - success: bool | None = None - error: HttpErrors | None = None + u: Annotated[DoubleValue | None, Field(alias='U')] = None + b: Annotated[DoubleValue | None, Field(alias='B')] = None + v: Annotated[DoubleValue | None, Field(alias='V')] = None + r: Annotated[DoubleValue | None, Field(alias='R')] = None + i: Annotated[DoubleValue | None, Field(alias='I')] = None + g: Annotated[DoubleValue | None, Field(alias='G')] = None + j: Annotated[DoubleValue | None, Field(alias='J')] = None + h: Annotated[DoubleValue | None, Field(alias='H')] = None + k: Annotated[DoubleValue | None, Field(alias='K')] = None + uu: DoubleValue | None = None + gg: DoubleValue | None = None + rr: DoubleValue | None = None + ii: DoubleValue | None = None + zz: DoubleValue | None = None + ab: Annotated[DoubleValue | None, Field(alias='AB')] = None -class ObservingObservingTemplateDeleteResponse(CFHTBaseModel): +class TargetDeleteResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) success: bool | None = None - error: HttpErrors | None = None + error: Errors | None = None -class ObservingPointingOffsetDeleteResponse(CFHTBaseModel): +class TargetIdentifiers(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - success: bool | None = None - error: HttpErrors | None = None - - -class ObservingTargetDeleteResponse(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - success: bool | None = None - error: HttpErrors | None = None - - -class ObservingTelescopePatternDeleteResponse(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - success: bool | None = None - error: HttpErrors | None = None - - -class ObservingpersistenceTimeConstraint(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - window: Annotated[ - list[ObservingBlockConditionWindow] | None, + catalog_identifier: Annotated[ + dict[str, ListOfIdentifiers] | None, Field( - description='Time windows during which the observation can be made.\n\nWindows can not overlap, and will always be stored in temporal order.' + description='All catalogs resolved by simbad for this target key: catalog name, catalog_identifier.key === catalog_identifier.get(key).catalog value: catalog identifier.' ), ] = None + two_mass: CatalogIdentifier | None = None + gaia1: CatalogIdentifier | None = None + gaia2: CatalogIdentifier | None = None + resolved_catalog_identifier: CatalogIdentifier | None = None -class PersistenceCatalogIdentifier(CFHTBaseModel): +class TargetMonitoringObservingGroupTimeConstraint(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - catalog: Annotated[str | None, Field(description='Catalog to reference.')] = None - identifier: Annotated[ - str | None, + number_of_iterations: Annotated[ + int | None, Field(description='How many observations to take.') + ] = None + minimum_number_of_iterations: Annotated[ + int | None, Field( - description='Valid identifier in the catalog.\n\nNote: this ID will contain a catalog name, because spacing matters in catalogs, the full ID for an object is like\nM 4. Catalog is M, id is identifier could be 4, but to reference m4, you need the spaces.\n\nNB: These identity values can not be set in fits headers because they do not have to conform to\nthe length restrictions.' + description='Minimum number of observations which still provides science value. If weather becomes an issue, this is used to determine if additional observations should be tried, or the whole observing group aborted to prioritize other observations. Cannot be greater than requested number of iterations.' ), ] = None - - -class PersistenceDynamicExposureTimeLimit(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - minimum_multiplier: Annotated[float | None, Field(alias='minimumMultiplier')] = None - maximum_multiplier: Annotated[float | None, Field(alias='maximumMultiplier')] = None - - -class PersistenceExposureTime(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - desired_snr: Annotated[float | None, Field(alias='desiredSnr')] = None - exposure_time_ms: Annotated[int | None, Field(alias='exposureTimeMs')] = None - - -class PersistenceFindingChartData(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - comment: str | None = None - url: str | None = None - token: str | None = None - staff_approval: Annotated[ - FindingChartDataApproval | None, Field(alias='staffApproval') - ] = None - pi_approval: Annotated[FindingChartDataApproval | None, Field(alias='piApproval')] = ( - None - ) - target_token: Annotated[str | None, Field(alias='targetToken')] = None - - -class PersistenceITimeData(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - exposure_time_millis: Annotated[ + time_interval: Duration | None = None + time_interval_tolerance_min: Duration | None = None + time_interval_tolerance_max: Duration | None = None + t0_millis: Annotated[ int | None, Field( - alias='exposureTimeMillis', - description='Time spent collecting photons (in milliseconds).', + description="Time the observing group should start being observed, in Unix time milliseconds. If omitted, CFHT will start observing at the QC's discretion." ), ] = None - overhead_time_millis: Annotated[ - int | None, + within_same_camera_run: Annotated[ + bool | None, Field( - alias='overheadTimeMillis', - description='All other times required for taking the exposure (in milliseconds).', + description='Whether all iterations need to occur within the same camera run.' ), ] = None -class PersistenceListOfIdentifiers(CFHTBaseModel): +class View(Enum): + requested = 'REQUESTED' + actual = 'ACTUAL' + + +class TargetTimeData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - catalog_identifier: Annotated[ - list[PersistenceCatalogIdentifier] | None, Field(alias='catalogIdentifier') - ] = None + requested_integration_time: ITimeData | None = None + actual_integration_time: ITimeData | None = None + view: View | None = None + exposure_count: int | None = None + child_count: int | None = None -class PersistenceObservingBlockCondition(CFHTBaseModel): +class TelescopePatternDataConfig(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - pre_requisite: Annotated[ - list[ObservingBlockConditionRequisite] | None, - Field( - alias='preRequisite', - description='Pre-requisite conditions before each observing block can be started.\nThe index of items in this list corresponds directly to the index of the\nobserving block on the observing group i.e. this list is one-to-one with\nread_observing_block.', - ), - ] = None - window: Annotated[ - list[ObservingBlockConditionWindow] | None, - Field( - description='Time windows during which the observing block can be observed.\n\nWindows can not overlap, and will always be stored in temporal order.' - ), + name: str | None = None + description: str | None = None + charge_model: Annotated[ + str | None, Field(description='Used for calculating cost for this pattern') ] = None + dithering: ConfigDithering | None = None + nodding: ConfigNodding | None = None + scale: float | None = None + sybase_pat_id: BigInteger | None = None + is_system: bool | None = None -class PersistenceObservingGroupPriority(Enum): - unknown_og_priority = 'UNKNOWN_OG_PRIORITY' - high = 'HIGH' - medium = 'MEDIUM' - low = 'LOW' - inactive = 'INACTIVE' +class Type(Enum): + object = 'OBJECT' + target = 'TARGET' + sky = 'SKY' + dark = 'DARK' + flat = 'FLAT' + snap = 'SNAP' + bias = 'BIAS' + focus = 'FOCUS' + align = 'ALIGN' + comparison = 'COMPARISON' + acquire = 'ACQUIRE' + fabry_perot = 'FABRY_PEROT' + twilight_flats = 'TWILIGHT_FLATS' + photometry = 'PHOTOMETRY' + sfocus = 'SFOCUS' -class PersistenceRollupValue(CFHTBaseModel): +class TelescopePatternDataOffset(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - requested: int | None = None - started: int | None = None - observed: int | None = None - validated: int | None = None + exposure_number: Annotated[ + int | None, + Field( + description='TODO: deprecate, no need to have duplicate information here.' + ), + ] = None + offset: OffsetCoordinate | None = None + type: Annotated[ + Type | None, Field(description='For dithering patterns type is always target') + ] = None -class PersistenceSingleObservingGroup(CFHTBaseModel): +class TelescopePatternDeleteResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - observing_block: Annotated[ - ObservingGroupDataObservingBlock | None, Field(alias='observingBlock') - ] = None + success: bool | None = None + error: Errors | None = None -class PersistenceState(Enum): - unobserved = 'UNOBSERVED' +class TimeAccountingStatus(Enum): + not_started = 'NOT_STARTED' + started = 'STARTED' observed = 'OBSERVED' - processed = 'PROCESSED' - graded = 'GRADED' validated = 'VALIDATED' - rejected = 'REJECTED' -class PersistenceStokesParameter(Enum): - q = 'Q' - u = 'U' - v = 'V' - w = 'W' - i = 'I' - - -class PersistenceSubTotalsData(CFHTBaseModel): +class TimeAccountingData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - etime: PersistenceRollupValue | None = None - overheadtime: PersistenceRollupValue | None = None - exposure_count: Annotated[ - PersistenceRollupValue | None, Field(alias='exposureCount') - ] = None - child_count: Annotated[PersistenceRollupValue | None, Field(alias='childCount')] = ( - None - ) - etime_configured: Annotated[ - PersistenceRollupValue | None, Field(alias='etimeConfigured') - ] = None - overhead_time_configured: Annotated[ - PersistenceRollupValue | None, Field(alias='overheadTimeConfigured') - ] = None - completeness: Annotated[ - float | None, Field(description='Percentage completed for the entity.') - ] = None + configured_time: TargetTimeData | None = None + charged_time: TargetTimeData | None = None + validated_time_on_sky: TargetTimeData | None = None + time_on_sky: TargetTimeData | None = None + time_accounting_status: TimeAccountingStatus | None = None + completion_ratio: float | None = None -class PersistenceTargetIdentifiers(CFHTBaseModel): +class TimeConstraint(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - catalog_identifier: Annotated[ - dict[str, PersistenceListOfIdentifiers] | None, + window: Annotated[ + list[ObservingBlockConditionWindow] | None, Field( - alias='catalogIdentifier', - description='key: catalog name, catalog_identifier.key === catalog_identifier.get(key).catalog\nvalue: catalog identifier.', - title='All catalogs resolved by simbad for this target', + description='Time windows during which the observation can be made. Windows can not overlap, and will always be stored in temporal order.' ), ] = None - two_mass: Annotated[PersistenceCatalogIdentifier | None, Field(alias='twoMass')] = ( - None - ) - gaia1: PersistenceCatalogIdentifier | None = None - gaia2: PersistenceCatalogIdentifier | None = None - resolved_catalog_identifier: Annotated[ - PersistenceCatalogIdentifier | None, Field(alias='resolvedCatalogIdentifier') - ] = None -class PersistenceTargetTimeData(CFHTBaseModel): +class UpdateProgramCommentRequest(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - requested_integration_time: Annotated[ - PersistenceITimeData | None, Field(alias='requestedIntegrationTime') - ] = None - actual_integration_time: Annotated[ - PersistenceITimeData | None, Field(alias='actualIntegrationTime') - ] = None - view: TargetTimeDataView | None = TargetTimeDataView.requested - exposure_count: Annotated[ - int | None, - Field( - alias='exposureCount', - description='The total number of exposures represented by the values here.\n\nThis value is the same for both views.', - ), + runid: Annotated[ + str | None, Field(description='Unique identifier for your science program') ] = None - child_count: Annotated[ - int | None, - Field( - alias='childCount', - description='The total number of child entities represented by the values here.\n\nIf this is a program, these are observing groups.\nIf this is an observing group, these are observing blocks.\nIf this is an observing block, these are exposures.\n\nUseful for e.g. seeing the number of iterations configuredor completed on an Observing Group.\nThis value is the same for both views.', - ), + comment: Annotated[ + str | None, Field(description='Comment set by the PI for the CFHT QSO Team') ] = None -class PersistenceTimeAccountingData(CFHTBaseModel): +class UpdateProgramCommentsRequest(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - configured_time: Annotated[ - PersistenceTargetTimeData | None, Field(alias='configuredTime') + runid: Annotated[ + str | None, Field(description='Unique identifier for your science program') ] = None - charged_time: Annotated[ - PersistenceTargetTimeData | None, Field(alias='chargedTime') + program_comment: Annotated[ + str | None, Field(description='Comment set by the PI for the CFHT QSO Team') ] = None - validated_time_on_sky: Annotated[ - PersistenceTargetTimeData | None, Field(alias='validatedTimeOnSky') + operations_comment: Annotated[ + str | None, Field(description='Comment set by the CFHT QSO Team') ] = None - time_on_sky: Annotated[PersistenceTargetTimeData | None, Field(alias='timeOnSky')] = ( - None - ) - time_accounting_status: Annotated[ - TimeAccountingDataTimeAccountingStatus | None, - Field(alias='timeAccountingStatus'), - ] = TimeAccountingDataTimeAccountingStatus.not_started - completion_ratio: Annotated[ - float | None, - Field( - alias='completionRatio', - description='Entity completion ratio, representing how much of the time configured has been charged.\n\nEqual to total charged time (as requested) divided by total configured time. For programs, if the total configured time is greater than the time allocated to the program, we divide by the time allocated instead.\nNote that if a program has INACTIVE observing groups that already have charged time, this could result in an inflated value here.', - le=1.0, - ), - ] = None - - -class ProtobufAny(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - type_url: Annotated[str | None, Field(alias='typeUrl')] = None - value: Annotated[ - Base64Str | None, - Field( - pattern='^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$' - ), - ] = None - - -class RpcStatus(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - code: int | None = None - message: str | None = None - details: list[ProtobufAny] | None = None - - -class TypesBigInteger(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - value: str | None = None -class TypesDoubleMinMax(CFHTBaseModel): +class UpenaProcessingResultH2oMm(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - min: float | None = None - max: float | None = None - - -class TypesDurationUnit(Enum): - days = 'DAYS' - hours = 'HOURS' - minutes = 'MINUTES' - seconds = 'SECONDS' - - -class TypesExposureType(Enum): - object = 'OBJECT' - target = 'TARGET' - sky = 'SKY' - dark = 'DARK' - flat = 'FLAT' - snap = 'SNAP' - bias = 'BIAS' - focus = 'FOCUS' - align = 'ALIGN' - comparison = 'COMPARISON' - acquire = 'ACQUIRE' - fabry_perot = 'FABRY_PEROT' - twilight_flats = 'TWILIGHT_FLATS' - photometry = 'PHOTOMETRY' - sfocus = 'SFOCUS' - - -class TypesInstrumentType(Enum): - unknown = 'UNKNOWN' - cfh12_k = 'CFH12K' - megacam = 'MEGACAM' - wircam = 'WIRCAM' - espadons = 'ESPADONS' - sitelle = 'SITELLE' - spirou = 'SPIROU' - wenaokeao = 'WENAOKEAO' - - -class TypesOffsetCoordinate(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - ra_offset: Annotated[ - float | None, - Field( - alias='raOffset', - description='Offset in the Right Ascension axis in arcsec of degrees.', - ge=-1296000.0, - le=1296000.0, - title='Right Ascension Offset', - ), - ] = None - dec_offset: Annotated[ - float | None, - Field( - alias='decOffset', - description='Offset in the Declination axis in arcsec of degrees.', - ge=-324000.0, - le=324000.0, - title='Declination Offset', - ), - ] = None - exposure_number: Annotated[ - float | None, - Field( - alias='exposureNumber', - description='Exposure number', - le=99.0, - title='Exposure number', - ), - ] = None + raw_density: float | None = None + at_zenith: float | None = None -class TypesSkyCoordinate(CFHTBaseModel): +class AllocationData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - ra: Annotated[ - float | None, - Field( - description='Right Ascension in degrees.', le=360.0, title='Right Ascension' - ), - ] = None - dec: Annotated[ - float | None, - Field( - description='Declination in degrees.', - ge=-90.0, - le=90.0, - title='Declination', - ), - ] = None + run_id: str | None = None + time_allocated_millis: int | None = None + tac_rank: int | None = None + tac_grade: str | None = None + instrument: Instrument | None = None + agency: AllocationDataAgency | None = None + cfht_rank: int | None = None -class ConfigNodding(CFHTBaseModel): +class Constraint(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - start_sequence: Annotated[ - TypesExposureType | None, Field(alias='startSequence') - ] = TypesExposureType.object - target_db: Annotated[float | None, Field(alias='targetDb')] = None + image_quality: DoubleMinMax | None = None + sky_background_max: float | None = None + airmass_max: float | None = None + extinction_max: float | None = None + moon_distance_arc_length_min: float | None = None + photometric: bool | None = None + name: str | None = None + sybase_cons_id: BigInteger | None = None + h2o_vapor_max: float | None = None + read_sky_background_name: str | None = None + sky_background_name: str | None = None class EspadonsStatusUpenaProcessingResult(CFHTBaseModel): @@ -1140,62 +1309,39 @@ class EspadonsStatusUpenaProcessingResult(CFHTBaseModel): validate_by_name=True, ) iq: float | None = None - h2o_column_density_from_telluric_lines: Annotated[ - float | None, Field(alias='h2oColumnDensityFromTelluricLines') - ] = None - radial_velocity_corrections: Annotated[ - float | None, Field(alias='radialVelocityCorrections') - ] = None - snr_per_ccd_bin: Annotated[float | None, Field(alias='snrPerCcdBin')] = None - snr_per_spec_bin: Annotated[float | None, Field(alias='snrPerSpecBin')] = None - actual_wavelength: Annotated[float | None, Field(alias='actualWavelength')] = None - snr_per_ccd_bin_polar: Annotated[float | None, Field(alias='snrPerCcdBinPolar')] = ( - None - ) - snr_per_spec_bin_polar: Annotated[float | None, Field(alias='snrPerSpecBinPolar')] = ( - None - ) - h2o_mm_data: Annotated[UpenaProcessingResulth2oMm | None, Field(alias='h2oMmData')] = ( - None - ) + h2o_column_density_from_telluric_lines: float | None = None + radial_velocity_corrections: float | None = None + snr_per_ccd_bin: float | None = None + snr_per_spec_bin: float | None = None + actual_wavelength: float | None = None + snr_per_ccd_bin_polar: float | None = None + snr_per_spec_bin_polar: float | None = None + h2o_mm_data: UpenaProcessingResultH2oMm | None = None class ExposureDataEspadonsStatus(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - instr_mode: Annotated[str | None, Field(alias='instrMode')] = None - actual_wavelength: Annotated[float | None, Field(alias='actualWavelength')] = None - snr_exp_meter: Annotated[float | None, Field(alias='snrExpMeter')] = None - snr_wave_meas_int: Annotated[float | None, Field(alias='snrWaveMeasInt')] = None - snr_wave_meas_pol: Annotated[float | None, Field(alias='snrWaveMeasPol')] = None - snr_meas_int_per_ccd_bin: Annotated[ - float | None, Field(alias='snrMeasIntPerCcdBin') - ] = None - snr_meas_int_per_spec_bin: Annotated[ - float | None, Field(alias='snrMeasIntPerSpecBin') - ] = None - snr_meas_polar_ccd_bin: Annotated[float | None, Field(alias='snrMeasPolarCcdBin')] = ( - None - ) - snr_meas_polar_spec_bin: Annotated[float | None, Field(alias='snrMeasPolarSpecBin')] = ( - None - ) - trg_type: Annotated[str | None, Field(alias='trgType')] = None - guider_seeing: Annotated[float | None, Field(alias='guiderSeeing')] = None - guider_seeing_processing_results: Annotated[ - EspadonsStatusGuiderSeeingProcessingResult | None, - Field(alias='guiderSeeingProcessingResults'), - ] = None - exposure_meter_processing_results: Annotated[ - ExposureDataEspadonsStatusExposureMeterProcessingResult | None, - Field(alias='exposureMeterProcessingResults'), - ] = None - upena_processing_results: Annotated[ - EspadonsStatusUpenaProcessingResult | None, - Field(alias='upenaProcessingResults'), - ] = None - observation_type: Annotated[str | None, Field(alias='observationType')] = None + instr_mode: str | None = None + actual_wavelength: float | None = None + snr_exp_meter: float | None = None + snr_wave_meas_int: float | None = None + snr_wave_meas_pol: float | None = None + snr_meas_int_per_ccd_bin: float | None = None + snr_meas_int_per_spec_bin: float | None = None + snr_meas_polar_ccd_bin: float | None = None + snr_meas_polar_spec_bin: float | None = None + trg_type: str | None = None + guider_seeing: float | None = None + guider_seeing_processing_results: ( + EspadonsStatusGuiderSeeingProcessingResult | None + ) = None + exposure_meter_processing_results: ( + EspadonsStatusExposureMeterProcessingResult | None + ) = None + upena_processing_results: EspadonsStatusUpenaProcessingResult | None = None + observation_type: str | None = None class ExposureDataMegacamStatus(CFHTBaseModel): @@ -1204,33 +1350,24 @@ class ExposureDataMegacamStatus(CFHTBaseModel): ) iq: float | None = None skybg: float | None = None - elixir_eval: Annotated[int | None, Field(alias='elixirEval')] = None - elixir_seval: Annotated[str | None, Field(alias='elixirSeval')] = None + elixir_eval: int | None = None + elixir_seval: str | None = None snr: float | None = None - atmospheric_transmission: Annotated[ - float | None, Field(alias='atmosphericTransmission') - ] = None - elixir_zeropoint: Annotated[float | None, Field(alias='elixirZeropoint')] = None - actual_filter: Annotated[str | None, Field(alias='actualFilter')] = None - elixir_processing_result: Annotated[ - MegacamStatusElixirProcessingResult | None, - Field(alias='elixirProcessingResult'), - ] = None + atmospheric_transmission: float | None = None + elixir_zeropoint: float | None = None + actual_filter: str | None = None + elixir_processing_result: MegacamStatusElixirProcessingResult | None = None class ExposureDataSitelleStatus(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - camera_a: Annotated[SitelleStatusCamera | None, Field(alias='cameraA')] = None - camera_b: Annotated[SitelleStatusCamera | None, Field(alias='cameraB')] = None - relative_extinction: Annotated[float | None, Field(alias='relativeExtinction')] = ( - None - ) - relative_extinction_rms: Annotated[ - float | None, Field(alias='relativeExtinctionRms') - ] = None - actual_filter: Annotated[str | None, Field(alias='actualFilter')] = None + camera_a: SitelleStatusCamera | None = None + camera_b: SitelleStatusCamera | None = None + relative_extinction: float | None = None + relative_extinction_rms: float | None = None + actual_filter: str | None = None class ExposureDataSpirouStatus(CFHTBaseModel): @@ -1239,8 +1376,8 @@ class ExposureDataSpirouStatus(CFHTBaseModel): ) acqtime: float | None = None acqtime1: str | None = None - asic_num: Annotated[str | None, Field(alias='asicNum')] = None - sca_id: Annotated[str | None, Field(alias='scaId')] = None + asic_num: str | None = None + sca_id: str | None = None muxtype: int | None = None noutputs: int | None = None nadcs: int | None = None @@ -1271,62 +1408,62 @@ class ExposureDataSpirouStatus(CFHTBaseModel): mclk: float | None = None inttime: float | None = None frameno: int | None = None - sb_adc_s: Annotated[str | None, Field(alias='sbAdcS')] = None - sbadc1_p: Annotated[float | None, Field(alias='sbadc1P')] = None - sbadc2_p: Annotated[float | None, Field(alias='sbadc2P')] = None - sbcali_p: Annotated[str | None, Field(alias='sbcaliP')] = None - sbdens_p: Annotated[float | None, Field(alias='sbdensP')] = None - sbrhb1_p: Annotated[str | None, Field(alias='sbrhb1P')] = None - sbrhb2_p: Annotated[str | None, Field(alias='sbrhb2P')] = None - sbetem_p: Annotated[str | None, Field(alias='sbetemP')] = None - sb_inj_t: Annotated[float | None, Field(alias='sbInjT')] = None - sb_pol_t: Annotated[float | None, Field(alias='sbPolT')] = None - sb_cs1_t: Annotated[float | None, Field(alias='sbCs1T')] = None - sb_cs2_t: Annotated[float | None, Field(alias='sbCs2T')] = None - sb_cb1_t: Annotated[float | None, Field(alias='sbCb1T')] = None - sb_cb2_t: Annotated[float | None, Field(alias='sbCb2T')] = None + sb_adc_s: str | None = None + sbadc1_p: float | None = None + sbadc2_p: float | None = None + sbcali_p: str | None = None + sbdens_p: float | None = None + sbrhb1_p: str | None = None + sbrhb2_p: str | None = None + sbetem_p: str | None = None + sb_inj_t: float | None = None + sb_pol_t: float | None = None + sb_cs1_t: float | None = None + sb_cs2_t: float | None = None + sb_cb1_t: float | None = None + sb_cb2_t: float | None = None sbiavl1s: str | None = None sbiavl2s: str | None = None - sbetel_s: Annotated[str | None, Field(alias='sbetelS')] = None - sb_csc_s: Annotated[str | None, Field(alias='sbCscS')] = None - sbcref_p: Annotated[str | None, Field(alias='sbcrefP')] = None - sbccas_p: Annotated[str | None, Field(alias='sbccasP')] = None - sbcden_p: Annotated[float | None, Field(alias='sbcdenP')] = None - sbagit_s: Annotated[str | None, Field(alias='sbagitS')] = None - sbcmir_p: Annotated[str | None, Field(alias='sbcmirP')] = None + sbetel_s: str | None = None + sb_csc_s: str | None = None + sbcref_p: str | None = None + sbccas_p: str | None = None + sbcden_p: float | None = None + sbagit_s: str | None = None + sbcmir_p: str | None = None sbclhc1s: str | None = None sbclhc1w: str | None = None sbclhc1c: float | None = None sbclhc2s: str | None = None sbclhc2w: str | None = None sbclhc2c: float | None = None - sbclwl_s: Annotated[str | None, Field(alias='sbclwlS')] = None - sbclwl_c: Annotated[float | None, Field(alias='sbclwlC')] = None - sbwlsh_s: Annotated[str | None, Field(alias='sbwlshS')] = None - sbctec_s: Annotated[str | None, Field(alias='sbctecS')] = None + sbclwl_s: str | None = None + sbclwl_c: float | None = None + sbwlsh_s: str | None = None + sbctec_s: str | None = None sbctecct: float | None = None sbctecht: float | None = None sbccprit: float | None = None - sbcccu_t: Annotated[float | None, Field(alias='sbcccuT')] = None + sbcccu_t: float | None = None sbccprot: float | None = None - sbcfbi_t: Annotated[float | None, Field(alias='sbcfbiT')] = None - sbcwls_t: Annotated[float | None, Field(alias='sbcwlsT')] = None + sbcfbi_t: float | None = None + sbcwls_t: float | None = None sbclsuat: float | None = None - sbcfpi_t: Annotated[float | None, Field(alias='sbcfpiT')] = None - sbcfpe_t: Annotated[float | None, Field(alias='sbcfpeT')] = None - sbcfpb_p: Annotated[float | None, Field(alias='sbcfpbP')] = None - sbvga_cp: Annotated[float | None, Field(alias='sbvgaCp')] = None - sbvga_ip: Annotated[float | None, Field(alias='sbvgaIp')] = None - sbvgb_cp: Annotated[float | None, Field(alias='sbvgbCp')] = None - sbvgb_ip: Annotated[float | None, Field(alias='sbvgbIp')] = None - sbmk00_t: Annotated[float | None, Field(alias='sbmk00T')] = None - sbmk01_t: Annotated[float | None, Field(alias='sbmk01T')] = None - sbmk02_t: Annotated[float | None, Field(alias='sbmk02T')] = None - sbmk03_t: Annotated[float | None, Field(alias='sbmk03T')] = None - sbmk04_t: Annotated[float | None, Field(alias='sbmk04T')] = None - sbmk05_t: Annotated[float | None, Field(alias='sbmk05T')] = None - sbmk06_t: Annotated[float | None, Field(alias='sbmk06T')] = None - sbmk07_t: Annotated[float | None, Field(alias='sbmk07T')] = None + sbcfpi_t: float | None = None + sbcfpe_t: float | None = None + sbcfpb_p: float | None = None + sbvga_cp: float | None = None + sbvga_ip: float | None = None + sbvgb_cp: float | None = None + sbvgb_ip: float | None = None + sbmk00_t: float | None = None + sbmk01_t: float | None = None + sbmk02_t: float | None = None + sbmk03_t: float | None = None + sbmk04_t: float | None = None + sbmk05_t: float | None = None + sbmk06_t: float | None = None + sbmk07_t: float | None = None sbls000t: float | None = None sbls001t: float | None = None sbls002t: float | None = None @@ -1351,70 +1488,70 @@ class ExposureDataSpirouStatus(CFHTBaseModel): sbls109t: float | None = None sbls110t: float | None = None sbls111t: float | None = None - sbl00i_t: Annotated[float | None, Field(alias='sbl00iT')] = None - sbl00h_v: Annotated[float | None, Field(alias='sbl00hV')] = None - sbl01i_t: Annotated[float | None, Field(alias='sbl01iT')] = None - sbl01h_v: Annotated[float | None, Field(alias='sbl01hV')] = None - sbl02i_t: Annotated[float | None, Field(alias='sbl02iT')] = None - sbl02h_v: Annotated[float | None, Field(alias='sbl02hV')] = None - sbl03i_t: Annotated[float | None, Field(alias='sbl03iT')] = None - sbl03h_v: Annotated[float | None, Field(alias='sbl03hV')] = None - sbl04i_t: Annotated[float | None, Field(alias='sbl04iT')] = None - sbl04h_v: Annotated[float | None, Field(alias='sbl04hV')] = None - sbl05i_t: Annotated[float | None, Field(alias='sbl05iT')] = None - sbl05h_v: Annotated[float | None, Field(alias='sbl05hV')] = None - sbl06i_t: Annotated[float | None, Field(alias='sbl06iT')] = None - sbl06h_v: Annotated[float | None, Field(alias='sbl06hV')] = None - sbl07i_t: Annotated[float | None, Field(alias='sbl07iT')] = None - sbl07h_v: Annotated[float | None, Field(alias='sbl07hV')] = None - sbl08i_t: Annotated[float | None, Field(alias='sbl08iT')] = None - sbl08h_v: Annotated[float | None, Field(alias='sbl08hV')] = None - sbl09i_t: Annotated[float | None, Field(alias='sbl09iT')] = None - sbl09h_v: Annotated[float | None, Field(alias='sbl09hV')] = None - sbl10i_t: Annotated[float | None, Field(alias='sbl10iT')] = None - sbl10h_v: Annotated[float | None, Field(alias='sbl10hV')] = None - sbl11i_t: Annotated[float | None, Field(alias='sbl11iT')] = None - sbl11h_v: Annotated[float | None, Field(alias='sbl11hV')] = None - sbl12i_t: Annotated[float | None, Field(alias='sbl12iT')] = None - sbl12h_v: Annotated[float | None, Field(alias='sbl12hV')] = None - sbl13i_t: Annotated[float | None, Field(alias='sbl13iT')] = None - sbl13h_v: Annotated[float | None, Field(alias='sbl13hV')] = None - seadc1_p: Annotated[float | None, Field(alias='seadc1P')] = None - seadc2_p: Annotated[float | None, Field(alias='seadc2P')] = None - sedens_p: int | None = None - sedens_p_float: float | None = None - se_inj_t: Annotated[float | None, Field(alias='seInjT')] = None - se_pol_t: Annotated[float | None, Field(alias='sePolT')] = None - se_cs1_t: Annotated[float | None, Field(alias='seCs1T')] = None - se_cs2_t: Annotated[float | None, Field(alias='seCs2T')] = None - se_cb1_t: Annotated[float | None, Field(alias='seCb1T')] = None - se_cb2_t: Annotated[float | None, Field(alias='seCb2T')] = None + sbl00i_t: float | None = None + sbl00h_v: float | None = None + sbl01i_t: float | None = None + sbl01h_v: float | None = None + sbl02i_t: float | None = None + sbl02h_v: float | None = None + sbl03i_t: float | None = None + sbl03h_v: float | None = None + sbl04i_t: float | None = None + sbl04h_v: float | None = None + sbl05i_t: float | None = None + sbl05h_v: float | None = None + sbl06i_t: float | None = None + sbl06h_v: float | None = None + sbl07i_t: float | None = None + sbl07h_v: float | None = None + sbl08i_t: float | None = None + sbl08h_v: float | None = None + sbl09i_t: float | None = None + sbl09h_v: float | None = None + sbl10i_t: float | None = None + sbl10h_v: float | None = None + sbl11i_t: float | None = None + sbl11h_v: float | None = None + sbl12i_t: float | None = None + sbl12h_v: float | None = None + sbl13i_t: float | None = None + sbl13h_v: float | None = None + seadc1_p: float | None = None + seadc2_p: float | None = None + sedens_p_deprecated: int | None = None + sedens_p: float | None = None + se_inj_t: float | None = None + se_pol_t: float | None = None + se_cs1_t: float | None = None + se_cs2_t: float | None = None + se_cb1_t: float | None = None + se_cb2_t: float | None = None sectecct: float | None = None sectecht: float | None = None seccprit: float | None = None - secccu_t: Annotated[float | None, Field(alias='secccuT')] = None + secccu_t: float | None = None seccprot: float | None = None - secfbi_t: Annotated[float | None, Field(alias='secfbiT')] = None - secwls_t: Annotated[float | None, Field(alias='secwlsT')] = None + secfbi_t: float | None = None + secwls_t: float | None = None seclhc1c: float | None = None seclhc2c: float | None = None - seclwl_c: Annotated[float | None, Field(alias='seclwlC')] = None + seclwl_c: float | None = None seclsuat: float | None = None - secfpi_t: Annotated[float | None, Field(alias='secfpiT')] = None - secfpe_t: Annotated[float | None, Field(alias='secfpeT')] = None - secfpb_p: Annotated[float | None, Field(alias='secfpbP')] = None - sevga_cp: Annotated[float | None, Field(alias='sevgaCp')] = None - sevga_ip: Annotated[float | None, Field(alias='sevgaIp')] = None - sevgb_cp: Annotated[float | None, Field(alias='sevgbCp')] = None - sevgb_ip: Annotated[float | None, Field(alias='sevgbIp')] = None - semk00_t: Annotated[float | None, Field(alias='semk00T')] = None - semk01_t: Annotated[float | None, Field(alias='semk01T')] = None - semk02_t: Annotated[float | None, Field(alias='semk02T')] = None - semk03_t: Annotated[float | None, Field(alias='semk03T')] = None - semk04_t: Annotated[float | None, Field(alias='semk04T')] = None - semk05_t: Annotated[float | None, Field(alias='semk05T')] = None - semk06_t: Annotated[float | None, Field(alias='semk06T')] = None - semk07_t: Annotated[float | None, Field(alias='semk07T')] = None + secfpi_t: float | None = None + secfpe_t: float | None = None + secfpb_p: float | None = None + sevga_cp: float | None = None + sevga_ip: float | None = None + sevgb_cp: float | None = None + sevgb_ip: float | None = None + semk00_t: float | None = None + semk01_t: float | None = None + semk02_t: float | None = None + semk03_t: float | None = None + semk04_t: float | None = None + semk05_t: float | None = None + semk06_t: float | None = None + semk07_t: float | None = None sels000t: float | None = None sels001t: float | None = None sels002t: float | None = None @@ -1439,68 +1576,91 @@ class ExposureDataSpirouStatus(CFHTBaseModel): sels109t: float | None = None sels110t: float | None = None sels111t: float | None = None - sel00i_t: Annotated[float | None, Field(alias='sel00iT')] = None - sel00h_v: Annotated[float | None, Field(alias='sel00hV')] = None - sel01i_t: Annotated[float | None, Field(alias='sel01iT')] = None - sel01h_v: Annotated[float | None, Field(alias='sel01hV')] = None - sel02i_t: Annotated[float | None, Field(alias='sel02iT')] = None - sel02h_v: Annotated[float | None, Field(alias='sel02hV')] = None - sel03i_t: Annotated[float | None, Field(alias='sel03iT')] = None - sel03h_v: Annotated[float | None, Field(alias='sel03hV')] = None - sel04i_t: Annotated[float | None, Field(alias='sel04iT')] = None - sel04h_v: Annotated[float | None, Field(alias='sel04hV')] = None - sel05i_t: Annotated[float | None, Field(alias='sel05iT')] = None - sel05h_v: Annotated[float | None, Field(alias='sel05hV')] = None - sel06i_t: Annotated[float | None, Field(alias='sel06iT')] = None - sel06h_v: Annotated[float | None, Field(alias='sel06hV')] = None - sel07i_t: Annotated[float | None, Field(alias='sel07iT')] = None - sel07h_v: Annotated[float | None, Field(alias='sel07hV')] = None - sel08i_t: Annotated[float | None, Field(alias='sel08iT')] = None - sel08h_v: Annotated[float | None, Field(alias='sel08hV')] = None - sel09i_t: Annotated[float | None, Field(alias='sel09iT')] = None - sel09h_v: Annotated[float | None, Field(alias='sel09hV')] = None - sel10i_t: Annotated[float | None, Field(alias='sel10iT')] = None - sel10h_v: Annotated[float | None, Field(alias='sel10hV')] = None - sel11i_t: Annotated[float | None, Field(alias='sel11iT')] = None - sel11h_v: Annotated[float | None, Field(alias='sel11hV')] = None - sel12i_t: Annotated[float | None, Field(alias='sel12iT')] = None - sel12h_v: Annotated[float | None, Field(alias='sel12hV')] = None - sel13i_t: Annotated[float | None, Field(alias='sel13iT')] = None - sel13h_v: Annotated[float | None, Field(alias='sel13hV')] = None - guider_processing: Annotated[ - SpirouStatusGuiderProcessingResult | None, Field(alias='guiderProcessing') - ] = None - exposure_meter_processing: Annotated[ - ExposureDataSpirouStatusExposureMeterProcessingResult | None, - Field(alias='exposureMeterProcessing'), - ] = None - pipeline_processing: Annotated[ - SpirouStatusPipelineProcessingResult | None, Field(alias='pipelineProcessing') - ] = None + sel00i_t: float | None = None + sel00h_v: float | None = None + sel01i_t: float | None = None + sel01h_v: float | None = None + sel02i_t: float | None = None + sel02h_v: float | None = None + sel03i_t: float | None = None + sel03h_v: float | None = None + sel04i_t: float | None = None + sel04h_v: float | None = None + sel05i_t: float | None = None + sel05h_v: float | None = None + sel06i_t: float | None = None + sel06h_v: float | None = None + sel07i_t: float | None = None + sel07h_v: float | None = None + sel08i_t: float | None = None + sel08h_v: float | None = None + sel09i_t: float | None = None + sel09h_v: float | None = None + sel10i_t: float | None = None + sel10h_v: float | None = None + sel11i_t: float | None = None + sel11h_v: float | None = None + sel12i_t: float | None = None + sel12h_v: float | None = None + sel13i_t: float | None = None + sel13h_v: float | None = None + guider_processing: SpirouStatusGuiderProcessingResult | None = None + exposure_meter_processing: SpirouStatusExposureMeterProcessingResult | None = None + pipeline_processing: SpirouStatusPipelineProcessingResult | None = None + + +class FindingChartData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, + ) + comment: str | None = None + url: str | None = None + token: str | None = None + staff_approval: FindingChartDataApproval | None = None + pi_approval: FindingChartDataApproval | None = None + target_token: str | None = None -class InstrumentConfigurationDataEspadonsConfigurationData(CFHTBaseModel): +class InstrumentConfigurationDataSitelleConfigurationData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - observing_mode: Annotated[ - InstrumentConfigurationDataEspadonsConfigurationDataOObservingMode | None, - Field(alias='observingMode'), - ] = InstrumentConfigurationDataEspadonsConfigurationDataOObservingMode.polarimetry - readout_mode: Annotated[RReadoutMode | None, Field(alias='readoutMode')] = ( - RReadoutMode.normal + filter: Filter1 | None = None + binning: Binning1 | None = None + resolution: int | None = None + snrwavelength: Annotated[ + float | None, + Field(description='This should be on target too , sitell, espedons, spirou'), + ] = None + calibration: SitelleConfigurationDataCalibration | None = None + resolution_mode: ResolutionMode | None = None + needs_precision: bool | None = None + needs_flux_calibration: bool | None = None + needs_target_calibration: bool | None = None + + +class InstrumentConfigurationDataSpirouConfigurationData(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, ) - stokes_parameter: Annotated[ - PersistenceStokesParameter | None, Field(alias='stokesParameter') - ] = PersistenceStokesParameter.q - number_of_sequences: Annotated[int | None, Field(alias='numberOfSequences')] = None - number_of_exposures: Annotated[int | None, Field(alias='numberOfExposures')] = None - needs_snr: bool | None = None - snr_wave: Annotated[float | None, Field(alias='snrWave')] = None - snr: float | None = None + template_name: Annotated[str | None, Field(description='Deprecated fields')] = None + observing_mode: ObservingMode1 | None = None + stokes_parameter: StokesParameter | None = None + mode: Mode | None = None + sky_observation: SkyObservation | None = None + number_of_sequences: Annotated[ + int | None, + Field( + description='Each sequence is 1 exposure for Star mode and 4 exposures for ObservingMode' + ), + ] = None + use_snr: Annotated[ + bool | None, Field(description='when true - use SNR for stoping exposures') + ] = None + sky_etime_millis: Int64Value | None = None -class InstrumentConfigurationDataMegacamConfigurationDataPhotometry(CFHTBaseModel): +class MegacamConfigurationDataPhotometry(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) @@ -1508,136 +1668,135 @@ class InstrumentConfigurationDataMegacamConfigurationDataPhotometry(CFHTBaseMode psf: bool | None = None -class InstrumentConfigurationDataSitelleConfigurationData(CFHTBaseModel): +class MovingTargetEphemeris(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - filter: InstrumentConfigurationDataSitelleConfigurationDataFilter | None = ( - InstrumentConfigurationDataSitelleConfigurationDataFilter.unknown - ) - binning: InstrumentConfigurationDataSitelleConfigurationDataBinning | None = ( - InstrumentConfigurationDataSitelleConfigurationDataBinning.one_one + mjd: Annotated[ + float | None, + Field( + description='Modified Julian date when the target is at this coordinate.' + ), + ] = None + coordinate: SkyCoordinate | None = None + computed_coordinate: SkyCoordinate | None = None + + +class ObservingBlockCondition(CFHTBaseModel): + model_config = ConfigDict( + validate_by_name=True, ) - resolution: int | None = None - snrwavelength: Annotated[ - float | None, - Field(title='This should be on target too , sitell, espedons, spirou'), + pre_requisite: Annotated[ + list[ObservingBlockConditionRequisite] | None, + Field( + description='Pre-requisite conditions before each observing block can be started. The index of items in this list corresponds directly to the index of the observing block on the observing group i.e. this list is one-to-one with read_observing_block.' + ), ] = None - calibration: SitelleConfigurationDataCalibration | None = None - resolution_mode: Annotated[ - SitelleConfigurationDataResolutionMode | None, Field(alias='resolutionMode') - ] = SitelleConfigurationDataResolutionMode.no_resolution_mode - needs_precision: Annotated[bool | None, Field(alias='needsPrecision')] = None - needs_flux_calibration: Annotated[bool | None, Field(alias='needsFluxCalibration')] = ( - None - ) - needs_target_calibration: Annotated[ - bool | None, Field(alias='needsTargetCalibration') + window: Annotated[ + list[ObservingBlockConditionWindow] | None, + Field( + description='Time windows during which the observing block can be observed. Windows can not overlap, and will always be stored in temporal order.' + ), ] = None -class InstrumentConfigurationDataSpirouConfigurationData(CFHTBaseModel): +class ObservingGroupContext(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - template_name: Annotated[ - str | None, Field(alias='templateName', title='Deprecated fields') - ] = None - observing_mode: Annotated[ - InstrumentConfigurationDataSpirouConfigurationDataOObservingMode | None, - Field(alias='observingMode'), - ] = InstrumentConfigurationDataSpirouConfigurationDataOObservingMode.polarimetry - stokes_parameter: Annotated[ - PersistenceStokesParameter | None, Field(alias='stokesParameter') - ] = PersistenceStokesParameter.q - mode: MMode | None = MMode.dark - sky_observation: Annotated[ - SpirouConfigurationDataSkyObservation | None, Field(alias='skyObservation') - ] = SpirouConfigurationDataSkyObservation.none - number_of_sequences: Annotated[ + iteration_count: Annotated[ int | None, Field( - alias='numberOfSequences', - title='Each sequence is 1 exposure for Star mode and 4 exposures for ObservingMode', + description='The iteration number this object was part of in the observing group' ), ] = None - use_snr: Annotated[ - bool | None, - Field(alias='useSnr', title='when true - use SNR for stoping exposures'), + iteration_total: Annotated[ + int | None, + Field( + description='The total number of iterations requested for the observing group' + ), ] = None - sky_etime_millis: Annotated[ + iteration_minimum: Annotated[ int | None, Field( - alias='skyEtimeMillis', - title='Used to override the etime for the sky_observation', + description='Minimum number of iterations this OG needs to provide science value' ), ] = None + window: Annotated[ + list[ObservingBlockConditionWindow] | None, + Field(description='Sorted by from past -> future on the observe_after key'), + ] = None + observing_group_token: str | None = None + observing_group_priority: ObservingGroupPriority | None = None + observing_group_state: ObservingGroupState | None = None + observing_group_label: int | None = None + observing_group_version: Annotated[ + int | None, + Field(description='Increments every time the observing group changes.'), + ] = None + legacy_reel: LegacyReel | None = None + single: bool | None = None + target_monitoring: TargetMonitoringObservingGroupTimeConstraint | None = None + phase_schedule: PhaseScheduleObservingGroupPhaseSchedule | None = None + time_accounting: TimeAccountingData | None = None + exposure_type: Annotated[ExposureType | None, Field(alias='exposureType')] = None + use_group_dither_scheme: bool | None = None -class InstrumentConfigurationDataWircamConfigurationData(CFHTBaseModel): +class PhaseScheduleObservingGroup(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - filter: InstrumentConfigurationDataWircamConfigurationDataFilter | None = ( - InstrumentConfigurationDataWircamConfigurationDataFilter.unknown - ) - is_microdithering: Annotated[bool | None, Field(alias='isMicrodithering')] = None - exposures_per_dp_position: Annotated[ - int | None, Field(alias='exposuresPerDpPosition', title='Must always be > 0') - ] = None - accurate_pointing: Annotated[bool | None, Field(alias='accuratePointing')] = None - defocus: float | None = None - is_staring: Annotated[bool | None, Field(alias='isStaring')] = None + observing_block: ObservingGroupDataObservingBlock | None = None + phase_schedule: PhaseScheduleObservingGroupPhaseSchedule | None = None -class MovingTargetEphemeris(CFHTBaseModel): +class ProgramData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - mjd: Annotated[ - float | None, + title: str | None = None + abstract: str | None = None + contact_info: ProgramDataContactInfo | None = None + comment: list[ProgramDataComments] | None = None + sybase_pi_user_id: Annotated[str | None, Field(alias='sybasePiUserId')] = None + sub_totals: SubTotalsData | None = None + token: Annotated[ + str | None, Field( - description='Modified Julian date when the target is at this coordinate.' + description='Unique identifier for the program. Used interchangeably with "runid".' ), ] = None - coordinate: TypesSkyCoordinate | None = None - computed_coordinate: Annotated[ - TypesSkyCoordinate | None, Field(alias='computedCoordinate') - ] = None + time_allocation: list[AllocationData] | None = None + release_date_millis: int | None = None + metadata_release_date_millis: int | None = None + comment_field: str | None = None + program_type: ProgramType | None = None + distribution_hash: str | None = None + time_accounting: TimeAccountingData | None = None + operations_comment_field: str | None = None + last_data_acquisition_notification_date_millis: int | None = None + dynamic_exposure_time_limit: DynamicExposureTimeLimit | None = None -class TargetDataFixedTarget(CFHTBaseModel): +class RollupFields(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - coordinate: TypesSkyCoordinate | None = None - proper_motion: Annotated[ - FixedTargetProperMotion | None, Field(alias='properMotion') - ] = None - computed_coordinate: Annotated[ - TypesSkyCoordinate | None, Field(alias='computedCoordinate') - ] = None - estimated_radial_velocity_kmps: Annotated[ - float | None, - Field( - alias='estimatedRadialVelocityKmps', - description='Expected radial velocity of the target in kilometers/second.\n\nUsed for targets observed with SPIRou.', - ), - ] = None + i_time: RollupValue | None = None + i_time_configured: RollupValue | None = None + sub_totals: SubTotalsData | None = None class TargetDataLinkedTargetIdentifiers(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - pi_confirmed_identifiers: Annotated[ - PersistenceTargetIdentifiers | None, Field(alias='piConfirmedIdentifiers') - ] = None + pi_confirmed_identifiers: TargetIdentifiers | None = None custom_target: Annotated[ bool | None, Field( - alias='customTarget', - description='Used to indicate this is a target without known identifiers.', + description='Used to indicate this is a target without known identifiers.' ), ] = None @@ -1649,328 +1808,142 @@ class TargetDataMovingTarget(CFHTBaseModel): ephemeris_point: Annotated[ list[MovingTargetEphemeris] | None, Field( - alias='ephemerisPoint', - description="The ephemerides that define the path of motion for the target.\n\nMust be provided using topocentric astrometric coordinates.\nDo not provide apparent coordinates.\nCFHT strongly recommends the use of JPL Horizons System for\ngenerating the proper coordinates. CFHT's observatory code is T14.\n\nAt least five distinct positions must be provided that span the\ndesired observing period. The first position should be well in\nadvance of the observation. The positions do not need to be\nuniformly sampled throughout the desired observing period.\n\nThe points will be interpolated to determine the position of the\ntarget at the time of observation, at which point there must be\nat least one ephemeris point defined in the past, and at least\nfour defined in the future.", + description="The ephemerides that define the path of motion for the target. Must be provided using topocentric astrometric coordinates. Do not provide apparent coordinates. CFHT strongly recommends the use of JPL Horizons System for generating the proper coordinates. CFHT's observatory code is T14. At least five distinct positions must be provided that span the desired observing period. The first position should be well in advance of the observation. The positions do not need to be uniformly sampled throughout the desired observing period. The points will be interpolated to determine the position of the target at the time of observation, at which point there must be at least one ephemeris point defined in the past, and at least four defined in the future." ), ] = None -class TelescopePatternDataConfig(CFHTBaseModel): +class TargetMonitoringObservingGroup(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - name: str | None = None - description: str | None = None - charge_model: Annotated[ - str | None, - Field(alias='chargeModel', title='Used for calculating cost for this pattern'), - ] = None - dithering: ConfigDithering | None = None - nodding: ConfigNodding | None = None - scale: float | None = None - sybase_pat_id: Annotated[TypesBigInteger | None, Field(alias='sybasePatId')] = None - is_system: Annotated[bool | None, Field(alias='isSystem')] = None + observing_block: ObservingGroupDataObservingBlock | None = None + time_constraint: TargetMonitoringObservingGroupTimeConstraint | None = None -class TelescopePatternDataOffset(CFHTBaseModel): +class TelescopePatternData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - exposure_number: Annotated[ - int | None, + offsets: Annotated[ + list[TelescopePatternDataOffset] | None, Field( - alias='exposureNumber', - description='TODO: deprecate, no need to have duplicate information here.', + description='Should only be set if manually specifying custom dithering patterns' ), ] = None - offset: TypesOffsetCoordinate | None = None - type: TypesExposureType | None = TypesExposureType.object + config: TelescopePatternDataConfig | None = None + token: str | None = None + label: int | None = None + version: int | None = None + user_token: str | None = None + instrument: Instrument | None = None + is_system: bool | None = None + name: str | None = None + offset_coordinate: list[OffsetCoordinate] | None = None -class ObservingRollupFields(CFHTBaseModel): +class TelescopePatternListResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - i_time: Annotated[PersistenceRollupValue | None, Field(alias='iTime')] = None - i_time_configured: Annotated[ - PersistenceRollupValue | None, Field(alias='iTimeConfigured') - ] = None - sub_totals: Annotated[PersistenceSubTotalsData | None, Field(alias='subTotals')] = ( - None - ) + success: bool | None = None + entity: list[TelescopePatternData] | None = None + error: Errors | None = None -class PersistenceAllocationData(CFHTBaseModel): +class TelescopePatternUpdateRequest(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - run_id: Annotated[str | None, Field(alias='runId')] = None - time_allocated_millis: Annotated[int | None, Field(alias='timeAllocatedMillis')] = ( - None - ) - tac_rank: Annotated[int | None, Field(alias='tacRank')] = None - tac_grade: Annotated[str | None, Field(alias='tacGrade')] = None - instrument: TypesInstrumentType | None = TypesInstrumentType.unknown - agency: AllocationDataAgency | None = None - cfht_rank: Annotated[int | None, Field(alias='cfhtRank')] = None + runid: str | None = None + token: str | None = None + lock_version: Int32Value | None = None + entity: TelescopePatternData | None = None -class PersistenceConstraint(CFHTBaseModel): +class TelescopePatternUpdateResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - image_quality: Annotated[TypesDoubleMinMax | None, Field(alias='imageQuality')] = ( - None - ) - sky_background_max: Annotated[float | None, Field(alias='skyBackgroundMax')] = None - airmass_max: Annotated[float | None, Field(alias='airmassMax')] = None - extinction_max: Annotated[float | None, Field(alias='extinctionMax')] = None - moon_distance_arc_length_min: Annotated[ - float | None, Field(alias='moonDistanceArcLengthMin') - ] = None - photometric: bool | None = None - name: str | None = None - sybase_cons_id: Annotated[TypesBigInteger | None, Field(alias='sybaseConsId')] = ( - None - ) - h2o_vapor_max: Annotated[float | None, Field(alias='h2oVaporMax')] = None - read_sky_background_name: Annotated[ - str | None, Field(alias='readSkyBackgroundName') - ] = None - sky_background_name: Annotated[str | None, Field(alias='skyBackgroundName')] = None + success: bool | None = None + error: Errors | None = None + entity: TelescopePatternData | None = None -class PersistencePointingOffsetData(CFHTBaseModel): +class UpdateProgramCommentResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - token: str | None = None - name: str | None = None - offset: TypesOffsetCoordinate | None = None - label: int | None = None - version: int | None = None - user_token: Annotated[ - str | None, - Field( - alias='userToken', - title='this is the PI of the program that this entity will be recorded for\nif null should be derived using the program_data.contact_info.user_token\nif null, then a system ?? gross', - ), - ] = None - instrument: TypesInstrumentType | None = TypesInstrumentType.unknown - is_system: Annotated[bool | None, Field(alias='isSystem')] = None + success: bool | None = None + program_data: ProgramData | None = None + error: Errors | None = None -class PersistenceProgramData(CFHTBaseModel): +class UpdateProgramCommentsResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - title: str | None = None - abstract: str | None = None - contact_info: Annotated[ProgramDataContactInfo | None, Field(alias='contactInfo')] = ( - None - ) - comment: list[ProgramDataComments] | None = None - sybase_pi_user_id: Annotated[str | None, Field(alias='sybasePiUserId')] = None - sub_totals: Annotated[PersistenceSubTotalsData | None, Field(alias='subTotals')] = ( - None - ) - token: Annotated[ - str | None, - Field( - description='Unique identifier for the program. Used interchangeably with "runid".' - ), - ] = None - time_allocation: Annotated[ - list[PersistenceAllocationData] | None, Field(alias='timeAllocation') - ] = None - release_date_millis: Annotated[int | None, Field(alias='releaseDateMillis')] = None - metadata_release_date_millis: Annotated[ - int | None, Field(alias='metadataReleaseDateMillis') - ] = None - comment_field: Annotated[str | None, Field(alias='commentField')] = None - program_type: Annotated[ - ProgramDataProgramType | None, Field(alias='programType') - ] = ProgramDataProgramType.regular - distribution_hash: Annotated[str | None, Field(alias='distributionHash')] = None - time_accounting: Annotated[ - PersistenceTimeAccountingData | None, Field(alias='timeAccounting') - ] = None - operations_comment_field: Annotated[ - str | None, Field(alias='operationsCommentField') - ] = None - last_data_acquisition_notification_date_millis: Annotated[ - int | None, Field(alias='lastDataAcquisitionNotificationDateMillis') - ] = None - dynamic_exposure_time_limit: Annotated[ - PersistenceDynamicExposureTimeLimit | None, - Field(alias='dynamicExposureTimeLimit'), - ] = None + success: bool | None = None + program_data: ProgramData | None = None + error: Errors | None = None -class PersistenceTargetData(CFHTBaseModel): +class EphemeridesUpdateRequest(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - token: Annotated[ - str | None, - Field( - description='Unique identifier for the target.\n\nWhen creating a new target, this will be inferred from the request data\nif left blank here.\n\nMust begin with the program token followed by a dash, e.g.\n20AE01-0123456789' - ), - ] = None - name: Annotated[ - str | None, - Field( - description='Name for the target entered by PI, or primary name as resolved by CDS.\n\nWill become OBJECT in the FITS header.\n\nRequired.\nLength limited to 39 characters.\nAllowed characters are letters, numbers, spaces, or any of:\n!@#$%^&*()_-+.,?/[]<>' - ), - ] = None - label: Annotated[ - int | None, - Field( - description='Index of the target within the program, set by the system automatically.\nUsed for reference to targets in communications with QSO staff.\n\nstorage: transient\napi_visibility: read only' - ), - ] = None - version: Annotated[ - int | None, - Field( - description='Increments when this object updates.\n\nstorage: transient\napi_visibility: read only, incremented by the server' - ), + runid: Annotated[ + str | None, Field(description='Unique identifier of the science program.') ] = None - fixed_target: Annotated[TargetDataFixedTarget | None, Field(alias='fixedTarget')] = ( + token: Annotated[str | None, Field(description='Unique identifier of the target.')] = ( None ) - moving_target: Annotated[TargetDataMovingTarget | None, Field(alias='movingTarget')] = ( - None - ) - magnitude: TargetDataMagnitude | None = None - temperature_effective: Annotated[ - float | None, - Field( - alias='temperatureEffective', - description='The estimated/measured effective temperature, in Kelvin.\n\nRequired for targets observed with SPIRou or ESPaDOnS.\nNot used for MegaCam.', - ), - ] = None - standard_star: Annotated[ - bool | None, - Field( - alias='standardStar', - description='Whether the star is a "standard" star, to be observed each time other\nscience targets are observed.\n\nNot used for MegaCam.', - ), - ] = None - linked_target_identifiers: Annotated[ - TargetDataLinkedTargetIdentifiers | None, Field(alias='linkedTargetIdentifiers') - ] = None - finding_chart: Annotated[ - list[PersistenceFindingChartData] | None, - Field( - alias='findingChart', - description='A read-only view of the finding charts for this target.\nTo create or update finding charts, see the FindingChartService.\n\nstorage: transient\napi_visibility: read only', - ), - ] = None - pointing_offset_token: Annotated[ - str | None, - Field( - alias='pointingOffsetToken', - description='The token of a pointing offset which should be used when observing\nthis target.\n\nTo view or manage pointing offsets, see the PointingOffsetService.', - ), - ] = None - pointing_offset: Annotated[ - PersistencePointingOffsetData | None, Field(alias='pointingOffset') - ] = None - - -class PersistenceTelescopePatternData(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - offsets: Annotated[ - list[TelescopePatternDataOffset] | None, - Field( - title='Should only be set if manually specifying custom dithering patterns' - ), - ] = None - config: TelescopePatternDataConfig | None = None - token: str | None = None - label: int | None = None - version: int | None = None - user_token: Annotated[str | None, Field(alias='userToken')] = None - instrument: TypesInstrumentType | None = TypesInstrumentType.unknown - is_system: Annotated[bool | None, Field(alias='isSystem')] = None - name: str | None = None - offset_coordinate: Annotated[ - list[TypesOffsetCoordinate] | None, Field(alias='offsetCoordinate') + ephemeris: Annotated[ + list[MovingTargetEphemeris] | None, + Field(description='List of ephemerides to set on the target.'), ] = None -class TypesDuration(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - millis: int | None = None - display_unit: Annotated[ - TypesDurationUnit | None, Field(alias='displayUnit') - ] = TypesDurationUnit.days - - class ExposureDataExposureStatus(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - megacam_status: Annotated[ - ExposureDataMegacamStatus | None, Field(alias='megacamStatus') - ] = None - wircam_status: Annotated[ - ExposureDataWircamStatus | None, Field(alias='wircamStatus') - ] = None - espadons_status: Annotated[ - ExposureDataEspadonsStatus | None, Field(alias='espadonsStatus') - ] = None - sitelle_status: Annotated[ - ExposureDataSitelleStatus | None, Field(alias='sitelleStatus') - ] = None - spirou_status: Annotated[ - ExposureDataSpirouStatus | None, Field(alias='spirouStatus') - ] = None - wena_status: Annotated[ExposureDataWenaStatus | None, Field(alias='wenaStatus')] = ( - None - ) - exposure_time: Annotated[ - PersistenceExposureTime | None, Field(alias='exposureTime') - ] = None - exp_date_mjd: Annotated[float | None, Field(alias='expDateMjd')] = None - queue_name: Annotated[str | None, Field(alias='queueName')] = None - exp_type: Annotated[str | None, Field(alias='expType')] = None - exp_status: Annotated[ - PersistenceState | None, Field(alias='expStatus') - ] = PersistenceState.unobserved - is_photometric: Annotated[bool | None, Field(alias='isPhotometric')] = None - obs_comment: Annotated[str | None, Field(alias='obsComment')] = None - seq_comment: Annotated[str | None, Field(alias='seqComment')] = None - actual_pointing: Annotated[TypesSkyCoordinate | None, Field(alias='actualPointing')] = ( - None - ) - actual_sky: Annotated[float | None, Field(alias='actualSky')] = None - actual_airmass: Annotated[float | None, Field(alias='actualAirmass')] = None - actual_moondist: Annotated[float | None, Field(alias='actualMoondist')] = None - observer_comment: Annotated[str | None, Field(alias='observerComment')] = None - qc_comment: Annotated[str | None, Field(alias='qcComment')] = None + megacam_status: ExposureDataMegacamStatus | None = None + wircam_status: ExposureDataWircamStatus | None = None + espadons_status: ExposureDataEspadonsStatus | None = None + sitelle_status: ExposureDataSitelleStatus | None = None + spirou_status: ExposureDataSpirouStatus | None = None + wena_status: ExposureDataWenaStatus | None = None + exposure_time: ExposureTime | None = None + exp_date_mjd: float | None = None + queue_name: str | None = None + exp_type: str | None = None + exp_status: ExpStatus | None = None + is_photometric: bool | None = None + obs_comment: str | None = None + seq_comment: str | None = None + actual_pointing: SkyCoordinate | None = None + actual_sky: float | None = None + actual_airmass: float | None = None + actual_moondist: float | None = None + observer_comment: str | None = None + qc_comment: str | None = None file: ExposureStatusFile | None = None - credited_qcoordinator: Annotated[str | None, Field(alias='creditedQcoordinator')] = ( - None - ) - credited_observer: Annotated[str | None, Field(alias='creditedObserver')] = None + credited_qcoordinator: str | None = None + credited_observer: str | None = None grade: int | None = None - read_obs_date_utc: Annotated[str | None, Field(alias='readObsDateUtc')] = None - read_obs_date_hst: Annotated[str | None, Field(alias='readObsDateHst')] = None - read_actual_sky_name: Annotated[str | None, Field(alias='readActualSkyName')] = None - ra: Annotated[str | None, Field(title='Values from headers')] = None + read_obs_date_utc: str | None = None + read_obs_date_hst: str | None = None + read_actual_sky_name: str | None = None + ra: Annotated[str | None, Field(description='Values from headers')] = None dec: str | None = None cmmtobs: str | None = None cmmtseq: str | None = None - pi_name: Annotated[str | None, Field(alias='piName')] = None + pi_name: str | None = None nexp: str | None = None cmpltexp: str | None = None ha: str | None = None - read_obs_date_millis: Annotated[int | None, Field(alias='readObsDateMillis')] = None + read_obs_date_millis: int | None = None object: str | None = None @@ -1978,577 +1951,410 @@ class InstrumentConfigurationDataMegacamConfigurationData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - filter: InstrumentConfigurationDataMegacamConfigurationDataFilter | None = ( - InstrumentConfigurationDataMegacamConfigurationDataFilter.unknown - ) - binning: InstrumentConfigurationDataMegacamConfigurationDataBinning | None = ( - InstrumentConfigurationDataMegacamConfigurationDataBinning.one_one - ) + filter: Filter | None = None + binning: Binning | None = None is_dynamic_exposure_time: Annotated[ bool | None, Field( - alias='isDynamicExposureTime', - title="TODO: is this needed billy? Can we use presence of snr or not to decide?\nI THINK SO. We have the capability to calculate SNR on every exposure, but we aren't necessarily doing SNR mode observations with everything", + description="TODO: is this needed billy? Can we use presence of snr or not to decide? I THINK SO. We have the capability to calculate SNR on every exposure, but we aren't necessarily doing SNR mode observations with everything" ), ] = None - minimum_exposures: Annotated[int | None, Field(alias='minimumExposures')] = None - min_exptime_ms: Annotated[int | None, Field(alias='minExptimeMs')] = None - max_exptime_ms: Annotated[int | None, Field(alias='maxExptimeMs')] = None + minimum_exposures: int | None = None + min_exptime_ms: int | None = None + max_exptime_ms: int | None = None mag_ab: Annotated[ float | None, Field( alias='magAB', - title='TODO: interesting field here because associated with the target, but in the legacy mp snr mode, the mag is carried with the IC! so have to go back to this :(', + description='TODO: interesting field here because associated with the target, but in the legacy mp snr mode, the mag is carried with the IC! so have to go back to this :(', ), ] = None - snr_mode: Annotated[ - MegacamConfigurationDataSnrMode | None, Field(alias='snrMode') - ] = MegacamConfigurationDataSnrMode.none - etc_photometry: Annotated[ - InstrumentConfigurationDataMegacamConfigurationDataPhotometry | None, - Field(alias='etcPhotometry'), - ] = None + snr_mode: SnrMode | None = None + etc_photometry: MegacamConfigurationDataPhotometry | None = None -class PhaseScheduleObservingGroupPhaseSchedule(CFHTBaseModel): +class ObservingGroupData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - earliest_observe_date_millis: Annotated[ + token: Annotated[ + str | None, Field(description='Unique identifier for the observing group.') + ] = None + label: Annotated[ int | None, Field( - alias='earliestObserveDateMillis', - description='The earliest moment to observe, in Unix time milliseconds.', + description='Index of the observing group within the program, set by the system automatically. Used for reference in communications with QSO staff. storage: transient api_visibility: read only' + ), + ] = None + og_priority: Annotated[ + OgPriority | None, + Field( + description='Priority of observing this observing group, relative to other observing groups within the same program.' + ), + ] = None + single_observing_group: SingleObservingGroup | None = None + target_monitoring_observing_group: TargetMonitoringObservingGroup | None = None + phase_schedule_observing_group: PhaseScheduleObservingGroup | None = None + read_observing_block: Annotated[ + list[ObservingGroupDataObservingBlock] | None, + Field( + description='The observing blocks that have been created based on the entered configuration. api_visibility: read only' + ), + ] = None + observing_block_condition: Annotated[ + dict[str, ObservingBlockCondition] | None, + Field( + description='The requirements that have been set for observing each observing blocks based on the entered configuration. api_visibility: read only' + ), + ] = None + minimum_observing_block_count: Annotated[ + int | None, + Field( + description='This field is unused, please ignore. api_visibility: read only' + ), + ] = None + time_accounting: TimeAccountingData | None = None + state: Annotated[ + State | None, + Field( + description='State of the observing group, based off of the time accounting status.' + ), + ] = None + version: Annotated[ + int | None, + Field( + description='Increments when this object updates. storage: transient api_visibility: read only, incremented by the server' + ), + ] = None + legacy_reel: LegacyReel | None = None + time_constraint: TimeConstraint | None = None + target_type: Annotated[ + TargetType | None, + Field( + description='This can be used to configure calibration observing groups. For internal use only. api_visibility: operations write only' + ), + ] = None + dither_scheme: Annotated[ + DitherScheme | None, + Field( + description='Used to customize the order of observation when observing multiple targets with the same telescope pattern within this observing group.' + ), + ] = None + low_surface_brightness: Annotated[ + bool | None, + Field( + description='Indicates the observing group uses low surface brightness (LSB) mode.with' ), ] = None - tolerance: TypesDuration | None = None - period: TypesDuration | None = None - period_tolerance: Annotated[TypesDuration | None, Field(alias='periodTolerance')] = ( - None - ) - - -class ObservingEphemeridesUpdateResponse(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - success: bool | None = None - error: HttpErrors | None = None - entity: PersistenceTargetData | None = None - - -class ObservingPointingOffsetGetResponse(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - success: bool | None = None - entity: PersistencePointingOffsetData | None = None - error: HttpErrors | None = None -class ObservingPointingOffsetListResponse(CFHTBaseModel): +class ObservingGroupListResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) success: bool | None = None - entity: list[PersistencePointingOffsetData] | None = None - error: HttpErrors | None = None + entity: list[ObservingGroupData] | None = None + error: Errors | None = None -class ObservingPointingOffsetUpdateResponse(CFHTBaseModel): +class ObservingGroupUpdateResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) success: bool | None = None - error: HttpErrors | None = None - entity: PersistencePointingOffsetData | None = None + error: Errors | None = None + entity: ObservingGroupData | None = None -class ObservingProgramInfo(CFHTBaseModel): +class ProgramInfo(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - program_data: Annotated[PersistenceProgramData | None, Field(alias='programData')] = ( - None - ) - rollup_fields: Annotated[ObservingRollupFields | None, Field(alias='rollupFields')] = ( - None - ) - time_accounting: Annotated[ - PersistenceTimeAccountingData | None, Field(alias='timeAccounting') - ] = None - pi_info: Annotated[ProgramInfoPiInfo | None, Field(alias='piInfo')] = None + program_data: ProgramData | None = None + rollup_fields: RollupFields | None = None + time_accounting: TimeAccountingData | None = None + pi_info: ProgramInfoPiInfo | None = None version: int | None = None -class ObservingTargetListResponse(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - success: bool | None = None - entity: list[PersistenceTargetData] | None = None - error: HttpErrors | None = None - - -class ObservingTargetShowResponse(CFHTBaseModel): +class TargetData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - success: bool | None = None - error: HttpErrors | None = None - entity: PersistenceTargetData | None = None + token: Annotated[ + str | None, + Field( + description='Unique identifier for the target. When creating a new target, this will be inferred from the request data if left blank here. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789' + ), + ] = None + name: Annotated[ + str | None, + Field( + description='Name for the target entered by PI, or primary name as resolved by CDS. Will become OBJECT in the FITS header. Required. Length limited to 39 characters. Allowed characters are letters, numbers, spaces, or any of: !@#$%^&*()_-+.,?/[]<>' + ), + ] = None + label: Annotated[ + int | None, + Field( + description='Index of the target within the program, set by the system automatically. Used for reference to targets in communications with QSO staff. storage: transient api_visibility: read only' + ), + ] = None + version: Annotated[ + int | None, + Field( + description='Increments when this object updates. storage: transient api_visibility: read only, incremented by the server' + ), + ] = None + fixed_target: TargetDataFixedTarget | None = None + moving_target: TargetDataMovingTarget | None = None + magnitude: TargetDataMagnitude | None = None + temperature_effective: Annotated[ + float | None, + Field( + description='The estimated/measured effective temperature, in Kelvin. Required for targets observed with SPIRou or ESPaDOnS. Not used for MegaCam.' + ), + ] = None + standard_star: Annotated[ + bool | None, + Field( + description='Whether the star is a "standard" star, to be observed each time other science targets are observed. Not used for MegaCam.' + ), + ] = None + linked_target_identifiers: TargetDataLinkedTargetIdentifiers | None = None + finding_chart: Annotated[ + list[FindingChartData] | None, + Field( + description='A read-only view of the finding charts for this target. To create or update finding charts, see the FindingChartService. storage: transient api_visibility: read only' + ), + ] = None + pointing_offset_token: Annotated[ + str | None, + Field( + description='The token of a pointing offset which should be used when observing this target. To view or manage pointing offsets, see the PointingOffsetService.' + ), + ] = None + pointing_offset: PointingOffsetData | None = None -class ObservingTargetUpdateResponse(CFHTBaseModel): +class TargetListResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) success: bool | None = None - error: HttpErrors | None = None - entity: PersistenceTargetData | None = None + entity: list[TargetData] | None = None + error: Errors | None = None -class ObservingTelescopePatternListResponse(CFHTBaseModel): +class TargetShowResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) success: bool | None = None - telescope_pattern: list[PersistenceTelescopePatternData] | None = None - error: HttpErrors | None = None + error: Errors | None = None + entity: TargetData | None = None -class ObservingTelescopePatternUpdateResponse(CFHTBaseModel): +class TargetUpdateRequest(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - success: bool | None = None - error: HttpErrors | None = None - telescope_pattern: PersistenceTelescopePatternData | None = None + runid: Annotated[ + str | None, Field(description='Unique identifier of the science program.') + ] = None + entity: TargetData | None = None + token: Annotated[ + str | None, + Field( + description='Unique identifier of the target. If a target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789' + ), + ] = None + lock_version: Int32Value | None = None + instrument: Annotated[ + Instrument | None, + Field( + description='If specified, instrument-specific validation checks will be performed.' + ), + ] = None -class ObservingUpdateProgramCommentResponse(CFHTBaseModel): +class TargetUpdateResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) success: bool | None = None - program_data: Annotated[PersistenceProgramData | None, Field(alias='programData')] = ( - None - ) - error: HttpErrors | None = None + error: Errors | None = None + entity: TargetData | None = None -class ObservingUpdateProgramCommentsResponse(CFHTBaseModel): +class EphemeridesUpdateResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) success: bool | None = None - program_data: Annotated[PersistenceProgramData | None, Field(alias='programData')] = ( - None - ) - error: HttpErrors | None = None + error: Errors | None = None + entity: TargetData | None = None -class PersistenceInstrumentConfigurationData(CFHTBaseModel): +class InstrumentConfigurationData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - espadons_configuration: Annotated[ - InstrumentConfigurationDataEspadonsConfigurationData | None, - Field(alias='espadonsConfiguration'), - ] = None - megacam_configuration: Annotated[ - InstrumentConfigurationDataMegacamConfigurationData | None, - Field(alias='megacamConfiguration'), - ] = None - wircam_configuration: Annotated[ - InstrumentConfigurationDataWircamConfigurationData | None, - Field(alias='wircamConfiguration'), - ] = None - sitelle_configuration: Annotated[ - InstrumentConfigurationDataSitelleConfigurationData | None, - Field(alias='sitelleConfiguration'), - ] = None - spirou_configuration: Annotated[ - InstrumentConfigurationDataSpirouConfigurationData | None, - Field(alias='spirouConfiguration'), - ] = None - wena_configuration: Annotated[ - InstrumentConfigurationDataWenaConfigurationData | None, - Field(alias='wenaConfiguration'), - ] = None - sybase_ic_id: Annotated[TypesBigInteger | None, Field(alias='sybaseIcId')] = None - etime_or_snr: Annotated[PersistenceExposureTime | None, Field(alias='etimeOrSnr')] = ( + espadons_configuration: ( + InstrumentConfigurationDataEspadonsConfigurationData | None + ) = None + megacam_configuration: ( + InstrumentConfigurationDataMegacamConfigurationData | None + ) = None + wircam_configuration: InstrumentConfigurationDataWircamConfigurationData | None = ( None ) - sybase_cons_id: Annotated[TypesBigInteger | None, Field(alias='sybaseConsId')] = ( + sitelle_configuration: ( + InstrumentConfigurationDataSitelleConfigurationData | None + ) = None + spirou_configuration: InstrumentConfigurationDataSpirouConfigurationData | None = ( None ) - tracking: InstrumentConfigurationDataTracking | None = ( - InstrumentConfigurationDataTracking.unknown - ) + wena_configuration: InstrumentConfigurationDataWenaConfigurationData | None = None + sybase_ic_id: BigInteger | None = None + etime_or_snr: ExposureTime | None = None + sybase_cons_id: BigInteger | None = None + tracking: Tracking | None = None -class PersistenceLegacyReel(CFHTBaseModel): +class ListProgramsResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - reel_og_token: Annotated[ - str | None, - Field(alias='reelOgToken', description='Token of the linked observing group.'), - ] = None - delay: TypesDuration | None = None - window: TypesDuration | None = None - reel_og_label: Annotated[ - int | None, - Field( - alias='reelOgLabel', - description='Label of the observing group which must be observed before this one.\n\nstorage: transient\napi_visibility: read only', - ), - ] = None + success: bool | None = None + entity: list[ProgramInfo] | None = None + error: Errors | None = None -class PersistenceObservingTemplateData(CFHTBaseModel): +class ObservingTemplateData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - instrument_configuration: Annotated[ - PersistenceInstrumentConfigurationData | None, - Field(alias='instrumentConfiguration'), - ] = None - constraint: PersistenceConstraint | None = None - telescope_offset: PersistenceTelescopePatternData | None = None + instrument_configuration: InstrumentConfigurationData | None = None + constraint: Constraint | None = None + telescope_pattern: TelescopePatternData | None = None charge_model: Annotated[ str | None, Field( - alias='chargeModel', - description='Used for calculating cost for this pattern\nInvokes code which reads the ExposureInstructions and\nattributes a cost to it.', + description='Used for calculating cost for this pattern Invokes code which reads the ExposureInstructions and attributes a cost to it.' ), ] = None itime: int | None = None - calculate_etime: Annotated[ - PersistenceExposureTime | None, Field(alias='calculateEtime') - ] = None - acquisition_time: Annotated[ - PersistenceExposureTime | None, Field(alias='acquisitionTime') - ] = None + calculate_etime: ExposureTime | None = None + acquisition_time: ExposureTime | None = None token: str | None = None associated_observing_blocks: Annotated[ - list[TypesBigInteger] | None, + list[BigInteger] | None, Field( - alias='associatedObservingBlocks', - description='select ob.* from ob, prg, icseq where ob.prg_id = prg.id\nand prg.runid=\nand icseq.ob_id=ob.id and ob.con_id = \nand icseq.ic_id = ;', - title='When just creating an OT, this list is empty\nWhen this OT belongs to an OG, then any OBs in sybase that this OT is related too must be filled in.\nselect * from blah where (select id from prg where prg\nQuery to find related OBs is, select ob.* from ob, prg where ob.prg_id = prg.id and prg.runid=? instrument_configuration.sybase_ic_id, constraint.sybase_cons_id', + description='When just creating an OT, this list is empty When this OT belongs to an OG, then any OBs in sybase that this OT is related too must be filled in. select * from blah where (select id from prg where prg Query to find related OBs is, select ob.* from ob, prg where ob.prg_id = prg.id and prg.runid=? instrument_configuration.sybase_ic_id, constraint.sybase_cons_idselect ob.* from ob, prg, icseq where ob.prg_id = prg.idand prg.runid=and icseq.ob_id=ob.id and ob.con_id = and icseq.ic_id = ;' ), ] = None name: str | None = None - total_exposure_count: Annotated[int | None, Field(alias='totalExposureCount')] = ( - None - ) + total_exposure_count: int | None = None label: int | None = None version: Annotated[ int | None, Field( - description='storage: transient\napi_visibility: read only, incremented by the server', - title='Increments when this object updates', - ), - ] = None - telescope_pattern_token: Annotated[str | None, Field(alias='telescopePatternToken')] = ( - None - ) - telescope_pattern_scale: Annotated[ - float | None, Field(alias='telescopePatternScale') - ] = None - megacam_cumulative_snr_system_generated: Annotated[ - bool | None, Field(alias='megacamCumulativeSnrSystemGenerated') - ] = None - - -class PersistencePhaseScheduleObservingGroup(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - observing_block: Annotated[ - ObservingGroupDataObservingBlock | None, Field(alias='observingBlock') - ] = None - phase_schedule: Annotated[ - PhaseScheduleObservingGroupPhaseSchedule | None, Field(alias='phaseSchedule') - ] = None - - -class PersistenceTargetMonitoringObservingGroupTimeConstraint(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - number_of_iterations: Annotated[ - int | None, - Field(alias='numberOfIterations', description='How many observations to take.'), - ] = None - minimum_number_of_iterations: Annotated[ - int | None, - Field( - alias='minimumNumberOfIterations', - description='Minimum number of observations which still provides science value.\n\nIf weather becomes an issue, this is used to determine if additional\nobservations should be tried, or the whole observing group aborted\nto prioritize other observations.\n\nCannot be greater than requested number of iterations.', - ), - ] = None - time_interval: Annotated[TypesDuration | None, Field(alias='timeInterval')] = None - time_interval_tolerance_min: Annotated[ - TypesDuration | None, Field(alias='timeIntervalToleranceMin') - ] = None - time_interval_tolerance_max: Annotated[ - TypesDuration | None, Field(alias='timeIntervalToleranceMax') - ] = None - t0_millis: Annotated[ - int | None, - Field( - alias='t0Millis', - description="Time the observing group should start being observed, in Unix time\nmilliseconds.\n\nIf omitted, CFHT will start observing at the QC's discretion.", - ), - ] = None - within_same_camera_run: Annotated[ - bool | None, - Field( - alias='withinSameCameraRun', - description='Whether all iterations need to occur within the same camera run.', + description='Increments when this object updates storage: transient api_visibility: read only, incremented by the server' ), ] = None + telescope_pattern_token: str | None = None + telescope_pattern_scale: float | None = None + megacam_cumulative_snr_system_generated: bool | None = None -class ObservingListProgramsResponse(CFHTBaseModel): +class ObservingTemplateListResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) success: bool | None = None - entity: list[ObservingProgramInfo] | None = None - error: HttpErrors | None = None + entity: list[ObservingTemplateData] | None = None + error: Errors | None = None -class ObservingObservingTemplateListResponse(CFHTBaseModel): +class ObservingTemplateUpdateRequest(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - success: bool | None = None - entity: list[PersistenceObservingTemplateData] | None = None - error: HttpErrors | None = None + runid: str | None = None + token: str | None = None + entity: ObservingTemplateData | None = None + lock_version: Int32Value | None = None -class ObservingObservingTemplateUpdateResponse(CFHTBaseModel): +class ObservingTemplateUpdateResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) success: bool | None = None - error: HttpErrors | None = None - observing_template: PersistenceObservingTemplateData | None = None - - -class PersistenceObservingGroupContext(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - iteration_count: Annotated[ - int | None, - Field( - alias='iterationCount', - title='The iteration number this object was part of in the observing group', - ), - ] = None - iteration_total: Annotated[ - int | None, - Field( - alias='iterationTotal', - title='The total number of iterations requested for the observing group', - ), - ] = None - iteration_minimum: Annotated[ - int | None, - Field( - alias='iterationMinimum', - title='Minimum number of iterations this OG needs to provide science value', - ), - ] = None - window: Annotated[ - list[ObservingBlockConditionWindow] | None, - Field(title='Sorted by from past -> future on the observe_after key'), - ] = None - observing_group_token: Annotated[str | None, Field(alias='observingGroupToken')] = ( - None - ) - observing_group_priority: Annotated[ - PersistenceObservingGroupPriority | None, Field(alias='observingGroupPriority') - ] = PersistenceObservingGroupPriority.unknown_og_priority - observing_group_state: Annotated[ - ObservingGroupDataObservingGroupState | None, Field(alias='observingGroupState') - ] = ObservingGroupDataObservingGroupState.not_started - observing_group_label: Annotated[int | None, Field(alias='observingGroupLabel')] = ( - None - ) - observing_group_version: Annotated[ - int | None, - Field( - alias='observingGroupVersion', - description='Increments every time the observing group changes.', - ), - ] = None - legacy_reel: Annotated[PersistenceLegacyReel | None, Field(alias='legacyReel')] = ( - None - ) - single: bool | None = None - target_monitoring: Annotated[ - PersistenceTargetMonitoringObservingGroupTimeConstraint | None, - Field(alias='targetMonitoring'), - ] = None - phase_schedule: Annotated[ - PhaseScheduleObservingGroupPhaseSchedule | None, Field(alias='phaseSchedule') - ] = None - time_accounting: Annotated[ - PersistenceTimeAccountingData | None, Field(alias='timeAccounting') - ] = None - exposure_type: Annotated[ - TypesExposureType | None, Field(alias='exposureType') - ] = TypesExposureType.object - use_group_dither_scheme: Annotated[bool | None, Field(alias='useGroupDitherScheme')] = ( - None - ) - - -class PersistenceTargetMonitoringObservingGroup(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - observing_block: Annotated[ - ObservingGroupDataObservingBlock | None, Field(alias='observingBlock') - ] = None - time_constraint: Annotated[ - PersistenceTargetMonitoringObservingGroupTimeConstraint | None, - Field(alias='timeConstraint'), - ] = None + error: Errors | None = None + entity: ObservingTemplateData | None = None -class PersistenceExposureData(CFHTBaseModel): +class ExposureData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) token: str | None = None obsid: str | None = None - exposure_number: Annotated[int | None, Field(alias='exposureNumber')] = None + exposure_number: int | None = None iteration: int | None = None - parent_exposure_token: Annotated[str | None, Field(alias='parentExposureToken')] = ( - None - ) - exposure_status: Annotated[ - ExposureDataExposureStatus | None, Field(alias='exposureStatus') - ] = None - observing_template: Annotated[ - PersistenceObservingTemplateData | None, Field(alias='observingTemplate') - ] = None - target_data: PersistenceTargetData | None = None - observing_group_context: Annotated[ - PersistenceObservingGroupContext | None, Field(alias='observingGroupContext') - ] = None + parent_exposure_token: str | None = None + exposure_status: ExposureDataExposureStatus | None = None + observing_template: ObservingTemplateData | None = None + target: TargetData | None = None + observing_group_context: ObservingGroupContext | None = None all_derivations_complete: Annotated[ bool | None, Field( - alias='allDerivationsComplete', - description='When false, there is data which is still being calculated for this exposure.\n\nData can still be displayed, but the client should attempt to refresh the data.', + description='When false, there is data which is still being calculated for this exposure. Data can still be displayed, but the client should attempt to refresh the data.' ), ] = None - queue_run_id: Annotated[str | None, Field(alias='queueRunId')] = None - camera_run_id: Annotated[str | None, Field(alias='cameraRunId')] = None - prg_run_id: Annotated[str | None, Field(alias='prgRunId')] = None - observing_component_token: Annotated[ - str | None, Field(alias='observingComponentToken') - ] = None - sub_totals: Annotated[PersistenceSubTotalsData | None, Field(alias='subTotals')] = ( - None - ) + queue_run_id: str | None = None + camera_run_id: str | None = None + prg_run_id: str | None = None + observing_component_token: str | None = None + sub_totals: SubTotalsData | None = None attribution: ExposureDataAttribution | None = None version: Annotated[ int | None, Field( - description='storage: transient\napi_visibility: read only, incremented by the server', - title='Increments when this object updates', + description='Increments when this object updates storage: transient api_visibility: read only, incremented by the server' ), ] = None -class PersistenceObservingGroupData(CFHTBaseModel): +class ObservingGroupUpdateRequest(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - token: Annotated[ - str | None, Field(description='Unique identifier for the observing group.') - ] = None - label: Annotated[ - int | None, - Field( - description='Index of the observing group within the program, set by the system\nautomatically. Used for reference in communications with QSO staff.\n\nstorage: transient\napi_visibility: read only' - ), - ] = None - og_priority: Annotated[ - PersistenceObservingGroupPriority | None, Field(alias='ogPriority') - ] = PersistenceObservingGroupPriority.unknown_og_priority - single_observing_group: Annotated[ - PersistenceSingleObservingGroup | None, Field(alias='singleObservingGroup') - ] = None - target_monitoring_observing_group: Annotated[ - PersistenceTargetMonitoringObservingGroup | None, - Field(alias='targetMonitoringObservingGroup'), - ] = None - phase_schedule_observing_group: Annotated[ - PersistencePhaseScheduleObservingGroup | None, - Field(alias='phaseScheduleObservingGroup'), - ] = None - read_observing_block: Annotated[ - list[ObservingGroupDataObservingBlock] | None, - Field( - alias='readObservingBlock', - description='The observing blocks that have been created based on the entered\nconfiguration.\n\napi_visibility: read only', - ), - ] = None - observing_block_condition: Annotated[ - dict[str, PersistenceObservingBlockCondition] | None, - Field( - alias='observingBlockCondition', - description='The requirements that have been set for observing each observing blocks\nbased on the entered configuration.\n\napi_visibility: read only', - ), + runid: Annotated[ + str | None, Field(description='Unique identifier of the science program.') ] = None - minimum_observing_block_count: Annotated[ - int | None, + token: Annotated[ + str | None, Field( - alias='minimumObservingBlockCount', - description='This field is unused, please ignore.\n\napi_visibility: read only', + description='Unique identifier of the observing group. If an observing group target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789' ), ] = None - time_accounting: Annotated[ - PersistenceTimeAccountingData | None, Field(alias='timeAccounting') - ] = None - state: ObservingGroupDataObservingGroupState | None = ( - ObservingGroupDataObservingGroupState.not_started - ) - version: Annotated[ - int | None, + entity: ObservingGroupData | None = None + target: Annotated[ + dict[str, TargetData] | None, Field( - description='Increments when this object updates.\n\nstorage: transient\napi_visibility: read only, incremented by the server' + description='If creating or updating targets with this request, set the data here. Key is the unique identifier of the target and must be prefixed with the runid. Value is the target data and must be valid. Otherwise, the whole request will fail. Observing components can then reference the unique identifier (the map key) in this same request.' ), ] = None - legacy_reel: Annotated[PersistenceLegacyReel | None, Field(alias='legacyReel')] = ( - None - ) - time_constraint: Annotated[ - ObservingpersistenceTimeConstraint | None, Field(alias='timeConstraint') - ] = None - target_type: Annotated[ - TypesExposureType | None, Field(alias='targetType') - ] = TypesExposureType.object - dither_scheme: Annotated[ - ObservingGroupDataDitherScheme | None, Field(alias='ditherScheme') - ] = ObservingGroupDataDitherScheme.none - low_surface_brightness: Annotated[ - bool | None, + observing_template: Annotated[ + dict[str, ObservingTemplateData] | None, Field( - alias='lowSurfaceBrightness', - title='Indicates the observing group uses low surface brightness (LSB) mode.with', + description='If creating or updating observing templates with this request, set the data here. Key is the unique identifier of the observing template and must be prefixed with the runid. Value is the observing template data and must be valid. Otherwise, the whole request will fail. Observing components can then reference the unique identifier (the map key) in this same request.' ), ] = None + lock_version: Int32Value | None = None class SnrBlockDataSnrBlockObservingComponents(CFHTBaseModel): @@ -2556,49 +2362,22 @@ class SnrBlockDataSnrBlockObservingComponents(CFHTBaseModel): validate_by_name=True, ) observing_component_token: Annotated[ - str | None, - Field(alias='observingComponentToken', title='the observing component'), + str | None, Field(description='the observing component') ] = None observing_group_token: Annotated[ - str | None, - Field( - alias='observingGroupToken', - title='should not be here, should be in the parent', - ), - ] = None - is_primary: Annotated[ - bool | None, Field(alias='isPrimary', title='is it the original oc?') + str | None, Field(description='should not be here, should be in the parent') ] = None - configured_exposures: Annotated[int | None, Field(alias='configuredExposures')] = ( + is_primary: Annotated[bool | None, Field(description='is it the original oc?')] = ( None ) - exposures: list[PersistenceExposureData] | None = None - ob_label: Annotated[int | None, Field(alias='obLabel')] = None - target_token: Annotated[str | None, Field(alias='targetToken')] = None - observing_template_token: Annotated[ - str | None, Field(alias='observingTemplateToken') - ] = None - - -class ObservingObservingGroupListResponse(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - success: bool | None = None - entity: list[PersistenceObservingGroupData] | None = None - error: HttpErrors | None = None - - -class ObservingObservingGroupUpdateResponse(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - success: bool | None = None - error: HttpErrors | None = None - entity: PersistenceObservingGroupData | None = None + configured_exposures: int | None = None + exposures: list[ExposureData] | None = None + ob_label: int | None = None + target_token: str | None = None + observing_template_token: str | None = None -class PersistenceSnrBlockData(CFHTBaseModel): +class SnrBlockData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) @@ -2607,74 +2386,53 @@ class PersistenceSnrBlockData(CFHTBaseModel): cumulative_snr: Annotated[ float | None, Field( - alias='cumulativeSnr', - title='the total snr achieved of all exposures\n of all observing components of this snr block', + description='the total snr achieved of all exposures of all observing components of this snr block' ), ] = None carry_forward_snr: Annotated[ float | None, Field( - alias='carryForwardSnr', - title='if the program is copied to a new program possibly for a new semester\n the cumulative snr will be moved to the carry_forward_snr and the\n carry forward snr will be counted in the cumulative snr of the new program', + description='if the program is copied to a new program possibly for a new semester the cumulative snr will be moved to the carry_forward_snr and the carry forward snr will be counted in the cumulative snr of the new program' ), ] = None - observing_components: Annotated[ - list[SnrBlockDataSnrBlockObservingComponents] | None, - Field(alias='observingComponents'), - ] = None + observing_components: list[SnrBlockDataSnrBlockObservingComponents] | None = None version: int | None = None requested_target_magnitude: Annotated[ - float | None, - Field(alias='requestedTargetMagnitude', title='this will come from the target'), + float | None, Field(description='this will come from the target') ] = None requested_cumulative_snr: Annotated[ - float | None, - Field(alias='requestedCumulativeSnr', title='this will come from the OT'), - ] = None - requested_minimum_exposures: Annotated[ - int | None, Field(alias='requestedMinimumExposures') + float | None, Field(description='this will come from the OT') ] = None + requested_minimum_exposures: int | None = None total_exposures: Annotated[ int | None, Field( - alias='totalExposures', - title='counting the total validated exposures for this OC (OT/TARGET)', + description='counting the total validated exposures for this OC (OT/TARGET)' ), ] = None - primary_observing_group_token: Annotated[ - str | None, Field(alias='primaryObservingGroupToken') - ] = None - primary_observing_component_token: Annotated[ - str | None, Field(alias='primaryObservingComponentToken') - ] = None - primary_observing_group_label: Annotated[ - int | None, Field(alias='primaryObservingGroupLabel') - ] = None + primary_observing_group_token: str | None = None + primary_observing_component_token: str | None = None + primary_observing_group_label: int | None = None total_configured_exposures: Annotated[ int | None, - Field( - alias='totalConfiguredExposures', - title='counting the total configured exposures for this OC', - ), + Field(description='counting the total configured exposures for this OC'), ] = None -class ObservingAddSingleToSnrBlockResponse(CFHTBaseModel): +class SnrBlockListResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) success: bool | None = None - error: HttpErrors | None = None - snr_block: Annotated[PersistenceSnrBlockData | None, Field(alias='snrBlock')] = None - observing_group: Annotated[ - PersistenceObservingGroupData | None, Field(alias='observingGroup') - ] = None + error: Errors | None = None + entity: list[SnrBlockData] | None = None -class ObservingSnrBlockListResponse(CFHTBaseModel): +class AddSingleToSnrBlockResponse(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) success: bool | None = None - error: HttpErrors | None = None - entity: list[PersistenceSnrBlockData] | None = None + error: Errors | None = None + snr_block: SnrBlockData | None = None + observing_group: ObservingGroupData | None = None diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index 05eb134..10ef2bb 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -8,5 +8,7 @@ def test_programs(): facility = CFHTFacility() programs = facility.programs() + print(programs) assert programs[0].pi_info assert programs[0].pi_info.first_name == "AEON" + assert False From a2e0c4b79a44258bd5afb217fef613d6dc8fb24b Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 23 Jul 2026 16:38:42 -0700 Subject: [PATCH 09/33] Making progress on example parity --- src/aeonlib/cfht/base_model.py | 2 +- src/aeonlib/cfht/facility.py | 30 ++++++++- tests/cfht/test_online.py | 119 +++++++++++++++++++++++++++++++-- 3 files changed, 145 insertions(+), 6 deletions(-) diff --git a/src/aeonlib/cfht/base_model.py b/src/aeonlib/cfht/base_model.py index 2b47a2d..fbd9ac8 100644 --- a/src/aeonlib/cfht/base_model.py +++ b/src/aeonlib/cfht/base_model.py @@ -7,4 +7,4 @@ class CFHTBaseModel(BaseModel): if necessary """ - model_config = ConfigDict(coerce_numbers_to_str=False) + model_config = ConfigDict(validate_assignment=True) diff --git a/src/aeonlib/cfht/facility.py b/src/aeonlib/cfht/facility.py index cf01d00..6189f6c 100644 --- a/src/aeonlib/cfht/facility.py +++ b/src/aeonlib/cfht/facility.py @@ -4,7 +4,7 @@ from aeonlib.conf import settings -from .models import ProgramInfo +from .models import Instrument, ProgramInfo, TargetData INSTRUMENTS = Literal["SPIROU", "ESPADONS", "MEGACAM"] @@ -33,3 +33,31 @@ def programs(self) -> list[ProgramInfo]: return [ ProgramInfo.model_validate(program) for program in payload.get("entity", []) ] + + def targets(self, program_token: str) -> list[TargetData]: + """Get the list of targets for a given program""" + response = self._client.get(f"/programs/{program_token}/targets/") + response.raise_for_status() + payload = response.json() + return [ + TargetData.model_validate(target) for target in payload.get("entity", []) + ] + + def create_or_update_target( + self, program_token: str, target: TargetData, instrument: Instrument + ) -> TargetData: + print(str(instrument.value)) + version = {"value": target.version} if target.version else None + data = { + "entity": target.model_dump(), + "lock_version": version, + "instrument": instrument.value, + } + response = self._client.put( + f"/programs/{program_token}/targets/{target.token}/", + json=data, + ) + print(response.text) + response.raise_for_status() + payload = response.json() + return TargetData.model_validate(payload["entity"]) diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index 10ef2bb..62e20d2 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -1,14 +1,125 @@ +import random + import pytest +from pydantic import ValidationError from aeonlib.cfht.facility import CFHTFacility +from aeonlib.cfht.models import ( + DoubleValue, # TODO: should be removed or coerced from float + Instrument, + MovingTargetEphemeris, # TODO: replace with common non-sidereal model + SkyCoordinate, + TargetData, + TargetDataFixedTarget, # TODO: replace with common target model + TargetDataMagnitude, + TargetDataMovingTarget, # TODO: replace with common non-sidereal target model +) pytestmark = pytest.mark.online +required_mag_by_instrument: dict[Instrument, str] = { + Instrument.spirou: "h", + Instrument.espadons: "v", + Instrument.megacam: "ab", +} + + +FACILITY = CFHTFacility() + def test_programs(): - facility = CFHTFacility() - programs = facility.programs() - print(programs) + programs = FACILITY.programs() assert programs[0].pi_info assert programs[0].pi_info.first_name == "AEON" - assert False + + +def example_fixed_target(program_token: str, instrument: Instrument) -> TargetData: + return TargetData( + token=f"{program_token}-{random.randint(1000000000, 9999999999)}", + name="my new aeonlib test target", + fixed_target=TargetDataFixedTarget( # TODO: use Aeonlib common Target + coordinate=SkyCoordinate( + ra=random.uniform(0, 359.9999), + dec=random.uniform(-90, 90), + ), + proper_motion=None, + estimated_radial_velocity_kmps=DoubleValue( + value=234.0 + ) # TODO: remove the need for DoubleValue + if instrument == Instrument.spirou + else None, + ), + magnitude=None, + temperature_effective=1234.5, + standard_star=False, + pointing_offset_token=f"00AZ00-PO+{instrument}+1", + ) + + +def example_moving_target(program_token: str, instrument: Instrument) -> TargetData: + return TargetData( + token=f"{program_token}-{random.randint(1000000000, 9999999999)}", + name="my new aeonlib moving target", + moving_target=TargetDataMovingTarget( + ephemeris_point=[ + MovingTargetEphemeris( + mjd=61041.0 + i, + coordinate=SkyCoordinate( + ra=random.uniform(0, 359.9999), + dec=random.uniform(-90, 90), + ), + ) + for i in range(0, 5) + ] + ), + magnitude=None, + temperature_effective=1234.5, + standard_star=False, + pointing_offset_token=f"00AZ00-PO+{instrument}+1", + ) + + +def target_api_examples(program_token: str, instrument: Instrument): + """ + Complete API examples for Kealahou targets + """ + targets = FACILITY.targets(program_token) + for target in targets: + assert target.name is not None + # assert "Test" in target.name # TODO: find a better invariant + + new_target = example_fixed_target(program_token, instrument) + + # This is the part in the example.py where the target is assigned an + # incorrect format for RA and sent to the API. We can't get that far in Aeonlib + # because Pydantic will raise a Validation Error before it can be sumitted. + + assert new_target.fixed_target is not None + assert new_target.fixed_target.coordinate is not None + assert new_target.fixed_target.coordinate.ra is not None + old_ra = new_target.fixed_target.coordinate.ra + with pytest.raises(ValidationError): + new_target.fixed_target.coordinate.ra = "invalid" # type: ignore + assert new_target.fixed_target.coordinate.ra == old_ra + + new_target.fixed_target.coordinate.ra = 20.7 + mag = TargetDataMagnitude() + setattr(mag, required_mag_by_instrument[instrument], DoubleValue(value=10.0)) + new_target.magnitude = mag + old_version = new_target.version if new_target.version is not None else 0 + target = FACILITY.create_or_update_target(program_token, new_target, instrument) + assert target.version == old_version + 1 + + +def test_example(): + programs = FACILITY.programs() + for program in programs: + program_data = program.program_data + assert program_data is not None + program_token = program_data.token + assert program_token is not None + time_allocation = program_data.time_allocation + assert time_allocation is not None + instrument = time_allocation[0].instrument + assert instrument is not None + target_api_examples(program_token, instrument) From 72e9946978914124363be7f288e6cc8652c8bca3 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 30 Jul 2026 15:37:55 -0700 Subject: [PATCH 10/33] Accept custom settings in cfht facility constructor --- src/aeonlib/cfht/facility.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/aeonlib/cfht/facility.py b/src/aeonlib/cfht/facility.py index 6189f6c..54d89bb 100644 --- a/src/aeonlib/cfht/facility.py +++ b/src/aeonlib/cfht/facility.py @@ -2,7 +2,8 @@ import httpx -from aeonlib.conf import settings +from aeonlib.conf import Settings +from aeonlib.conf import settings as default_settings from .models import Instrument, ProgramInfo, TargetData @@ -12,7 +13,7 @@ class CFHTFacility: """CFHT Facility class""" - def __init__(self): + def __init__(self, settings: Settings = default_settings): base_url = settings.cfht_api_root if not base_url: raise ValueError("AEON_CFHT_API_ROOT is not set") From c042e12912b002ba0446c2139ba76458dea96a73 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 30 Jul 2026 16:28:55 -0700 Subject: [PATCH 11/33] More example parity, but CFHT api appears to be broken --- src/aeonlib/cfht/base_model.py | 8 ++++++++ src/aeonlib/cfht/facility.py | 4 +--- tests/cfht/test_online.py | 14 ++++++++++++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/aeonlib/cfht/base_model.py b/src/aeonlib/cfht/base_model.py index fbd9ac8..3d8c17d 100644 --- a/src/aeonlib/cfht/base_model.py +++ b/src/aeonlib/cfht/base_model.py @@ -8,3 +8,11 @@ class CFHTBaseModel(BaseModel): """ model_config = ConfigDict(validate_assignment=True) + + def api_dump(self, **kwargs): + return self.model_dump( + mode="json", + by_alias=True, + exclude_none=True, + **kwargs, + ) diff --git a/src/aeonlib/cfht/facility.py b/src/aeonlib/cfht/facility.py index 54d89bb..a38b207 100644 --- a/src/aeonlib/cfht/facility.py +++ b/src/aeonlib/cfht/facility.py @@ -47,10 +47,9 @@ def targets(self, program_token: str) -> list[TargetData]: def create_or_update_target( self, program_token: str, target: TargetData, instrument: Instrument ) -> TargetData: - print(str(instrument.value)) version = {"value": target.version} if target.version else None data = { - "entity": target.model_dump(), + "entity": target.api_dump(), "lock_version": version, "instrument": instrument.value, } @@ -58,7 +57,6 @@ def create_or_update_target( f"/programs/{program_token}/targets/{target.token}/", json=data, ) - print(response.text) response.raise_for_status() payload = response.json() return TargetData.model_validate(payload["entity"]) diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index 62e20d2..fbd7e3e 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -52,7 +52,7 @@ def example_fixed_target(program_token: str, instrument: Instrument) -> TargetDa magnitude=None, temperature_effective=1234.5, standard_star=False, - pointing_offset_token=f"00AZ00-PO+{instrument}+1", + pointing_offset_token=f"00AZ00-PO+{instrument.value}+1", ) @@ -75,7 +75,7 @@ def example_moving_target(program_token: str, instrument: Instrument) -> TargetD magnitude=None, temperature_effective=1234.5, standard_star=False, - pointing_offset_token=f"00AZ00-PO+{instrument}+1", + pointing_offset_token=f"00AZ00-PO+{instrument.value}+1", ) @@ -102,13 +102,23 @@ def target_api_examples(program_token: str, instrument: Instrument): new_target.fixed_target.coordinate.ra = "invalid" # type: ignore assert new_target.fixed_target.coordinate.ra == old_ra + # Successfully create new target with instrument-specific checks new_target.fixed_target.coordinate.ra = 20.7 mag = TargetDataMagnitude() setattr(mag, required_mag_by_instrument[instrument], DoubleValue(value=10.0)) + print(mag) new_target.magnitude = mag old_version = new_target.version if new_target.version is not None else 0 target = FACILITY.create_or_update_target(program_token, new_target, instrument) assert target.version == old_version + 1 + old_version += 1 + + # Update target + update_target = target + update_target.standard_star = True + target = FACILITY.create_or_update_target(program_token, update_target, instrument) + assert target.standard_star is True + assert target.version == old_version + 1 def test_example(): From c2ac226cc6f1fc32f9d3d008428b2999748021de Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 6 Aug 2026 14:13:29 -0700 Subject: [PATCH 12/33] Full example api online test --- src/aeonlib/cfht/facility.py | 31 ++++++++++++++++++ tests/cfht/test_online.py | 61 +++++++++++++++++++++++++++++------- 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/src/aeonlib/cfht/facility.py b/src/aeonlib/cfht/facility.py index a38b207..e7729ff 100644 --- a/src/aeonlib/cfht/facility.py +++ b/src/aeonlib/cfht/facility.py @@ -10,6 +10,10 @@ INSTRUMENTS = Literal["SPIROU", "ESPADONS", "MEGACAM"] +class VersionMismatchError(ValueError): + """Raised when the version of the target does not match the server""" + + class CFHTFacility: """CFHT Facility class""" @@ -22,6 +26,7 @@ def __init__(self, settings: Settings = default_settings): raise ValueError("AEON_CFHT_ACCESS_TOKEN token is not set") headers = { "Authorization": f"Bearer {access_token}", + "Content-Type": "application/json", } self._client = httpx.Client(base_url=base_url, headers=headers) @@ -44,6 +49,28 @@ def targets(self, program_token: str) -> list[TargetData]: TargetData.model_validate(target) for target in payload.get("entity", []) ] + def get_target(self, program_token: str, target_token: str) -> TargetData | None: + response = self._client.get(f"/programs/{program_token}/targets/{target_token}") + if response.status_code == 404: + return None + + response.raise_for_status() + payload = response.json() + return ( + TargetData.model_validate(payload.get("entity")) + if payload.get("entity") + else None + ) + + def delete_target(self, program_token: str, target_token: str) -> None: + response = self._client.delete( + f"/programs/{program_token}/targets/{target_token}" + ) + if response.status_code == 406: + return + + response.raise_for_status() + def create_or_update_target( self, program_token: str, target: TargetData, instrument: Instrument ) -> TargetData: @@ -57,6 +84,10 @@ def create_or_update_target( f"/programs/{program_token}/targets/{target.token}/", json=data, ) + if response.status_code == 409: + raise VersionMismatchError( + "Version mismatch. Payload had version %s", target.version + ) response.raise_for_status() payload = response.json() return TargetData.model_validate(payload["entity"]) diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index fbd7e3e..757ab53 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -1,9 +1,10 @@ import random +import uuid import pytest from pydantic import ValidationError -from aeonlib.cfht.facility import CFHTFacility +from aeonlib.cfht.facility import CFHTFacility, VersionMismatchError from aeonlib.cfht.models import ( DoubleValue, # TODO: should be removed or coerced from float Instrument, @@ -33,10 +34,12 @@ def test_programs(): assert programs[0].pi_info.first_name == "AEON" -def example_fixed_target(program_token: str, instrument: Instrument) -> TargetData: +def example_fixed_target( + program_token: str, instrument: Instrument, test_run_id: str +) -> TargetData: return TargetData( token=f"{program_token}-{random.randint(1000000000, 9999999999)}", - name="my new aeonlib test target", + name=f"my new aeonlib test target {test_run_id}", fixed_target=TargetDataFixedTarget( # TODO: use Aeonlib common Target coordinate=SkyCoordinate( ra=random.uniform(0, 359.9999), @@ -56,10 +59,12 @@ def example_fixed_target(program_token: str, instrument: Instrument) -> TargetDa ) -def example_moving_target(program_token: str, instrument: Instrument) -> TargetData: +def example_moving_target( + program_token: str, instrument: Instrument, test_run_id: str +) -> TargetData: return TargetData( token=f"{program_token}-{random.randint(1000000000, 9999999999)}", - name="my new aeonlib moving target", + name=f"my new aeonlib moving target {test_run_id}", moving_target=TargetDataMovingTarget( ephemeris_point=[ MovingTargetEphemeris( @@ -69,17 +74,19 @@ def example_moving_target(program_token: str, instrument: Instrument) -> TargetD dec=random.uniform(-90, 90), ), ) - for i in range(0, 5) + for i in range(5) ] ), - magnitude=None, + magnitude=TargetDataMagnitude( + **{required_mag_by_instrument[instrument]: DoubleValue(value=10.0)} + ), temperature_effective=1234.5, standard_star=False, pointing_offset_token=f"00AZ00-PO+{instrument.value}+1", ) -def target_api_examples(program_token: str, instrument: Instrument): +def target_api_examples(program_token: str, instrument: Instrument, test_run_id: str): """ Complete API examples for Kealahou targets """ @@ -88,7 +95,7 @@ def target_api_examples(program_token: str, instrument: Instrument): assert target.name is not None # assert "Test" in target.name # TODO: find a better invariant - new_target = example_fixed_target(program_token, instrument) + new_target = example_fixed_target(program_token, instrument, test_run_id) # This is the part in the example.py where the target is assigned an # incorrect format for RA and sent to the API. We can't get that far in Aeonlib @@ -106,7 +113,6 @@ def target_api_examples(program_token: str, instrument: Instrument): new_target.fixed_target.coordinate.ra = 20.7 mag = TargetDataMagnitude() setattr(mag, required_mag_by_instrument[instrument], DoubleValue(value=10.0)) - print(mag) new_target.magnitude = mag old_version = new_target.version if new_target.version is not None else 0 target = FACILITY.create_or_update_target(program_token, new_target, instrument) @@ -120,8 +126,41 @@ def target_api_examples(program_token: str, instrument: Instrument): assert target.standard_star is True assert target.version == old_version + 1 + # Attempt to update target with invalid version + # TODO should aeonlib handle versioning client side? + with pytest.raises(VersionMismatchError): + update_target.temperature_effective = 2345.6 + FACILITY.create_or_update_target(program_token, update_target, instrument) + + # Fetch single target + token = target.token + assert token, "target token should not be None" + target = FACILITY.get_target(program_token, token) + assert target is not None + assert token == target.token + + # Delete the new (unobserved) target + assert target.token + FACILITY.delete_target(program_token, target.token) + target = FACILITY.get_target(program_token, target.token) + assert target is None + + # create moving target + new_moving_target = example_moving_target(program_token, instrument, test_run_id) + moving_target = FACILITY.create_or_update_target( + program_token, new_moving_target, instrument + ) + assert moving_target is not None + + # Delete moving target + assert moving_target.token + FACILITY.delete_target(program_token, moving_target.token) + moving_target = FACILITY.get_target(program_token, moving_target.token) + assert moving_target is None + def test_example(): + test_run_id = f"{uuid.uuid4()}"[:8] programs = FACILITY.programs() for program in programs: program_data = program.program_data @@ -132,4 +171,4 @@ def test_example(): assert time_allocation is not None instrument = time_allocation[0].instrument assert instrument is not None - target_api_examples(program_token, instrument) + target_api_examples(program_token, instrument, test_run_id) From 6bf265d0512b8d2c86fc8f7188988cc294c14fb0 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 6 Aug 2026 14:23:40 -0700 Subject: [PATCH 13/33] Minor cleanup --- codegen/cfht/generator.py | 4 ++-- codegen/cfht/generator_oas3.py | 4 ++-- pyproject.toml | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) mode change 100644 => 100755 codegen/cfht/generator_oas3.py diff --git a/codegen/cfht/generator.py b/codegen/cfht/generator.py index 832cc38..22c338e 100755 --- a/codegen/cfht/generator.py +++ b/codegen/cfht/generator.py @@ -73,7 +73,7 @@ def generate_models(openapi_document: dict[str, Any]) -> str: result = generate(openapi_document, config=config) if not isinstance(result, str): - raise RuntimeError( + raise TypeError( "Expected string output from datamodel-code-generator" f"got {type(result).__name__}." ) @@ -94,6 +94,6 @@ def main() -> int: if __name__ == "__main__": try: raise SystemExit(main()) - except (httpx.HTTPError, DataModelCodegenError, OSError, RuntimeError) as exc: + except (httpx.HTTPError, DataModelCodegenError, OSError, TypeError) as exc: print(f"CFHT model generation failed: {exc}", file=sys.stderr) raise SystemExit(1) diff --git a/codegen/cfht/generator_oas3.py b/codegen/cfht/generator_oas3.py old mode 100644 new mode 100755 index 50d3195..f88f5c7 --- a/codegen/cfht/generator_oas3.py +++ b/codegen/cfht/generator_oas3.py @@ -39,7 +39,7 @@ def generate_models(openapi_document: dict[str, Any]) -> str: result = generate(openapi_document, config=config) if not isinstance(result, str): - raise RuntimeError( + raise TypeError( "Expected string output from datamodel-code-generator " f"got {type(result).__name__}." ) @@ -59,6 +59,6 @@ def main() -> int: if __name__ == "__main__": try: raise SystemExit(main()) - except (yaml.YAMLError, DataModelCodegenError, OSError, RuntimeError) as exc: + except (yaml.YAMLError, DataModelCodegenError, OSError, TypeError) as exc: print(f"CFHT model generation failed: {exc}", file=sys.stderr) raise SystemExit(1) diff --git a/pyproject.toml b/pyproject.toml index 234b5da..dd484b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,6 +56,7 @@ log_cli_format = "%(levelname)s [%(name)s %(filename)s:%(lineno)s %(funcName)s() extend-exclude = [ "*.ipynb", "src/aeonlib/ocs/**/instruments.py", + "src/aeonlib/cfht/models.py" ] force-exclude = true From 747b88e1664fe0a61df6c0f5e9178e0d0b571d6d Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 6 Aug 2026 14:32:30 -0700 Subject: [PATCH 14/33] Commit to oas3 cfht spec even if local for now --- codegen/cfht/cfhtopenapi.yaml | 3814 ++++++++++++++++++++++++++++++++ codegen/cfht/generator.py | 52 +- codegen/cfht/generator_oas3.py | 64 - 3 files changed, 3823 insertions(+), 107 deletions(-) create mode 100644 codegen/cfht/cfhtopenapi.yaml delete mode 100755 codegen/cfht/generator_oas3.py diff --git a/codegen/cfht/cfhtopenapi.yaml b/codegen/cfht/cfhtopenapi.yaml new file mode 100644 index 0000000..22fb9d9 --- /dev/null +++ b/codegen/cfht/cfhtopenapi.yaml @@ -0,0 +1,3814 @@ +# Generated with protoc-gen-openapi +# https://github.com/google/gnostic/tree/master/cmd/protoc-gen-openapi + +openapi: 3.0.3 +info: + title: Kealahou API + version: 1.0.0 +paths: + /pointing_offset: + get: + tags: + - PointingOffsetService + operationId: PointingOffsetService_ListPointingOffset + parameters: + - name: runid + in: query + description: Set this to limit scope to custom pointing offsets for the specified science program + schema: + type: string + - name: instrument + in: query + description: Set this to scope to system-provided pointing offsets for the specified instrument + schema: + enum: + - UNKNOWN + - CFH12K + - MEGACAM + - WIRCAM + - ESPADONS + - SITELLE + - SPIROU + - WENAOKEAO + type: string + format: enum + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PointingOffsetListResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /pointing_offset/user: + get: + tags: + - PointingOffsetService + operationId: PointingOffsetService_GetPointingOffset + parameters: + - name: runid + in: query + description: Unique identifier your science program. + schema: + type: string + - name: name + in: query + schema: + type: string + - name: offset.ra_offset + in: query + description: Right ascension, in degrees + schema: + type: number + format: double + - name: offset.dec_offset + in: query + description: Declination, in degrees + schema: + type: number + format: double + - name: offset.exposure_number + in: query + schema: + type: integer + format: int64 + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PointingOffsetGetResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /pointing_offset/{token}: + put: + tags: + - PointingOffsetService + operationId: PointingOffsetService_CreateOrUpdatePointingOffset + parameters: + - name: token + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PointingOffsetUpdateRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PointingOffsetUpdateResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + delete: + tags: + - PointingOffsetService + operationId: PointingOffsetService_DeletePointingOffset + parameters: + - name: token + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PointingOffsetDeleteResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /programs: + get: + tags: + - ProgramService + operationId: ProgramService_ListPrograms + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListProgramsResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /programs/{runid}/comment: + put: + tags: + - ProgramService + operationId: ProgramService_UpdateProgramComment + parameters: + - name: runid + in: path + description: Unique identifier for your science program + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateProgramCommentRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateProgramCommentResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /programs/{runid}/comments: + put: + tags: + - ProgramService + operationId: ProgramService_UpdateProgramComments + parameters: + - name: runid + in: path + description: Unique identifier for your science program + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateProgramCommentsRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateProgramCommentsResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /programs/{runid}/observing-groups: + get: + tags: + - ObservingGroupService + operationId: ObservingGroupService_ListObservingGroups + parameters: + - name: runid + in: path + description: Unique identifier of the science program. + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ObservingGroupListResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /programs/{runid}/observing-groups/{token}: + put: + tags: + - ObservingGroupService + operationId: ObservingGroupService_CreateOrUpdateObservingGroup + parameters: + - name: runid + in: path + description: Unique identifier of the science program. + required: true + schema: + type: string + - name: token + in: path + description: Unique identifier of the observing group. If an observing group target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789 + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ObservingGroupUpdateRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ObservingGroupUpdateResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + delete: + tags: + - ObservingGroupService + operationId: ObservingGroupService_DeleteObservingGroup + parameters: + - name: runid + in: path + description: Unique identifier of the science program. + required: true + schema: + type: string + - name: token + in: path + description: Unique identifier of the observing group. + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ObservingGroupDeleteResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /programs/{runid}/observing-templates: + get: + tags: + - ObservingTemplateService + operationId: ObservingTemplateService_ListObservingTemplates + parameters: + - name: runid + in: path + description: Unique identifier of the science program. + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ObservingTemplateListResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /programs/{runid}/observing-templates/{token}: + put: + tags: + - ObservingTemplateService + operationId: ObservingTemplateService_CreateOrUpdateObservingTemplate + parameters: + - name: runid + in: path + required: true + schema: + type: string + - name: token + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ObservingTemplateUpdateRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ObservingTemplateUpdateResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + delete: + tags: + - ObservingTemplateService + operationId: ObservingTemplateService_DeleteObservingTemplate + parameters: + - name: runid + in: path + required: true + schema: + type: string + - name: token + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ObservingTemplateDeleteResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /programs/{runid}/snr-blocks: + get: + tags: + - ObservingGroupService + operationId: ObservingGroupService_ListSnrBlocks + parameters: + - name: runid + in: path + description: Unique identifier of the science program. + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/SnrBlockListResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /programs/{runid}/snr-blocks/{snr_block_token}: + put: + tags: + - ObservingGroupService + operationId: ObservingGroupService_AddSingleToSnrBlock + parameters: + - name: runid + in: path + description: Unique identifier of the science program. + required: true + schema: + type: string + - name: snr_block_token + in: path + description: Unique identifier of an existing SNR block to create the new observing group for. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AddSingleToSnrBlockRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/AddSingleToSnrBlockResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /programs/{runid}/targets: + get: + tags: + - TargetService + operationId: TargetService_ListTarget + parameters: + - name: runid + in: path + description: Unique identifier for the science program. + required: true + schema: + type: string + - name: target_type + in: query + description: Filter the returned targets to only include the type specified. + schema: + enum: + - ALL + - FIXED + - MOVING + type: string + format: enum + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/TargetListResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /programs/{runid}/targets/{token}: + get: + tags: + - TargetService + operationId: TargetService_ShowTarget + parameters: + - name: runid + in: path + description: Unique identifier of the science program. + required: true + schema: + type: string + - name: token + in: path + description: Unique identifier of the target. + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/TargetShowResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + put: + tags: + - TargetService + operationId: TargetService_CreateOrUpdateTarget + parameters: + - name: runid + in: path + description: Unique identifier of the science program. + required: true + schema: + type: string + - name: token + in: path + description: Unique identifier of the target. If a target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789 + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TargetUpdateRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/TargetUpdateResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + delete: + tags: + - TargetService + operationId: TargetService_DeleteTarget + parameters: + - name: runid + in: path + description: Unique identifier of the science program. + required: true + schema: + type: string + - name: token + in: path + description: Unique identifier of the target. + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/TargetDeleteResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /programs/{runid}/targets/{token}/ephemerides: + put: + tags: + - TargetService + operationId: TargetService_UpdateEphemerides + parameters: + - name: runid + in: path + description: Unique identifier of the science program. + required: true + schema: + type: string + - name: token + in: path + description: Unique identifier of the target. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EphemeridesUpdateRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/EphemeridesUpdateResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /telescope_pattern: + get: + tags: + - ObservingTemplateService + operationId: ObservingTemplateService_ListTelescopePattern + parameters: + - name: runid + in: query + schema: + type: string + - name: instrument + in: query + schema: + enum: + - UNKNOWN + - CFH12K + - MEGACAM + - WIRCAM + - ESPADONS + - SITELLE + - SPIROU + - WENAOKEAO + type: string + format: enum + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/TelescopePatternListResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /telescope_pattern/{token}: + put: + tags: + - ObservingTemplateService + operationId: ObservingTemplateService_CreateOrUpdateTelescopePattern + parameters: + - name: token + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TelescopePatternUpdateRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/TelescopePatternUpdateResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + delete: + tags: + - ObservingTemplateService + operationId: ObservingTemplateService_DeleteTelescopePattern + parameters: + - name: token + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/TelescopePatternDeleteResponse' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' +components: + schemas: + AddSingleToSnrBlockRequest: + type: object + properties: + runid: + type: string + description: Unique identifier of the science program. + snr_block_token: + type: string + description: Unique identifier of an existing SNR block to create the new observing group for. + observing_group_token: + type: string + description: Unique identifier of the observing group. If an observing group target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789 + AddSingleToSnrBlockResponse: + type: object + properties: + success: + type: boolean + error: + $ref: '#/components/schemas/Errors' + snr_block: + $ref: '#/components/schemas/SnrBlockData' + observing_group: + $ref: '#/components/schemas/ObservingGroupData' + AllocationData: + type: object + properties: + run_id: + type: string + time_allocated_millis: + type: integer + format: int64 + tac_rank: + type: integer + format: int32 + tac_grade: + type: string + instrument: + enum: + - UNKNOWN + - CFH12K + - MEGACAM + - WIRCAM + - ESPADONS + - SITELLE + - SPIROU + - WENAOKEAO + type: string + format: enum + agency: + $ref: '#/components/schemas/AllocationData_Agency' + cfht_rank: + type: integer + format: int32 + AllocationData_Agency: + type: object + properties: + letter: + type: string + name: + type: string + BigInteger: + type: object + properties: + value: + type: string + description: Used for primary database fields in sybase Should be parsed as a BigInteger + CatalogIdentifier: + type: object + properties: + catalog: + type: string + description: Catalog to reference. + identifier: + type: string + description: 'Valid identifier in the catalog. Note: this ID will contain a catalog name, because spacing matters in catalogs, the full ID for an object is like M 4. Catalog is M, id is identifier could be 4, but to reference m4, you need the spaces. NB: These identity values can not be set in fits headers because they do not have to conform to the length restrictions.' + description: Ties to some external system. A string representation can take the form of : + Config_Dithering: + type: object + properties: + is_staring: + type: boolean + Config_Nodding: + type: object + properties: + start_sequence: + enum: + - OBJECT + - TARGET + - SKY + - DARK + - FLAT + - SNAP + - BIAS + - FOCUS + - ALIGN + - COMPARISON + - ACQUIRE + - FABRY_PEROT + - TWILIGHT_FLATS + - PHOTOMETRY + - SFOCUS + type: string + format: enum + target_db: + type: number + format: double + Constraint: + type: object + properties: + image_quality: + $ref: '#/components/schemas/DoubleMinMax' + sky_background_max: + type: number + format: double + airmass_max: + type: number + format: double + extinction_max: + type: number + format: double + moon_distance_arc_length_min: + type: number + format: double + photometric: + type: boolean + name: + type: string + sybase_cons_id: + $ref: '#/components/schemas/BigInteger' + h2o_vapor_max: + type: number + format: double + read_sky_background_name: + type: string + sky_background_name: + type: string + description: Part of observing_template_data - not stored on any table individually + DoubleMinMax: + type: object + properties: + min: + type: number + format: double + max: + type: number + format: double + DoubleValue: + type: object + properties: + value: + type: number + description: The double value. + format: double + description: Wrapper message for `double`. The JSON representation for `DoubleValue` is JSON number. Not recommended for use in new APIs, but still useful for legacy APIs and has no plan to be removed. + Duration: + type: object + properties: + millis: + type: integer + format: int64 + display_unit: + enum: + - DAYS + - HOURS + - MINUTES + - SECONDS + type: string + description: Only needed for rendering UI, should display the duration using the units specified. If unset, assume DAYS + format: enum + DynamicExposureTimeLimit: + type: object + properties: + minimum_multiplier: + type: number + format: float + maximum_multiplier: + type: number + format: float + EphemeridesUpdateRequest: + type: object + properties: + runid: + type: string + description: Unique identifier of the science program. + token: + type: string + description: Unique identifier of the target. + ephemeris: + type: array + items: + $ref: '#/components/schemas/MovingTarget_Ephemeris' + description: List of ephemerides to set on the target. + EphemeridesUpdateResponse: + type: object + properties: + success: + type: boolean + error: + $ref: '#/components/schemas/Errors' + entity: + $ref: '#/components/schemas/TargetData' + Errors: + type: object + properties: + message: + type: array + items: + type: string + EspadonsStatus_ExposureMeterProcessingResult: + type: object + properties: + snr: + type: number + format: double + EspadonsStatus_GuiderSeeingProcessingResult: + type: object + properties: + guider_seeing: + type: number + format: double + EspadonsStatus_UpenaProcessingResult: + type: object + properties: + iq: + type: number + format: double + h2o_column_density_from_telluric_lines: + type: number + format: double + radial_velocity_corrections: + type: number + format: double + snr_per_ccd_bin: + type: number + format: double + snr_per_spec_bin: + type: number + format: double + actual_wavelength: + type: number + format: double + snr_per_ccd_bin_polar: + type: number + format: double + snr_per_spec_bin_polar: + type: number + format: double + h2o_mm_data: + $ref: '#/components/schemas/UpenaProcessingResult_h2o_mm' + ExposureData: + type: object + properties: + token: + type: string + obsid: + type: string + exposure_number: + type: integer + format: int32 + iteration: + type: integer + format: int32 + parent_exposure_token: + type: string + exposure_status: + $ref: '#/components/schemas/ExposureData_ExposureStatus' + observing_template: + $ref: '#/components/schemas/ObservingTemplateData' + target: + $ref: '#/components/schemas/TargetData' + observing_group_context: + $ref: '#/components/schemas/ObservingGroupContext' + all_derivations_complete: + type: boolean + description: When false, there is data which is still being calculated for this exposure. Data can still be displayed, but the client should attempt to refresh the data. + queue_run_id: + type: string + camera_run_id: + type: string + prg_run_id: + type: string + observing_component_token: + type: string + sub_totals: + $ref: '#/components/schemas/SubTotalsData' + attribution: + $ref: '#/components/schemas/ExposureData_Attribution' + version: + type: integer + description: 'Increments when this object updates storage: transient api_visibility: read only, incremented by the server' + format: int32 + ExposureData_Attribution: + type: object + properties: + runid: + type: string + observing_block_display_label: + type: string + description: If displaying the observing block to the user, this uniquely identifies it in the context of an observing group, i.e. OG3-2 + observing_block_label: + type: integer + description: Label of the observing block + format: int32 + observing_block_token: + type: string + description: Token of the observing block this exposure is attributed to. + observing_group_label: + type: integer + description: Label of the observing group this exposure is attributed to. + format: int32 + observing_group_token: + type: string + description: Token of the observing group this exposure is attributed to. + observing_component_token: + type: string + description: Token of the observing component this exposure is attributed to. + exposure_number: + type: integer + description: exposure number this exposure is attributed to. + format: int32 + queue_run: + type: string + description: the queue run this exposure was taken under + attributing_agency: + type: string + description: the agency id + tac_rank: + type: integer + format: int32 + tac_grade: + type: string + ExposureData_EspadonsStatus: + type: object + properties: + instr_mode: + type: string + actual_wavelength: + type: number + format: double + snr_exp_meter: + type: number + format: double + snr_wave_meas_int: + type: number + format: double + snr_wave_meas_pol: + type: number + format: double + snr_meas_int_per_ccd_bin: + type: number + format: double + snr_meas_int_per_spec_bin: + type: number + format: double + snr_meas_polar_ccd_bin: + type: number + format: double + snr_meas_polar_spec_bin: + type: number + format: double + trg_type: + type: string + guider_seeing: + type: number + format: double + guider_seeing_processing_results: + $ref: '#/components/schemas/EspadonsStatus_GuiderSeeingProcessingResult' + exposure_meter_processing_results: + $ref: '#/components/schemas/EspadonsStatus_ExposureMeterProcessingResult' + upena_processing_results: + $ref: '#/components/schemas/EspadonsStatus_UpenaProcessingResult' + observation_type: + type: string + ExposureData_ExposureStatus: + type: object + properties: + megacam_status: + $ref: '#/components/schemas/ExposureData_MegacamStatus' + wircam_status: + $ref: '#/components/schemas/ExposureData_WircamStatus' + espadons_status: + $ref: '#/components/schemas/ExposureData_EspadonsStatus' + sitelle_status: + $ref: '#/components/schemas/ExposureData_SitelleStatus' + spirou_status: + $ref: '#/components/schemas/ExposureData_SpirouStatus' + wena_status: + $ref: '#/components/schemas/ExposureData_WenaStatus' + exposure_time: + $ref: '#/components/schemas/ExposureTime' + exp_date_mjd: + type: number + format: double + queue_name: + type: string + exp_type: + type: string + exp_status: + enum: + - UNOBSERVED + - OBSERVED + - PROCESSED + - GRADED + - VALIDATED + - REJECTED + type: string + format: enum + is_photometric: + type: boolean + obs_comment: + type: string + seq_comment: + type: string + actual_pointing: + $ref: '#/components/schemas/SkyCoordinate' + actual_sky: + type: number + format: double + actual_airmass: + type: number + format: double + actual_moondist: + type: number + format: double + observer_comment: + type: string + qc_comment: + type: string + file: + $ref: '#/components/schemas/ExposureStatus_File' + credited_qcoordinator: + type: string + credited_observer: + type: string + grade: + type: integer + format: int32 + read_obs_date_utc: + type: string + read_obs_date_hst: + type: string + read_actual_sky_name: + type: string + ra: + type: string + description: Values from headers + dec: + type: string + cmmtobs: + type: string + cmmtseq: + type: string + pi_name: + type: string + nexp: + type: string + cmpltexp: + type: string + ha: + type: string + read_obs_date_millis: + type: integer + format: int64 + object: + type: string + ExposureData_MegacamStatus: + type: object + properties: + iq: + type: number + format: double + skybg: + type: number + format: double + elixir_eval: + type: integer + format: int32 + elixir_seval: + type: string + snr: + type: number + format: double + atmospheric_transmission: + type: number + format: double + elixir_zeropoint: + type: number + format: double + actual_filter: + type: string + elixir_processing_result: + $ref: '#/components/schemas/MegacamStatus_ElixirProcessingResult' + ExposureData_SitelleStatus: + type: object + properties: + camera_a: + $ref: '#/components/schemas/SitelleStatus_Camera' + camera_b: + $ref: '#/components/schemas/SitelleStatus_Camera' + relative_extinction: + type: number + format: double + relative_extinction_rms: + type: number + format: double + actual_filter: + type: string + ExposureData_SpirouStatus: + type: object + properties: + acqtime: + type: number + format: double + acqtime1: + type: string + asic_num: + type: string + sca_id: + type: string + muxtype: + type: integer + format: int32 + noutputs: + type: integer + format: int32 + nadcs: + type: integer + format: int32 + pddector: + type: integer + format: int32 + clkoff: + type: integer + format: int32 + warmtst: + type: integer + format: int32 + clocking: + type: integer + format: int32 + glbreset: + type: integer + format: int32 + frmode: + type: integer + format: int32 + camlink: + type: integer + format: int32 + expmode: + type: integer + format: int32 + nresets: + type: integer + format: int32 + frmtime: + type: number + format: double + exptime: + type: number + format: double + acqtype: + type: integer + format: int32 + datamode: + type: integer + format: int32 + datlevel: + type: integer + format: int32 + asicgain: + type: integer + format: int32 + nomgain: + type: integer + format: int32 + ampreset: + type: integer + format: int32 + ktcremov: + type: integer + format: int32 + srccur: + type: integer + format: int32 + ampinput: + type: integer + format: int32 + v4v3v2v1: + type: string + units: + type: string + tstation: + type: string + hxrgver: + type: string + mclk: + type: number + format: double + inttime: + type: number + format: double + frameno: + type: integer + format: int32 + sb_adc_s: + type: string + sbadc1_p: + type: number + format: double + sbadc2_p: + type: number + format: double + sbcali_p: + type: string + sbdens_p: + type: number + format: double + sbrhb1_p: + type: string + sbrhb2_p: + type: string + sbetem_p: + type: string + sb_inj_t: + type: number + format: double + sb_pol_t: + type: number + format: double + sb_cs1_t: + type: number + format: double + sb_cs2_t: + type: number + format: double + sb_cb1_t: + type: number + format: double + sb_cb2_t: + type: number + format: double + sbiavl1s: + type: string + sbiavl2s: + type: string + sbetel_s: + type: string + sb_csc_s: + type: string + sbcref_p: + type: string + sbccas_p: + type: string + sbcden_p: + type: number + format: double + sbagit_s: + type: string + sbcmir_p: + type: string + sbclhc1s: + type: string + sbclhc1w: + type: string + sbclhc1c: + type: number + format: double + sbclhc2s: + type: string + sbclhc2w: + type: string + sbclhc2c: + type: number + format: double + sbclwl_s: + type: string + sbclwl_c: + type: number + format: double + sbwlsh_s: + type: string + sbctec_s: + type: string + sbctecct: + type: number + format: double + sbctecht: + type: number + format: double + sbccprit: + type: number + format: double + sbcccu_t: + type: number + format: double + sbccprot: + type: number + format: double + sbcfbi_t: + type: number + format: double + sbcwls_t: + type: number + format: double + sbclsuat: + type: number + format: double + sbcfpi_t: + type: number + format: double + sbcfpe_t: + type: number + format: double + sbcfpb_p: + type: number + format: double + sbvga_cp: + type: number + format: double + sbvga_ip: + type: number + format: double + sbvgb_cp: + type: number + format: double + sbvgb_ip: + type: number + format: double + sbmk00_t: + type: number + format: double + sbmk01_t: + type: number + format: double + sbmk02_t: + type: number + format: double + sbmk03_t: + type: number + format: double + sbmk04_t: + type: number + format: double + sbmk05_t: + type: number + format: double + sbmk06_t: + type: number + format: double + sbmk07_t: + type: number + format: double + sbls000t: + type: number + format: double + sbls001t: + type: number + format: double + sbls002t: + type: number + format: double + sbls003t: + type: number + format: double + sbls004t: + type: number + format: double + sbls005t: + type: number + format: double + sbls006t: + type: number + format: double + sbls007t: + type: number + format: double + sbls008t: + type: number + format: double + sbls009t: + type: number + format: double + sbls010t: + type: number + format: double + sbls011t: + type: number + format: double + sbls100t: + type: number + format: double + sbls101t: + type: number + format: double + sbls102t: + type: number + format: double + sbls103t: + type: number + format: double + sbls104t: + type: number + format: double + sbls105t: + type: number + format: double + sbls106t: + type: number + format: double + sbls107t: + type: number + format: double + sbls108t: + type: number + format: double + sbls109t: + type: number + format: double + sbls110t: + type: number + format: double + sbls111t: + type: number + format: double + sbl00i_t: + type: number + format: double + sbl00h_v: + type: number + format: double + sbl01i_t: + type: number + format: double + sbl01h_v: + type: number + format: double + sbl02i_t: + type: number + format: double + sbl02h_v: + type: number + format: double + sbl03i_t: + type: number + format: double + sbl03h_v: + type: number + format: double + sbl04i_t: + type: number + format: double + sbl04h_v: + type: number + format: double + sbl05i_t: + type: number + format: double + sbl05h_v: + type: number + format: double + sbl06i_t: + type: number + format: double + sbl06h_v: + type: number + format: double + sbl07i_t: + type: number + format: double + sbl07h_v: + type: number + format: double + sbl08i_t: + type: number + format: double + sbl08h_v: + type: number + format: double + sbl09i_t: + type: number + format: double + sbl09h_v: + type: number + format: double + sbl10i_t: + type: number + format: double + sbl10h_v: + type: number + format: double + sbl11i_t: + type: number + format: double + sbl11h_v: + type: number + format: double + sbl12i_t: + type: number + format: double + sbl12h_v: + type: number + format: double + sbl13i_t: + type: number + format: double + sbl13h_v: + type: number + format: double + seadc1_p: + type: number + format: double + seadc2_p: + type: number + format: double + sedens_p_deprecated: + type: integer + format: int32 + sedens_p: + type: number + format: double + se_inj_t: + type: number + format: double + se_pol_t: + type: number + format: double + se_cs1_t: + type: number + format: double + se_cs2_t: + type: number + format: double + se_cb1_t: + type: number + format: double + se_cb2_t: + type: number + format: double + sectecct: + type: number + format: double + sectecht: + type: number + format: double + seccprit: + type: number + format: double + secccu_t: + type: number + format: double + seccprot: + type: number + format: double + secfbi_t: + type: number + format: double + secwls_t: + type: number + format: double + seclhc1c: + type: number + format: double + seclhc2c: + type: number + format: double + seclwl_c: + type: number + format: double + seclsuat: + type: number + format: double + secfpi_t: + type: number + format: double + secfpe_t: + type: number + format: double + secfpb_p: + type: number + format: double + sevga_cp: + type: number + format: double + sevga_ip: + type: number + format: double + sevgb_cp: + type: number + format: double + sevgb_ip: + type: number + format: double + semk00_t: + type: number + format: double + semk01_t: + type: number + format: double + semk02_t: + type: number + format: double + semk03_t: + type: number + format: double + semk04_t: + type: number + format: double + semk05_t: + type: number + format: double + semk06_t: + type: number + format: double + semk07_t: + type: number + format: double + sels000t: + type: number + format: double + sels001t: + type: number + format: double + sels002t: + type: number + format: double + sels003t: + type: number + format: double + sels004t: + type: number + format: double + sels005t: + type: number + format: double + sels006t: + type: number + format: double + sels007t: + type: number + format: double + sels008t: + type: number + format: double + sels009t: + type: number + format: double + sels010t: + type: number + format: double + sels011t: + type: number + format: double + sels100t: + type: number + format: double + sels101t: + type: number + format: double + sels102t: + type: number + format: double + sels103t: + type: number + format: double + sels104t: + type: number + format: double + sels105t: + type: number + format: double + sels106t: + type: number + format: double + sels107t: + type: number + format: double + sels108t: + type: number + format: double + sels109t: + type: number + format: double + sels110t: + type: number + format: double + sels111t: + type: number + format: double + sel00i_t: + type: number + format: double + sel00h_v: + type: number + format: double + sel01i_t: + type: number + format: double + sel01h_v: + type: number + format: double + sel02i_t: + type: number + format: double + sel02h_v: + type: number + format: double + sel03i_t: + type: number + format: double + sel03h_v: + type: number + format: double + sel04i_t: + type: number + format: double + sel04h_v: + type: number + format: double + sel05i_t: + type: number + format: double + sel05h_v: + type: number + format: double + sel06i_t: + type: number + format: double + sel06h_v: + type: number + format: double + sel07i_t: + type: number + format: double + sel07h_v: + type: number + format: double + sel08i_t: + type: number + format: double + sel08h_v: + type: number + format: double + sel09i_t: + type: number + format: double + sel09h_v: + type: number + format: double + sel10i_t: + type: number + format: double + sel10h_v: + type: number + format: double + sel11i_t: + type: number + format: double + sel11h_v: + type: number + format: double + sel12i_t: + type: number + format: double + sel12h_v: + type: number + format: double + sel13i_t: + type: number + format: double + sel13h_v: + type: number + format: double + guider_processing: + $ref: '#/components/schemas/SpirouStatus_GuiderProcessingResult' + exposure_meter_processing: + $ref: '#/components/schemas/SpirouStatus_ExposureMeterProcessingResult' + pipeline_processing: + $ref: '#/components/schemas/SpirouStatus_PipelineProcessingResult' + ExposureData_WenaStatus: + type: object + properties: {} + ExposureData_WircamStatus: + type: object + properties: + iq_uncertainty: + type: number + format: double + absorption: + type: number + format: double + absorption_uncertainty: + type: number + format: double + number_of_stars_iq: + type: integer + format: int32 + number_of_stars_absorption: + type: integer + format: int32 + absolute_sky_level: + type: number + format: double + elongation: + type: number + format: double + mdcoords: + type: integer + format: int32 + mdrepeat: + type: integer + format: int32 + actual_filter: + type: string + iq: + type: number + format: double + ExposureStatus_File: + type: object + properties: + absolute_path: + type: string + description: Absolute path to directory file is homed in. Starts with / and ends with /. + base_name: + type: string + description: Name of file without extension + ext: + type: string + description: Name of extenion including . + ExposureTime: + type: object + properties: + desired_snr: + type: number + format: double + exposure_time_ms: + type: integer + format: int64 + FindingChartData: + type: object + properties: + comment: + type: string + url: + type: string + token: + type: string + staff_approval: + $ref: '#/components/schemas/FindingChartData_Approval' + pi_approval: + $ref: '#/components/schemas/FindingChartData_Approval' + target_token: + type: string + FindingChartData_Approval: + type: object + properties: + approved_at_millis: + type: integer + format: int64 + approved_by_usertoken: + type: string + FixedTarget_ProperMotion: + type: object + properties: + ra_mas: + type: number + description: Motion in the right ascension axis in milliarcseconds/year. + format: double + dec_mas: + type: number + description: Motion in the declination axis in milliarcseconds/year. + format: double + description: The rate of change in angular coordinates for the target. + GoogleProtobufAny: + type: object + properties: + '@type': + type: string + description: The type of the serialized message. + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. + ITimeData: + type: object + properties: + exposure_time_millis: + type: integer + format: int64 + overhead_time_millis: + type: integer + format: int64 + InstrumentConfigurationData: + type: object + properties: + espadons_configuration: + $ref: '#/components/schemas/InstrumentConfigurationData_EspadonsConfigurationData' + megacam_configuration: + $ref: '#/components/schemas/InstrumentConfigurationData_MegacamConfigurationData' + wircam_configuration: + $ref: '#/components/schemas/InstrumentConfigurationData_WircamConfigurationData' + sitelle_configuration: + $ref: '#/components/schemas/InstrumentConfigurationData_SitelleConfigurationData' + spirou_configuration: + $ref: '#/components/schemas/InstrumentConfigurationData_SpirouConfigurationData' + wena_configuration: + $ref: '#/components/schemas/InstrumentConfigurationData_WenaConfigurationData' + sybase_ic_id: + $ref: '#/components/schemas/BigInteger' + etime_or_snr: + $ref: '#/components/schemas/ExposureTime' + sybase_cons_id: + $ref: '#/components/schemas/BigInteger' + tracking: + enum: + - Unknown + - SID + - NONSID + - SID_NOG + - NONSID_G + type: string + format: enum + description: Part of observing_template_data - not stored on any table individually + InstrumentConfigurationData_EspadonsConfigurationData: + type: object + properties: + observing_mode: + enum: + - Polarimetry + - Spectroscopy_star_only + - Spectroscopy_star_sky + type: string + format: enum + readout_mode: + enum: + - Normal + - Slow + - Fast + type: string + format: enum + stokes_parameter: + enum: + - Q + - U + - V + - W + - I + type: string + format: enum + number_of_sequences: + type: integer + format: int32 + number_of_exposures: + type: integer + format: int32 + use_snr: + type: boolean + snr_wave: + type: number + format: double + snr: + type: number + format: double + InstrumentConfigurationData_MegacamConfigurationData: + type: object + properties: + filter: + enum: + - Unknown + - u + - g + - i + - r + - z + - gri + - CaHK + - Ha + - HaOFF + - OIII + - OIIIOFF + - uS + - gS + - rS + - iS + - zS + - N393S + - HaS + - HaOFFS + - TiOS + - CNS + - OIIIS + - PHGS + - M4112 + - M4376 + type: string + format: enum + binning: + enum: + - one_one + - two_two + - three_three + - four_four + type: string + format: enum + is_dynamic_exposure_time: + type: boolean + description: 'TODO: is this needed billy? Can we use presence of snr or not to decide? I THINK SO. We have the capability to calculate SNR on every exposure, but we aren''t necessarily doing SNR mode observations with everything' + minimum_exposures: + type: integer + format: int32 + min_exptime_ms: + type: integer + format: int64 + max_exptime_ms: + type: integer + format: int64 + magAB: + type: number + description: 'TODO: interesting field here because associated with the target, but in the legacy mp snr mode, the mag is carried with the IC! so have to go back to this :(' + format: double + snr_mode: + enum: + - NONE + - STANDARD + - CUMULATIVE + - ZERO_POINT + type: string + format: enum + etc_photometry: + $ref: '#/components/schemas/MegacamConfigurationData_Photometry' + InstrumentConfigurationData_SitelleConfigurationData: + type: object + properties: + filter: + enum: + - UNKNOWN + - NONE + - C1 + - C2 + - C3 + - C4 + - SN1 + - SN2 + - SN3 + - SN4 + - SN5 + - SN6 + type: string + format: enum + binning: + enum: + - one_one + - two_two + - three_three + type: string + format: enum + resolution: + type: integer + format: int32 + snrwavelength: + type: number + description: This should be on target too , sitell, espedons, spirou + format: double + calibration: + $ref: '#/components/schemas/SitelleConfigurationData_Calibration' + resolution_mode: + enum: + - no_resolution_mode + - hires + - medres + type: string + format: enum + needs_precision: + type: boolean + needs_flux_calibration: + type: boolean + needs_target_calibration: + type: boolean + InstrumentConfigurationData_SpirouConfigurationData: + type: object + properties: + template_name: + type: string + description: Deprecated fields + observing_mode: + enum: + - Polarimetry + - Star + type: string + format: enum + stokes_parameter: + enum: + - Q + - U + - V + - W + - I + type: string + format: enum + mode: + enum: + - Dark + - Fabry_Perot + - Wave_Hc1 + - Wave_Hc2 + - FLAT + type: string + format: enum + sky_observation: + enum: + - None + - Before + - After + - BeforeAndAfter + type: string + format: enum + number_of_sequences: + type: integer + description: Each sequence is 1 exposure for Star mode and 4 exposures for ObservingMode + format: int32 + use_snr: + type: boolean + description: when true - use SNR for stoping exposures + sky_etime_millis: + $ref: '#/components/schemas/Int64Value' + InstrumentConfigurationData_WenaConfigurationData: + type: object + properties: {} + InstrumentConfigurationData_WircamConfigurationData: + type: object + properties: + filter: + enum: + - UNKNOWN + - W + - H + - J + - Ks + - Y + - H2 + - KCont + - CH4On + - CH4Off + - LowOH1 + - LowOH2 + - BrG + - CO + - COnar + type: string + format: enum + is_microdithering: + type: boolean + exposures_per_dp_position: + type: integer + description: Must always be > 0 + format: int32 + accurate_pointing: + type: boolean + defocus: + type: number + format: double + is_staring: + type: boolean + Int32Value: + type: object + properties: + value: + type: integer + description: The int32 value. + format: int32 + description: Wrapper message for `int32`. The JSON representation for `Int32Value` is JSON number. Not recommended for use in new APIs, but still useful for legacy APIs and has no plan to be removed. + Int64Value: + type: object + properties: + value: + type: integer + description: The int64 value. + format: int64 + description: Wrapper message for `int64`. The JSON representation for `Int64Value` is JSON string. Not recommended for use in new APIs, but still useful for legacy APIs and has no plan to be removed. + LegacyReel: + type: object + properties: + reel_og_token: + type: string + description: Token of the linked observing group. + delay: + $ref: '#/components/schemas/Duration' + window: + $ref: '#/components/schemas/Duration' + reel_og_label: + type: integer + description: 'Label of the observing group which must be observed before this one. storage: transient api_visibility: read only' + format: int32 + description: A constraint indicating that a "linked" observing group must be observed before this observing group. + ListOfIdentifiers: + type: object + properties: + catalog_identifier: + type: array + items: + $ref: '#/components/schemas/CatalogIdentifier' + ListProgramsResponse: + type: object + properties: + success: + type: boolean + entity: + type: array + items: + $ref: '#/components/schemas/ProgramInfo' + error: + $ref: '#/components/schemas/Errors' + MegacamConfigurationData_Photometry: + type: object + properties: + aperture: + $ref: '#/components/schemas/Photometry_Aperture' + psf: + type: boolean + MegacamStatus_ElixirProcessingResult: + type: object + properties: + iq: + type: number + format: double + sky_background: + type: number + format: double + snr: + type: number + format: double + zeropoint: + type: number + format: double + MovingTarget_Ephemeris: + type: object + properties: + mjd: + type: number + description: Modified Julian date when the target is at this coordinate. + format: double + coordinate: + $ref: '#/components/schemas/SkyCoordinate' + computed_coordinate: + $ref: '#/components/schemas/SkyCoordinate' + description: A time and corresponding coordinate. + ObservingBlockCondition: + type: object + properties: + pre_requisite: + type: array + items: + $ref: '#/components/schemas/ObservingBlockCondition_Requisite' + description: Pre-requisite conditions before each observing block can be started. The index of items in this list corresponds directly to the index of the observing block on the observing group i.e. this list is one-to-one with read_observing_block. + window: + type: array + items: + $ref: '#/components/schemas/ObservingBlockCondition_Window' + description: Time windows during which the observing block can be observed. Windows can not overlap, and will always be stored in temporal order. + description: Set of requirements that must be met for an observing block to be observed. This is compiled from the observing group configuration details into each of the observing blocks, and used to store the requirements corresponding to each one. + ObservingBlockCondition_Requisite: + type: object + properties: + requisite_observing_block_index: + type: integer + description: This should match the index of the observing block in the read_observing_block list. + format: int32 + wait_period_min_millis: + type: integer + description: Minimum time to wait after requisite observing block exposure has been taken. + format: int64 + wait_period_millis: + type: integer + description: Optimal to wait after requisite observing block exposure has been taken. + format: int64 + wait_period_max_millis: + type: integer + description: Minimum time to wait after requisite observing block exposure has been taken. + format: int64 + unbounded: + type: boolean + description: If this observation can happen any time after the requisite. + description: A constraint indicating that another observing block must be observed before this observing block. + ObservingBlockCondition_Window: + type: object + properties: + observe_after: + type: integer + description: Start of the observation window, in Unix time milliseconds. + format: int64 + observe_before: + type: integer + description: End of the observation window, in Unix time milliseconds. + format: int64 + description: A time window during which an observation can be made. + ObservingBlock_ObservingComponent: + type: object + properties: + target_token: + type: string + description: Unique identifier of the target used in this observing component. When creating or updating an observing group, this must be an existing target, or on the map of the request to be created. + observing_template_token: + type: string + description: Unique identifier of the observing template used in this observing component. When creating or updating an observing group, this must be an existing observing template, or on the map of the request to be created. + target_label: + type: integer + description: 'Label of the target used in this observing component. This value is only set on the read_observing_block. api_visibility: read only' + format: int32 + observing_template_label: + type: integer + description: 'Label of the observing template used in this observing component. This value is only set on the read_observing_block. api_visibility: read only' + format: int32 + snr_block_token: + type: string + description: Unique identifier of the SNR block used in this observing component, if any. Must be set if and only if the observing component uses a MegaCam cumulative SNR mode observing template. If this is set when creating or updating an observing group, the observing component will be tied to the SNR block with this token if it already exists, otherwise a new SNR block will be created using this token. + description: An observing component ties together a target with observation details. Each observing component roughly corresponds with one observation, although this may actually be multiple exposures depending on how the observing template has been configured e.g. for a polar sequence or multiple sequences. When there are multiple exposures in one observing component, they will typically be observed consecutively, in the specified order, however this is not guaranteed, as repeat exposures may need to be taken at a later point due to poor conditions. + ObservingGroupContext: + type: object + properties: + iteration_count: + type: integer + description: The iteration number this object was part of in the observing group + format: int32 + iteration_total: + type: integer + description: The total number of iterations requested for the observing group + format: int32 + iteration_minimum: + type: integer + description: Minimum number of iterations this OG needs to provide science value + format: int32 + window: + type: array + items: + $ref: '#/components/schemas/ObservingBlockCondition_Window' + description: Sorted by from past -> future on the observe_after key + observing_group_token: + type: string + observing_group_priority: + enum: + - UNKNOWN_OG_PRIORITY + - HIGH + - MEDIUM + - LOW + - INACTIVE + type: string + format: enum + observing_group_state: + enum: + - NOT_STARTED + - OBSERVED + - STARTED + - VALIDATED + type: string + format: enum + observing_group_label: + type: integer + format: int32 + observing_group_version: + type: integer + description: Increments every time the observing group changes. + format: int32 + legacy_reel: + $ref: '#/components/schemas/LegacyReel' + single: + type: boolean + target_monitoring: + $ref: '#/components/schemas/TargetMonitoringObservingGroup_TimeConstraint' + phase_schedule: + $ref: '#/components/schemas/PhaseScheduleObservingGroup_PhaseSchedule' + time_accounting: + $ref: '#/components/schemas/TimeAccountingData' + exposureType: + enum: + - OBJECT + - TARGET + - SKY + - DARK + - FLAT + - SNAP + - BIAS + - FOCUS + - ALIGN + - COMPARISON + - ACQUIRE + - FABRY_PEROT + - TWILIGHT_FLATS + - PHOTOMETRY + - SFOCUS + type: string + format: enum + use_group_dither_scheme: + type: boolean + ObservingGroupData: + type: object + properties: + token: + type: string + description: Unique identifier for the observing group. + label: + type: integer + description: 'Index of the observing group within the program, set by the system automatically. Used for reference in communications with QSO staff. storage: transient api_visibility: read only' + format: int32 + og_priority: + enum: + - UNKNOWN_OG_PRIORITY + - HIGH + - MEDIUM + - LOW + - INACTIVE + type: string + description: Priority of observing this observing group, relative to other observing groups within the same program. + format: enum + single_observing_group: + $ref: '#/components/schemas/SingleObservingGroup' + target_monitoring_observing_group: + $ref: '#/components/schemas/TargetMonitoringObservingGroup' + phase_schedule_observing_group: + $ref: '#/components/schemas/PhaseScheduleObservingGroup' + read_observing_block: + type: array + items: + $ref: '#/components/schemas/ObservingGroupData_ObservingBlock' + description: 'The observing blocks that have been created based on the entered configuration. api_visibility: read only' + observing_block_condition: + type: object + additionalProperties: + $ref: '#/components/schemas/ObservingBlockCondition' + description: 'The requirements that have been set for observing each observing blocks based on the entered configuration. api_visibility: read only' + minimum_observing_block_count: + type: integer + description: 'This field is unused, please ignore. api_visibility: read only' + format: int32 + time_accounting: + $ref: '#/components/schemas/TimeAccountingData' + state: + enum: + - NOT_STARTED + - OBSERVED + - STARTED + - VALIDATED + type: string + description: State of the observing group, based off of the time accounting status. + format: enum + version: + type: integer + description: 'Increments when this object updates. storage: transient api_visibility: read only, incremented by the server' + format: int32 + legacy_reel: + $ref: '#/components/schemas/LegacyReel' + time_constraint: + $ref: '#/components/schemas/TimeConstraint' + target_type: + enum: + - OBJECT + - TARGET + - SKY + - DARK + - FLAT + - SNAP + - BIAS + - FOCUS + - ALIGN + - COMPARISON + - ACQUIRE + - FABRY_PEROT + - TWILIGHT_FLATS + - PHOTOMETRY + - SFOCUS + type: string + description: 'This can be used to configure calibration observing groups. For internal use only. api_visibility: operations write only' + format: enum + dither_scheme: + enum: + - NONE + - STANDARD + - GROUP + type: string + description: Used to customize the order of observation when observing multiple targets with the same telescope pattern within this observing group. + format: enum + low_surface_brightness: + type: boolean + description: Indicates the observing group uses low surface brightness (LSB) mode.with + description: An observing group ties together a block of observations with scheduling constraints. It can also contain configuration details for observing multiple iterations of that block. The configuration gets "rendered out" on creation/update into a sequence of observing blocks and corresponding observing block conditions which drive individual observations. + ObservingGroupData_ObservingBlock: + type: object + properties: + observing_component: + type: array + items: + $ref: '#/components/schemas/ObservingBlock_ObservingComponent' + description: List of observing components that comprise this observing block. + token: + type: string + description: 'Unique identifier for the observing block. api_visibility: operations read only' + label: + type: integer + description: 'Index of the observing block within the program, set by the system automatically. Used for reference in communications with QSO staff. storage: transient api_visibility: read only' + format: int32 + description: An observing block is a collection of observations expected to be observed together. These are typically observed consecutively, in the specified order, however this is not guaranteed, as repeat exposures may need to be taken at a later point due to poor conditions. When multiple observations are in the same observing block, this signals that the observations are not of scientific value unless the entire block is successfully completed. For details, see TimeAccountingData. + ObservingGroupDeleteResponse: + type: object + properties: + success: + type: boolean + error: + $ref: '#/components/schemas/Errors' + ObservingGroupListResponse: + type: object + properties: + success: + type: boolean + entity: + type: array + items: + $ref: '#/components/schemas/ObservingGroupData' + error: + $ref: '#/components/schemas/Errors' + ObservingGroupUpdateRequest: + type: object + properties: + runid: + type: string + description: Unique identifier of the science program. + token: + type: string + description: Unique identifier of the observing group. If an observing group target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789 + entity: + $ref: '#/components/schemas/ObservingGroupData' + target: + type: object + additionalProperties: + $ref: '#/components/schemas/TargetData' + description: If creating or updating targets with this request, set the data here. Key is the unique identifier of the target and must be prefixed with the runid. Value is the target data and must be valid. Otherwise, the whole request will fail. Observing components can then reference the unique identifier (the map key) in this same request. + observing_template: + type: object + additionalProperties: + $ref: '#/components/schemas/ObservingTemplateData' + description: If creating or updating observing templates with this request, set the data here. Key is the unique identifier of the observing template and must be prefixed with the runid. Value is the observing template data and must be valid. Otherwise, the whole request will fail. Observing components can then reference the unique identifier (the map key) in this same request. + lock_version: + $ref: '#/components/schemas/Int32Value' + ObservingGroupUpdateResponse: + type: object + properties: + success: + type: boolean + error: + $ref: '#/components/schemas/Errors' + entity: + $ref: '#/components/schemas/ObservingGroupData' + ObservingTemplateData: + type: object + properties: + instrument_configuration: + $ref: '#/components/schemas/InstrumentConfigurationData' + constraint: + $ref: '#/components/schemas/Constraint' + telescope_pattern: + $ref: '#/components/schemas/TelescopePatternData' + charge_model: + type: string + description: Used for calculating cost for this pattern Invokes code which reads the ExposureInstructions and attributes a cost to it. + itime: + type: integer + format: int64 + calculate_etime: + $ref: '#/components/schemas/ExposureTime' + acquisition_time: + $ref: '#/components/schemas/ExposureTime' + token: + type: string + associated_observing_blocks: + type: array + items: + $ref: '#/components/schemas/BigInteger' + description: When just creating an OT, this list is empty When this OT belongs to an OG, then any OBs in sybase that this OT is related too must be filled in. select * from blah where (select id from prg where prg Query to find related OBs is, select ob.* from ob, prg where ob.prg_id = prg.id and prg.runid=? instrument_configuration.sybase_ic_id, constraint.sybase_cons_idselect ob.* from ob, prg, icseq where ob.prg_id = prg.idand prg.runid=and icseq.ob_id=ob.id and ob.con_id = and icseq.ic_id = ; + name: + type: string + total_exposure_count: + type: integer + format: int32 + label: + type: integer + format: int32 + version: + type: integer + description: 'Increments when this object updates storage: transient api_visibility: read only, incremented by the server' + format: int32 + telescope_pattern_token: + type: string + telescope_pattern_scale: + type: number + format: double + megacam_cumulative_snr_system_generated: + type: boolean + description: Stored in observing_templates table in column observing_template_data + ObservingTemplateDeleteResponse: + type: object + properties: + success: + type: boolean + error: + $ref: '#/components/schemas/Errors' + ObservingTemplateListResponse: + type: object + properties: + success: + type: boolean + entity: + type: array + items: + $ref: '#/components/schemas/ObservingTemplateData' + error: + $ref: '#/components/schemas/Errors' + ObservingTemplateUpdateRequest: + type: object + properties: + runid: + type: string + token: + type: string + entity: + $ref: '#/components/schemas/ObservingTemplateData' + lock_version: + $ref: '#/components/schemas/Int32Value' + ObservingTemplateUpdateResponse: + type: object + properties: + success: + type: boolean + error: + $ref: '#/components/schemas/Errors' + entity: + $ref: '#/components/schemas/ObservingTemplateData' + OffsetCoordinate: + type: object + properties: + ra_offset: + type: number + description: Right ascension, in degrees + format: double + dec_offset: + type: number + description: Declination, in degrees + format: double + exposure_number: + type: integer + format: int64 + PhaseScheduleObservingGroup: + type: object + properties: + observing_block: + $ref: '#/components/schemas/ObservingGroupData_ObservingBlock' + phase_schedule: + $ref: '#/components/schemas/PhaseScheduleObservingGroup_PhaseSchedule' + description: An observing group consisting of a single observing block and observation windows defined by a phase schedule. Used for an observing block that needs to be observed once, with possible observation windows that occur with a regular period. + PhaseScheduleObservingGroup_PhaseSchedule: + type: object + properties: + earliest_observe_date_millis: + type: integer + description: The earliest moment to observe, in Unix time milliseconds. + format: int64 + tolerance: + $ref: '#/components/schemas/Duration' + period: + $ref: '#/components/schemas/Duration' + period_tolerance: + $ref: '#/components/schemas/Duration' + Photometry_Aperture: + type: object + properties: + optimal_aperture: + type: boolean + fixed_aperture: + type: number + description: Radius of the aperture in arcsec + format: double + ninety_six_percent_flux_aperture: + type: boolean + PointingOffsetData: + type: object + properties: + token: + type: string + name: + type: string + offset: + $ref: '#/components/schemas/OffsetCoordinate' + label: + type: integer + format: int32 + version: + type: integer + format: int32 + user_token: + type: string + description: this is the PI of the program that this entity will be recorded forif null should be derived using the program_data.contact_info.user_tokenif null, then a system ?? gross + instrument: + enum: + - UNKNOWN + - CFH12K + - MEGACAM + - WIRCAM + - ESPADONS + - SITELLE + - SPIROU + - WENAOKEAO + type: string + description: the instrument for the pointing + format: enum + is_system: + type: boolean + PointingOffsetDeleteResponse: + type: object + properties: + success: + type: boolean + error: + $ref: '#/components/schemas/Errors' + PointingOffsetGetResponse: + type: object + properties: + success: + type: boolean + entity: + $ref: '#/components/schemas/PointingOffsetData' + error: + $ref: '#/components/schemas/Errors' + PointingOffsetListResponse: + type: object + properties: + success: + type: boolean + entity: + type: array + items: + $ref: '#/components/schemas/PointingOffsetData' + error: + $ref: '#/components/schemas/Errors' + PointingOffsetUpdateRequest: + type: object + properties: + runid: + type: string + description: Unique identifier your science program. + token: + type: string + lock_version: + $ref: '#/components/schemas/Int32Value' + entity: + $ref: '#/components/schemas/PointingOffsetData' + PointingOffsetUpdateResponse: + type: object + properties: + success: + type: boolean + error: + $ref: '#/components/schemas/Errors' + entity: + $ref: '#/components/schemas/PointingOffsetData' + ProgramData: + type: object + properties: + title: + type: string + abstract: + type: string + contact_info: + $ref: '#/components/schemas/ProgramData_ContactInfo' + comment: + type: array + items: + $ref: '#/components/schemas/ProgramData_Comments' + sybasePiUserId: + type: string + sub_totals: + $ref: '#/components/schemas/SubTotalsData' + token: + type: string + description: Unique identifier for the program. Used interchangeably with "runid". + time_allocation: + type: array + items: + $ref: '#/components/schemas/AllocationData' + release_date_millis: + type: integer + format: int64 + metadata_release_date_millis: + type: integer + format: int64 + comment_field: + type: string + program_type: + enum: + - REGULAR + - TARGET_OF_OPPORTUNITY + - CALIBRATION + - SNAPSHOT + type: string + format: enum + distribution_hash: + type: string + time_accounting: + $ref: '#/components/schemas/TimeAccountingData' + operations_comment_field: + type: string + last_data_acquisition_notification_date_millis: + type: integer + format: int64 + dynamic_exposure_time_limit: + $ref: '#/components/schemas/DynamicExposureTimeLimit' + ProgramData_Comments: + type: object + properties: + question: + type: string + comment: + type: string + ProgramData_ContactInfo: + type: object + properties: + pi_first_name: + type: string + pi_last_name: + type: string + pi_email: + type: string + user_token: + type: string + ProgramInfo: + type: object + properties: + program_data: + $ref: '#/components/schemas/ProgramData' + rollup_fields: + $ref: '#/components/schemas/RollupFields' + time_accounting: + $ref: '#/components/schemas/TimeAccountingData' + pi_info: + $ref: '#/components/schemas/ProgramInfo_PiInfo' + version: + type: integer + format: int32 + ProgramInfo_PiInfo: + type: object + properties: + ldap_username: + type: string + email_address: + type: string + first_name: + type: string + last_name: + type: string + legacy_username: + type: string + user_token: + type: string + RollupFields: + type: object + properties: + i_time: + $ref: '#/components/schemas/RollupValue' + i_time_configured: + $ref: '#/components/schemas/RollupValue' + sub_totals: + $ref: '#/components/schemas/SubTotalsData' + RollupValue: + type: object + properties: + requested: + type: integer + format: int64 + started: + type: integer + format: int64 + observed: + type: integer + format: int64 + validated: + type: integer + format: int64 + SingleObservingGroup: + type: object + properties: + observing_block: + $ref: '#/components/schemas/ObservingGroupData_ObservingBlock' + description: An observing group consisting of one observing block. Contingencies on other observations can be set through legacy_reel. Constraints on the time of observation can be set through time_constraint. + SitelleConfigurationData_Calibration: + type: object + properties: + calibration_position: + enum: + - no_calibration_position + - zenith + - target + type: string + format: enum + calibration_mode: + enum: + - no_calibration_mode + - laser + - phase + type: string + format: enum + SitelleStatus_Camera: + type: object + properties: + iq: + type: number + format: double + iq_rms: + type: number + format: double + pixel_shift_x: + type: number + format: double + pixel_shift_y: + type: number + format: double + number_of_stars: + type: integer + format: int32 + me: + type: number + format: double + SkyCoordinate: + type: object + properties: + ra: + type: number + description: Right Ascension, in degrees + format: double + dec: + type: number + description: Declination, in degrees + format: double + SnrBlockData: + type: object + properties: + token: + type: string + label: + type: integer + format: int32 + cumulative_snr: + type: number + description: the total snr achieved of all exposures of all observing components of this snr block + format: double + carry_forward_snr: + type: number + description: if the program is copied to a new program possibly for a new semester the cumulative snr will be moved to the carry_forward_snr and the carry forward snr will be counted in the cumulative snr of the new program + format: double + observing_components: + type: array + items: + $ref: '#/components/schemas/SnrBlockData_SnrBlockObservingComponents' + version: + type: integer + format: int32 + requested_target_magnitude: + type: number + description: this will come from the target + format: double + requested_cumulative_snr: + type: number + description: this will come from the OT + format: double + requested_minimum_exposures: + type: integer + format: int32 + total_exposures: + type: integer + description: counting the total validated exposures for this OC (OT/TARGET) + format: int32 + primary_observing_group_token: + type: string + primary_observing_component_token: + type: string + primary_observing_group_label: + type: integer + format: int32 + total_configured_exposures: + type: integer + description: counting the total configured exposures for this OC + format: int32 + SnrBlockData_SnrBlockObservingComponents: + type: object + properties: + observing_component_token: + type: string + description: the observing component + observing_group_token: + type: string + description: should not be here, should be in the parent + is_primary: + type: boolean + description: is it the original oc? + configured_exposures: + type: integer + format: int32 + exposures: + type: array + items: + $ref: '#/components/schemas/ExposureData' + ob_label: + type: integer + format: int32 + target_token: + type: string + observing_template_token: + type: string + description: a pointer to an observing component object and whether it is the primary/original + SnrBlockListResponse: + type: object + properties: + success: + type: boolean + error: + $ref: '#/components/schemas/Errors' + entity: + type: array + items: + $ref: '#/components/schemas/SnrBlockData' + SpirouStatus_ExposureMeterProcessingResult: + type: object + properties: + flux: + type: number + format: double + snr: + type: number + format: double + mid_exposure_date_millis: + type: number + format: double + spemsnr: + type: number + description: Calculated SNR + format: double + spemsnrc: + type: number + format: double + SpirouStatus_GuiderProcessingResult: + type: object + properties: + etime: + type: number + format: double + isustate: + type: string + sbdens_p: + type: number + format: double + hole_position_offset_x: + type: number + format: double + hole_position_offset_y: + type: number + format: double + star_to_null_position_offset_x: + type: number + format: double + star_to_null_position_offset_y: + type: number + format: double + magnitude_estimate: + type: number + format: double + seeing_estimate: + type: number + format: double + sgisust: + type: string + description: per 160676372 + sggdst: + type: string + sgfrmrt: + type: number + format: double + sgetime: + type: number + format: double + sgnullx: + type: number + format: double + sgnully: + type: number + format: double + sgwinx0: + type: number + format: double + sgwiny0: + type: number + format: double + sgwinx1: + type: number + format: double + sgwiny1: + type: number + format: double + sgoffx0: + type: number + format: double + sgoffy0: + type: number + format: double + sgoffx1: + type: number + format: double + sgoffy1: + type: number + format: double + sgholex: + type: number + format: double + sgholey: + type: number + format: double + sgcholex: + type: number + format: double + sgcholey: + type: number + format: double + sgeholex: + type: number + format: double + sgeholey: + type: number + format: double + sgcstarx: + type: number + format: double + sgcstary: + type: number + format: double + sgestarx: + type: number + format: double + sgestary: + type: number + format: double + sgcnoffx: + type: number + format: double + sgcnoffy: + type: number + format: double + sgcsee: + type: number + format: double + sgesee: + type: number + format: double + sgcmagn: + type: number + format: double + sgemagn: + type: number + format: double + sgssee: + type: number + format: double + trg_type: + type: string + description: from processing, the expected data in the log book are + SpirouStatus_PipelineProcessingResult: + type: object + properties: + snr10: + type: number + format: double + snr34: + type: number + format: double + snr44: + type: number + format: double + dprtype: + type: string + ccfmask: + type: number + format: double + ccfmacpp: + type: number + format: double + ccfrv: + type: number + format: double + ccfcontr: + type: number + format: double + ccfrvc: + type: number + format: double + ccffwhm: + type: number + format: double + ccfmask_str: + type: string + Status: + type: object + properties: + code: + type: integer + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + format: int32 + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + details: + type: array + items: + $ref: '#/components/schemas/GoogleProtobufAny' + description: A list of messages that carry the error details. There is a common set of message types for APIs to use. + description: 'The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).' + SubTotalsData: + type: object + properties: + etime: + $ref: '#/components/schemas/RollupValue' + overheadtime: + $ref: '#/components/schemas/RollupValue' + exposure_count: + $ref: '#/components/schemas/RollupValue' + child_count: + $ref: '#/components/schemas/RollupValue' + etime_configured: + $ref: '#/components/schemas/RollupValue' + overhead_time_configured: + $ref: '#/components/schemas/RollupValue' + completeness: + type: number + description: Percentage completed for the entity. + format: float + TargetData: + type: object + properties: + token: + type: string + description: Unique identifier for the target. When creating a new target, this will be inferred from the request data if left blank here. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789 + name: + type: string + description: 'Name for the target entered by PI, or primary name as resolved by CDS. Will become OBJECT in the FITS header. Required. Length limited to 39 characters. Allowed characters are letters, numbers, spaces, or any of: !@#$%^&*()_-+.,?/[]<>' + label: + type: integer + description: 'Index of the target within the program, set by the system automatically. Used for reference to targets in communications with QSO staff. storage: transient api_visibility: read only' + format: int32 + version: + type: integer + description: 'Increments when this object updates. storage: transient api_visibility: read only, incremented by the server' + format: int32 + fixed_target: + $ref: '#/components/schemas/TargetData_FixedTarget' + moving_target: + $ref: '#/components/schemas/TargetData_MovingTarget' + magnitude: + $ref: '#/components/schemas/TargetData_Magnitude' + temperature_effective: + type: number + description: The estimated/measured effective temperature, in Kelvin. Required for targets observed with SPIRou or ESPaDOnS. Not used for MegaCam. + format: double + standard_star: + type: boolean + description: Whether the star is a "standard" star, to be observed each time other science targets are observed. Not used for MegaCam. + linked_target_identifiers: + $ref: '#/components/schemas/TargetData_LinkedTargetIdentifiers' + finding_chart: + type: array + items: + $ref: '#/components/schemas/FindingChartData' + description: 'A read-only view of the finding charts for this target. To create or update finding charts, see the FindingChartService. storage: transient api_visibility: read only' + pointing_offset_token: + type: string + description: The token of a pointing offset which should be used when observing this target. To view or manage pointing offsets, see the PointingOffsetService. + pointing_offset: + $ref: '#/components/schemas/PointingOffsetData' + TargetData_FixedTarget: + type: object + properties: + coordinate: + $ref: '#/components/schemas/SkyCoordinate' + proper_motion: + $ref: '#/components/schemas/FixedTarget_ProperMotion' + computed_coordinate: + $ref: '#/components/schemas/SkyCoordinate' + estimated_radial_velocity_kmps: + $ref: '#/components/schemas/DoubleValue' + description: Target at fixed coordinates, including any provided proper motion. + TargetData_LinkedTargetIdentifiers: + type: object + properties: + pi_confirmed_identifiers: + $ref: '#/components/schemas/TargetIdentifiers' + custom_target: + type: boolean + description: Used to indicate this is a target without known identifiers. + description: Contains identifiers discovered for this target. + TargetData_Magnitude: + type: object + properties: + U: + $ref: '#/components/schemas/DoubleValue' + B: + $ref: '#/components/schemas/DoubleValue' + V: + $ref: '#/components/schemas/DoubleValue' + R: + $ref: '#/components/schemas/DoubleValue' + I: + $ref: '#/components/schemas/DoubleValue' + G: + $ref: '#/components/schemas/DoubleValue' + J: + $ref: '#/components/schemas/DoubleValue' + H: + $ref: '#/components/schemas/DoubleValue' + K: + $ref: '#/components/schemas/DoubleValue' + uu: + $ref: '#/components/schemas/DoubleValue' + gg: + $ref: '#/components/schemas/DoubleValue' + rr: + $ref: '#/components/schemas/DoubleValue' + ii: + $ref: '#/components/schemas/DoubleValue' + zz: + $ref: '#/components/schemas/DoubleValue' + AB: + $ref: '#/components/schemas/DoubleValue' + description: Estimated/measured magnitude values per-band. Each value is optional, except where noted. + TargetData_MovingTarget: + type: object + properties: + ephemeris_point: + type: array + items: + $ref: '#/components/schemas/MovingTarget_Ephemeris' + description: The ephemerides that define the path of motion for the target. Must be provided using topocentric astrometric coordinates. Do not provide apparent coordinates. CFHT strongly recommends the use of JPL Horizons System for generating the proper coordinates. CFHT's observatory code is T14. At least five distinct positions must be provided that span the desired observing period. The first position should be well in advance of the observation. The positions do not need to be uniformly sampled throughout the desired observing period. The points will be interpolated to determine the position of the target at the time of observation, at which point there must be at least one ephemeris point defined in the past, and at least four defined in the future. + description: Target with motion defined by a list of ephemerides. + TargetDeleteResponse: + type: object + properties: + success: + type: boolean + error: + $ref: '#/components/schemas/Errors' + TargetIdentifiers: + type: object + properties: + catalog_identifier: + type: object + additionalProperties: + $ref: '#/components/schemas/ListOfIdentifiers' + description: 'All catalogs resolved by simbad for this target key: catalog name, catalog_identifier.key === catalog_identifier.get(key).catalog value: catalog identifier.' + two_mass: + $ref: '#/components/schemas/CatalogIdentifier' + gaia1: + $ref: '#/components/schemas/CatalogIdentifier' + gaia2: + $ref: '#/components/schemas/CatalogIdentifier' + resolved_catalog_identifier: + $ref: '#/components/schemas/CatalogIdentifier' + description: TargetIdentifiers are a collection of identifiers which uniquely identify the target. All possible identifiers are stored in the catalog_identifier map, but a few interesting ones to systems are pulled out for ease of use. + TargetListResponse: + type: object + properties: + success: + type: boolean + entity: + type: array + items: + $ref: '#/components/schemas/TargetData' + error: + $ref: '#/components/schemas/Errors' + TargetMonitoringObservingGroup: + type: object + properties: + observing_block: + $ref: '#/components/schemas/ObservingGroupData_ObservingBlock' + time_constraint: + $ref: '#/components/schemas/TargetMonitoringObservingGroup_TimeConstraint' + description: An observing group defined by an observing block, a number of iterations, and schedule constraints on the iterations. Used for observations that need to be repeated with a regular period. One copy of the observing block is added to the observing group for each iteration. + TargetMonitoringObservingGroup_TimeConstraint: + type: object + properties: + number_of_iterations: + type: integer + description: How many observations to take. + format: int32 + minimum_number_of_iterations: + type: integer + description: Minimum number of observations which still provides science value. If weather becomes an issue, this is used to determine if additional observations should be tried, or the whole observing group aborted to prioritize other observations. Cannot be greater than requested number of iterations. + format: int32 + time_interval: + $ref: '#/components/schemas/Duration' + time_interval_tolerance_min: + $ref: '#/components/schemas/Duration' + time_interval_tolerance_max: + $ref: '#/components/schemas/Duration' + t0_millis: + type: integer + description: Time the observing group should start being observed, in Unix time milliseconds. If omitted, CFHT will start observing at the QC's discretion. + format: int64 + within_same_camera_run: + type: boolean + description: Whether all iterations need to occur within the same camera run. + TargetShowResponse: + type: object + properties: + success: + type: boolean + error: + $ref: '#/components/schemas/Errors' + entity: + $ref: '#/components/schemas/TargetData' + TargetTimeData: + type: object + properties: + requested_integration_time: + $ref: '#/components/schemas/ITimeData' + actual_integration_time: + $ref: '#/components/schemas/ITimeData' + view: + enum: + - REQUESTED + - ACTUAL + type: string + format: enum + exposure_count: + type: integer + format: int32 + child_count: + type: integer + format: int32 + TargetUpdateRequest: + type: object + properties: + runid: + type: string + description: Unique identifier of the science program. + entity: + $ref: '#/components/schemas/TargetData' + token: + type: string + description: Unique identifier of the target. If a target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789 + lock_version: + $ref: '#/components/schemas/Int32Value' + instrument: + enum: + - UNKNOWN + - CFH12K + - MEGACAM + - WIRCAM + - ESPADONS + - SITELLE + - SPIROU + - WENAOKEAO + type: string + description: If specified, instrument-specific validation checks will be performed. + format: enum + TargetUpdateResponse: + type: object + properties: + success: + type: boolean + error: + $ref: '#/components/schemas/Errors' + entity: + $ref: '#/components/schemas/TargetData' + TelescopePatternData: + type: object + properties: + offsets: + type: array + items: + $ref: '#/components/schemas/TelescopePatternData_Offset' + description: Should only be set if manually specifying custom dithering patterns + config: + $ref: '#/components/schemas/TelescopePatternData_Config' + token: + type: string + label: + type: integer + format: int32 + version: + type: integer + format: int32 + user_token: + type: string + instrument: + enum: + - UNKNOWN + - CFH12K + - MEGACAM + - WIRCAM + - ESPADONS + - SITELLE + - SPIROU + - WENAOKEAO + type: string + format: enum + is_system: + type: boolean + name: + type: string + offset_coordinate: + type: array + items: + $ref: '#/components/schemas/OffsetCoordinate' + description: Part of observing_template_data - pattern entity stored in pattern table + TelescopePatternData_Config: + type: object + properties: + name: + type: string + description: + type: string + charge_model: + type: string + description: Used for calculating cost for this pattern + dithering: + $ref: '#/components/schemas/Config_Dithering' + nodding: + $ref: '#/components/schemas/Config_Nodding' + scale: + type: number + format: double + sybase_pat_id: + $ref: '#/components/schemas/BigInteger' + is_system: + type: boolean + TelescopePatternData_Offset: + type: object + properties: + exposure_number: + type: integer + description: 'TODO: deprecate, no need to have duplicate information here.' + format: int32 + offset: + $ref: '#/components/schemas/OffsetCoordinate' + type: + enum: + - OBJECT + - TARGET + - SKY + - DARK + - FLAT + - SNAP + - BIAS + - FOCUS + - ALIGN + - COMPARISON + - ACQUIRE + - FABRY_PEROT + - TWILIGHT_FLATS + - PHOTOMETRY + - SFOCUS + type: string + description: For dithering patterns type is always target + format: enum + TelescopePatternDeleteResponse: + type: object + properties: + success: + type: boolean + error: + $ref: '#/components/schemas/Errors' + TelescopePatternListResponse: + type: object + properties: + success: + type: boolean + entity: + type: array + items: + $ref: '#/components/schemas/TelescopePatternData' + error: + $ref: '#/components/schemas/Errors' + TelescopePatternUpdateRequest: + type: object + properties: + runid: + type: string + token: + type: string + lock_version: + $ref: '#/components/schemas/Int32Value' + entity: + $ref: '#/components/schemas/TelescopePatternData' + TelescopePatternUpdateResponse: + type: object + properties: + success: + type: boolean + error: + $ref: '#/components/schemas/Errors' + entity: + $ref: '#/components/schemas/TelescopePatternData' + TimeAccountingData: + type: object + properties: + configured_time: + $ref: '#/components/schemas/TargetTimeData' + charged_time: + $ref: '#/components/schemas/TargetTimeData' + validated_time_on_sky: + $ref: '#/components/schemas/TargetTimeData' + time_on_sky: + $ref: '#/components/schemas/TargetTimeData' + time_accounting_status: + enum: + - NOT_STARTED + - STARTED + - OBSERVED + - VALIDATED + type: string + format: enum + completion_ratio: + type: number + format: double + description: 'Provides information about the time configured, time spent observing, and overall progress of an entity configured for observation. The entities with TimeAccountingData data are: * Observing Blocks * Observing Groups * Science Programs All fields in TimeAccountingData are readonly because they are derived from the entity and its associated exposures.' + TimeConstraint: + type: object + properties: + window: + type: array + items: + $ref: '#/components/schemas/ObservingBlockCondition_Window' + description: Time windows during which the observation can be made. Windows can not overlap, and will always be stored in temporal order. + description: Constraints on when an observation can be made. + UpdateProgramCommentRequest: + type: object + properties: + runid: + type: string + description: Unique identifier for your science program + comment: + type: string + description: Comment set by the PI for the CFHT QSO Team + UpdateProgramCommentResponse: + type: object + properties: + success: + type: boolean + program_data: + $ref: '#/components/schemas/ProgramData' + error: + $ref: '#/components/schemas/Errors' + UpdateProgramCommentsRequest: + type: object + properties: + runid: + type: string + description: Unique identifier for your science program + program_comment: + type: string + description: Comment set by the PI for the CFHT QSO Team + operations_comment: + type: string + description: Comment set by the CFHT QSO Team + UpdateProgramCommentsResponse: + type: object + properties: + success: + type: boolean + program_data: + $ref: '#/components/schemas/ProgramData' + error: + $ref: '#/components/schemas/Errors' + UpenaProcessingResult_h2o_mm: + type: object + properties: + raw_density: + type: number + format: double + at_zenith: + type: number + format: double +tags: + - name: ObservingGroupService + - name: ObservingTemplateService + - name: PointingOffsetService + - name: ProgramService + - name: TargetService diff --git a/codegen/cfht/generator.py b/codegen/cfht/generator.py index 22c338e..78e8e79 100755 --- a/codegen/cfht/generator.py +++ b/codegen/cfht/generator.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 import sys +from pathlib import Path from typing import Any -import httpx +import yaml from datamodel_code_generator import ( DataModelType, Formatter, @@ -16,49 +17,15 @@ Error as DataModelCodegenError, ) -SWAGGER_V2_URL = ( - "https://hou-stage.cfht.hawaii.edu/api-docs/piapi_openapiv2.swagger.json" -) -SWAGGER_CONVERTER_URL = "https://converter.swagger.io/api/convert" - +# Convert this to a URL once CFHT confirms the OAS3 spec is availanble online somewhere. +OPENAPI_SPEC_PATH = Path(__file__).with_name("cfhtopenapi.yaml") GENERATED_HEADER = "# Auto generated file, do not edit\n# ruff: noqa: E741\n" -def fetch_converted_openapi(swagger_v2_url: str) -> dict[str, Any]: - response = httpx.get( - SWAGGER_CONVERTER_URL, - params={"url": swagger_v2_url}, - timeout=120.0, - follow_redirects=True, - ) - response.raise_for_status() - return response.json() - - -def normalize_string_int64_to_integer(node: Any) -> None: - """ - The CFHT spec marks some integer-like fields as: - {"type": "string", "format": "int64"} - but the API returns true integers. Normalize schema to integers so that - the generated pydantic models can be used without schema validation errors. - """ - - if isinstance(node, dict): - if node.get("type") == "string" and node.get("format") == "int64": - node["type"] = "integer" - - for value in node.values(): - normalize_string_int64_to_integer(value) - - elif isinstance(node, list): - for item in node: - normalize_string_int64_to_integer(item) - - def generate_models(openapi_document: dict[str, Any]) -> str: config = GenerateConfig( input_file_type=InputFileType.OpenAPI, - input_filename="cfht.openapi3.json", + input_filename="cfhtopenapi.yaml", openapi_scopes=[OpenAPIScope.Schemas], output_model_type=DataModelType.PydanticV2BaseModel, base_class="aeonlib.cfht.base_model.CFHTBaseModel", @@ -74,7 +41,7 @@ def generate_models(openapi_document: dict[str, Any]) -> str: result = generate(openapi_document, config=config) if not isinstance(result, str): raise TypeError( - "Expected string output from datamodel-code-generator" + "Expected string output from datamodel-code-generator " f"got {type(result).__name__}." ) @@ -82,9 +49,8 @@ def generate_models(openapi_document: dict[str, Any]) -> str: def main() -> int: - document = fetch_converted_openapi(SWAGGER_V2_URL) - normalize_string_int64_to_integer(document) - + with OPENAPI_SPEC_PATH.open(encoding="utf-8") as f: + document = yaml.safe_load(f) generated = generate_models(document) _ = sys.stdout.write(generated) @@ -94,6 +60,6 @@ def main() -> int: if __name__ == "__main__": try: raise SystemExit(main()) - except (httpx.HTTPError, DataModelCodegenError, OSError, TypeError) as exc: + except (yaml.YAMLError, DataModelCodegenError, OSError, TypeError) as exc: print(f"CFHT model generation failed: {exc}", file=sys.stderr) raise SystemExit(1) diff --git a/codegen/cfht/generator_oas3.py b/codegen/cfht/generator_oas3.py deleted file mode 100755 index f88f5c7..0000000 --- a/codegen/cfht/generator_oas3.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python3 -import sys -from pathlib import Path -from typing import Any - -import yaml -from datamodel_code_generator import ( - DataModelType, - Formatter, - GenerateConfig, - InputFileType, - OpenAPIScope, - TargetPydanticVersion, - generate, -) -from datamodel_code_generator import ( - Error as DataModelCodegenError, -) - -OPENAPI_SPEC_PATH = Path(__file__).with_name("cfhtopenapi.yaml") -GENERATED_HEADER = "# Auto generated file, do not edit\n# ruff: noqa: E741\n" - - -def generate_models(openapi_document: dict[str, Any]) -> str: - config = GenerateConfig( - input_file_type=InputFileType.OpenAPI, - input_filename="cfhtopenapi.yaml", - openapi_scopes=[OpenAPIScope.Schemas], - output_model_type=DataModelType.PydanticV2BaseModel, - base_class="aeonlib.cfht.base_model.CFHTBaseModel", - target_pydantic_version=TargetPydanticVersion.V2_11, - use_annotated=True, - field_constraints=True, - set_default_enum_member=True, - snake_case_field=True, - allow_population_by_field_name=True, - formatters=[Formatter.BUILTIN], - ) - - result = generate(openapi_document, config=config) - if not isinstance(result, str): - raise TypeError( - "Expected string output from datamodel-code-generator " - f"got {type(result).__name__}." - ) - - return GENERATED_HEADER + result + "\n" - - -def main() -> int: - with OPENAPI_SPEC_PATH.open(encoding="utf-8") as f: - document = yaml.safe_load(f) - generated = generate_models(document) - _ = sys.stdout.write(generated) - - return 0 - - -if __name__ == "__main__": - try: - raise SystemExit(main()) - except (yaml.YAMLError, DataModelCodegenError, OSError, TypeError) as exc: - print(f"CFHT model generation failed: {exc}", file=sys.stderr) - raise SystemExit(1) From 4b94cb508debc1852a4a88c3e8d58a3d3dbcc017 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 6 Aug 2026 16:27:32 -0700 Subject: [PATCH 15/33] A few client improvements --- src/aeonlib/cfht/facility.py | 103 +++++++++++++++++++++-------------- tests/cfht/test_online.py | 44 ++++++++------- 2 files changed, 85 insertions(+), 62 deletions(-) diff --git a/src/aeonlib/cfht/facility.py b/src/aeonlib/cfht/facility.py index e7729ff..02b9209 100644 --- a/src/aeonlib/cfht/facility.py +++ b/src/aeonlib/cfht/facility.py @@ -1,4 +1,4 @@ -from typing import Literal +from typing import Any, Literal import httpx @@ -14,22 +14,71 @@ class VersionMismatchError(ValueError): """Raised when the version of the target does not match the server""" +class EntityNotFoundError(ValueError): + """Raised when the entity is not found""" + + +class InvalidResponseError(ValueError): + """Raised when the response is invalid""" + + class CFHTFacility: """CFHT Facility class""" - def __init__(self, settings: Settings = default_settings): + program_token: str | None = None + + def __init__( + self, settings: Settings = default_settings, program: ProgramInfo | None = None + ): base_url = settings.cfht_api_root if not base_url: raise ValueError("AEON_CFHT_API_ROOT is not set") access_token = settings.cfht_access_token if not access_token: raise ValueError("AEON_CFHT_ACCESS_TOKEN token is not set") + if program is not None: + self.select_program(program) + headers = { "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", } self._client = httpx.Client(base_url=base_url, headers=headers) + def _request(self, method: str, url: str, **kwargs: Any) -> Any: + if not self.program_token: + raise ValueError( + "Program must be set. Initialize the facility with a ProgramInfo or use `select_program`" + ) + url = f"/programs/{self.program_token}/{url.lstrip('/')}" + response = self._client.request(method, url, **kwargs) + + if response.status_code == httpx.codes.CONFLICT: + raise VersionMismatchError( + f"Version mismatch while requesting {response.request.url}" + ) + + if response.status_code == httpx.codes.NOT_FOUND: + raise EntityNotFoundError(f"Entity not found: {response.request.url}") + + response.raise_for_status() + if method == "DELETE": + return None + try: + return response.json()["entity"] + except (ValueError, TypeError, KeyError) as exc: + raise InvalidResponseError( + f"CFHT API response from {response.request.url} did not contain an entity" + ) from exc + + def select_program(self, program: ProgramInfo) -> None: + if program.program_data is None: + raise ValueError("Program data is not set") + token = program.program_data.token + if not token: + raise ValueError("Program token is not set") + self.program_token = token + def programs(self) -> list[ProgramInfo]: """Get the list of observing programs""" response = self._client.get("/programs/") @@ -40,39 +89,20 @@ def programs(self) -> list[ProgramInfo]: ProgramInfo.model_validate(program) for program in payload.get("entity", []) ] - def targets(self, program_token: str) -> list[TargetData]: + def targets(self) -> list[TargetData]: """Get the list of targets for a given program""" - response = self._client.get(f"/programs/{program_token}/targets/") - response.raise_for_status() - payload = response.json() - return [ - TargetData.model_validate(target) for target in payload.get("entity", []) - ] - - def get_target(self, program_token: str, target_token: str) -> TargetData | None: - response = self._client.get(f"/programs/{program_token}/targets/{target_token}") - if response.status_code == 404: - return None + entities = self._request("GET", "targets/") + return [TargetData.model_validate(target) for target in entities] - response.raise_for_status() - payload = response.json() - return ( - TargetData.model_validate(payload.get("entity")) - if payload.get("entity") - else None - ) - - def delete_target(self, program_token: str, target_token: str) -> None: - response = self._client.delete( - f"/programs/{program_token}/targets/{target_token}" - ) - if response.status_code == 406: - return + def get_target(self, target_token: str) -> TargetData: + entity = self._request("GET", f"targets/{target_token}") + return TargetData.model_validate(entity) - response.raise_for_status() + def delete_target(self, target_token: str) -> None: + self._request("DELETE", f"targets/{target_token}") def create_or_update_target( - self, program_token: str, target: TargetData, instrument: Instrument + self, target: TargetData, instrument: Instrument ) -> TargetData: version = {"value": target.version} if target.version else None data = { @@ -80,14 +110,5 @@ def create_or_update_target( "lock_version": version, "instrument": instrument.value, } - response = self._client.put( - f"/programs/{program_token}/targets/{target.token}/", - json=data, - ) - if response.status_code == 409: - raise VersionMismatchError( - "Version mismatch. Payload had version %s", target.version - ) - response.raise_for_status() - payload = response.json() - return TargetData.model_validate(payload["entity"]) + entity = self._request("PUT", f"targets/{target.token}/", json=data) + return TargetData.model_validate(entity) diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index 757ab53..1121560 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -4,7 +4,11 @@ import pytest from pydantic import ValidationError -from aeonlib.cfht.facility import CFHTFacility, VersionMismatchError +from aeonlib.cfht.facility import ( + CFHTFacility, + EntityNotFoundError, + VersionMismatchError, +) from aeonlib.cfht.models import ( DoubleValue, # TODO: should be removed or coerced from float Instrument, @@ -86,11 +90,14 @@ def example_moving_target( ) -def target_api_examples(program_token: str, instrument: Instrument, test_run_id: str): +def target_api_examples(instrument: Instrument, test_run_id: str): """ Complete API examples for Kealahou targets """ - targets = FACILITY.targets(program_token) + program_token = FACILITY.program_token + assert program_token is not None + + targets = FACILITY.targets() for target in targets: assert target.name is not None # assert "Test" in target.name # TODO: find a better invariant @@ -115,14 +122,14 @@ def target_api_examples(program_token: str, instrument: Instrument, test_run_id: setattr(mag, required_mag_by_instrument[instrument], DoubleValue(value=10.0)) new_target.magnitude = mag old_version = new_target.version if new_target.version is not None else 0 - target = FACILITY.create_or_update_target(program_token, new_target, instrument) + target = FACILITY.create_or_update_target(new_target, instrument) assert target.version == old_version + 1 old_version += 1 # Update target update_target = target update_target.standard_star = True - target = FACILITY.create_or_update_target(program_token, update_target, instrument) + target = FACILITY.create_or_update_target(update_target, instrument) assert target.standard_star is True assert target.version == old_version + 1 @@ -130,45 +137,40 @@ def target_api_examples(program_token: str, instrument: Instrument, test_run_id: # TODO should aeonlib handle versioning client side? with pytest.raises(VersionMismatchError): update_target.temperature_effective = 2345.6 - FACILITY.create_or_update_target(program_token, update_target, instrument) + FACILITY.create_or_update_target(update_target, instrument) # Fetch single target token = target.token assert token, "target token should not be None" - target = FACILITY.get_target(program_token, token) - assert target is not None + target = FACILITY.get_target(token) assert token == target.token # Delete the new (unobserved) target assert target.token - FACILITY.delete_target(program_token, target.token) - target = FACILITY.get_target(program_token, target.token) - assert target is None + FACILITY.delete_target(target.token) + with pytest.raises(EntityNotFoundError): + FACILITY.get_target(target.token) # create moving target new_moving_target = example_moving_target(program_token, instrument, test_run_id) - moving_target = FACILITY.create_or_update_target( - program_token, new_moving_target, instrument - ) - assert moving_target is not None + moving_target = FACILITY.create_or_update_target(new_moving_target, instrument) # Delete moving target assert moving_target.token - FACILITY.delete_target(program_token, moving_target.token) - moving_target = FACILITY.get_target(program_token, moving_target.token) - assert moving_target is None + FACILITY.delete_target(moving_target.token) + with pytest.raises(EntityNotFoundError): + FACILITY.get_target(moving_target.token) def test_example(): test_run_id = f"{uuid.uuid4()}"[:8] programs = FACILITY.programs() for program in programs: + FACILITY.select_program(program) program_data = program.program_data assert program_data is not None - program_token = program_data.token - assert program_token is not None time_allocation = program_data.time_allocation assert time_allocation is not None instrument = time_allocation[0].instrument assert instrument is not None - target_api_examples(program_token, instrument, test_run_id) + target_api_examples(instrument, test_run_id) From d5a0da30dc994acc9f2d1c08983ceceb1542e0c2 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 6 Aug 2026 16:32:21 -0700 Subject: [PATCH 16/33] Remove global facility --- tests/cfht/test_online.py | 42 +++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index 1121560..16b54f4 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -29,11 +29,13 @@ } -FACILITY = CFHTFacility() +@pytest.fixture(scope="module") +def facility() -> CFHTFacility: + return CFHTFacility() -def test_programs(): - programs = FACILITY.programs() +def test_programs(facility: CFHTFacility): + programs = facility.programs() assert programs[0].pi_info assert programs[0].pi_info.first_name == "AEON" @@ -90,14 +92,16 @@ def example_moving_target( ) -def target_api_examples(instrument: Instrument, test_run_id: str): +def target_api_examples( + facility: CFHTFacility, instrument: Instrument, test_run_id: str +): """ Complete API examples for Kealahou targets """ - program_token = FACILITY.program_token + program_token = facility.program_token assert program_token is not None - targets = FACILITY.targets() + targets = facility.targets() for target in targets: assert target.name is not None # assert "Test" in target.name # TODO: find a better invariant @@ -122,14 +126,14 @@ def target_api_examples(instrument: Instrument, test_run_id: str): setattr(mag, required_mag_by_instrument[instrument], DoubleValue(value=10.0)) new_target.magnitude = mag old_version = new_target.version if new_target.version is not None else 0 - target = FACILITY.create_or_update_target(new_target, instrument) + target = facility.create_or_update_target(new_target, instrument) assert target.version == old_version + 1 old_version += 1 # Update target update_target = target update_target.standard_star = True - target = FACILITY.create_or_update_target(update_target, instrument) + target = facility.create_or_update_target(update_target, instrument) assert target.standard_star is True assert target.version == old_version + 1 @@ -137,40 +141,40 @@ def target_api_examples(instrument: Instrument, test_run_id: str): # TODO should aeonlib handle versioning client side? with pytest.raises(VersionMismatchError): update_target.temperature_effective = 2345.6 - FACILITY.create_or_update_target(update_target, instrument) + facility.create_or_update_target(update_target, instrument) # Fetch single target token = target.token assert token, "target token should not be None" - target = FACILITY.get_target(token) + target = facility.get_target(token) assert token == target.token # Delete the new (unobserved) target assert target.token - FACILITY.delete_target(target.token) + facility.delete_target(target.token) with pytest.raises(EntityNotFoundError): - FACILITY.get_target(target.token) + facility.get_target(target.token) # create moving target new_moving_target = example_moving_target(program_token, instrument, test_run_id) - moving_target = FACILITY.create_or_update_target(new_moving_target, instrument) + moving_target = facility.create_or_update_target(new_moving_target, instrument) # Delete moving target assert moving_target.token - FACILITY.delete_target(moving_target.token) + facility.delete_target(moving_target.token) with pytest.raises(EntityNotFoundError): - FACILITY.get_target(moving_target.token) + facility.get_target(moving_target.token) -def test_example(): +def test_example(facility: CFHTFacility): test_run_id = f"{uuid.uuid4()}"[:8] - programs = FACILITY.programs() + programs = facility.programs() for program in programs: - FACILITY.select_program(program) + facility.select_program(program) program_data = program.program_data assert program_data is not None time_allocation = program_data.time_allocation assert time_allocation is not None instrument = time_allocation[0].instrument assert instrument is not None - target_api_examples(instrument, test_run_id) + target_api_examples(facility, instrument, test_run_id) From 1b0c9db954f6ce57d461c781ffac6339fc130694 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 6 Aug 2026 16:33:32 -0700 Subject: [PATCH 17/33] Fix could be 0 case --- src/aeonlib/cfht/facility.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aeonlib/cfht/facility.py b/src/aeonlib/cfht/facility.py index 02b9209..001e630 100644 --- a/src/aeonlib/cfht/facility.py +++ b/src/aeonlib/cfht/facility.py @@ -104,7 +104,7 @@ def delete_target(self, target_token: str) -> None: def create_or_update_target( self, target: TargetData, instrument: Instrument ) -> TargetData: - version = {"value": target.version} if target.version else None + version = {"value": target.version} if target.version is not None else None data = { "entity": target.api_dump(), "lock_version": version, From ee30cdfd5828753e8c9d9bf816738898880c178f Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 6 Aug 2026 16:42:33 -0700 Subject: [PATCH 18/33] Split _request and _program_request --- src/aeonlib/cfht/facility.py | 38 +++++++++++++++--------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/src/aeonlib/cfht/facility.py b/src/aeonlib/cfht/facility.py index 001e630..e4f0e8f 100644 --- a/src/aeonlib/cfht/facility.py +++ b/src/aeonlib/cfht/facility.py @@ -1,4 +1,4 @@ -from typing import Any, Literal +from typing import Any import httpx @@ -7,8 +7,6 @@ from .models import Instrument, ProgramInfo, TargetData -INSTRUMENTS = Literal["SPIROU", "ESPADONS", "MEGACAM"] - class VersionMismatchError(ValueError): """Raised when the version of the target does not match the server""" @@ -46,21 +44,13 @@ def __init__( self._client = httpx.Client(base_url=base_url, headers=headers) def _request(self, method: str, url: str, **kwargs: Any) -> Any: - if not self.program_token: - raise ValueError( - "Program must be set. Initialize the facility with a ProgramInfo or use `select_program`" - ) - url = f"/programs/{self.program_token}/{url.lstrip('/')}" response = self._client.request(method, url, **kwargs) - if response.status_code == httpx.codes.CONFLICT: raise VersionMismatchError( f"Version mismatch while requesting {response.request.url}" ) - if response.status_code == httpx.codes.NOT_FOUND: raise EntityNotFoundError(f"Entity not found: {response.request.url}") - response.raise_for_status() if method == "DELETE": return None @@ -71,6 +61,15 @@ def _request(self, method: str, url: str, **kwargs: Any) -> Any: f"CFHT API response from {response.request.url} did not contain an entity" ) from exc + def _program_request(self, method: str, url: str, **kwargs: Any) -> Any: + if not self.program_token: + raise ValueError( + "Program must be set. Initialize the facility with a ProgramInfo or use `select_program`" + ) + return self._request( + method, f"programs/{self.program_token}/{url.lstrip('/')}", **kwargs + ) + def select_program(self, program: ProgramInfo) -> None: if program.program_data is None: raise ValueError("Program data is not set") @@ -81,25 +80,20 @@ def select_program(self, program: ProgramInfo) -> None: def programs(self) -> list[ProgramInfo]: """Get the list of observing programs""" - response = self._client.get("/programs/") - response.raise_for_status() - - payload = response.json() - return [ - ProgramInfo.model_validate(program) for program in payload.get("entity", []) - ] + entities = self._request("GET", "programs/") + return [ProgramInfo.model_validate(entity) for entity in entities] def targets(self) -> list[TargetData]: """Get the list of targets for a given program""" - entities = self._request("GET", "targets/") + entities = self._program_request("GET", "targets/") return [TargetData.model_validate(target) for target in entities] def get_target(self, target_token: str) -> TargetData: - entity = self._request("GET", f"targets/{target_token}") + entity = self._program_request("GET", f"targets/{target_token}") return TargetData.model_validate(entity) def delete_target(self, target_token: str) -> None: - self._request("DELETE", f"targets/{target_token}") + self._program_request("DELETE", f"targets/{target_token}") def create_or_update_target( self, target: TargetData, instrument: Instrument @@ -110,5 +104,5 @@ def create_or_update_target( "lock_version": version, "instrument": instrument.value, } - entity = self._request("PUT", f"targets/{target.token}/", json=data) + entity = self._program_request("PUT", f"targets/{target.token}/", json=data) return TargetData.model_validate(entity) From fa4669c3daa551cb3e23555d18dae43fef914785 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 6 Aug 2026 17:22:39 -0700 Subject: [PATCH 19/33] Mark all as side effects for now --- tests/cfht/test_online.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index 16b54f4..ad00022 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -20,7 +20,7 @@ TargetDataMovingTarget, # TODO: replace with common non-sidereal target model ) -pytestmark = pytest.mark.online +pytestmark = [pytest.mark.online, pytest.mark.side_effect] required_mag_by_instrument: dict[Instrument, str] = { Instrument.spirou: "h", From b03e418576a125b6a6af5a9b7e5230a958626674 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 6 Aug 2026 17:28:39 -0700 Subject: [PATCH 20/33] Fix programs test --- src/aeonlib/cfht/facility.py | 9 +++------ tests/cfht/test_online.py | 5 +++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/aeonlib/cfht/facility.py b/src/aeonlib/cfht/facility.py index e4f0e8f..d3a46a8 100644 --- a/src/aeonlib/cfht/facility.py +++ b/src/aeonlib/cfht/facility.py @@ -23,10 +23,8 @@ class InvalidResponseError(ValueError): class CFHTFacility: """CFHT Facility class""" - program_token: str | None = None - def __init__( - self, settings: Settings = default_settings, program: ProgramInfo | None = None + self, settings: Settings = default_settings, program_token: str | None = None ): base_url = settings.cfht_api_root if not base_url: @@ -34,14 +32,13 @@ def __init__( access_token = settings.cfht_access_token if not access_token: raise ValueError("AEON_CFHT_ACCESS_TOKEN token is not set") - if program is not None: - self.select_program(program) - headers = { "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", } + self._client = httpx.Client(base_url=base_url, headers=headers) + self.program_token = program_token def _request(self, method: str, url: str, **kwargs: Any) -> Any: response = self._client.request(method, url, **kwargs) diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index ad00022..a507e8c 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -36,8 +36,9 @@ def facility() -> CFHTFacility: def test_programs(facility: CFHTFacility): programs = facility.programs() - assert programs[0].pi_info - assert programs[0].pi_info.first_name == "AEON" + assert any( + program.pi_info and program.pi_info.first_name == "AEON" for program in programs + ) def example_fixed_target( From d46ba2ffeab738010eb8c4cc1953aea841ec2f82 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 6 Aug 2026 21:58:53 -0700 Subject: [PATCH 21/33] Mega test factored. --- src/aeonlib/cfht/facility.py | 21 +++- tests/cfht/test_online.py | 223 ++++++++++++++++++++--------------- 2 files changed, 150 insertions(+), 94 deletions(-) diff --git a/src/aeonlib/cfht/facility.py b/src/aeonlib/cfht/facility.py index d3a46a8..599d1e3 100644 --- a/src/aeonlib/cfht/facility.py +++ b/src/aeonlib/cfht/facility.py @@ -61,7 +61,7 @@ def _request(self, method: str, url: str, **kwargs: Any) -> Any: def _program_request(self, method: str, url: str, **kwargs: Any) -> Any: if not self.program_token: raise ValueError( - "Program must be set. Initialize the facility with a ProgramInfo or use `select_program`" + "Program must be set. Initialize the facility with program_token or use `select_program`" ) return self._request( method, f"programs/{self.program_token}/{url.lstrip('/')}", **kwargs @@ -80,6 +80,25 @@ def programs(self) -> list[ProgramInfo]: entities = self._request("GET", "programs/") return [ProgramInfo.model_validate(entity) for entity in entities] + def instruments(self) -> set[Instrument]: + """Return the instruments allocated to the selected program""" + if not self.program_token: + raise ValueError( + "Program must be set. Initialize the facility with program_token or use `select_program`" + ) + for program in self.programs(): + program_data = program.program_data + if program_data is None or program_data.token != self.program_token: + continue + + return { + allocation.instrument + for allocation in program_data.time_allocation or [] + if allocation.instrument is not None + } + + raise EntityNotFoundError(f"Program not found: {self.program_token}") + def targets(self) -> list[TargetData]: """Get the list of targets for a given program""" entities = self._program_request("GET", "targets/") diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index a507e8c..fac0b38 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -1,8 +1,8 @@ import random import uuid +from collections.abc import Iterator import pytest -from pydantic import ValidationError from aeonlib.cfht.facility import ( CFHTFacility, @@ -20,9 +20,9 @@ TargetDataMovingTarget, # TODO: replace with common non-sidereal target model ) -pytestmark = [pytest.mark.online, pytest.mark.side_effect] +pytestmark = pytest.mark.online -required_mag_by_instrument: dict[Instrument, str] = { +example_mag_by_instrument: dict[Instrument, str] = { Instrument.spirou: "h", Instrument.espadons: "v", Instrument.megacam: "ab", @@ -34,11 +34,38 @@ def facility() -> CFHTFacility: return CFHTFacility() -def test_programs(facility: CFHTFacility): - programs = facility.programs() - assert any( - program.pi_info and program.pi_info.first_name == "AEON" for program in programs - ) +@pytest.fixture(scope="module") +def test_run_id() -> str: + return uuid.uuid4().hex[:8] + + +@pytest.fixture(scope="module") +def program_facilities(facility: CFHTFacility) -> list[CFHTFacility]: + facilities = [ + CFHTFacility(program_token=data.token) + for program in facility.programs() + if (data := program.program_data) + if data.token + ] + assert facilities, "No programs with tokens were returned" + return facilities + + +def program_instruments( + facilities: list[CFHTFacility], +) -> Iterator[tuple[CFHTFacility, str, Instrument]]: + for facility in facilities: + program_token = facility.program_token + assert program_token is not None + instruments = facility.instruments() + assert instruments, f"No instruments allocated to {program_token}" + unsupported = instruments.difference(example_mag_by_instrument) + assert not unsupported, ( + "Example required mag not defined " + f"for: {', '.join(sorted(i.value for i in unsupported))}" + ) + for instrument in instruments: + yield facility, program_token, instrument def example_fixed_target( @@ -52,14 +79,15 @@ def example_fixed_target( ra=random.uniform(0, 359.9999), dec=random.uniform(-90, 90), ), - proper_motion=None, estimated_radial_velocity_kmps=DoubleValue( value=234.0 ) # TODO: remove the need for DoubleValue if instrument == Instrument.spirou else None, ), - magnitude=None, + magnitude=TargetDataMagnitude( + **{example_mag_by_instrument[instrument]: DoubleValue(value=10.0)} + ), temperature_effective=1234.5, standard_star=False, pointing_offset_token=f"00AZ00-PO+{instrument.value}+1", @@ -85,7 +113,7 @@ def example_moving_target( ] ), magnitude=TargetDataMagnitude( - **{required_mag_by_instrument[instrument]: DoubleValue(value=10.0)} + **{example_mag_by_instrument[instrument]: DoubleValue(value=10.0)} ), temperature_effective=1234.5, standard_star=False, @@ -93,89 +121,98 @@ def example_moving_target( ) -def target_api_examples( - facility: CFHTFacility, instrument: Instrument, test_run_id: str -): - """ - Complete API examples for Kealahou targets - """ - program_token = facility.program_token - assert program_token is not None +def test_programs(facility: CFHTFacility): + programs = facility.programs() + assert any( + program.pi_info and program.pi_info.first_name == "AEON" for program in programs + ) - targets = facility.targets() - for target in targets: - assert target.name is not None - # assert "Test" in target.name # TODO: find a better invariant - new_target = example_fixed_target(program_token, instrument, test_run_id) +def test_get_targets(program_facilities: list[CFHTFacility]): + for facility in program_facilities: + targets = facility.targets() + assert isinstance(targets, list) + assert all(target.name is not None for target in targets) - # This is the part in the example.py where the target is assigned an - # incorrect format for RA and sent to the API. We can't get that far in Aeonlib - # because Pydantic will raise a Validation Error before it can be sumitted. - - assert new_target.fixed_target is not None - assert new_target.fixed_target.coordinate is not None - assert new_target.fixed_target.coordinate.ra is not None - old_ra = new_target.fixed_target.coordinate.ra - with pytest.raises(ValidationError): - new_target.fixed_target.coordinate.ra = "invalid" # type: ignore - assert new_target.fixed_target.coordinate.ra == old_ra - - # Successfully create new target with instrument-specific checks - new_target.fixed_target.coordinate.ra = 20.7 - mag = TargetDataMagnitude() - setattr(mag, required_mag_by_instrument[instrument], DoubleValue(value=10.0)) - new_target.magnitude = mag - old_version = new_target.version if new_target.version is not None else 0 + +@pytest.mark.side_effect +def test_create_fixed_targets(program_facilities: list[CFHTFacility], test_run_id: str): + for facility, program_token, instrument in program_instruments(program_facilities): + new_target = example_fixed_target(program_token, instrument, test_run_id) + target = facility.create_or_update_target(new_target, instrument) + try: + assert target.version == 1 + finally: + if target.token is not None: + facility.delete_target(target.token) + + +@pytest.mark.side_effect +def test_create_moving_targets( + program_facilities: list[CFHTFacility], test_run_id: str +): + for facility, program_token, instrument in program_instruments(program_facilities): + new_target = example_moving_target(program_token, instrument, test_run_id) + target = facility.create_or_update_target(new_target, instrument) + try: + assert target.version == 1 + finally: + if target.token is not None: + facility.delete_target(target.token) + + +@pytest.mark.side_effect +def test_get_target(program_facilities: list[CFHTFacility], test_run_id: str): + for facility, program_token, instrument in program_instruments(program_facilities): + new_target = example_fixed_target(program_token, instrument, test_run_id) + target = facility.create_or_update_target(new_target, instrument) + try: + assert target.token is not None + fetched_target = facility.get_target(target.token) + assert fetched_target.token == target.token + finally: + if target.token is not None: + facility.delete_target(target.token) + + +@pytest.mark.side_effect +def test_delete_target(program_facilities: list[CFHTFacility], test_run_id: str): + for facility, program_token, instrument in program_instruments(program_facilities): + new_target = example_fixed_target(program_token, instrument, test_run_id) + target = facility.create_or_update_target(new_target, instrument) + assert target.token is not None + facility.delete_target(target.token) + with pytest.raises(EntityNotFoundError): + facility.get_target(target.token) + + +@pytest.mark.side_effect +def test_update_fixed_targets(program_facilities: list[CFHTFacility], test_run_id: str): + for facility, program_token, instrument in program_instruments(program_facilities): + new_target = example_fixed_target(program_token, instrument, test_run_id) + target = facility.create_or_update_target(new_target, instrument) + try: + assert target.version == 1 + updated_name = f"updates aeonlib target {test_run_id}" + target.name = updated_name + target = facility.create_or_update_target(target, instrument) + assert target.name == updated_name + assert target.version == 2 + finally: + if target.token is not None: + facility.delete_target(target.token) + + +@pytest.mark.side_effect +def test_target_bad_version(program_facilities: list[CFHTFacility], test_run_id: str): + facility, program_token, instrument = next(program_instruments(program_facilities)) + new_target = example_fixed_target(program_token, instrument, test_run_id) target = facility.create_or_update_target(new_target, instrument) - assert target.version == old_version + 1 - old_version += 1 - - # Update target - update_target = target - update_target.standard_star = True - target = facility.create_or_update_target(update_target, instrument) - assert target.standard_star is True - assert target.version == old_version + 1 - - # Attempt to update target with invalid version - # TODO should aeonlib handle versioning client side? - with pytest.raises(VersionMismatchError): - update_target.temperature_effective = 2345.6 - facility.create_or_update_target(update_target, instrument) - - # Fetch single target - token = target.token - assert token, "target token should not be None" - target = facility.get_target(token) - assert token == target.token - - # Delete the new (unobserved) target - assert target.token - facility.delete_target(target.token) - with pytest.raises(EntityNotFoundError): - facility.get_target(target.token) - - # create moving target - new_moving_target = example_moving_target(program_token, instrument, test_run_id) - moving_target = facility.create_or_update_target(new_moving_target, instrument) - - # Delete moving target - assert moving_target.token - facility.delete_target(moving_target.token) - with pytest.raises(EntityNotFoundError): - facility.get_target(moving_target.token) - - -def test_example(facility: CFHTFacility): - test_run_id = f"{uuid.uuid4()}"[:8] - programs = facility.programs() - for program in programs: - facility.select_program(program) - program_data = program.program_data - assert program_data is not None - time_allocation = program_data.time_allocation - assert time_allocation is not None - instrument = time_allocation[0].instrument - assert instrument is not None - target_api_examples(facility, instrument, test_run_id) + try: + assert target.version == 1 + target.version = 0 + with pytest.raises(VersionMismatchError): + facility.create_or_update_target(target, instrument) + finally: + if target.token is not None: + facility.delete_target(target.token) From 2cf4d21f36ec93cc8b7fbd70312039e4e3e9047d Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Wed, 12 Aug 2026 10:57:39 -0700 Subject: [PATCH 22/33] Use online OAS3 spec instead of downloaded file --- codegen/cfht/cfhtopenapi.yaml | 3814 --------------------------------- codegen/cfht/generator.py | 10 +- src/aeonlib/cfht/models.py | 93 +- 3 files changed, 81 insertions(+), 3836 deletions(-) delete mode 100644 codegen/cfht/cfhtopenapi.yaml diff --git a/codegen/cfht/cfhtopenapi.yaml b/codegen/cfht/cfhtopenapi.yaml deleted file mode 100644 index 22fb9d9..0000000 --- a/codegen/cfht/cfhtopenapi.yaml +++ /dev/null @@ -1,3814 +0,0 @@ -# Generated with protoc-gen-openapi -# https://github.com/google/gnostic/tree/master/cmd/protoc-gen-openapi - -openapi: 3.0.3 -info: - title: Kealahou API - version: 1.0.0 -paths: - /pointing_offset: - get: - tags: - - PointingOffsetService - operationId: PointingOffsetService_ListPointingOffset - parameters: - - name: runid - in: query - description: Set this to limit scope to custom pointing offsets for the specified science program - schema: - type: string - - name: instrument - in: query - description: Set this to scope to system-provided pointing offsets for the specified instrument - schema: - enum: - - UNKNOWN - - CFH12K - - MEGACAM - - WIRCAM - - ESPADONS - - SITELLE - - SPIROU - - WENAOKEAO - type: string - format: enum - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/PointingOffsetListResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /pointing_offset/user: - get: - tags: - - PointingOffsetService - operationId: PointingOffsetService_GetPointingOffset - parameters: - - name: runid - in: query - description: Unique identifier your science program. - schema: - type: string - - name: name - in: query - schema: - type: string - - name: offset.ra_offset - in: query - description: Right ascension, in degrees - schema: - type: number - format: double - - name: offset.dec_offset - in: query - description: Declination, in degrees - schema: - type: number - format: double - - name: offset.exposure_number - in: query - schema: - type: integer - format: int64 - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/PointingOffsetGetResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /pointing_offset/{token}: - put: - tags: - - PointingOffsetService - operationId: PointingOffsetService_CreateOrUpdatePointingOffset - parameters: - - name: token - in: path - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PointingOffsetUpdateRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/PointingOffsetUpdateResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - delete: - tags: - - PointingOffsetService - operationId: PointingOffsetService_DeletePointingOffset - parameters: - - name: token - in: path - required: true - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/PointingOffsetDeleteResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /programs: - get: - tags: - - ProgramService - operationId: ProgramService_ListPrograms - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/ListProgramsResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /programs/{runid}/comment: - put: - tags: - - ProgramService - operationId: ProgramService_UpdateProgramComment - parameters: - - name: runid - in: path - description: Unique identifier for your science program - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateProgramCommentRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateProgramCommentResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /programs/{runid}/comments: - put: - tags: - - ProgramService - operationId: ProgramService_UpdateProgramComments - parameters: - - name: runid - in: path - description: Unique identifier for your science program - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateProgramCommentsRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateProgramCommentsResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /programs/{runid}/observing-groups: - get: - tags: - - ObservingGroupService - operationId: ObservingGroupService_ListObservingGroups - parameters: - - name: runid - in: path - description: Unique identifier of the science program. - required: true - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/ObservingGroupListResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /programs/{runid}/observing-groups/{token}: - put: - tags: - - ObservingGroupService - operationId: ObservingGroupService_CreateOrUpdateObservingGroup - parameters: - - name: runid - in: path - description: Unique identifier of the science program. - required: true - schema: - type: string - - name: token - in: path - description: Unique identifier of the observing group. If an observing group target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789 - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ObservingGroupUpdateRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/ObservingGroupUpdateResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - delete: - tags: - - ObservingGroupService - operationId: ObservingGroupService_DeleteObservingGroup - parameters: - - name: runid - in: path - description: Unique identifier of the science program. - required: true - schema: - type: string - - name: token - in: path - description: Unique identifier of the observing group. - required: true - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/ObservingGroupDeleteResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /programs/{runid}/observing-templates: - get: - tags: - - ObservingTemplateService - operationId: ObservingTemplateService_ListObservingTemplates - parameters: - - name: runid - in: path - description: Unique identifier of the science program. - required: true - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/ObservingTemplateListResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /programs/{runid}/observing-templates/{token}: - put: - tags: - - ObservingTemplateService - operationId: ObservingTemplateService_CreateOrUpdateObservingTemplate - parameters: - - name: runid - in: path - required: true - schema: - type: string - - name: token - in: path - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ObservingTemplateUpdateRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/ObservingTemplateUpdateResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - delete: - tags: - - ObservingTemplateService - operationId: ObservingTemplateService_DeleteObservingTemplate - parameters: - - name: runid - in: path - required: true - schema: - type: string - - name: token - in: path - required: true - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/ObservingTemplateDeleteResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /programs/{runid}/snr-blocks: - get: - tags: - - ObservingGroupService - operationId: ObservingGroupService_ListSnrBlocks - parameters: - - name: runid - in: path - description: Unique identifier of the science program. - required: true - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/SnrBlockListResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /programs/{runid}/snr-blocks/{snr_block_token}: - put: - tags: - - ObservingGroupService - operationId: ObservingGroupService_AddSingleToSnrBlock - parameters: - - name: runid - in: path - description: Unique identifier of the science program. - required: true - schema: - type: string - - name: snr_block_token - in: path - description: Unique identifier of an existing SNR block to create the new observing group for. - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/AddSingleToSnrBlockRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/AddSingleToSnrBlockResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /programs/{runid}/targets: - get: - tags: - - TargetService - operationId: TargetService_ListTarget - parameters: - - name: runid - in: path - description: Unique identifier for the science program. - required: true - schema: - type: string - - name: target_type - in: query - description: Filter the returned targets to only include the type specified. - schema: - enum: - - ALL - - FIXED - - MOVING - type: string - format: enum - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/TargetListResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /programs/{runid}/targets/{token}: - get: - tags: - - TargetService - operationId: TargetService_ShowTarget - parameters: - - name: runid - in: path - description: Unique identifier of the science program. - required: true - schema: - type: string - - name: token - in: path - description: Unique identifier of the target. - required: true - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/TargetShowResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - put: - tags: - - TargetService - operationId: TargetService_CreateOrUpdateTarget - parameters: - - name: runid - in: path - description: Unique identifier of the science program. - required: true - schema: - type: string - - name: token - in: path - description: Unique identifier of the target. If a target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789 - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/TargetUpdateRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/TargetUpdateResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - delete: - tags: - - TargetService - operationId: TargetService_DeleteTarget - parameters: - - name: runid - in: path - description: Unique identifier of the science program. - required: true - schema: - type: string - - name: token - in: path - description: Unique identifier of the target. - required: true - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/TargetDeleteResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /programs/{runid}/targets/{token}/ephemerides: - put: - tags: - - TargetService - operationId: TargetService_UpdateEphemerides - parameters: - - name: runid - in: path - description: Unique identifier of the science program. - required: true - schema: - type: string - - name: token - in: path - description: Unique identifier of the target. - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/EphemeridesUpdateRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/EphemeridesUpdateResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /telescope_pattern: - get: - tags: - - ObservingTemplateService - operationId: ObservingTemplateService_ListTelescopePattern - parameters: - - name: runid - in: query - schema: - type: string - - name: instrument - in: query - schema: - enum: - - UNKNOWN - - CFH12K - - MEGACAM - - WIRCAM - - ESPADONS - - SITELLE - - SPIROU - - WENAOKEAO - type: string - format: enum - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/TelescopePatternListResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /telescope_pattern/{token}: - put: - tags: - - ObservingTemplateService - operationId: ObservingTemplateService_CreateOrUpdateTelescopePattern - parameters: - - name: token - in: path - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/TelescopePatternUpdateRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/TelescopePatternUpdateResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - delete: - tags: - - ObservingTemplateService - operationId: ObservingTemplateService_DeleteTelescopePattern - parameters: - - name: token - in: path - required: true - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/TelescopePatternDeleteResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' -components: - schemas: - AddSingleToSnrBlockRequest: - type: object - properties: - runid: - type: string - description: Unique identifier of the science program. - snr_block_token: - type: string - description: Unique identifier of an existing SNR block to create the new observing group for. - observing_group_token: - type: string - description: Unique identifier of the observing group. If an observing group target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789 - AddSingleToSnrBlockResponse: - type: object - properties: - success: - type: boolean - error: - $ref: '#/components/schemas/Errors' - snr_block: - $ref: '#/components/schemas/SnrBlockData' - observing_group: - $ref: '#/components/schemas/ObservingGroupData' - AllocationData: - type: object - properties: - run_id: - type: string - time_allocated_millis: - type: integer - format: int64 - tac_rank: - type: integer - format: int32 - tac_grade: - type: string - instrument: - enum: - - UNKNOWN - - CFH12K - - MEGACAM - - WIRCAM - - ESPADONS - - SITELLE - - SPIROU - - WENAOKEAO - type: string - format: enum - agency: - $ref: '#/components/schemas/AllocationData_Agency' - cfht_rank: - type: integer - format: int32 - AllocationData_Agency: - type: object - properties: - letter: - type: string - name: - type: string - BigInteger: - type: object - properties: - value: - type: string - description: Used for primary database fields in sybase Should be parsed as a BigInteger - CatalogIdentifier: - type: object - properties: - catalog: - type: string - description: Catalog to reference. - identifier: - type: string - description: 'Valid identifier in the catalog. Note: this ID will contain a catalog name, because spacing matters in catalogs, the full ID for an object is like M 4. Catalog is M, id is identifier could be 4, but to reference m4, you need the spaces. NB: These identity values can not be set in fits headers because they do not have to conform to the length restrictions.' - description: Ties to some external system. A string representation can take the form of : - Config_Dithering: - type: object - properties: - is_staring: - type: boolean - Config_Nodding: - type: object - properties: - start_sequence: - enum: - - OBJECT - - TARGET - - SKY - - DARK - - FLAT - - SNAP - - BIAS - - FOCUS - - ALIGN - - COMPARISON - - ACQUIRE - - FABRY_PEROT - - TWILIGHT_FLATS - - PHOTOMETRY - - SFOCUS - type: string - format: enum - target_db: - type: number - format: double - Constraint: - type: object - properties: - image_quality: - $ref: '#/components/schemas/DoubleMinMax' - sky_background_max: - type: number - format: double - airmass_max: - type: number - format: double - extinction_max: - type: number - format: double - moon_distance_arc_length_min: - type: number - format: double - photometric: - type: boolean - name: - type: string - sybase_cons_id: - $ref: '#/components/schemas/BigInteger' - h2o_vapor_max: - type: number - format: double - read_sky_background_name: - type: string - sky_background_name: - type: string - description: Part of observing_template_data - not stored on any table individually - DoubleMinMax: - type: object - properties: - min: - type: number - format: double - max: - type: number - format: double - DoubleValue: - type: object - properties: - value: - type: number - description: The double value. - format: double - description: Wrapper message for `double`. The JSON representation for `DoubleValue` is JSON number. Not recommended for use in new APIs, but still useful for legacy APIs and has no plan to be removed. - Duration: - type: object - properties: - millis: - type: integer - format: int64 - display_unit: - enum: - - DAYS - - HOURS - - MINUTES - - SECONDS - type: string - description: Only needed for rendering UI, should display the duration using the units specified. If unset, assume DAYS - format: enum - DynamicExposureTimeLimit: - type: object - properties: - minimum_multiplier: - type: number - format: float - maximum_multiplier: - type: number - format: float - EphemeridesUpdateRequest: - type: object - properties: - runid: - type: string - description: Unique identifier of the science program. - token: - type: string - description: Unique identifier of the target. - ephemeris: - type: array - items: - $ref: '#/components/schemas/MovingTarget_Ephemeris' - description: List of ephemerides to set on the target. - EphemeridesUpdateResponse: - type: object - properties: - success: - type: boolean - error: - $ref: '#/components/schemas/Errors' - entity: - $ref: '#/components/schemas/TargetData' - Errors: - type: object - properties: - message: - type: array - items: - type: string - EspadonsStatus_ExposureMeterProcessingResult: - type: object - properties: - snr: - type: number - format: double - EspadonsStatus_GuiderSeeingProcessingResult: - type: object - properties: - guider_seeing: - type: number - format: double - EspadonsStatus_UpenaProcessingResult: - type: object - properties: - iq: - type: number - format: double - h2o_column_density_from_telluric_lines: - type: number - format: double - radial_velocity_corrections: - type: number - format: double - snr_per_ccd_bin: - type: number - format: double - snr_per_spec_bin: - type: number - format: double - actual_wavelength: - type: number - format: double - snr_per_ccd_bin_polar: - type: number - format: double - snr_per_spec_bin_polar: - type: number - format: double - h2o_mm_data: - $ref: '#/components/schemas/UpenaProcessingResult_h2o_mm' - ExposureData: - type: object - properties: - token: - type: string - obsid: - type: string - exposure_number: - type: integer - format: int32 - iteration: - type: integer - format: int32 - parent_exposure_token: - type: string - exposure_status: - $ref: '#/components/schemas/ExposureData_ExposureStatus' - observing_template: - $ref: '#/components/schemas/ObservingTemplateData' - target: - $ref: '#/components/schemas/TargetData' - observing_group_context: - $ref: '#/components/schemas/ObservingGroupContext' - all_derivations_complete: - type: boolean - description: When false, there is data which is still being calculated for this exposure. Data can still be displayed, but the client should attempt to refresh the data. - queue_run_id: - type: string - camera_run_id: - type: string - prg_run_id: - type: string - observing_component_token: - type: string - sub_totals: - $ref: '#/components/schemas/SubTotalsData' - attribution: - $ref: '#/components/schemas/ExposureData_Attribution' - version: - type: integer - description: 'Increments when this object updates storage: transient api_visibility: read only, incremented by the server' - format: int32 - ExposureData_Attribution: - type: object - properties: - runid: - type: string - observing_block_display_label: - type: string - description: If displaying the observing block to the user, this uniquely identifies it in the context of an observing group, i.e. OG3-2 - observing_block_label: - type: integer - description: Label of the observing block - format: int32 - observing_block_token: - type: string - description: Token of the observing block this exposure is attributed to. - observing_group_label: - type: integer - description: Label of the observing group this exposure is attributed to. - format: int32 - observing_group_token: - type: string - description: Token of the observing group this exposure is attributed to. - observing_component_token: - type: string - description: Token of the observing component this exposure is attributed to. - exposure_number: - type: integer - description: exposure number this exposure is attributed to. - format: int32 - queue_run: - type: string - description: the queue run this exposure was taken under - attributing_agency: - type: string - description: the agency id - tac_rank: - type: integer - format: int32 - tac_grade: - type: string - ExposureData_EspadonsStatus: - type: object - properties: - instr_mode: - type: string - actual_wavelength: - type: number - format: double - snr_exp_meter: - type: number - format: double - snr_wave_meas_int: - type: number - format: double - snr_wave_meas_pol: - type: number - format: double - snr_meas_int_per_ccd_bin: - type: number - format: double - snr_meas_int_per_spec_bin: - type: number - format: double - snr_meas_polar_ccd_bin: - type: number - format: double - snr_meas_polar_spec_bin: - type: number - format: double - trg_type: - type: string - guider_seeing: - type: number - format: double - guider_seeing_processing_results: - $ref: '#/components/schemas/EspadonsStatus_GuiderSeeingProcessingResult' - exposure_meter_processing_results: - $ref: '#/components/schemas/EspadonsStatus_ExposureMeterProcessingResult' - upena_processing_results: - $ref: '#/components/schemas/EspadonsStatus_UpenaProcessingResult' - observation_type: - type: string - ExposureData_ExposureStatus: - type: object - properties: - megacam_status: - $ref: '#/components/schemas/ExposureData_MegacamStatus' - wircam_status: - $ref: '#/components/schemas/ExposureData_WircamStatus' - espadons_status: - $ref: '#/components/schemas/ExposureData_EspadonsStatus' - sitelle_status: - $ref: '#/components/schemas/ExposureData_SitelleStatus' - spirou_status: - $ref: '#/components/schemas/ExposureData_SpirouStatus' - wena_status: - $ref: '#/components/schemas/ExposureData_WenaStatus' - exposure_time: - $ref: '#/components/schemas/ExposureTime' - exp_date_mjd: - type: number - format: double - queue_name: - type: string - exp_type: - type: string - exp_status: - enum: - - UNOBSERVED - - OBSERVED - - PROCESSED - - GRADED - - VALIDATED - - REJECTED - type: string - format: enum - is_photometric: - type: boolean - obs_comment: - type: string - seq_comment: - type: string - actual_pointing: - $ref: '#/components/schemas/SkyCoordinate' - actual_sky: - type: number - format: double - actual_airmass: - type: number - format: double - actual_moondist: - type: number - format: double - observer_comment: - type: string - qc_comment: - type: string - file: - $ref: '#/components/schemas/ExposureStatus_File' - credited_qcoordinator: - type: string - credited_observer: - type: string - grade: - type: integer - format: int32 - read_obs_date_utc: - type: string - read_obs_date_hst: - type: string - read_actual_sky_name: - type: string - ra: - type: string - description: Values from headers - dec: - type: string - cmmtobs: - type: string - cmmtseq: - type: string - pi_name: - type: string - nexp: - type: string - cmpltexp: - type: string - ha: - type: string - read_obs_date_millis: - type: integer - format: int64 - object: - type: string - ExposureData_MegacamStatus: - type: object - properties: - iq: - type: number - format: double - skybg: - type: number - format: double - elixir_eval: - type: integer - format: int32 - elixir_seval: - type: string - snr: - type: number - format: double - atmospheric_transmission: - type: number - format: double - elixir_zeropoint: - type: number - format: double - actual_filter: - type: string - elixir_processing_result: - $ref: '#/components/schemas/MegacamStatus_ElixirProcessingResult' - ExposureData_SitelleStatus: - type: object - properties: - camera_a: - $ref: '#/components/schemas/SitelleStatus_Camera' - camera_b: - $ref: '#/components/schemas/SitelleStatus_Camera' - relative_extinction: - type: number - format: double - relative_extinction_rms: - type: number - format: double - actual_filter: - type: string - ExposureData_SpirouStatus: - type: object - properties: - acqtime: - type: number - format: double - acqtime1: - type: string - asic_num: - type: string - sca_id: - type: string - muxtype: - type: integer - format: int32 - noutputs: - type: integer - format: int32 - nadcs: - type: integer - format: int32 - pddector: - type: integer - format: int32 - clkoff: - type: integer - format: int32 - warmtst: - type: integer - format: int32 - clocking: - type: integer - format: int32 - glbreset: - type: integer - format: int32 - frmode: - type: integer - format: int32 - camlink: - type: integer - format: int32 - expmode: - type: integer - format: int32 - nresets: - type: integer - format: int32 - frmtime: - type: number - format: double - exptime: - type: number - format: double - acqtype: - type: integer - format: int32 - datamode: - type: integer - format: int32 - datlevel: - type: integer - format: int32 - asicgain: - type: integer - format: int32 - nomgain: - type: integer - format: int32 - ampreset: - type: integer - format: int32 - ktcremov: - type: integer - format: int32 - srccur: - type: integer - format: int32 - ampinput: - type: integer - format: int32 - v4v3v2v1: - type: string - units: - type: string - tstation: - type: string - hxrgver: - type: string - mclk: - type: number - format: double - inttime: - type: number - format: double - frameno: - type: integer - format: int32 - sb_adc_s: - type: string - sbadc1_p: - type: number - format: double - sbadc2_p: - type: number - format: double - sbcali_p: - type: string - sbdens_p: - type: number - format: double - sbrhb1_p: - type: string - sbrhb2_p: - type: string - sbetem_p: - type: string - sb_inj_t: - type: number - format: double - sb_pol_t: - type: number - format: double - sb_cs1_t: - type: number - format: double - sb_cs2_t: - type: number - format: double - sb_cb1_t: - type: number - format: double - sb_cb2_t: - type: number - format: double - sbiavl1s: - type: string - sbiavl2s: - type: string - sbetel_s: - type: string - sb_csc_s: - type: string - sbcref_p: - type: string - sbccas_p: - type: string - sbcden_p: - type: number - format: double - sbagit_s: - type: string - sbcmir_p: - type: string - sbclhc1s: - type: string - sbclhc1w: - type: string - sbclhc1c: - type: number - format: double - sbclhc2s: - type: string - sbclhc2w: - type: string - sbclhc2c: - type: number - format: double - sbclwl_s: - type: string - sbclwl_c: - type: number - format: double - sbwlsh_s: - type: string - sbctec_s: - type: string - sbctecct: - type: number - format: double - sbctecht: - type: number - format: double - sbccprit: - type: number - format: double - sbcccu_t: - type: number - format: double - sbccprot: - type: number - format: double - sbcfbi_t: - type: number - format: double - sbcwls_t: - type: number - format: double - sbclsuat: - type: number - format: double - sbcfpi_t: - type: number - format: double - sbcfpe_t: - type: number - format: double - sbcfpb_p: - type: number - format: double - sbvga_cp: - type: number - format: double - sbvga_ip: - type: number - format: double - sbvgb_cp: - type: number - format: double - sbvgb_ip: - type: number - format: double - sbmk00_t: - type: number - format: double - sbmk01_t: - type: number - format: double - sbmk02_t: - type: number - format: double - sbmk03_t: - type: number - format: double - sbmk04_t: - type: number - format: double - sbmk05_t: - type: number - format: double - sbmk06_t: - type: number - format: double - sbmk07_t: - type: number - format: double - sbls000t: - type: number - format: double - sbls001t: - type: number - format: double - sbls002t: - type: number - format: double - sbls003t: - type: number - format: double - sbls004t: - type: number - format: double - sbls005t: - type: number - format: double - sbls006t: - type: number - format: double - sbls007t: - type: number - format: double - sbls008t: - type: number - format: double - sbls009t: - type: number - format: double - sbls010t: - type: number - format: double - sbls011t: - type: number - format: double - sbls100t: - type: number - format: double - sbls101t: - type: number - format: double - sbls102t: - type: number - format: double - sbls103t: - type: number - format: double - sbls104t: - type: number - format: double - sbls105t: - type: number - format: double - sbls106t: - type: number - format: double - sbls107t: - type: number - format: double - sbls108t: - type: number - format: double - sbls109t: - type: number - format: double - sbls110t: - type: number - format: double - sbls111t: - type: number - format: double - sbl00i_t: - type: number - format: double - sbl00h_v: - type: number - format: double - sbl01i_t: - type: number - format: double - sbl01h_v: - type: number - format: double - sbl02i_t: - type: number - format: double - sbl02h_v: - type: number - format: double - sbl03i_t: - type: number - format: double - sbl03h_v: - type: number - format: double - sbl04i_t: - type: number - format: double - sbl04h_v: - type: number - format: double - sbl05i_t: - type: number - format: double - sbl05h_v: - type: number - format: double - sbl06i_t: - type: number - format: double - sbl06h_v: - type: number - format: double - sbl07i_t: - type: number - format: double - sbl07h_v: - type: number - format: double - sbl08i_t: - type: number - format: double - sbl08h_v: - type: number - format: double - sbl09i_t: - type: number - format: double - sbl09h_v: - type: number - format: double - sbl10i_t: - type: number - format: double - sbl10h_v: - type: number - format: double - sbl11i_t: - type: number - format: double - sbl11h_v: - type: number - format: double - sbl12i_t: - type: number - format: double - sbl12h_v: - type: number - format: double - sbl13i_t: - type: number - format: double - sbl13h_v: - type: number - format: double - seadc1_p: - type: number - format: double - seadc2_p: - type: number - format: double - sedens_p_deprecated: - type: integer - format: int32 - sedens_p: - type: number - format: double - se_inj_t: - type: number - format: double - se_pol_t: - type: number - format: double - se_cs1_t: - type: number - format: double - se_cs2_t: - type: number - format: double - se_cb1_t: - type: number - format: double - se_cb2_t: - type: number - format: double - sectecct: - type: number - format: double - sectecht: - type: number - format: double - seccprit: - type: number - format: double - secccu_t: - type: number - format: double - seccprot: - type: number - format: double - secfbi_t: - type: number - format: double - secwls_t: - type: number - format: double - seclhc1c: - type: number - format: double - seclhc2c: - type: number - format: double - seclwl_c: - type: number - format: double - seclsuat: - type: number - format: double - secfpi_t: - type: number - format: double - secfpe_t: - type: number - format: double - secfpb_p: - type: number - format: double - sevga_cp: - type: number - format: double - sevga_ip: - type: number - format: double - sevgb_cp: - type: number - format: double - sevgb_ip: - type: number - format: double - semk00_t: - type: number - format: double - semk01_t: - type: number - format: double - semk02_t: - type: number - format: double - semk03_t: - type: number - format: double - semk04_t: - type: number - format: double - semk05_t: - type: number - format: double - semk06_t: - type: number - format: double - semk07_t: - type: number - format: double - sels000t: - type: number - format: double - sels001t: - type: number - format: double - sels002t: - type: number - format: double - sels003t: - type: number - format: double - sels004t: - type: number - format: double - sels005t: - type: number - format: double - sels006t: - type: number - format: double - sels007t: - type: number - format: double - sels008t: - type: number - format: double - sels009t: - type: number - format: double - sels010t: - type: number - format: double - sels011t: - type: number - format: double - sels100t: - type: number - format: double - sels101t: - type: number - format: double - sels102t: - type: number - format: double - sels103t: - type: number - format: double - sels104t: - type: number - format: double - sels105t: - type: number - format: double - sels106t: - type: number - format: double - sels107t: - type: number - format: double - sels108t: - type: number - format: double - sels109t: - type: number - format: double - sels110t: - type: number - format: double - sels111t: - type: number - format: double - sel00i_t: - type: number - format: double - sel00h_v: - type: number - format: double - sel01i_t: - type: number - format: double - sel01h_v: - type: number - format: double - sel02i_t: - type: number - format: double - sel02h_v: - type: number - format: double - sel03i_t: - type: number - format: double - sel03h_v: - type: number - format: double - sel04i_t: - type: number - format: double - sel04h_v: - type: number - format: double - sel05i_t: - type: number - format: double - sel05h_v: - type: number - format: double - sel06i_t: - type: number - format: double - sel06h_v: - type: number - format: double - sel07i_t: - type: number - format: double - sel07h_v: - type: number - format: double - sel08i_t: - type: number - format: double - sel08h_v: - type: number - format: double - sel09i_t: - type: number - format: double - sel09h_v: - type: number - format: double - sel10i_t: - type: number - format: double - sel10h_v: - type: number - format: double - sel11i_t: - type: number - format: double - sel11h_v: - type: number - format: double - sel12i_t: - type: number - format: double - sel12h_v: - type: number - format: double - sel13i_t: - type: number - format: double - sel13h_v: - type: number - format: double - guider_processing: - $ref: '#/components/schemas/SpirouStatus_GuiderProcessingResult' - exposure_meter_processing: - $ref: '#/components/schemas/SpirouStatus_ExposureMeterProcessingResult' - pipeline_processing: - $ref: '#/components/schemas/SpirouStatus_PipelineProcessingResult' - ExposureData_WenaStatus: - type: object - properties: {} - ExposureData_WircamStatus: - type: object - properties: - iq_uncertainty: - type: number - format: double - absorption: - type: number - format: double - absorption_uncertainty: - type: number - format: double - number_of_stars_iq: - type: integer - format: int32 - number_of_stars_absorption: - type: integer - format: int32 - absolute_sky_level: - type: number - format: double - elongation: - type: number - format: double - mdcoords: - type: integer - format: int32 - mdrepeat: - type: integer - format: int32 - actual_filter: - type: string - iq: - type: number - format: double - ExposureStatus_File: - type: object - properties: - absolute_path: - type: string - description: Absolute path to directory file is homed in. Starts with / and ends with /. - base_name: - type: string - description: Name of file without extension - ext: - type: string - description: Name of extenion including . - ExposureTime: - type: object - properties: - desired_snr: - type: number - format: double - exposure_time_ms: - type: integer - format: int64 - FindingChartData: - type: object - properties: - comment: - type: string - url: - type: string - token: - type: string - staff_approval: - $ref: '#/components/schemas/FindingChartData_Approval' - pi_approval: - $ref: '#/components/schemas/FindingChartData_Approval' - target_token: - type: string - FindingChartData_Approval: - type: object - properties: - approved_at_millis: - type: integer - format: int64 - approved_by_usertoken: - type: string - FixedTarget_ProperMotion: - type: object - properties: - ra_mas: - type: number - description: Motion in the right ascension axis in milliarcseconds/year. - format: double - dec_mas: - type: number - description: Motion in the declination axis in milliarcseconds/year. - format: double - description: The rate of change in angular coordinates for the target. - GoogleProtobufAny: - type: object - properties: - '@type': - type: string - description: The type of the serialized message. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. - ITimeData: - type: object - properties: - exposure_time_millis: - type: integer - format: int64 - overhead_time_millis: - type: integer - format: int64 - InstrumentConfigurationData: - type: object - properties: - espadons_configuration: - $ref: '#/components/schemas/InstrumentConfigurationData_EspadonsConfigurationData' - megacam_configuration: - $ref: '#/components/schemas/InstrumentConfigurationData_MegacamConfigurationData' - wircam_configuration: - $ref: '#/components/schemas/InstrumentConfigurationData_WircamConfigurationData' - sitelle_configuration: - $ref: '#/components/schemas/InstrumentConfigurationData_SitelleConfigurationData' - spirou_configuration: - $ref: '#/components/schemas/InstrumentConfigurationData_SpirouConfigurationData' - wena_configuration: - $ref: '#/components/schemas/InstrumentConfigurationData_WenaConfigurationData' - sybase_ic_id: - $ref: '#/components/schemas/BigInteger' - etime_or_snr: - $ref: '#/components/schemas/ExposureTime' - sybase_cons_id: - $ref: '#/components/schemas/BigInteger' - tracking: - enum: - - Unknown - - SID - - NONSID - - SID_NOG - - NONSID_G - type: string - format: enum - description: Part of observing_template_data - not stored on any table individually - InstrumentConfigurationData_EspadonsConfigurationData: - type: object - properties: - observing_mode: - enum: - - Polarimetry - - Spectroscopy_star_only - - Spectroscopy_star_sky - type: string - format: enum - readout_mode: - enum: - - Normal - - Slow - - Fast - type: string - format: enum - stokes_parameter: - enum: - - Q - - U - - V - - W - - I - type: string - format: enum - number_of_sequences: - type: integer - format: int32 - number_of_exposures: - type: integer - format: int32 - use_snr: - type: boolean - snr_wave: - type: number - format: double - snr: - type: number - format: double - InstrumentConfigurationData_MegacamConfigurationData: - type: object - properties: - filter: - enum: - - Unknown - - u - - g - - i - - r - - z - - gri - - CaHK - - Ha - - HaOFF - - OIII - - OIIIOFF - - uS - - gS - - rS - - iS - - zS - - N393S - - HaS - - HaOFFS - - TiOS - - CNS - - OIIIS - - PHGS - - M4112 - - M4376 - type: string - format: enum - binning: - enum: - - one_one - - two_two - - three_three - - four_four - type: string - format: enum - is_dynamic_exposure_time: - type: boolean - description: 'TODO: is this needed billy? Can we use presence of snr or not to decide? I THINK SO. We have the capability to calculate SNR on every exposure, but we aren''t necessarily doing SNR mode observations with everything' - minimum_exposures: - type: integer - format: int32 - min_exptime_ms: - type: integer - format: int64 - max_exptime_ms: - type: integer - format: int64 - magAB: - type: number - description: 'TODO: interesting field here because associated with the target, but in the legacy mp snr mode, the mag is carried with the IC! so have to go back to this :(' - format: double - snr_mode: - enum: - - NONE - - STANDARD - - CUMULATIVE - - ZERO_POINT - type: string - format: enum - etc_photometry: - $ref: '#/components/schemas/MegacamConfigurationData_Photometry' - InstrumentConfigurationData_SitelleConfigurationData: - type: object - properties: - filter: - enum: - - UNKNOWN - - NONE - - C1 - - C2 - - C3 - - C4 - - SN1 - - SN2 - - SN3 - - SN4 - - SN5 - - SN6 - type: string - format: enum - binning: - enum: - - one_one - - two_two - - three_three - type: string - format: enum - resolution: - type: integer - format: int32 - snrwavelength: - type: number - description: This should be on target too , sitell, espedons, spirou - format: double - calibration: - $ref: '#/components/schemas/SitelleConfigurationData_Calibration' - resolution_mode: - enum: - - no_resolution_mode - - hires - - medres - type: string - format: enum - needs_precision: - type: boolean - needs_flux_calibration: - type: boolean - needs_target_calibration: - type: boolean - InstrumentConfigurationData_SpirouConfigurationData: - type: object - properties: - template_name: - type: string - description: Deprecated fields - observing_mode: - enum: - - Polarimetry - - Star - type: string - format: enum - stokes_parameter: - enum: - - Q - - U - - V - - W - - I - type: string - format: enum - mode: - enum: - - Dark - - Fabry_Perot - - Wave_Hc1 - - Wave_Hc2 - - FLAT - type: string - format: enum - sky_observation: - enum: - - None - - Before - - After - - BeforeAndAfter - type: string - format: enum - number_of_sequences: - type: integer - description: Each sequence is 1 exposure for Star mode and 4 exposures for ObservingMode - format: int32 - use_snr: - type: boolean - description: when true - use SNR for stoping exposures - sky_etime_millis: - $ref: '#/components/schemas/Int64Value' - InstrumentConfigurationData_WenaConfigurationData: - type: object - properties: {} - InstrumentConfigurationData_WircamConfigurationData: - type: object - properties: - filter: - enum: - - UNKNOWN - - W - - H - - J - - Ks - - Y - - H2 - - KCont - - CH4On - - CH4Off - - LowOH1 - - LowOH2 - - BrG - - CO - - COnar - type: string - format: enum - is_microdithering: - type: boolean - exposures_per_dp_position: - type: integer - description: Must always be > 0 - format: int32 - accurate_pointing: - type: boolean - defocus: - type: number - format: double - is_staring: - type: boolean - Int32Value: - type: object - properties: - value: - type: integer - description: The int32 value. - format: int32 - description: Wrapper message for `int32`. The JSON representation for `Int32Value` is JSON number. Not recommended for use in new APIs, but still useful for legacy APIs and has no plan to be removed. - Int64Value: - type: object - properties: - value: - type: integer - description: The int64 value. - format: int64 - description: Wrapper message for `int64`. The JSON representation for `Int64Value` is JSON string. Not recommended for use in new APIs, but still useful for legacy APIs and has no plan to be removed. - LegacyReel: - type: object - properties: - reel_og_token: - type: string - description: Token of the linked observing group. - delay: - $ref: '#/components/schemas/Duration' - window: - $ref: '#/components/schemas/Duration' - reel_og_label: - type: integer - description: 'Label of the observing group which must be observed before this one. storage: transient api_visibility: read only' - format: int32 - description: A constraint indicating that a "linked" observing group must be observed before this observing group. - ListOfIdentifiers: - type: object - properties: - catalog_identifier: - type: array - items: - $ref: '#/components/schemas/CatalogIdentifier' - ListProgramsResponse: - type: object - properties: - success: - type: boolean - entity: - type: array - items: - $ref: '#/components/schemas/ProgramInfo' - error: - $ref: '#/components/schemas/Errors' - MegacamConfigurationData_Photometry: - type: object - properties: - aperture: - $ref: '#/components/schemas/Photometry_Aperture' - psf: - type: boolean - MegacamStatus_ElixirProcessingResult: - type: object - properties: - iq: - type: number - format: double - sky_background: - type: number - format: double - snr: - type: number - format: double - zeropoint: - type: number - format: double - MovingTarget_Ephemeris: - type: object - properties: - mjd: - type: number - description: Modified Julian date when the target is at this coordinate. - format: double - coordinate: - $ref: '#/components/schemas/SkyCoordinate' - computed_coordinate: - $ref: '#/components/schemas/SkyCoordinate' - description: A time and corresponding coordinate. - ObservingBlockCondition: - type: object - properties: - pre_requisite: - type: array - items: - $ref: '#/components/schemas/ObservingBlockCondition_Requisite' - description: Pre-requisite conditions before each observing block can be started. The index of items in this list corresponds directly to the index of the observing block on the observing group i.e. this list is one-to-one with read_observing_block. - window: - type: array - items: - $ref: '#/components/schemas/ObservingBlockCondition_Window' - description: Time windows during which the observing block can be observed. Windows can not overlap, and will always be stored in temporal order. - description: Set of requirements that must be met for an observing block to be observed. This is compiled from the observing group configuration details into each of the observing blocks, and used to store the requirements corresponding to each one. - ObservingBlockCondition_Requisite: - type: object - properties: - requisite_observing_block_index: - type: integer - description: This should match the index of the observing block in the read_observing_block list. - format: int32 - wait_period_min_millis: - type: integer - description: Minimum time to wait after requisite observing block exposure has been taken. - format: int64 - wait_period_millis: - type: integer - description: Optimal to wait after requisite observing block exposure has been taken. - format: int64 - wait_period_max_millis: - type: integer - description: Minimum time to wait after requisite observing block exposure has been taken. - format: int64 - unbounded: - type: boolean - description: If this observation can happen any time after the requisite. - description: A constraint indicating that another observing block must be observed before this observing block. - ObservingBlockCondition_Window: - type: object - properties: - observe_after: - type: integer - description: Start of the observation window, in Unix time milliseconds. - format: int64 - observe_before: - type: integer - description: End of the observation window, in Unix time milliseconds. - format: int64 - description: A time window during which an observation can be made. - ObservingBlock_ObservingComponent: - type: object - properties: - target_token: - type: string - description: Unique identifier of the target used in this observing component. When creating or updating an observing group, this must be an existing target, or on the map of the request to be created. - observing_template_token: - type: string - description: Unique identifier of the observing template used in this observing component. When creating or updating an observing group, this must be an existing observing template, or on the map of the request to be created. - target_label: - type: integer - description: 'Label of the target used in this observing component. This value is only set on the read_observing_block. api_visibility: read only' - format: int32 - observing_template_label: - type: integer - description: 'Label of the observing template used in this observing component. This value is only set on the read_observing_block. api_visibility: read only' - format: int32 - snr_block_token: - type: string - description: Unique identifier of the SNR block used in this observing component, if any. Must be set if and only if the observing component uses a MegaCam cumulative SNR mode observing template. If this is set when creating or updating an observing group, the observing component will be tied to the SNR block with this token if it already exists, otherwise a new SNR block will be created using this token. - description: An observing component ties together a target with observation details. Each observing component roughly corresponds with one observation, although this may actually be multiple exposures depending on how the observing template has been configured e.g. for a polar sequence or multiple sequences. When there are multiple exposures in one observing component, they will typically be observed consecutively, in the specified order, however this is not guaranteed, as repeat exposures may need to be taken at a later point due to poor conditions. - ObservingGroupContext: - type: object - properties: - iteration_count: - type: integer - description: The iteration number this object was part of in the observing group - format: int32 - iteration_total: - type: integer - description: The total number of iterations requested for the observing group - format: int32 - iteration_minimum: - type: integer - description: Minimum number of iterations this OG needs to provide science value - format: int32 - window: - type: array - items: - $ref: '#/components/schemas/ObservingBlockCondition_Window' - description: Sorted by from past -> future on the observe_after key - observing_group_token: - type: string - observing_group_priority: - enum: - - UNKNOWN_OG_PRIORITY - - HIGH - - MEDIUM - - LOW - - INACTIVE - type: string - format: enum - observing_group_state: - enum: - - NOT_STARTED - - OBSERVED - - STARTED - - VALIDATED - type: string - format: enum - observing_group_label: - type: integer - format: int32 - observing_group_version: - type: integer - description: Increments every time the observing group changes. - format: int32 - legacy_reel: - $ref: '#/components/schemas/LegacyReel' - single: - type: boolean - target_monitoring: - $ref: '#/components/schemas/TargetMonitoringObservingGroup_TimeConstraint' - phase_schedule: - $ref: '#/components/schemas/PhaseScheduleObservingGroup_PhaseSchedule' - time_accounting: - $ref: '#/components/schemas/TimeAccountingData' - exposureType: - enum: - - OBJECT - - TARGET - - SKY - - DARK - - FLAT - - SNAP - - BIAS - - FOCUS - - ALIGN - - COMPARISON - - ACQUIRE - - FABRY_PEROT - - TWILIGHT_FLATS - - PHOTOMETRY - - SFOCUS - type: string - format: enum - use_group_dither_scheme: - type: boolean - ObservingGroupData: - type: object - properties: - token: - type: string - description: Unique identifier for the observing group. - label: - type: integer - description: 'Index of the observing group within the program, set by the system automatically. Used for reference in communications with QSO staff. storage: transient api_visibility: read only' - format: int32 - og_priority: - enum: - - UNKNOWN_OG_PRIORITY - - HIGH - - MEDIUM - - LOW - - INACTIVE - type: string - description: Priority of observing this observing group, relative to other observing groups within the same program. - format: enum - single_observing_group: - $ref: '#/components/schemas/SingleObservingGroup' - target_monitoring_observing_group: - $ref: '#/components/schemas/TargetMonitoringObservingGroup' - phase_schedule_observing_group: - $ref: '#/components/schemas/PhaseScheduleObservingGroup' - read_observing_block: - type: array - items: - $ref: '#/components/schemas/ObservingGroupData_ObservingBlock' - description: 'The observing blocks that have been created based on the entered configuration. api_visibility: read only' - observing_block_condition: - type: object - additionalProperties: - $ref: '#/components/schemas/ObservingBlockCondition' - description: 'The requirements that have been set for observing each observing blocks based on the entered configuration. api_visibility: read only' - minimum_observing_block_count: - type: integer - description: 'This field is unused, please ignore. api_visibility: read only' - format: int32 - time_accounting: - $ref: '#/components/schemas/TimeAccountingData' - state: - enum: - - NOT_STARTED - - OBSERVED - - STARTED - - VALIDATED - type: string - description: State of the observing group, based off of the time accounting status. - format: enum - version: - type: integer - description: 'Increments when this object updates. storage: transient api_visibility: read only, incremented by the server' - format: int32 - legacy_reel: - $ref: '#/components/schemas/LegacyReel' - time_constraint: - $ref: '#/components/schemas/TimeConstraint' - target_type: - enum: - - OBJECT - - TARGET - - SKY - - DARK - - FLAT - - SNAP - - BIAS - - FOCUS - - ALIGN - - COMPARISON - - ACQUIRE - - FABRY_PEROT - - TWILIGHT_FLATS - - PHOTOMETRY - - SFOCUS - type: string - description: 'This can be used to configure calibration observing groups. For internal use only. api_visibility: operations write only' - format: enum - dither_scheme: - enum: - - NONE - - STANDARD - - GROUP - type: string - description: Used to customize the order of observation when observing multiple targets with the same telescope pattern within this observing group. - format: enum - low_surface_brightness: - type: boolean - description: Indicates the observing group uses low surface brightness (LSB) mode.with - description: An observing group ties together a block of observations with scheduling constraints. It can also contain configuration details for observing multiple iterations of that block. The configuration gets "rendered out" on creation/update into a sequence of observing blocks and corresponding observing block conditions which drive individual observations. - ObservingGroupData_ObservingBlock: - type: object - properties: - observing_component: - type: array - items: - $ref: '#/components/schemas/ObservingBlock_ObservingComponent' - description: List of observing components that comprise this observing block. - token: - type: string - description: 'Unique identifier for the observing block. api_visibility: operations read only' - label: - type: integer - description: 'Index of the observing block within the program, set by the system automatically. Used for reference in communications with QSO staff. storage: transient api_visibility: read only' - format: int32 - description: An observing block is a collection of observations expected to be observed together. These are typically observed consecutively, in the specified order, however this is not guaranteed, as repeat exposures may need to be taken at a later point due to poor conditions. When multiple observations are in the same observing block, this signals that the observations are not of scientific value unless the entire block is successfully completed. For details, see TimeAccountingData. - ObservingGroupDeleteResponse: - type: object - properties: - success: - type: boolean - error: - $ref: '#/components/schemas/Errors' - ObservingGroupListResponse: - type: object - properties: - success: - type: boolean - entity: - type: array - items: - $ref: '#/components/schemas/ObservingGroupData' - error: - $ref: '#/components/schemas/Errors' - ObservingGroupUpdateRequest: - type: object - properties: - runid: - type: string - description: Unique identifier of the science program. - token: - type: string - description: Unique identifier of the observing group. If an observing group target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789 - entity: - $ref: '#/components/schemas/ObservingGroupData' - target: - type: object - additionalProperties: - $ref: '#/components/schemas/TargetData' - description: If creating or updating targets with this request, set the data here. Key is the unique identifier of the target and must be prefixed with the runid. Value is the target data and must be valid. Otherwise, the whole request will fail. Observing components can then reference the unique identifier (the map key) in this same request. - observing_template: - type: object - additionalProperties: - $ref: '#/components/schemas/ObservingTemplateData' - description: If creating or updating observing templates with this request, set the data here. Key is the unique identifier of the observing template and must be prefixed with the runid. Value is the observing template data and must be valid. Otherwise, the whole request will fail. Observing components can then reference the unique identifier (the map key) in this same request. - lock_version: - $ref: '#/components/schemas/Int32Value' - ObservingGroupUpdateResponse: - type: object - properties: - success: - type: boolean - error: - $ref: '#/components/schemas/Errors' - entity: - $ref: '#/components/schemas/ObservingGroupData' - ObservingTemplateData: - type: object - properties: - instrument_configuration: - $ref: '#/components/schemas/InstrumentConfigurationData' - constraint: - $ref: '#/components/schemas/Constraint' - telescope_pattern: - $ref: '#/components/schemas/TelescopePatternData' - charge_model: - type: string - description: Used for calculating cost for this pattern Invokes code which reads the ExposureInstructions and attributes a cost to it. - itime: - type: integer - format: int64 - calculate_etime: - $ref: '#/components/schemas/ExposureTime' - acquisition_time: - $ref: '#/components/schemas/ExposureTime' - token: - type: string - associated_observing_blocks: - type: array - items: - $ref: '#/components/schemas/BigInteger' - description: When just creating an OT, this list is empty When this OT belongs to an OG, then any OBs in sybase that this OT is related too must be filled in. select * from blah where (select id from prg where prg Query to find related OBs is, select ob.* from ob, prg where ob.prg_id = prg.id and prg.runid=? instrument_configuration.sybase_ic_id, constraint.sybase_cons_idselect ob.* from ob, prg, icseq where ob.prg_id = prg.idand prg.runid=and icseq.ob_id=ob.id and ob.con_id = and icseq.ic_id = ; - name: - type: string - total_exposure_count: - type: integer - format: int32 - label: - type: integer - format: int32 - version: - type: integer - description: 'Increments when this object updates storage: transient api_visibility: read only, incremented by the server' - format: int32 - telescope_pattern_token: - type: string - telescope_pattern_scale: - type: number - format: double - megacam_cumulative_snr_system_generated: - type: boolean - description: Stored in observing_templates table in column observing_template_data - ObservingTemplateDeleteResponse: - type: object - properties: - success: - type: boolean - error: - $ref: '#/components/schemas/Errors' - ObservingTemplateListResponse: - type: object - properties: - success: - type: boolean - entity: - type: array - items: - $ref: '#/components/schemas/ObservingTemplateData' - error: - $ref: '#/components/schemas/Errors' - ObservingTemplateUpdateRequest: - type: object - properties: - runid: - type: string - token: - type: string - entity: - $ref: '#/components/schemas/ObservingTemplateData' - lock_version: - $ref: '#/components/schemas/Int32Value' - ObservingTemplateUpdateResponse: - type: object - properties: - success: - type: boolean - error: - $ref: '#/components/schemas/Errors' - entity: - $ref: '#/components/schemas/ObservingTemplateData' - OffsetCoordinate: - type: object - properties: - ra_offset: - type: number - description: Right ascension, in degrees - format: double - dec_offset: - type: number - description: Declination, in degrees - format: double - exposure_number: - type: integer - format: int64 - PhaseScheduleObservingGroup: - type: object - properties: - observing_block: - $ref: '#/components/schemas/ObservingGroupData_ObservingBlock' - phase_schedule: - $ref: '#/components/schemas/PhaseScheduleObservingGroup_PhaseSchedule' - description: An observing group consisting of a single observing block and observation windows defined by a phase schedule. Used for an observing block that needs to be observed once, with possible observation windows that occur with a regular period. - PhaseScheduleObservingGroup_PhaseSchedule: - type: object - properties: - earliest_observe_date_millis: - type: integer - description: The earliest moment to observe, in Unix time milliseconds. - format: int64 - tolerance: - $ref: '#/components/schemas/Duration' - period: - $ref: '#/components/schemas/Duration' - period_tolerance: - $ref: '#/components/schemas/Duration' - Photometry_Aperture: - type: object - properties: - optimal_aperture: - type: boolean - fixed_aperture: - type: number - description: Radius of the aperture in arcsec - format: double - ninety_six_percent_flux_aperture: - type: boolean - PointingOffsetData: - type: object - properties: - token: - type: string - name: - type: string - offset: - $ref: '#/components/schemas/OffsetCoordinate' - label: - type: integer - format: int32 - version: - type: integer - format: int32 - user_token: - type: string - description: this is the PI of the program that this entity will be recorded forif null should be derived using the program_data.contact_info.user_tokenif null, then a system ?? gross - instrument: - enum: - - UNKNOWN - - CFH12K - - MEGACAM - - WIRCAM - - ESPADONS - - SITELLE - - SPIROU - - WENAOKEAO - type: string - description: the instrument for the pointing - format: enum - is_system: - type: boolean - PointingOffsetDeleteResponse: - type: object - properties: - success: - type: boolean - error: - $ref: '#/components/schemas/Errors' - PointingOffsetGetResponse: - type: object - properties: - success: - type: boolean - entity: - $ref: '#/components/schemas/PointingOffsetData' - error: - $ref: '#/components/schemas/Errors' - PointingOffsetListResponse: - type: object - properties: - success: - type: boolean - entity: - type: array - items: - $ref: '#/components/schemas/PointingOffsetData' - error: - $ref: '#/components/schemas/Errors' - PointingOffsetUpdateRequest: - type: object - properties: - runid: - type: string - description: Unique identifier your science program. - token: - type: string - lock_version: - $ref: '#/components/schemas/Int32Value' - entity: - $ref: '#/components/schemas/PointingOffsetData' - PointingOffsetUpdateResponse: - type: object - properties: - success: - type: boolean - error: - $ref: '#/components/schemas/Errors' - entity: - $ref: '#/components/schemas/PointingOffsetData' - ProgramData: - type: object - properties: - title: - type: string - abstract: - type: string - contact_info: - $ref: '#/components/schemas/ProgramData_ContactInfo' - comment: - type: array - items: - $ref: '#/components/schemas/ProgramData_Comments' - sybasePiUserId: - type: string - sub_totals: - $ref: '#/components/schemas/SubTotalsData' - token: - type: string - description: Unique identifier for the program. Used interchangeably with "runid". - time_allocation: - type: array - items: - $ref: '#/components/schemas/AllocationData' - release_date_millis: - type: integer - format: int64 - metadata_release_date_millis: - type: integer - format: int64 - comment_field: - type: string - program_type: - enum: - - REGULAR - - TARGET_OF_OPPORTUNITY - - CALIBRATION - - SNAPSHOT - type: string - format: enum - distribution_hash: - type: string - time_accounting: - $ref: '#/components/schemas/TimeAccountingData' - operations_comment_field: - type: string - last_data_acquisition_notification_date_millis: - type: integer - format: int64 - dynamic_exposure_time_limit: - $ref: '#/components/schemas/DynamicExposureTimeLimit' - ProgramData_Comments: - type: object - properties: - question: - type: string - comment: - type: string - ProgramData_ContactInfo: - type: object - properties: - pi_first_name: - type: string - pi_last_name: - type: string - pi_email: - type: string - user_token: - type: string - ProgramInfo: - type: object - properties: - program_data: - $ref: '#/components/schemas/ProgramData' - rollup_fields: - $ref: '#/components/schemas/RollupFields' - time_accounting: - $ref: '#/components/schemas/TimeAccountingData' - pi_info: - $ref: '#/components/schemas/ProgramInfo_PiInfo' - version: - type: integer - format: int32 - ProgramInfo_PiInfo: - type: object - properties: - ldap_username: - type: string - email_address: - type: string - first_name: - type: string - last_name: - type: string - legacy_username: - type: string - user_token: - type: string - RollupFields: - type: object - properties: - i_time: - $ref: '#/components/schemas/RollupValue' - i_time_configured: - $ref: '#/components/schemas/RollupValue' - sub_totals: - $ref: '#/components/schemas/SubTotalsData' - RollupValue: - type: object - properties: - requested: - type: integer - format: int64 - started: - type: integer - format: int64 - observed: - type: integer - format: int64 - validated: - type: integer - format: int64 - SingleObservingGroup: - type: object - properties: - observing_block: - $ref: '#/components/schemas/ObservingGroupData_ObservingBlock' - description: An observing group consisting of one observing block. Contingencies on other observations can be set through legacy_reel. Constraints on the time of observation can be set through time_constraint. - SitelleConfigurationData_Calibration: - type: object - properties: - calibration_position: - enum: - - no_calibration_position - - zenith - - target - type: string - format: enum - calibration_mode: - enum: - - no_calibration_mode - - laser - - phase - type: string - format: enum - SitelleStatus_Camera: - type: object - properties: - iq: - type: number - format: double - iq_rms: - type: number - format: double - pixel_shift_x: - type: number - format: double - pixel_shift_y: - type: number - format: double - number_of_stars: - type: integer - format: int32 - me: - type: number - format: double - SkyCoordinate: - type: object - properties: - ra: - type: number - description: Right Ascension, in degrees - format: double - dec: - type: number - description: Declination, in degrees - format: double - SnrBlockData: - type: object - properties: - token: - type: string - label: - type: integer - format: int32 - cumulative_snr: - type: number - description: the total snr achieved of all exposures of all observing components of this snr block - format: double - carry_forward_snr: - type: number - description: if the program is copied to a new program possibly for a new semester the cumulative snr will be moved to the carry_forward_snr and the carry forward snr will be counted in the cumulative snr of the new program - format: double - observing_components: - type: array - items: - $ref: '#/components/schemas/SnrBlockData_SnrBlockObservingComponents' - version: - type: integer - format: int32 - requested_target_magnitude: - type: number - description: this will come from the target - format: double - requested_cumulative_snr: - type: number - description: this will come from the OT - format: double - requested_minimum_exposures: - type: integer - format: int32 - total_exposures: - type: integer - description: counting the total validated exposures for this OC (OT/TARGET) - format: int32 - primary_observing_group_token: - type: string - primary_observing_component_token: - type: string - primary_observing_group_label: - type: integer - format: int32 - total_configured_exposures: - type: integer - description: counting the total configured exposures for this OC - format: int32 - SnrBlockData_SnrBlockObservingComponents: - type: object - properties: - observing_component_token: - type: string - description: the observing component - observing_group_token: - type: string - description: should not be here, should be in the parent - is_primary: - type: boolean - description: is it the original oc? - configured_exposures: - type: integer - format: int32 - exposures: - type: array - items: - $ref: '#/components/schemas/ExposureData' - ob_label: - type: integer - format: int32 - target_token: - type: string - observing_template_token: - type: string - description: a pointer to an observing component object and whether it is the primary/original - SnrBlockListResponse: - type: object - properties: - success: - type: boolean - error: - $ref: '#/components/schemas/Errors' - entity: - type: array - items: - $ref: '#/components/schemas/SnrBlockData' - SpirouStatus_ExposureMeterProcessingResult: - type: object - properties: - flux: - type: number - format: double - snr: - type: number - format: double - mid_exposure_date_millis: - type: number - format: double - spemsnr: - type: number - description: Calculated SNR - format: double - spemsnrc: - type: number - format: double - SpirouStatus_GuiderProcessingResult: - type: object - properties: - etime: - type: number - format: double - isustate: - type: string - sbdens_p: - type: number - format: double - hole_position_offset_x: - type: number - format: double - hole_position_offset_y: - type: number - format: double - star_to_null_position_offset_x: - type: number - format: double - star_to_null_position_offset_y: - type: number - format: double - magnitude_estimate: - type: number - format: double - seeing_estimate: - type: number - format: double - sgisust: - type: string - description: per 160676372 - sggdst: - type: string - sgfrmrt: - type: number - format: double - sgetime: - type: number - format: double - sgnullx: - type: number - format: double - sgnully: - type: number - format: double - sgwinx0: - type: number - format: double - sgwiny0: - type: number - format: double - sgwinx1: - type: number - format: double - sgwiny1: - type: number - format: double - sgoffx0: - type: number - format: double - sgoffy0: - type: number - format: double - sgoffx1: - type: number - format: double - sgoffy1: - type: number - format: double - sgholex: - type: number - format: double - sgholey: - type: number - format: double - sgcholex: - type: number - format: double - sgcholey: - type: number - format: double - sgeholex: - type: number - format: double - sgeholey: - type: number - format: double - sgcstarx: - type: number - format: double - sgcstary: - type: number - format: double - sgestarx: - type: number - format: double - sgestary: - type: number - format: double - sgcnoffx: - type: number - format: double - sgcnoffy: - type: number - format: double - sgcsee: - type: number - format: double - sgesee: - type: number - format: double - sgcmagn: - type: number - format: double - sgemagn: - type: number - format: double - sgssee: - type: number - format: double - trg_type: - type: string - description: from processing, the expected data in the log book are - SpirouStatus_PipelineProcessingResult: - type: object - properties: - snr10: - type: number - format: double - snr34: - type: number - format: double - snr44: - type: number - format: double - dprtype: - type: string - ccfmask: - type: number - format: double - ccfmacpp: - type: number - format: double - ccfrv: - type: number - format: double - ccfcontr: - type: number - format: double - ccfrvc: - type: number - format: double - ccffwhm: - type: number - format: double - ccfmask_str: - type: string - Status: - type: object - properties: - code: - type: integer - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - format: int32 - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/GoogleProtobufAny' - description: A list of messages that carry the error details. There is a common set of message types for APIs to use. - description: 'The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).' - SubTotalsData: - type: object - properties: - etime: - $ref: '#/components/schemas/RollupValue' - overheadtime: - $ref: '#/components/schemas/RollupValue' - exposure_count: - $ref: '#/components/schemas/RollupValue' - child_count: - $ref: '#/components/schemas/RollupValue' - etime_configured: - $ref: '#/components/schemas/RollupValue' - overhead_time_configured: - $ref: '#/components/schemas/RollupValue' - completeness: - type: number - description: Percentage completed for the entity. - format: float - TargetData: - type: object - properties: - token: - type: string - description: Unique identifier for the target. When creating a new target, this will be inferred from the request data if left blank here. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789 - name: - type: string - description: 'Name for the target entered by PI, or primary name as resolved by CDS. Will become OBJECT in the FITS header. Required. Length limited to 39 characters. Allowed characters are letters, numbers, spaces, or any of: !@#$%^&*()_-+.,?/[]<>' - label: - type: integer - description: 'Index of the target within the program, set by the system automatically. Used for reference to targets in communications with QSO staff. storage: transient api_visibility: read only' - format: int32 - version: - type: integer - description: 'Increments when this object updates. storage: transient api_visibility: read only, incremented by the server' - format: int32 - fixed_target: - $ref: '#/components/schemas/TargetData_FixedTarget' - moving_target: - $ref: '#/components/schemas/TargetData_MovingTarget' - magnitude: - $ref: '#/components/schemas/TargetData_Magnitude' - temperature_effective: - type: number - description: The estimated/measured effective temperature, in Kelvin. Required for targets observed with SPIRou or ESPaDOnS. Not used for MegaCam. - format: double - standard_star: - type: boolean - description: Whether the star is a "standard" star, to be observed each time other science targets are observed. Not used for MegaCam. - linked_target_identifiers: - $ref: '#/components/schemas/TargetData_LinkedTargetIdentifiers' - finding_chart: - type: array - items: - $ref: '#/components/schemas/FindingChartData' - description: 'A read-only view of the finding charts for this target. To create or update finding charts, see the FindingChartService. storage: transient api_visibility: read only' - pointing_offset_token: - type: string - description: The token of a pointing offset which should be used when observing this target. To view or manage pointing offsets, see the PointingOffsetService. - pointing_offset: - $ref: '#/components/schemas/PointingOffsetData' - TargetData_FixedTarget: - type: object - properties: - coordinate: - $ref: '#/components/schemas/SkyCoordinate' - proper_motion: - $ref: '#/components/schemas/FixedTarget_ProperMotion' - computed_coordinate: - $ref: '#/components/schemas/SkyCoordinate' - estimated_radial_velocity_kmps: - $ref: '#/components/schemas/DoubleValue' - description: Target at fixed coordinates, including any provided proper motion. - TargetData_LinkedTargetIdentifiers: - type: object - properties: - pi_confirmed_identifiers: - $ref: '#/components/schemas/TargetIdentifiers' - custom_target: - type: boolean - description: Used to indicate this is a target without known identifiers. - description: Contains identifiers discovered for this target. - TargetData_Magnitude: - type: object - properties: - U: - $ref: '#/components/schemas/DoubleValue' - B: - $ref: '#/components/schemas/DoubleValue' - V: - $ref: '#/components/schemas/DoubleValue' - R: - $ref: '#/components/schemas/DoubleValue' - I: - $ref: '#/components/schemas/DoubleValue' - G: - $ref: '#/components/schemas/DoubleValue' - J: - $ref: '#/components/schemas/DoubleValue' - H: - $ref: '#/components/schemas/DoubleValue' - K: - $ref: '#/components/schemas/DoubleValue' - uu: - $ref: '#/components/schemas/DoubleValue' - gg: - $ref: '#/components/schemas/DoubleValue' - rr: - $ref: '#/components/schemas/DoubleValue' - ii: - $ref: '#/components/schemas/DoubleValue' - zz: - $ref: '#/components/schemas/DoubleValue' - AB: - $ref: '#/components/schemas/DoubleValue' - description: Estimated/measured magnitude values per-band. Each value is optional, except where noted. - TargetData_MovingTarget: - type: object - properties: - ephemeris_point: - type: array - items: - $ref: '#/components/schemas/MovingTarget_Ephemeris' - description: The ephemerides that define the path of motion for the target. Must be provided using topocentric astrometric coordinates. Do not provide apparent coordinates. CFHT strongly recommends the use of JPL Horizons System for generating the proper coordinates. CFHT's observatory code is T14. At least five distinct positions must be provided that span the desired observing period. The first position should be well in advance of the observation. The positions do not need to be uniformly sampled throughout the desired observing period. The points will be interpolated to determine the position of the target at the time of observation, at which point there must be at least one ephemeris point defined in the past, and at least four defined in the future. - description: Target with motion defined by a list of ephemerides. - TargetDeleteResponse: - type: object - properties: - success: - type: boolean - error: - $ref: '#/components/schemas/Errors' - TargetIdentifiers: - type: object - properties: - catalog_identifier: - type: object - additionalProperties: - $ref: '#/components/schemas/ListOfIdentifiers' - description: 'All catalogs resolved by simbad for this target key: catalog name, catalog_identifier.key === catalog_identifier.get(key).catalog value: catalog identifier.' - two_mass: - $ref: '#/components/schemas/CatalogIdentifier' - gaia1: - $ref: '#/components/schemas/CatalogIdentifier' - gaia2: - $ref: '#/components/schemas/CatalogIdentifier' - resolved_catalog_identifier: - $ref: '#/components/schemas/CatalogIdentifier' - description: TargetIdentifiers are a collection of identifiers which uniquely identify the target. All possible identifiers are stored in the catalog_identifier map, but a few interesting ones to systems are pulled out for ease of use. - TargetListResponse: - type: object - properties: - success: - type: boolean - entity: - type: array - items: - $ref: '#/components/schemas/TargetData' - error: - $ref: '#/components/schemas/Errors' - TargetMonitoringObservingGroup: - type: object - properties: - observing_block: - $ref: '#/components/schemas/ObservingGroupData_ObservingBlock' - time_constraint: - $ref: '#/components/schemas/TargetMonitoringObservingGroup_TimeConstraint' - description: An observing group defined by an observing block, a number of iterations, and schedule constraints on the iterations. Used for observations that need to be repeated with a regular period. One copy of the observing block is added to the observing group for each iteration. - TargetMonitoringObservingGroup_TimeConstraint: - type: object - properties: - number_of_iterations: - type: integer - description: How many observations to take. - format: int32 - minimum_number_of_iterations: - type: integer - description: Minimum number of observations which still provides science value. If weather becomes an issue, this is used to determine if additional observations should be tried, or the whole observing group aborted to prioritize other observations. Cannot be greater than requested number of iterations. - format: int32 - time_interval: - $ref: '#/components/schemas/Duration' - time_interval_tolerance_min: - $ref: '#/components/schemas/Duration' - time_interval_tolerance_max: - $ref: '#/components/schemas/Duration' - t0_millis: - type: integer - description: Time the observing group should start being observed, in Unix time milliseconds. If omitted, CFHT will start observing at the QC's discretion. - format: int64 - within_same_camera_run: - type: boolean - description: Whether all iterations need to occur within the same camera run. - TargetShowResponse: - type: object - properties: - success: - type: boolean - error: - $ref: '#/components/schemas/Errors' - entity: - $ref: '#/components/schemas/TargetData' - TargetTimeData: - type: object - properties: - requested_integration_time: - $ref: '#/components/schemas/ITimeData' - actual_integration_time: - $ref: '#/components/schemas/ITimeData' - view: - enum: - - REQUESTED - - ACTUAL - type: string - format: enum - exposure_count: - type: integer - format: int32 - child_count: - type: integer - format: int32 - TargetUpdateRequest: - type: object - properties: - runid: - type: string - description: Unique identifier of the science program. - entity: - $ref: '#/components/schemas/TargetData' - token: - type: string - description: Unique identifier of the target. If a target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789 - lock_version: - $ref: '#/components/schemas/Int32Value' - instrument: - enum: - - UNKNOWN - - CFH12K - - MEGACAM - - WIRCAM - - ESPADONS - - SITELLE - - SPIROU - - WENAOKEAO - type: string - description: If specified, instrument-specific validation checks will be performed. - format: enum - TargetUpdateResponse: - type: object - properties: - success: - type: boolean - error: - $ref: '#/components/schemas/Errors' - entity: - $ref: '#/components/schemas/TargetData' - TelescopePatternData: - type: object - properties: - offsets: - type: array - items: - $ref: '#/components/schemas/TelescopePatternData_Offset' - description: Should only be set if manually specifying custom dithering patterns - config: - $ref: '#/components/schemas/TelescopePatternData_Config' - token: - type: string - label: - type: integer - format: int32 - version: - type: integer - format: int32 - user_token: - type: string - instrument: - enum: - - UNKNOWN - - CFH12K - - MEGACAM - - WIRCAM - - ESPADONS - - SITELLE - - SPIROU - - WENAOKEAO - type: string - format: enum - is_system: - type: boolean - name: - type: string - offset_coordinate: - type: array - items: - $ref: '#/components/schemas/OffsetCoordinate' - description: Part of observing_template_data - pattern entity stored in pattern table - TelescopePatternData_Config: - type: object - properties: - name: - type: string - description: - type: string - charge_model: - type: string - description: Used for calculating cost for this pattern - dithering: - $ref: '#/components/schemas/Config_Dithering' - nodding: - $ref: '#/components/schemas/Config_Nodding' - scale: - type: number - format: double - sybase_pat_id: - $ref: '#/components/schemas/BigInteger' - is_system: - type: boolean - TelescopePatternData_Offset: - type: object - properties: - exposure_number: - type: integer - description: 'TODO: deprecate, no need to have duplicate information here.' - format: int32 - offset: - $ref: '#/components/schemas/OffsetCoordinate' - type: - enum: - - OBJECT - - TARGET - - SKY - - DARK - - FLAT - - SNAP - - BIAS - - FOCUS - - ALIGN - - COMPARISON - - ACQUIRE - - FABRY_PEROT - - TWILIGHT_FLATS - - PHOTOMETRY - - SFOCUS - type: string - description: For dithering patterns type is always target - format: enum - TelescopePatternDeleteResponse: - type: object - properties: - success: - type: boolean - error: - $ref: '#/components/schemas/Errors' - TelescopePatternListResponse: - type: object - properties: - success: - type: boolean - entity: - type: array - items: - $ref: '#/components/schemas/TelescopePatternData' - error: - $ref: '#/components/schemas/Errors' - TelescopePatternUpdateRequest: - type: object - properties: - runid: - type: string - token: - type: string - lock_version: - $ref: '#/components/schemas/Int32Value' - entity: - $ref: '#/components/schemas/TelescopePatternData' - TelescopePatternUpdateResponse: - type: object - properties: - success: - type: boolean - error: - $ref: '#/components/schemas/Errors' - entity: - $ref: '#/components/schemas/TelescopePatternData' - TimeAccountingData: - type: object - properties: - configured_time: - $ref: '#/components/schemas/TargetTimeData' - charged_time: - $ref: '#/components/schemas/TargetTimeData' - validated_time_on_sky: - $ref: '#/components/schemas/TargetTimeData' - time_on_sky: - $ref: '#/components/schemas/TargetTimeData' - time_accounting_status: - enum: - - NOT_STARTED - - STARTED - - OBSERVED - - VALIDATED - type: string - format: enum - completion_ratio: - type: number - format: double - description: 'Provides information about the time configured, time spent observing, and overall progress of an entity configured for observation. The entities with TimeAccountingData data are: * Observing Blocks * Observing Groups * Science Programs All fields in TimeAccountingData are readonly because they are derived from the entity and its associated exposures.' - TimeConstraint: - type: object - properties: - window: - type: array - items: - $ref: '#/components/schemas/ObservingBlockCondition_Window' - description: Time windows during which the observation can be made. Windows can not overlap, and will always be stored in temporal order. - description: Constraints on when an observation can be made. - UpdateProgramCommentRequest: - type: object - properties: - runid: - type: string - description: Unique identifier for your science program - comment: - type: string - description: Comment set by the PI for the CFHT QSO Team - UpdateProgramCommentResponse: - type: object - properties: - success: - type: boolean - program_data: - $ref: '#/components/schemas/ProgramData' - error: - $ref: '#/components/schemas/Errors' - UpdateProgramCommentsRequest: - type: object - properties: - runid: - type: string - description: Unique identifier for your science program - program_comment: - type: string - description: Comment set by the PI for the CFHT QSO Team - operations_comment: - type: string - description: Comment set by the CFHT QSO Team - UpdateProgramCommentsResponse: - type: object - properties: - success: - type: boolean - program_data: - $ref: '#/components/schemas/ProgramData' - error: - $ref: '#/components/schemas/Errors' - UpenaProcessingResult_h2o_mm: - type: object - properties: - raw_density: - type: number - format: double - at_zenith: - type: number - format: double -tags: - - name: ObservingGroupService - - name: ObservingTemplateService - - name: PointingOffsetService - - name: ProgramService - - name: TargetService diff --git a/codegen/cfht/generator.py b/codegen/cfht/generator.py index 78e8e79..b3c7c9a 100755 --- a/codegen/cfht/generator.py +++ b/codegen/cfht/generator.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 import sys -from pathlib import Path from typing import Any +import httpx import yaml from datamodel_code_generator import ( DataModelType, @@ -17,8 +17,7 @@ Error as DataModelCodegenError, ) -# Convert this to a URL once CFHT confirms the OAS3 spec is availanble online somewhere. -OPENAPI_SPEC_PATH = Path(__file__).with_name("cfhtopenapi.yaml") +OPENAPI_SPEC_URL = "https://hou-stage.cfht.hawaii.edu/api-docs/pi_api.yaml" GENERATED_HEADER = "# Auto generated file, do not edit\n# ruff: noqa: E741\n" @@ -49,8 +48,9 @@ def generate_models(openapi_document: dict[str, Any]) -> str: def main() -> int: - with OPENAPI_SPEC_PATH.open(encoding="utf-8") as f: - document = yaml.safe_load(f) + response = httpx.get(OPENAPI_SPEC_URL) + response.raise_for_status() + document = yaml.safe_load(response.content) generated = generate_models(document) _ = sys.stdout.write(generated) diff --git a/src/aeonlib/cfht/models.py b/src/aeonlib/cfht/models.py index d29ecf1..d035e22 100644 --- a/src/aeonlib/cfht/models.py +++ b/src/aeonlib/cfht/models.py @@ -2,7 +2,7 @@ # ruff: noqa: E741 # generated by datamodel-codegen: # filename: cfhtopenapi.yaml -# timestamp: 2026-07-23T17:17:02+00:00 +# timestamp: 2026-08-12T17:56:39+00:00 from __future__ import annotations @@ -314,8 +314,16 @@ class ITimeData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - exposure_time_millis: int | None = None - overhead_time_millis: int | None = None + exposure_time_millis: Annotated[ + int | None, + Field(description='Time spent collecting photons (in milliseconds).'), + ] = None + overhead_time_millis: Annotated[ + int | None, + Field( + description='All other times required for taking the exposure (in milliseconds).' + ), + ] = None class Tracking(Enum): @@ -740,13 +748,27 @@ class OffsetCoordinate(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - ra_offset: Annotated[float | None, Field(description='Right ascension, in degrees')] = ( - None - ) - dec_offset: Annotated[float | None, Field(description='Declination, in degrees')] = ( - None - ) - exposure_number: int | None = None + ra_offset: Annotated[ + float | None, + Field( + description='Offset in the Right Ascension axis in arcsec of degrees.', + ge=-1296000.0, + le=1296000.0, + title='Right Ascension Offset', + ), + ] = None + dec_offset: Annotated[ + float | None, + Field( + description='Offset in the Declination axis in arcsec of degrees.', + ge=-324000.0, + le=324000.0, + title='Declination Offset', + ), + ] = None + exposure_number: Annotated[ + int | None, Field(description='Exposure number', le=99, title='Exposure number') + ] = None class PhaseScheduleObservingGroupPhaseSchedule(CFHTBaseModel): @@ -931,8 +953,21 @@ class SkyCoordinate(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - ra: Annotated[float | None, Field(description='Right Ascension, in degrees')] = None - dec: Annotated[float | None, Field(description='Declination, in degrees')] = None + ra: Annotated[ + float | None, + Field( + description='Right Ascension in degrees.', le=360.0, title='Right Ascension' + ), + ] = None + dec: Annotated[ + float | None, + Field( + description='Declination in degrees.', + ge=-90.0, + le=90.0, + title='Declination', + ), + ] = None class SpirouStatusExposureMeterProcessingResult(CFHTBaseModel): @@ -1145,9 +1180,24 @@ class TargetTimeData(CFHTBaseModel): ) requested_integration_time: ITimeData | None = None actual_integration_time: ITimeData | None = None - view: View | None = None - exposure_count: int | None = None - child_count: int | None = None + view: Annotated[ + View | None, + Field( + description='Which time clients should prefer showing users when presenting a single integration time for this data.\n\nREQUESTED - prefer requested_integration_time\nACTUAL - prefer actual_integration_time' + ), + ] = None + exposure_count: Annotated[ + int | None, + Field( + description='The total number of exposures represented by the values here.\n\nThis value is the same for both views.' + ), + ] = None + child_count: Annotated[ + int | None, + Field( + description='The total number of child entities represented by the values here.\n\nIf this is a program, these are observing groups.\nIf this is an observing group, these are observing blocks.\nIf this is an observing block, these are exposures.\n\nUseful for e.g. seeing the number of iterations configuredor completed on an Observing Group.\nThis value is the same for both views.' + ), + ] = None class TelescopePatternDataConfig(CFHTBaseModel): @@ -1223,8 +1273,17 @@ class TimeAccountingData(CFHTBaseModel): charged_time: TargetTimeData | None = None validated_time_on_sky: TargetTimeData | None = None time_on_sky: TargetTimeData | None = None - time_accounting_status: TimeAccountingStatus | None = None - completion_ratio: float | None = None + time_accounting_status: Annotated[ + TimeAccountingStatus | None, + Field(description='Lifecycle state this entity is in.'), + ] = None + completion_ratio: Annotated[ + float | None, + Field( + description='Entity completion ratio, representing how much of the time configured has been charged.\n\nEqual to total charged time (as requested) divided by total configured time. For programs, if the total configured time is greater than the time allocated to the program, we divide by the time allocated instead.\nNote that if a program has INACTIVE observing groups that already have charged time, this could result in an inflated value here.', + le=1.0, + ), + ] = None class TimeConstraint(CFHTBaseModel): From b74adf71c033d565b9eb436f4fceef0e025e6c73 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Wed, 12 Aug 2026 15:49:38 -0700 Subject: [PATCH 23/33] Start aeonlib SiderealTarget -> cfht support --- src/aeonlib/cfht/conversions.py | 60 +++++++++++++++++++++++++++++++++ src/aeonlib/types.py | 9 ++--- tests/cfht/test_models.py | 45 +++++++++++++++++++++++++ tests/cfht/test_online.py | 45 ++++++++++++++----------- 4 files changed, 133 insertions(+), 26 deletions(-) create mode 100644 src/aeonlib/cfht/conversions.py create mode 100644 tests/cfht/test_models.py diff --git a/src/aeonlib/cfht/conversions.py b/src/aeonlib/cfht/conversions.py new file mode 100644 index 0000000..8a36754 --- /dev/null +++ b/src/aeonlib/cfht/conversions.py @@ -0,0 +1,60 @@ +from functools import singledispatch +from typing import Any, overload + +from aeonlib.models import TARGET_TYPES, SiderealTarget + +from .models import TargetData, TargetDataFixedTarget + +# This is a bit over-engineered at the moment. I started writing +# it before I realized that non-sidereal targets were going to require +# a lot more work. Well, the infrastructure is here if we ever implement +# non-sidereal target conversions. + + +class FixedTargetData(TargetData): + """CFHT target data guaranteed to contain sidereal target fields.""" + + fixed_target: TargetDataFixedTarget + + +def _sidereal_target_payload(target: SiderealTarget) -> dict[str, Any]: + return { + "name": target.name, + "fixed_target": { + "coordinate": { + "ra": target.ra.to_value("deg"), + "dec": target.dec.to_value("deg"), + }, + "proper_motion": { + "ra_mas": target.proper_motion_ra, + "dec_mas": target.proper_motion_dec, + }, + }, + } + + +@singledispatch +def _target_data_from_aeon(target: object) -> TargetData: + raise TypeError(f"Cannot convert {type(target).__name__} to CFHT TargetData") + + +@_target_data_from_aeon.register +def _convert_sidereal_target_data(target: SiderealTarget) -> FixedTargetData: + return FixedTargetData.model_validate(_sidereal_target_payload(target)) + + +# TODO: Register non-sidereal target conversions here. +# They will require a JPL Horizons client or API call because CFHT +# uses a series of astrometric coordinates instead of orbital elements. + + +@overload +def target_data_from_aeon(target: SiderealTarget) -> FixedTargetData: ... + + +@overload +def target_data_from_aeon(target: TARGET_TYPES) -> TargetData: ... + + +def target_data_from_aeon(target: TARGET_TYPES) -> TargetData: + return _target_data_from_aeon(target) diff --git a/src/aeonlib/types.py b/src/aeonlib/types.py index 9ac4cbd..9761f5c 100644 --- a/src/aeonlib/types.py +++ b/src/aeonlib/types.py @@ -243,9 +243,6 @@ def __get_pydantic_json_schema__( } -Time = Annotated[astropy.time.Time | datetime, _AstropyTimeType] -TimeMJD = Annotated[astropy.time.Time | datetime | float, _AstropyTimeMJDType] -Angle = Annotated[ - astropy.coordinates.Angle | Quantity | str | float, - _AstropyAngleType, -] +Time = Annotated[astropy.time.Time, _AstropyTimeType] +TimeMJD = Annotated[astropy.time.Time, _AstropyTimeMJDType] +Angle = Annotated[astropy.coordinates.Angle, _AstropyAngleType] diff --git a/tests/cfht/test_models.py b/tests/cfht/test_models.py new file mode 100644 index 0000000..7e99312 --- /dev/null +++ b/tests/cfht/test_models.py @@ -0,0 +1,45 @@ +import pytest + +from aeonlib.cfht.conversions import ( + FixedTargetData, + _sidereal_target_payload, + target_data_from_aeon, +) +from aeonlib.cfht.models import TargetData +from aeonlib.models import SiderealTarget + + +@pytest.fixture(scope="module") +def sidereal_target(): + return SiderealTarget( + name="test", + type="ICRS", + ra=12.3, + dec=45.6, + proper_motion_ra=1.0, + proper_motion_dec=0.1, + ) + + +def test_sidereal_target_payload(sidereal_target): + payload = _sidereal_target_payload(sidereal_target) + assert payload["name"] == "test" + assert payload["fixed_target"]["coordinate"]["ra"] == 12.3 + assert payload["fixed_target"]["coordinate"]["dec"] == 45.6 + assert payload["fixed_target"]["proper_motion"]["ra_mas"] == 1.0 + assert payload["fixed_target"]["proper_motion"]["dec_mas"] == 0.1 + + +def test_sidereal_target_to_target_data(sidereal_target): + result = target_data_from_aeon(sidereal_target) + assert isinstance(result, FixedTargetData) + assert isinstance(result, TargetData) + assert result.name == "test" + assert result.fixed_target + assert result.moving_target is None + assert result.fixed_target.coordinate + assert result.fixed_target.coordinate.ra == 12.3 + assert result.fixed_target.coordinate.dec == 45.6 + assert result.fixed_target.proper_motion + assert result.fixed_target.proper_motion.ra_mas == 1.0 + assert result.fixed_target.proper_motion.dec_mas == 0.1 diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index fac0b38..a40dda8 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -4,6 +4,7 @@ import pytest +from aeonlib.cfht.conversions import target_data_from_aeon from aeonlib.cfht.facility import ( CFHTFacility, EntityNotFoundError, @@ -15,10 +16,10 @@ MovingTargetEphemeris, # TODO: replace with common non-sidereal model SkyCoordinate, TargetData, - TargetDataFixedTarget, # TODO: replace with common target model TargetDataMagnitude, TargetDataMovingTarget, # TODO: replace with common non-sidereal target model ) +from aeonlib.models import SiderealTarget pytestmark = pytest.mark.online @@ -71,32 +72,36 @@ def program_instruments( def example_fixed_target( program_token: str, instrument: Instrument, test_run_id: str ) -> TargetData: - return TargetData( - token=f"{program_token}-{random.randint(1000000000, 9999999999)}", + # Start with an Aeonlib common SiderealTarget + sidereal_target = SiderealTarget( name=f"my new aeonlib test target {test_run_id}", - fixed_target=TargetDataFixedTarget( # TODO: use Aeonlib common Target - coordinate=SkyCoordinate( - ra=random.uniform(0, 359.9999), - dec=random.uniform(-90, 90), - ), - estimated_radial_velocity_kmps=DoubleValue( - value=234.0 - ) # TODO: remove the need for DoubleValue - if instrument == Instrument.spirou - else None, - ), - magnitude=TargetDataMagnitude( - **{example_mag_by_instrument[instrument]: DoubleValue(value=10.0)} - ), - temperature_effective=1234.5, - standard_star=False, - pointing_offset_token=f"00AZ00-PO+{instrument.value}+1", + type="ICRS", + ra=random.uniform(0, 359.9999), + dec=random.uniform(-90, 90), + ) + # Get a CFHT TargetData + target_data = target_data_from_aeon(sidereal_target) + + # Add CFHT specific fields + target_data.token = f"{program_token}-{random.randint(1000000000, 9999999999)}" + target_data.fixed_target.estimated_radial_velocity_kmps = ( + DoubleValue(value=234.0) if instrument == Instrument.spirou else None + ) + target_data.magnitude = TargetDataMagnitude( + **{example_mag_by_instrument[instrument]: DoubleValue(value=10.0)} ) + target_data.temperature_effective = 1234.5 + target_data.standard_star = False + target_data.pointing_offset_token = f"00AZ00-PO+{instrument.value}+1" + + return target_data def example_moving_target( program_token: str, instrument: Instrument, test_run_id: str ) -> TargetData: + # For now constructing a moving target needs to be done manually as + # generating the ephemeris points automatically is not yet supported return TargetData( token=f"{program_token}-{random.randint(1000000000, 9999999999)}", name=f"my new aeonlib moving target {test_run_id}", From 1bad04a96212741a01163b276ad8cee734b30c2f Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Wed, 12 Aug 2026 16:21:19 -0700 Subject: [PATCH 24/33] Remove pesky DoubleValue type --- codegen/cfht/generator.py | 1 + src/aeonlib/cfht/models.py | 10 ++-------- src/aeonlib/cfht/types.py | 20 +++++++++++++++++++ tests/cfht/test_models.py | 39 +++++++++++++++++++++++++++++++++++++- tests/cfht/test_online.py | 9 +++------ 5 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 src/aeonlib/cfht/types.py diff --git a/codegen/cfht/generator.py b/codegen/cfht/generator.py index b3c7c9a..11797e2 100755 --- a/codegen/cfht/generator.py +++ b/codegen/cfht/generator.py @@ -34,6 +34,7 @@ def generate_models(openapi_document: dict[str, Any]) -> str: set_default_enum_member=True, snake_case_field=True, allow_population_by_field_name=True, + type_overrides={"DoubleValue": "aeonlib.cfht.types.DoubleValue"}, formatters=[Formatter.BUILTIN], ) diff --git a/src/aeonlib/cfht/models.py b/src/aeonlib/cfht/models.py index d035e22..467c809 100644 --- a/src/aeonlib/cfht/models.py +++ b/src/aeonlib/cfht/models.py @@ -2,7 +2,7 @@ # ruff: noqa: E741 # generated by datamodel-codegen: # filename: cfhtopenapi.yaml -# timestamp: 2026-08-12T17:56:39+00:00 +# timestamp: 2026-08-12T23:00:57+00:00 from __future__ import annotations @@ -10,6 +10,7 @@ from typing import Annotated from aeonlib.cfht.base_model import CFHTBaseModel +from aeonlib.cfht.types import DoubleValue from pydantic import ConfigDict, Field @@ -114,13 +115,6 @@ class DoubleMinMax(CFHTBaseModel): max: float | None = None -class DoubleValue(CFHTBaseModel): - model_config = ConfigDict( - validate_by_name=True, - ) - value: Annotated[float | None, Field(description='The double value.')] = None - - class DisplayUnit(Enum): days = 'DAYS' hours = 'HOURS' diff --git a/src/aeonlib/cfht/types.py b/src/aeonlib/cfht/types.py new file mode 100644 index 0000000..fa3aa19 --- /dev/null +++ b/src/aeonlib/cfht/types.py @@ -0,0 +1,20 @@ +from typing import Annotated, Any + +from pydantic import BeforeValidator, PlainSerializer + + +def _unwrap_double_value(value: Any) -> Any: + if isinstance(value, dict): + return value.get("value") + return value + + +def _wrap_double_value(value: float | None) -> dict[str, float | None]: + return {"value": value} + + +DoubleValue = Annotated[ + float | None, + BeforeValidator(_unwrap_double_value), + PlainSerializer(_wrap_double_value, return_type=dict[str, float | None]), +] diff --git a/tests/cfht/test_models.py b/tests/cfht/test_models.py index 7e99312..863c242 100644 --- a/tests/cfht/test_models.py +++ b/tests/cfht/test_models.py @@ -5,7 +5,11 @@ _sidereal_target_payload, target_data_from_aeon, ) -from aeonlib.cfht.models import TargetData +from aeonlib.cfht.models import ( + TargetData, + TargetDataFixedTarget, + TargetDataMagnitude, +) from aeonlib.models import SiderealTarget @@ -43,3 +47,36 @@ def test_sidereal_target_to_target_data(sidereal_target): assert result.fixed_target.proper_motion assert result.fixed_target.proper_motion.ra_mas == 1.0 assert result.fixed_target.proper_motion.dec_mas == 0.1 + + +def test_double_value_field_accepts_float(): + fixed_target = TargetDataFixedTarget(estimated_radial_velocity_kmps=234.0) + + assert fixed_target.estimated_radial_velocity_kmps == 234.0 + assert fixed_target.api_dump() == { + "estimated_radial_velocity_kmps": {"value": 234.0} + } + + +def test_aliased_double_value_field_accepts_float(): + magnitude = TargetDataMagnitude(V=10.0) + + assert magnitude.v == 10.0 + assert magnitude.api_dump() == {"V": {"value": 10.0}} + + +def test_double_value_field_accepts_wrapped_api_input(): + magnitude = TargetDataMagnitude.model_validate({"V": {"value": 10.0}}) + assert magnitude.v == 10.0 + + null_magnitude = TargetDataMagnitude.model_validate({"V": {"value": None}}) + assert null_magnitude.v is None + + +def test_double_value_field_accepts_float_assignment(): + magnitude = TargetDataMagnitude() + + magnitude.ab = 10.0 + + assert magnitude.ab == 10.0 + assert magnitude.api_dump() == {"AB": {"value": 10.0}} diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index a40dda8..2a247fc 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -11,7 +11,6 @@ VersionMismatchError, ) from aeonlib.cfht.models import ( - DoubleValue, # TODO: should be removed or coerced from float Instrument, MovingTargetEphemeris, # TODO: replace with common non-sidereal model SkyCoordinate, @@ -85,10 +84,10 @@ def example_fixed_target( # Add CFHT specific fields target_data.token = f"{program_token}-{random.randint(1000000000, 9999999999)}" target_data.fixed_target.estimated_radial_velocity_kmps = ( - DoubleValue(value=234.0) if instrument == Instrument.spirou else None + 234.0 if instrument == Instrument.spirou else None ) target_data.magnitude = TargetDataMagnitude( - **{example_mag_by_instrument[instrument]: DoubleValue(value=10.0)} + **{example_mag_by_instrument[instrument]: 10.0} ) target_data.temperature_effective = 1234.5 target_data.standard_star = False @@ -117,9 +116,7 @@ def example_moving_target( for i in range(5) ] ), - magnitude=TargetDataMagnitude( - **{example_mag_by_instrument[instrument]: DoubleValue(value=10.0)} - ), + magnitude=TargetDataMagnitude(**{example_mag_by_instrument[instrument]: 10.0}), temperature_effective=1234.5, standard_star=False, pointing_offset_token=f"00AZ00-PO+{instrument.value}+1", From 07d7069fb54c1fdcb08fa60f3fb659023442999a Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 13 Aug 2026 09:37:41 -0700 Subject: [PATCH 25/33] get observing templates --- src/aeonlib/cfht/facility.py | 6 +++++- tests/cfht/test_online.py | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/aeonlib/cfht/facility.py b/src/aeonlib/cfht/facility.py index 599d1e3..e0cb4af 100644 --- a/src/aeonlib/cfht/facility.py +++ b/src/aeonlib/cfht/facility.py @@ -5,7 +5,7 @@ from aeonlib.conf import Settings from aeonlib.conf import settings as default_settings -from .models import Instrument, ProgramInfo, TargetData +from .models import Instrument, ObservingTemplateData, ProgramInfo, TargetData class VersionMismatchError(ValueError): @@ -122,3 +122,7 @@ def create_or_update_target( } entity = self._program_request("PUT", f"targets/{target.token}/", json=data) return TargetData.model_validate(entity) + + def observing_templates(self) -> list[ObservingTemplateData]: + entities = self._program_request("GET", "observing-templates/") + return [ObservingTemplateData.model_validate(entity) for entity in entities] diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index 2a247fc..c568adc 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -13,6 +13,7 @@ from aeonlib.cfht.models import ( Instrument, MovingTargetEphemeris, # TODO: replace with common non-sidereal model + ObservingTemplateData, SkyCoordinate, TargetData, TargetDataMagnitude, @@ -218,3 +219,11 @@ def test_target_bad_version(program_facilities: list[CFHTFacility], test_run_id: finally: if target.token is not None: facility.delete_target(target.token) + + +def test_get_observing_templates(program_facilities: list[CFHTFacility]): + for facility, program_token, instrument in program_instruments(program_facilities): + templates = facility.observing_templates() + assert isinstance(templates, list) + for template in templates: + assert isinstance(template, ObservingTemplateData) From fd2736ebf155b9fca0a633671463ef5fd8eb8996 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 13 Aug 2026 10:43:44 -0700 Subject: [PATCH 26/33] Add create observing group test and api --- src/aeonlib/cfht/facility.py | 29 ++++++++++++++++++++-- tests/cfht/test_online.py | 48 ++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/aeonlib/cfht/facility.py b/src/aeonlib/cfht/facility.py index e0cb4af..5a27156 100644 --- a/src/aeonlib/cfht/facility.py +++ b/src/aeonlib/cfht/facility.py @@ -5,7 +5,13 @@ from aeonlib.conf import Settings from aeonlib.conf import settings as default_settings -from .models import Instrument, ObservingTemplateData, ProgramInfo, TargetData +from .models import ( + Instrument, + ObservingGroupData, + ObservingTemplateData, + ProgramInfo, + TargetData, +) class VersionMismatchError(ValueError): @@ -20,6 +26,10 @@ class InvalidResponseError(ValueError): """Raised when the response is invalid""" +class ServerError(RuntimeError): + """Raised when the server returns an error""" + + class CFHTFacility: """CFHT Facility class""" @@ -48,7 +58,10 @@ def _request(self, method: str, url: str, **kwargs: Any) -> Any: ) if response.status_code == httpx.codes.NOT_FOUND: raise EntityNotFoundError(f"Entity not found: {response.request.url}") - response.raise_for_status() + try: + response.raise_for_status() + except httpx.HTTPStatusError as exc: + raise ServerError(f"CFHT API error: {exc}") from exc if method == "DELETE": return None try: @@ -126,3 +139,15 @@ def create_or_update_target( def observing_templates(self) -> list[ObservingTemplateData]: entities = self._program_request("GET", "observing-templates/") return [ObservingTemplateData.model_validate(entity) for entity in entities] + + def create_observing_group( + self, observing_group: ObservingGroupData + ) -> ObservingGroupData: + data = {"entity": observing_group.api_dump()} + entity = self._program_request( + "PUT", f"observing-groups/{observing_group.token}/", json=data + ) + return ObservingGroupData.model_validate(entity) + + def delete_observing_group(self, observing_group_token: str) -> None: + self._program_request("DELETE", f"observing-groups/{observing_group_token}") diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index c568adc..44afb73 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -8,16 +8,23 @@ from aeonlib.cfht.facility import ( CFHTFacility, EntityNotFoundError, + ServerError, VersionMismatchError, ) from aeonlib.cfht.models import ( Instrument, MovingTargetEphemeris, # TODO: replace with common non-sidereal model + ObservingBlockObservingComponent, + ObservingGroupData, + ObservingGroupDataObservingBlock, ObservingTemplateData, + OgPriority, + SingleObservingGroup, SkyCoordinate, TargetData, TargetDataMagnitude, TargetDataMovingTarget, # TODO: replace with common non-sidereal target model + TargetType, ) from aeonlib.models import SiderealTarget @@ -227,3 +234,44 @@ def test_get_observing_templates(program_facilities: list[CFHTFacility]): assert isinstance(templates, list) for template in templates: assert isinstance(template, ObservingTemplateData) + + +@pytest.mark.side_effect +def test_create_observing_group( + program_facilities: list[CFHTFacility], test_run_id: str +): + facility, program_token, instrument = next(program_instruments(program_facilities)) + templates = facility.observing_templates() + assert templates, "No observing templates available" + first_ot = templates[0] + new_target = example_fixed_target(program_token, instrument, test_run_id) + target = facility.create_or_update_target(new_target, instrument) + new_observing_group = ObservingGroupData( + token=f"{program_token}-{random.randint(1000000000, 9999999999)}", + og_priority=OgPriority.medium, + target_type=TargetType.object, + single_observing_group=SingleObservingGroup( + observing_block=ObservingGroupDataObservingBlock( + observing_component=[ + ObservingBlockObservingComponent( + target_token=target.token, + observing_template_token=first_ot.token, + ) + ] + ) + ), + ) + observing_group = facility.create_observing_group(new_observing_group) + assert isinstance(observing_group, ObservingGroupData) + try: + assert observing_group.token + assert observing_group.label + + # Attempt to delete the target that is used in an observing group + with pytest.raises(ServerError): + if target.token is not None: + facility.delete_target(target.token) + finally: + facility.delete_observing_group(observing_group.token) + if target.token is not None: + facility.delete_target(target.token) From a350077ca5de651db016a7d7936e7c1006e8dd66 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 13 Aug 2026 11:26:27 -0700 Subject: [PATCH 27/33] Add list exposures endpoint --- src/aeonlib/cfht/facility.py | 36 +++++++++++++++++++++++++++++++----- tests/cfht/test_online.py | 24 ++++++++++++++++++------ 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/aeonlib/cfht/facility.py b/src/aeonlib/cfht/facility.py index 5a27156..07a9013 100644 --- a/src/aeonlib/cfht/facility.py +++ b/src/aeonlib/cfht/facility.py @@ -1,3 +1,4 @@ +from pprint import pprint from typing import Any import httpx @@ -6,6 +7,7 @@ from aeonlib.conf import settings as default_settings from .models import ( + ExposureData, Instrument, ObservingGroupData, ObservingTemplateData, @@ -50,7 +52,14 @@ def __init__( self._client = httpx.Client(base_url=base_url, headers=headers) self.program_token = program_token - def _request(self, method: str, url: str, **kwargs: Any) -> Any: + def _request( + self, + method: str, + url: str, + *, + response_key: str = "entity", + **kwargs: Any, + ) -> Any: response = self._client.request(method, url, **kwargs) if response.status_code == httpx.codes.CONFLICT: raise VersionMismatchError( @@ -64,20 +73,32 @@ def _request(self, method: str, url: str, **kwargs: Any) -> Any: raise ServerError(f"CFHT API error: {exc}") from exc if method == "DELETE": return None + try: - return response.json()["entity"] + return response.json()[response_key] except (ValueError, TypeError, KeyError) as exc: raise InvalidResponseError( - f"CFHT API response from {response.request.url} did not contain an entity" + f"CFHT API response from {response.request.url} did not contain " + f"the expected {response_key!r} key" ) from exc - def _program_request(self, method: str, url: str, **kwargs: Any) -> Any: + def _program_request( + self, + method: str, + url: str, + *, + response_key: str = "entity", + **kwargs: Any, + ) -> Any: if not self.program_token: raise ValueError( "Program must be set. Initialize the facility with program_token or use `select_program`" ) return self._request( - method, f"programs/{self.program_token}/{url.lstrip('/')}", **kwargs + method, + f"programs/{self.program_token}/{url.lstrip('/')}", + response_key=response_key, + **kwargs, ) def select_program(self, program: ProgramInfo) -> None: @@ -151,3 +172,8 @@ def create_observing_group( def delete_observing_group(self, observing_group_token: str) -> None: self._program_request("DELETE", f"observing-groups/{observing_group_token}") + + def exposures(self) -> list[ExposureData]: + exposures = self._program_request("GET", "exposures/", response_key="exposure") + print(pprint(exposures)) + return [ExposureData.model_validate(exposure) for exposure in exposures] diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index 44afb73..8e77420 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -12,6 +12,7 @@ VersionMismatchError, ) from aeonlib.cfht.models import ( + ExposureData, Instrument, MovingTargetEphemeris, # TODO: replace with common non-sidereal model ObservingBlockObservingComponent, @@ -19,6 +20,7 @@ ObservingGroupDataObservingBlock, ObservingTemplateData, OgPriority, + ProgramInfo, SingleObservingGroup, SkyCoordinate, TargetData, @@ -133,16 +135,15 @@ def example_moving_target( def test_programs(facility: CFHTFacility): programs = facility.programs() - assert any( - program.pi_info and program.pi_info.first_name == "AEON" for program in programs - ) + assert isinstance(programs, list) + assert all(isinstance(program, ProgramInfo) for program in programs) def test_get_targets(program_facilities: list[CFHTFacility]): for facility in program_facilities: targets = facility.targets() assert isinstance(targets, list) - assert all(target.name is not None for target in targets) + assert all(isinstance(target, TargetData) for target in targets) @pytest.mark.side_effect @@ -232,8 +233,9 @@ def test_get_observing_templates(program_facilities: list[CFHTFacility]): for facility, program_token, instrument in program_instruments(program_facilities): templates = facility.observing_templates() assert isinstance(templates, list) - for template in templates: - assert isinstance(template, ObservingTemplateData) + assert all( + isinstance(template, ObservingTemplateData) for template in templates + ) @pytest.mark.side_effect @@ -275,3 +277,13 @@ def test_create_observing_group( facility.delete_observing_group(observing_group.token) if target.token is not None: facility.delete_target(target.token) + + +@pytest.mark.skip("response does not match schema") +def test_get_exposures(program_facilities: list[CFHTFacility]): + for facility, _program_token, _instrument in program_instruments( + program_facilities + ): + exposures = facility.exposures() + assert isinstance(exposures, list) + assert all(isinstance(exposure, ExposureData) for exposure in exposures) From e4896a25d07d49cd0132e9cccc20c6aeb931e7a4 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 13 Aug 2026 11:51:26 -0700 Subject: [PATCH 28/33] Simplify facility iteration for some tests --- tests/cfht/test_online.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index 8e77420..2a0131a 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -230,7 +230,7 @@ def test_target_bad_version(program_facilities: list[CFHTFacility], test_run_id: def test_get_observing_templates(program_facilities: list[CFHTFacility]): - for facility, program_token, instrument in program_instruments(program_facilities): + for facility in program_facilities: templates = facility.observing_templates() assert isinstance(templates, list) assert all( @@ -281,9 +281,7 @@ def test_create_observing_group( @pytest.mark.skip("response does not match schema") def test_get_exposures(program_facilities: list[CFHTFacility]): - for facility, _program_token, _instrument in program_instruments( - program_facilities - ): + for facility in program_facilities: exposures = facility.exposures() assert isinstance(exposures, list) assert all(isinstance(exposure, ExposureData) for exposure in exposures) From a4537fb9d4ab0645649c77bbe35439277176aee4 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 13 Aug 2026 14:52:53 -0700 Subject: [PATCH 29/33] Add CFHT example ipython notebook --- examples/cfht.ipynb | 172 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 examples/cfht.ipynb diff --git a/examples/cfht.ipynb b/examples/cfht.ipynb new file mode 100644 index 0000000..a981c8a --- /dev/null +++ b/examples/cfht.ipynb @@ -0,0 +1,172 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5accbf56-995b-4a45-9d64-701be9fd9b49", + "metadata": {}, + "source": [ + "# CFHT AEONLib Demonstration Notebook\n", + "The online tests are also a good reference and are more extensive. See tests/cfht/test_online.py" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "08a7f23c-7c4e-4a1f-9481-562a2a391ad0", + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "# Import generic Aeonlib models, as well as CFHT facilties\n", + "from aeonlib.cfht.facility import CFHTFacility\n", + "from aeonlib.cfht.conversions import target_data_from_aeon\n", + "from aeonlib.cfht.models import Instrument, TargetDataMagnitude\n", + "from aeonlib.conf import settings\n", + "from aeonlib.models import SiderealTarget" + ] + }, + { + "cell_type": "markdown", + "id": "a7097b42-3a61-4a13-bc61-1543c231037f", + "metadata": {}, + "source": [ + "## Initialize the CFHT facility and select an observing program" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "c10a85c8-613b-4a57-8de6-2c0c107f2b51", + "metadata": {}, + "outputs": [], + "source": [ + "# Normally credentials are automatically picked up by the environment. For this notebook we supply them directly\n", + "settings.cfht_access_token = \"\"\n", + "settings.cfht_api_root = \"https://api-stage.cfht.hawaii.edu/\"\n", + "facility = CFHTFacility(settings=settings)\n", + "\n", + "# Get a list of Programs available to us, and select the one we wish to use. Alternatively, the facility can be\n", + "# instatiated with a program token directly, which is probably what you'd want to use in normal circumstances.\n", + "programs = facility.programs()\n", + "if not programs:\n", + " raise ValueError(\"No programs found. Check the Kealahou Phase2 Tool\")\n", + "# Set the first available program as the active one\n", + "program = programs[0]\n", + "facility.select_program(program)" + ] + }, + { + "cell_type": "markdown", + "id": "44a88310-a8b5-4bd2-a0d2-5885728572d1", + "metadata": {}, + "source": [ + "## Define a target\n", + "We use a generic AEONLib target as a starting point here, as it makes it easier to share between other non-CFHT facilities. If CFHT is the only facility you will be using, it might be easier to construct an aeonlib.cfht.models.TargetData instance directly." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "8707a1a0-fddc-41f0-b931-049c56703129", + "metadata": {}, + "outputs": [], + "source": [ + "# Start with the AEONLib target\n", + "sidereal_target = SiderealTarget(\n", + " name=\"AEONLib CFHT Notebook Target\",\n", + " type=\"ICRS\",\n", + " ra=320.11,\n", + " dec=-42.0\n", + ")\n", + "# Use it to bootstrap a full CFHT TargetData\n", + "target_data = target_data_from_aeon(sidereal_target)\n", + "# Fill in CFHT specific data\n", + "target_data.token = f\"{facility.program_token}-{random.randint(1000000000, 9999999999)}\"\n", + "target_data.magnitude = TargetDataMagnitude(ab=10.0)\n", + "target_data.temperature_effective = 1234.5\n", + "target_data.standard_star = False\n", + "target_data.pointing_offset_token = f\"00AZ00-PO+{Instrument.megacam.value}+1\"" + ] + }, + { + "cell_type": "markdown", + "id": "2ac0a48f-bd0f-43a0-912f-6c4e7d0340a9", + "metadata": {}, + "source": [ + "## Create and list targets" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "7df70290-4fd6-4958-b59e-cb6aee52d32c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "token='25BE25-5950669302' name='AEONLib CFHT Notebook Target' label=7 version=1 fixed_target=TargetDataFixedTarget(coordinate=SkyCoordinate(ra=320.11, dec=-42.0), proper_motion=FixedTargetProperMotion(ra_mas=None, dec_mas=None), computed_coordinate=None, estimated_radial_velocity_kmps=None) moving_target=None magnitude=TargetDataMagnitude(u=None, b=None, v=None, r=None, i=None, g=None, j=None, h=None, k=None, uu=None, gg=None, rr=None, ii=None, zz=None, ab=10.0) temperature_effective=1234.5 standard_star=None linked_target_identifiers=None finding_chart=[] pointing_offset_token='00AZ00-PO+MEGACAM+1' pointing_offset=PointingOffsetData(token='00AZ00-PO+MEGACAM+1', name='1', offset=OffsetCoordinate(ra_offset=None, dec_offset=None, exposure_number=None), label=None, version=None, user_token='SYSTEM', instrument=, is_system=True)\n" + ] + } + ], + "source": [ + "target = facility.create_or_update_target(target_data, Instrument.megacam)\n", + "all_targets = facility.targets()\n", + "try:\n", + " found_target = next(t for t in all_targets if t.token == target.token)\n", + " print(found_target)\n", + "except StopIteration:\n", + " raise ValueError(\"Created target did not appear in target list!\")" + ] + }, + { + "cell_type": "markdown", + "id": "728dc77e-24ce-411d-a549-c00e3eb85492", + "metadata": {}, + "source": [ + "## Delete created target\n", + "Now would be a good time to check the K2 tool in your browser to confirm the target appears in the targets section before it is deleted." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "828e4200-232c-4e82-b69b-f10ed839a6eb", + "metadata": {}, + "outputs": [], + "source": [ + "facility.delete_target(target.token)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51eb8bf2-0f7c-4a01-b990-04d6bcc4a402", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.14.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From d448122d863df2ff5c044337cf7a82a7c7dd5797 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Thu, 13 Aug 2026 14:59:03 -0700 Subject: [PATCH 30/33] Fix type error in python notebook. --- examples/cfht.ipynb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/cfht.ipynb b/examples/cfht.ipynb index a981c8a..656dd45 100644 --- a/examples/cfht.ipynb +++ b/examples/cfht.ipynb @@ -136,7 +136,8 @@ "metadata": {}, "outputs": [], "source": [ - "facility.delete_target(target.token)" + "if target.token is not None:\n", + " facility.delete_target(target.token)" ] }, { From 02f0715ed1c475f854edd8924953243907093266 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Mon, 17 Aug 2026 08:24:44 -0700 Subject: [PATCH 31/33] Update cfht models --- src/aeonlib/cfht/models.py | 479 ++++++++++++++++++++++++++----------- tests/cfht/test_models.py | 14 +- tests/cfht/test_online.py | 3 +- 3 files changed, 351 insertions(+), 145 deletions(-) diff --git a/src/aeonlib/cfht/models.py b/src/aeonlib/cfht/models.py index 467c809..33cedb3 100644 --- a/src/aeonlib/cfht/models.py +++ b/src/aeonlib/cfht/models.py @@ -2,7 +2,7 @@ # ruff: noqa: E741 # generated by datamodel-codegen: # filename: cfhtopenapi.yaml -# timestamp: 2026-08-12T23:00:57+00:00 +# timestamp: 2026-08-17T15:18:13+00:00 from __future__ import annotations @@ -24,13 +24,13 @@ class AddSingleToSnrBlockRequest(CFHTBaseModel): snr_block_token: Annotated[ str | None, Field( - description='Unique identifier of an existing SNR block to create the new observing group for.' + description='Unique identifier of an existing SNR block to create the new observing\n group for.' ), ] = None observing_group_token: Annotated[ str | None, Field( - description='Unique identifier of the observing group. If an observing group target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789' + description='Unique identifier of the observing group. If an observing group target\n with this token already exists, it will be updated, otherwise it will be\n created.\n\n Must begin with the program token followed by a dash, e.g.\n 20AE01-0123456789' ), ] = None @@ -69,7 +69,7 @@ class CatalogIdentifier(CFHTBaseModel): identifier: Annotated[ str | None, Field( - description='Valid identifier in the catalog. Note: this ID will contain a catalog name, because spacing matters in catalogs, the full ID for an object is like M 4. Catalog is M, id is identifier could be 4, but to reference m4, you need the spaces. NB: These identity values can not be set in fits headers because they do not have to conform to the length restrictions.' + description='Valid identifier in the catalog.\n\n Note: this ID will contain a catalog name, because spacing matters in catalogs, the full ID for an object is like\n M 4. Catalog is M, id is identifier could be 4, but to reference m4, you need the spaces.\n\n NB: These identity values can not be set in fits headers because they do not have to conform to\n the length restrictions.' ), ] = None @@ -130,7 +130,7 @@ class Duration(CFHTBaseModel): display_unit: Annotated[ DisplayUnit | None, Field( - description='Only needed for rendering UI, should display the duration using the units specified. If unset, assume DAYS' + description='Only needed for rendering UI, should display the duration using the units specified.\n If unset, assume DAYS' ), ] = None @@ -147,7 +147,7 @@ class Errors(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - message: list[str] | None = None + messages: list[str] | None = None class EspadonsStatusExposureMeterProcessingResult(CFHTBaseModel): @@ -169,13 +169,13 @@ class ExposureDataAttribution(CFHTBaseModel): validate_by_name=True, ) runid: str | None = None - observing_block_display_label: Annotated[ + observing_block_label: Annotated[ str | None, Field( - description='If displaying the observing block to the user, this uniquely identifies it in the context of an observing group, i.e. OG3-2' + description='If displaying the observing block to the user, this uniquely identifies it in the context\n of an observing group, i.e. OG3-2' ), ] = None - observing_block_label: Annotated[ + observing_block_label_int: Annotated[ int | None, Field(description='Label of the observing block') ] = None observing_block_token: Annotated[ @@ -205,7 +205,7 @@ class ExposureDataAttribution(CFHTBaseModel): exposure_number: Annotated[ int | None, Field(description='exposure number this exposure is attributed to.') ] = None - queue_run: Annotated[ + instrument_run: Annotated[ str | None, Field(description='the queue run this exposure was taken under') ] = None attributing_agency: Annotated[str | None, Field(description='the agency id')] = None @@ -357,7 +357,7 @@ class InstrumentConfigurationDataEspadonsConfigurationData(CFHTBaseModel): stokes_parameter: StokesParameter | None = None number_of_sequences: int | None = None number_of_exposures: int | None = None - use_snr: bool | None = None + needs_snr: bool | None = None snr_wave: float | None = None snr: float | None = None @@ -511,12 +511,22 @@ class LegacyReel(CFHTBaseModel): reel_og_token: Annotated[ str | None, Field(description='Token of the linked observing group.') ] = None - delay: Duration | None = None - window: Duration | None = None + delay: Annotated[ + Duration | None, + Field( + description='Required wait after the observation of the linked observing group before\n this observing group can be observed.' + ), + ] = None + window: Annotated[ + Duration | None, + Field( + description='Maximum allowed wait after the delay until this observing group can no\n longer be observed.' + ), + ] = None reel_og_label: Annotated[ int | None, Field( - description='Label of the observing group which must be observed before this one. storage: transient api_visibility: read only' + description='Label of the observing group which must be observed before this one.\n\n storage: transient\n api_visibility: read only' ), ] = None @@ -545,25 +555,25 @@ class ObservingBlockConditionRequisite(CFHTBaseModel): requisite_observing_block_index: Annotated[ int | None, Field( - description='This should match the index of the observing block in the read_observing_block list.' + description='This should match the index of the observing block in the\n read_observing_block list.' ), ] = None wait_period_min_millis: Annotated[ int | None, Field( - description='Minimum time to wait after requisite observing block exposure has been taken.' + description='Minimum time to wait after requisite observing block exposure has\n been taken.' ), ] = None wait_period_millis: Annotated[ int | None, Field( - description='Optimal to wait after requisite observing block exposure has been taken.' + description='Optimal to wait after requisite observing block exposure has\n been taken.' ), ] = None wait_period_max_millis: Annotated[ int | None, Field( - description='Minimum time to wait after requisite observing block exposure has been taken.' + description='Minimum time to wait after requisite observing block exposure has\n been taken.' ), ] = None unbounded: Annotated[ @@ -597,31 +607,31 @@ class ObservingBlockObservingComponent(CFHTBaseModel): target_token: Annotated[ str | None, Field( - description='Unique identifier of the target used in this observing component. When creating or updating an observing group, this must be an existing target, or on the map of the request to be created.' + description='Unique identifier of the target used in this observing component.\n\n When creating or updating an observing group, this must be an\n existing target, or on the map of the request to be created.' ), ] = None observing_template_token: Annotated[ str | None, Field( - description='Unique identifier of the observing template used in this observing component. When creating or updating an observing group, this must be an existing observing template, or on the map of the request to be created.' + description='Unique identifier of the observing template used in this\n observing component.\n\n When creating or updating an observing group, this must be an\n existing observing template, or on the map of the request to be\n created.' ), ] = None target_label: Annotated[ int | None, Field( - description='Label of the target used in this observing component. This value is only set on the read_observing_block. api_visibility: read only' + description='Label of the target used in this observing component.\n This value is only set on the read_observing_block.\n\n api_visibility: read only' ), ] = None observing_template_label: Annotated[ int | None, Field( - description='Label of the observing template used in this observing component. This value is only set on the read_observing_block. api_visibility: read only' + description='Label of the observing template used in this observing component.\n This value is only set on the read_observing_block.\n\n api_visibility: read only' ), ] = None snr_block_token: Annotated[ str | None, Field( - description='Unique identifier of the SNR block used in this observing component, if any. Must be set if and only if the observing component uses a MegaCam cumulative SNR mode observing template. If this is set when creating or updating an observing group, the observing component will be tied to the SNR block with this token if it already exists, otherwise a new SNR block will be created using this token.' + description='Unique identifier of the SNR block used in this observing\n component, if any. Must be set if and only if the observing\n component uses a MegaCam cumulative SNR mode observing template.\n\n If this is set when creating or updating an observing group,\n the observing component will be tied to the SNR block with this\n token if it already exists, otherwise a new SNR block will be\n created using this token.' ), ] = None @@ -711,13 +721,13 @@ class ObservingGroupDataObservingBlock(CFHTBaseModel): token: Annotated[ str | None, Field( - description='Unique identifier for the observing block. api_visibility: operations read only' + description='Unique identifier for the observing block.\n\n api_visibility: operations read only' ), ] = None label: Annotated[ int | None, Field( - description='Index of the observing block within the program, set by the system automatically. Used for reference in communications with QSO staff. storage: transient api_visibility: read only' + description='Index of the observing block within the program, set by the system\n automatically. Used for reference in communications with QSO staff.\n\n storage: transient\n api_visibility: read only' ), ] = None @@ -773,9 +783,22 @@ class PhaseScheduleObservingGroupPhaseSchedule(CFHTBaseModel): int | None, Field(description='The earliest moment to observe, in Unix time milliseconds.'), ] = None - tolerance: Duration | None = None - period: Duration | None = None - period_tolerance: Duration | None = None + tolerance: Annotated[ + Duration | None, + Field( + description='The amount of time before or after earliest_observe_date_millis that\n the initial observation can be made.' + ), + ] = None + period: Annotated[ + Duration | None, + Field(description='The amount of time to wait between each iteration.'), + ] = None + period_tolerance: Annotated[ + Duration | None, + Field( + description='The amount of time before or after the period that each iteration\n can be made.' + ), + ] = None class PhotometryAperture(CFHTBaseModel): @@ -801,7 +824,7 @@ class PointingOffsetData(CFHTBaseModel): user_token: Annotated[ str | None, Field( - description='this is the PI of the program that this entity will be recorded forif null should be derived using the program_data.contact_info.user_tokenif null, then a system ?? gross' + description='this is the PI of the program that this entity will be recorded for\nif null should be derived using the program_data.contact_info.user_token\nif null, then a system ?? gross' ), ] = None instrument: Annotated[ @@ -1082,31 +1105,62 @@ class TargetDataFixedTarget(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - coordinate: SkyCoordinate | None = None - proper_motion: FixedTargetProperMotion | None = None - computed_coordinate: SkyCoordinate | None = None - estimated_radial_velocity_kmps: DoubleValue | None = None + coordinate: Annotated[ + SkyCoordinate | None, + Field(description='Coordinate for this target, specified in ICRS (ep=J2000).'), + ] = None + proper_motion: Annotated[ + FixedTargetProperMotion | None, + Field(description='Proper motions for this target.'), + ] = None + computed_coordinate: Annotated[ + SkyCoordinate | None, + Field( + description='Internal use only.\n The coordinate of the target at the time of observation,\n specified in ICRS (ep=J2000).\n\n storage: transient\n api_visibility: operations read only' + ), + ] = None + estimated_radial_velocity_kmps: Annotated[ + DoubleValue | None, + Field( + description='Expected radial velocity of the target in kilometers/second.\n\n Used for targets observed with SPIRou.' + ), + ] = None class TargetDataMagnitude(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - u: Annotated[DoubleValue | None, Field(alias='U')] = None - b: Annotated[DoubleValue | None, Field(alias='B')] = None - v: Annotated[DoubleValue | None, Field(alias='V')] = None - r: Annotated[DoubleValue | None, Field(alias='R')] = None - i: Annotated[DoubleValue | None, Field(alias='I')] = None - g: Annotated[DoubleValue | None, Field(alias='G')] = None - j: Annotated[DoubleValue | None, Field(alias='J')] = None - h: Annotated[DoubleValue | None, Field(alias='H')] = None - k: Annotated[DoubleValue | None, Field(alias='K')] = None - uu: DoubleValue | None = None - gg: DoubleValue | None = None - rr: DoubleValue | None = None - ii: DoubleValue | None = None - zz: DoubleValue | None = None - ab: Annotated[DoubleValue | None, Field(alias='AB')] = None + u: Annotated[DoubleValue | None, Field(description='U-band magnitude')] = None + b: Annotated[DoubleValue | None, Field(description='B-band magnitude')] = None + v: Annotated[ + DoubleValue | None, + Field( + description='V-band magnitude\n\n Required for targets observed with ESPaDonS.' + ), + ] = None + r: Annotated[DoubleValue | None, Field(description='R-band magnitude')] = None + i: Annotated[DoubleValue | None, Field(description='I-band magnitude')] = None + g: Annotated[DoubleValue | None, Field(description='G-band magnitude')] = None + j: Annotated[DoubleValue | None, Field(description='J-band magnitude')] = None + h: Annotated[ + DoubleValue | None, + Field( + description='H-band magnitude\n\n Required for targets observed with SPIRou.' + ), + ] = None + k: Annotated[DoubleValue | None, Field(description='K-band magnitude')] = None + uu: Annotated[DoubleValue | None, Field(description='u-band magnitude')] = None + gg: Annotated[DoubleValue | None, Field(description='g-band magnitude')] = None + rr: Annotated[DoubleValue | None, Field(description='r-band magnitude')] = None + ii: Annotated[DoubleValue | None, Field(description='i-band magnitude')] = None + zz: Annotated[DoubleValue | None, Field(description='z-band magnitude')] = None + a_b: Annotated[ + DoubleValue | None, + Field( + description='AB magnitude\n\n Required for targets observed with MegaCam.' + ), + ] = None class TargetDeleteResponse(CFHTBaseModel): @@ -1124,13 +1178,18 @@ class TargetIdentifiers(CFHTBaseModel): catalog_identifier: Annotated[ dict[str, ListOfIdentifiers] | None, Field( - description='All catalogs resolved by simbad for this target key: catalog name, catalog_identifier.key === catalog_identifier.get(key).catalog value: catalog identifier.' + description='All catalogs resolved by simbad for this target\n\n key: catalog name, catalog_identifier.key === catalog_identifier.get(key).catalog\n value: catalog identifier.' ), ] = None two_mass: CatalogIdentifier | None = None gaia1: CatalogIdentifier | None = None gaia2: CatalogIdentifier | None = None - resolved_catalog_identifier: CatalogIdentifier | None = None + resolved_catalog_identifier: Annotated[ + CatalogIdentifier | None, + Field( + description='Contains the identifier returned by the resolution service which it determined was the\n catalog the lookup was searching in.' + ), + ] = None class TargetMonitoringObservingGroupTimeConstraint(CFHTBaseModel): @@ -1143,16 +1202,28 @@ class TargetMonitoringObservingGroupTimeConstraint(CFHTBaseModel): minimum_number_of_iterations: Annotated[ int | None, Field( - description='Minimum number of observations which still provides science value. If weather becomes an issue, this is used to determine if additional observations should be tried, or the whole observing group aborted to prioritize other observations. Cannot be greater than requested number of iterations.' + description='Minimum number of observations which still provides science value.\n\n If weather becomes an issue, this is used to determine if additional\n observations should be tried, or the whole observing group aborted\n to prioritize other observations.\n\n Cannot be greater than requested number of iterations.' + ), + ] = None + time_interval: Annotated[ + Duration | None, Field(description='The time interval between observations.') + ] = None + time_interval_tolerance_min: Annotated[ + Duration | None, + Field( + description='How much earlier an iteration can be observed before the time\n interval has elapsed.\n\n At least one of tolerance before or after must be non-zero.' + ), + ] = None + time_interval_tolerance_max: Annotated[ + Duration | None, + Field( + description='How much later an iteration can be observed after the time interval\n has elapsed.\n\n At least one of tolerance before or after must be non-zero.' ), ] = None - time_interval: Duration | None = None - time_interval_tolerance_min: Duration | None = None - time_interval_tolerance_max: Duration | None = None t0_millis: Annotated[ int | None, Field( - description="Time the observing group should start being observed, in Unix time milliseconds. If omitted, CFHT will start observing at the QC's discretion." + description="Time the observing group should start being observed, in Unix time\n milliseconds.\n\n If omitted, CFHT will start observing at the QC's discretion." ), ] = None within_same_camera_run: Annotated[ @@ -1287,7 +1358,7 @@ class TimeConstraint(CFHTBaseModel): window: Annotated[ list[ObservingBlockConditionWindow] | None, Field( - description='Time windows during which the observation can be made. Windows can not overlap, and will always be stored in temporal order.' + description='Time windows during which the observation can be made.\n\n Windows can not overlap, and will always be stored in temporal order.' ), ] = None @@ -1571,8 +1642,8 @@ class ExposureDataSpirouStatus(CFHTBaseModel): sbl13h_v: float | None = None seadc1_p: float | None = None seadc2_p: float | None = None - sedens_p_deprecated: int | None = None - sedens_p: float | None = None + sedens_p: int | None = None + sedens_p_float: float | None = None se_inj_t: float | None = None se_pol_t: float | None = None se_cs1_t: float | None = None @@ -1710,7 +1781,10 @@ class InstrumentConfigurationDataSpirouConfigurationData(CFHTBaseModel): use_snr: Annotated[ bool | None, Field(description='when true - use SNR for stoping exposures') ] = None - sky_etime_millis: Int64Value | None = None + sky_etime_millis: Annotated[ + Int64Value | None, + Field(description='Used to override the etime for the sky_observation'), + ] = None class MegacamConfigurationDataPhotometry(CFHTBaseModel): @@ -1731,8 +1805,18 @@ class MovingTargetEphemeris(CFHTBaseModel): description='Modified Julian date when the target is at this coordinate.' ), ] = None - coordinate: SkyCoordinate | None = None - computed_coordinate: SkyCoordinate | None = None + coordinate: Annotated[ + SkyCoordinate | None, + Field( + description='Coordinate for this target at this time, specified in\n ICRS (ep=J2000).' + ), + ] = None + computed_coordinate: Annotated[ + SkyCoordinate | None, + Field( + description='Internal use only.\n The coordinate of the target at the time of observation,\n specified in ICRS (ep=J2000).\n\n storage: transient\n api_visibility: operations read only' + ), + ] = None class ObservingBlockCondition(CFHTBaseModel): @@ -1742,13 +1826,13 @@ class ObservingBlockCondition(CFHTBaseModel): pre_requisite: Annotated[ list[ObservingBlockConditionRequisite] | None, Field( - description='Pre-requisite conditions before each observing block can be started. The index of items in this list corresponds directly to the index of the observing block on the observing group i.e. this list is one-to-one with read_observing_block.' + description='Pre-requisite conditions before each observing block can be started.\n The index of items in this list corresponds directly to the index of the\n observing block on the observing group i.e. this list is one-to-one with\n read_observing_block.' ), ] = None window: Annotated[ list[ObservingBlockConditionWindow] | None, Field( - description='Time windows during which the observing block can be observed. Windows can not overlap, and will always be stored in temporal order.' + description='Time windows during which the observing block can be observed.\n\n Windows can not overlap, and will always be stored in temporal order.' ), ] = None @@ -1791,8 +1875,13 @@ class ObservingGroupContext(CFHTBaseModel): single: bool | None = None target_monitoring: TargetMonitoringObservingGroupTimeConstraint | None = None phase_schedule: PhaseScheduleObservingGroupPhaseSchedule | None = None - time_accounting: TimeAccountingData | None = None - exposure_type: Annotated[ExposureType | None, Field(alias='exposureType')] = None + time_accounting: Annotated[ + TimeAccountingData | None, + Field( + description='*\n TimeAccounting for the observing group.\n\n api_visibility: readonly' + ), + ] = None + exposure_type: ExposureType | None = None use_group_dither_scheme: bool | None = None @@ -1800,8 +1889,14 @@ class PhaseScheduleObservingGroup(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - observing_block: ObservingGroupDataObservingBlock | None = None - phase_schedule: PhaseScheduleObservingGroupPhaseSchedule | None = None + observing_block: Annotated[ + ObservingGroupDataObservingBlock | None, + Field(description='The observing block that is added once per iteration.'), + ] = None + phase_schedule: Annotated[ + PhaseScheduleObservingGroupPhaseSchedule | None, + Field(description='The schedule that defines when each iteration can occur.'), + ] = None class ProgramData(CFHTBaseModel): @@ -1812,7 +1907,7 @@ class ProgramData(CFHTBaseModel): abstract: str | None = None contact_info: ProgramDataContactInfo | None = None comment: list[ProgramDataComments] | None = None - sybase_pi_user_id: Annotated[str | None, Field(alias='sybasePiUserId')] = None + sybase_pi_user_id: str | None = None sub_totals: SubTotalsData | None = None token: Annotated[ str | None, @@ -1826,7 +1921,12 @@ class ProgramData(CFHTBaseModel): comment_field: str | None = None program_type: ProgramType | None = None distribution_hash: str | None = None - time_accounting: TimeAccountingData | None = None + time_accounting: Annotated[ + TimeAccountingData | None, + Field( + description='TimeAccounting for the program.\n\n api_visibility: readonly' + ), + ] = None operations_comment_field: str | None = None last_data_acquisition_notification_date_millis: int | None = None dynamic_exposure_time_limit: DynamicExposureTimeLimit | None = None @@ -1845,7 +1945,12 @@ class TargetDataLinkedTargetIdentifiers(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - pi_confirmed_identifiers: TargetIdentifiers | None = None + pi_confirmed_identifiers: Annotated[ + TargetIdentifiers | None, + Field( + description='Ids confirmed by the PI through the K2 web UI, or other\n confirmation step.\n\n When absent, the PI never confirmed a linking for this target.' + ), + ] = None custom_target: Annotated[ bool | None, Field( @@ -1861,7 +1966,7 @@ class TargetDataMovingTarget(CFHTBaseModel): ephemeris_point: Annotated[ list[MovingTargetEphemeris] | None, Field( - description="The ephemerides that define the path of motion for the target. Must be provided using topocentric astrometric coordinates. Do not provide apparent coordinates. CFHT strongly recommends the use of JPL Horizons System for generating the proper coordinates. CFHT's observatory code is T14. At least five distinct positions must be provided that span the desired observing period. The first position should be well in advance of the observation. The positions do not need to be uniformly sampled throughout the desired observing period. The points will be interpolated to determine the position of the target at the time of observation, at which point there must be at least one ephemeris point defined in the past, and at least four defined in the future." + description="The ephemerides that define the path of motion for the target.\n\n Must be provided using topocentric astrometric coordinates.\n Do not provide apparent coordinates.\n CFHT strongly recommends the use of JPL Horizons System for\n generating the proper coordinates. CFHT's observatory code is T14.\n\n At least five distinct positions must be provided that span the\n desired observing period. The first position should be well in\n advance of the observation. The positions do not need to be\n uniformly sampled throughout the desired observing period.\n\n The points will be interpolated to determine the position of the\n target at the time of observation, at which point there must be\n at least one ephemeris point defined in the past, and at least\n four defined in the future." ), ] = None @@ -1870,8 +1975,14 @@ class TargetMonitoringObservingGroup(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - observing_block: ObservingGroupDataObservingBlock | None = None - time_constraint: TargetMonitoringObservingGroupTimeConstraint | None = None + observing_block: Annotated[ + ObservingGroupDataObservingBlock | None, + Field(description='The observing block that is added once per iteration.'), + ] = None + time_constraint: Annotated[ + TargetMonitoringObservingGroupTimeConstraint | None, + Field(description='The schedule that defines when each iteration can occur.'), + ] = None class TelescopePatternData(CFHTBaseModel): @@ -1900,7 +2011,7 @@ class TelescopePatternListResponse(CFHTBaseModel): validate_by_name=True, ) success: bool | None = None - entity: list[TelescopePatternData] | None = None + telescope_pattern: list[TelescopePatternData] | None = None error: Errors | None = None @@ -1909,9 +2020,9 @@ class TelescopePatternUpdateRequest(CFHTBaseModel): validate_by_name=True, ) runid: str | None = None - token: str | None = None + telescope_pattern_token: str | None = None lock_version: Int32Value | None = None - entity: TelescopePatternData | None = None + telescope_pattern: TelescopePatternData | None = None class TelescopePatternUpdateResponse(CFHTBaseModel): @@ -1920,7 +2031,7 @@ class TelescopePatternUpdateResponse(CFHTBaseModel): ) success: bool | None = None error: Errors | None = None - entity: TelescopePatternData | None = None + telescope_pattern: TelescopePatternData | None = None class UpdateProgramCommentResponse(CFHTBaseModel): @@ -1975,13 +2086,21 @@ class ExposureDataExposureStatus(CFHTBaseModel): is_photometric: bool | None = None obs_comment: str | None = None seq_comment: str | None = None - actual_pointing: SkyCoordinate | None = None + actual_pointing: Annotated[ + SkyCoordinate | None, + Field( + description='TODO: come up with word which means it was read from instruments, measured, observed, reported, etc.' + ), + ] = None actual_sky: float | None = None actual_airmass: float | None = None actual_moondist: float | None = None observer_comment: str | None = None qc_comment: str | None = None - file: ExposureStatusFile | None = None + file: Annotated[ + ExposureStatusFile | None, + Field(description='To read a file, simply absolute_path + base_name + ext.'), + ] = None credited_qcoordinator: str | None = None credited_observer: str | None = None grade: int | None = None @@ -2009,17 +2128,16 @@ class InstrumentConfigurationDataMegacamConfigurationData(CFHTBaseModel): is_dynamic_exposure_time: Annotated[ bool | None, Field( - description="TODO: is this needed billy? Can we use presence of snr or not to decide? I THINK SO. We have the capability to calculate SNR on every exposure, but we aren't necessarily doing SNR mode observations with everything" + description="TODO: is this needed billy? Can we use presence of snr or not to decide?\n I THINK SO. We have the capability to calculate SNR on every exposure, but we aren't necessarily doing SNR mode observations with everything" ), ] = None minimum_exposures: int | None = None min_exptime_ms: int | None = None max_exptime_ms: int | None = None - mag_ab: Annotated[ + mag_a_b: Annotated[ float | None, Field( - alias='magAB', - description='TODO: interesting field here because associated with the target, but in the legacy mp snr mode, the mag is carried with the IC! so have to go back to this :(', + description='TODO: interesting field here because associated with the target, but in the legacy mp snr mode, the mag is carried with the IC! so have to go back to this :(' ), ] = None snr_mode: SnrMode | None = None @@ -2036,37 +2154,55 @@ class ObservingGroupData(CFHTBaseModel): label: Annotated[ int | None, Field( - description='Index of the observing group within the program, set by the system automatically. Used for reference in communications with QSO staff. storage: transient api_visibility: read only' + description='Index of the observing group within the program, set by the system\n automatically. Used for reference in communications with QSO staff.\n\n storage: transient\n api_visibility: read only' ), ] = None og_priority: Annotated[ OgPriority | None, Field( - description='Priority of observing this observing group, relative to other observing groups within the same program.' + description='Priority of observing this observing group, relative to other observing\n groups within the same program.' + ), + ] = None + single_observing_group: Annotated[ + SingleObservingGroup | None, + Field(description='The observing group has a single observing block.'), + ] = None + target_monitoring_observing_group: Annotated[ + TargetMonitoringObservingGroup | None, + Field( + description='The observing group has multiple observing blocks driven by target\n monitoring constraints.' + ), + ] = None + phase_schedule_observing_group: Annotated[ + PhaseScheduleObservingGroup | None, + Field( + description='The observing group multiple observing blocks driven by a phase\n schedule.' ), ] = None - single_observing_group: SingleObservingGroup | None = None - target_monitoring_observing_group: TargetMonitoringObservingGroup | None = None - phase_schedule_observing_group: PhaseScheduleObservingGroup | None = None read_observing_block: Annotated[ list[ObservingGroupDataObservingBlock] | None, Field( - description='The observing blocks that have been created based on the entered configuration. api_visibility: read only' + description='The observing blocks that have been created based on the entered\n configuration.\n\n api_visibility: read only' ), ] = None observing_block_condition: Annotated[ dict[str, ObservingBlockCondition] | None, Field( - description='The requirements that have been set for observing each observing blocks based on the entered configuration. api_visibility: read only' + description='The requirements that have been set for observing each observing blocks\n based on the entered configuration.\n\n api_visibility: read only' ), ] = None minimum_observing_block_count: Annotated[ int | None, Field( - description='This field is unused, please ignore. api_visibility: read only' + description='This field is unused, please ignore.\n\n api_visibility: read only' + ), + ] = None + time_accounting: Annotated[ + TimeAccountingData | None, + Field( + description='Time accounting information for the observing group.\n\n See the Time Accounting documentation for more details.\n\n api_visibility: readonly' ), ] = None - time_accounting: TimeAccountingData | None = None state: Annotated[ State | None, Field( @@ -2076,21 +2212,29 @@ class ObservingGroupData(CFHTBaseModel): version: Annotated[ int | None, Field( - description='Increments when this object updates. storage: transient api_visibility: read only, incremented by the server' + description='Increments when this object updates.\n\n storage: transient\n api_visibility: read only, incremented by the server' ), ] = None - legacy_reel: LegacyReel | None = None - time_constraint: TimeConstraint | None = None + legacy_reel: Annotated[ + LegacyReel | None, + Field( + description='Conditions the observation of this observing group on the observation of\n another observing group first.' + ), + ] = None + time_constraint: Annotated[ + TimeConstraint | None, + Field(description='Constraints on when the observing group can be observed.'), + ] = None target_type: Annotated[ TargetType | None, Field( - description='This can be used to configure calibration observing groups. For internal use only. api_visibility: operations write only' + description='This can be used to configure calibration observing groups. For internal\n use only.\n\n api_visibility: operations write only' ), ] = None dither_scheme: Annotated[ DitherScheme | None, Field( - description='Used to customize the order of observation when observing multiple targets with the same telescope pattern within this observing group.' + description='Used to customize the order of observation when observing multiple\n targets with the same telescope pattern within this observing group.' ), ] = None low_surface_brightness: Annotated[ @@ -2137,56 +2281,77 @@ class TargetData(CFHTBaseModel): token: Annotated[ str | None, Field( - description='Unique identifier for the target. When creating a new target, this will be inferred from the request data if left blank here. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789' + description='Unique identifier for the target.\n\n When creating a new target, this will be inferred from the request data\n if left blank here.\n\n Must begin with the program token followed by a dash, e.g.\n 20AE01-0123456789' ), ] = None name: Annotated[ str | None, Field( - description='Name for the target entered by PI, or primary name as resolved by CDS. Will become OBJECT in the FITS header. Required. Length limited to 39 characters. Allowed characters are letters, numbers, spaces, or any of: !@#$%^&*()_-+.,?/[]<>' + description='Name for the target entered by PI, or primary name as resolved by CDS.\n\n Will become OBJECT in the FITS header.\n\n Required.\n Length limited to 39 characters.\n Allowed characters are letters, numbers, spaces, or any of:\n !@#$%^&*()_-+.,?/[]<>' ), ] = None label: Annotated[ int | None, Field( - description='Index of the target within the program, set by the system automatically. Used for reference to targets in communications with QSO staff. storage: transient api_visibility: read only' + description='Index of the target within the program, set by the system automatically.\n Used for reference to targets in communications with QSO staff.\n\n storage: transient\n api_visibility: read only' ), ] = None version: Annotated[ int | None, Field( - description='Increments when this object updates. storage: transient api_visibility: read only, incremented by the server' + description='Increments when this object updates.\n\n storage: transient\n api_visibility: read only, incremented by the server' + ), + ] = None + fixed_target: Annotated[ + TargetDataFixedTarget | None, + Field( + description='Used for a target at fixed coordinates, including any provided\n proper motion.' ), ] = None - fixed_target: TargetDataFixedTarget | None = None - moving_target: TargetDataMovingTarget | None = None - magnitude: TargetDataMagnitude | None = None + moving_target: Annotated[ + TargetDataMovingTarget | None, + Field( + description='Used for a target with motion defined by a list of ephemerides.' + ), + ] = None + magnitude: Annotated[ + TargetDataMagnitude | None, + Field(description='The magnitude of the target for bands which are known.'), + ] = None temperature_effective: Annotated[ float | None, Field( - description='The estimated/measured effective temperature, in Kelvin. Required for targets observed with SPIRou or ESPaDOnS. Not used for MegaCam.' + description='The estimated/measured effective temperature, in Kelvin.\n\n Required for targets observed with SPIRou or ESPaDOnS.\n Not used for MegaCam.' ), ] = None standard_star: Annotated[ bool | None, Field( - description='Whether the star is a "standard" star, to be observed each time other science targets are observed. Not used for MegaCam.' + description='Whether the star is a "standard" star, to be observed each time other\n science targets are observed.\n\n Not used for MegaCam.' ), ] = None - linked_target_identifiers: TargetDataLinkedTargetIdentifiers | None = None + linked_target_identifiers: Annotated[ + TargetDataLinkedTargetIdentifiers | None, + Field(description='Identifiers linked to this target.'), + ] = None finding_chart: Annotated[ list[FindingChartData] | None, Field( - description='A read-only view of the finding charts for this target. To create or update finding charts, see the FindingChartService. storage: transient api_visibility: read only' + description='A read-only view of the finding charts for this target.\n To create or update finding charts, see the FindingChartService.\n\n storage: transient\n api_visibility: read only' ), ] = None pointing_offset_token: Annotated[ str | None, Field( - description='The token of a pointing offset which should be used when observing this target. To view or manage pointing offsets, see the PointingOffsetService.' + description='The token of a pointing offset which should be used when observing\n this target.\n\n To view or manage pointing offsets, see the PointingOffsetService.' + ), + ] = None + pointing_offset: Annotated[ + PointingOffsetData | None, + Field( + description='A read-only view of the pointing offset which has been specified by\n pointing_offset_token.\n\n To view or manage pointing offsets, see the PointingOffsetService.\n\n storage: transient\n api_visibility: read only' ), ] = None - pointing_offset: PointingOffsetData | None = None class TargetListResponse(CFHTBaseModel): @@ -2214,14 +2379,21 @@ class TargetUpdateRequest(CFHTBaseModel): runid: Annotated[ str | None, Field(description='Unique identifier of the science program.') ] = None - entity: TargetData | None = None + entity: Annotated[ + TargetData | None, Field(description='Target data to be created or updated.') + ] = None token: Annotated[ str | None, Field( - description='Unique identifier of the target. If a target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789' + description='Unique identifier of the target. If a target with this token already\n exists, it will be updated, otherwise it will be created.\n\n Must begin with the program token followed by a dash, e.g.\n 20AE01-0123456789' + ), + ] = None + lock_version: Annotated[ + Int32Value | None, + Field( + description='If specified, the endpoint will use optimistic locking instead of last\n write wins. This should be set to the current version on entity.' ), ] = None - lock_version: Int32Value | None = None instrument: Annotated[ Instrument | None, Field( @@ -2287,23 +2459,35 @@ class ObservingTemplateData(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - instrument_configuration: InstrumentConfigurationData | None = None + instrument_configuration: Annotated[ + InstrumentConfigurationData | None, Field(description='Next available ID 17') + ] = None constraint: Constraint | None = None - telescope_pattern: TelescopePatternData | None = None + telescope_offset: TelescopePatternData | None = None charge_model: Annotated[ str | None, Field( - description='Used for calculating cost for this pattern Invokes code which reads the ExposureInstructions and attributes a cost to it.' + description='Used for calculating cost for this pattern\n Invokes code which reads the ExposureInstructions and\n attributes a cost to it.' ), ] = None itime: int | None = None - calculate_etime: ExposureTime | None = None - acquisition_time: ExposureTime | None = None + calculate_etime: Annotated[ + ExposureTime | None, + Field( + description='int calculated_etime = 6; /* from the ETC, can be different than the input etime */' + ), + ] = None + acquisition_time: Annotated[ + ExposureTime | None, + Field( + description='SNR or E-Time per exposure => therefore we do not need the ExposureTime message in each IC? question above as well' + ), + ] = None token: str | None = None associated_observing_blocks: Annotated[ list[BigInteger] | None, Field( - description='When just creating an OT, this list is empty When this OT belongs to an OG, then any OBs in sybase that this OT is related too must be filled in. select * from blah where (select id from prg where prg Query to find related OBs is, select ob.* from ob, prg where ob.prg_id = prg.id and prg.runid=? instrument_configuration.sybase_ic_id, constraint.sybase_cons_idselect ob.* from ob, prg, icseq where ob.prg_id = prg.idand prg.runid=and icseq.ob_id=ob.id and ob.con_id = and icseq.ic_id = ;' + description='When just creating an OT, this list is empty\n When this OT belongs to an OG, then any OBs in sybase that this OT is related too must be filled in.\n select * from blah where (select id from prg where prg\n Query to find related OBs is, select ob.* from ob, prg where ob.prg_id = prg.id and prg.runid=? instrument_configuration.sybase_ic_id, constraint.sybase_cons_id\n\nselect ob.* from ob, prg, icseq where ob.prg_id = prg.id\nand prg.runid=\nand icseq.ob_id=ob.id and ob.con_id = \nand icseq.ic_id = ;' ), ] = None name: str | None = None @@ -2312,7 +2496,7 @@ class ObservingTemplateData(CFHTBaseModel): version: Annotated[ int | None, Field( - description='Increments when this object updates storage: transient api_visibility: read only, incremented by the server' + description='Increments when this object updates\n\n storage: transient\n api_visibility: read only, incremented by the server' ), ] = None telescope_pattern_token: str | None = None @@ -2334,9 +2518,14 @@ class ObservingTemplateUpdateRequest(CFHTBaseModel): validate_by_name=True, ) runid: str | None = None - token: str | None = None - entity: ObservingTemplateData | None = None - lock_version: Int32Value | None = None + observing_template_token: str | None = None + observing_template: ObservingTemplateData | None = None + lock_version: Annotated[ + Int32Value | None, + Field( + description='If specified, the endpoint will use optimistic locking instead of last write wins' + ), + ] = None class ObservingTemplateUpdateResponse(CFHTBaseModel): @@ -2345,7 +2534,7 @@ class ObservingTemplateUpdateResponse(CFHTBaseModel): ) success: bool | None = None error: Errors | None = None - entity: ObservingTemplateData | None = None + observing_template: ObservingTemplateData | None = None class ExposureData(CFHTBaseModel): @@ -2359,12 +2548,17 @@ class ExposureData(CFHTBaseModel): parent_exposure_token: str | None = None exposure_status: ExposureDataExposureStatus | None = None observing_template: ObservingTemplateData | None = None - target: TargetData | None = None - observing_group_context: ObservingGroupContext | None = None + target_data: TargetData | None = None + observing_group_context: Annotated[ + ObservingGroupContext | None, + Field( + description='Contains live data for the overall state of the observing group.' + ), + ] = None all_derivations_complete: Annotated[ bool | None, Field( - description='When false, there is data which is still being calculated for this exposure. Data can still be displayed, but the client should attempt to refresh the data.' + description='When false, there is data which is still being calculated for this exposure.\n\n Data can still be displayed, but the client should attempt to refresh the data.' ), ] = None queue_run_id: str | None = None @@ -2372,11 +2566,16 @@ class ExposureData(CFHTBaseModel): prg_run_id: str | None = None observing_component_token: str | None = None sub_totals: SubTotalsData | None = None - attribution: ExposureDataAttribution | None = None + attribution: Annotated[ + ExposureDataAttribution | None, + Field( + description='Allows exposures to be moved to different observing blocks\n\n As soon as an exposure is marked observed, this attribution is available' + ), + ] = None version: Annotated[ int | None, Field( - description='Increments when this object updates storage: transient api_visibility: read only, incremented by the server' + description='Increments when this object updates\n\n storage: transient\n api_visibility: read only, incremented by the server' ), ] = None @@ -2391,23 +2590,31 @@ class ObservingGroupUpdateRequest(CFHTBaseModel): token: Annotated[ str | None, Field( - description='Unique identifier of the observing group. If an observing group target with this token already exists, it will be updated, otherwise it will be created. Must begin with the program token followed by a dash, e.g. 20AE01-0123456789' + description='Unique identifier of the observing group. If an observing group target\n with this token already exists, it will be updated, otherwise it will be\n created.\n\n Must begin with the program token followed by a dash, e.g.\n 20AE01-0123456789' ), ] = None - entity: ObservingGroupData | None = None + entity: Annotated[ + ObservingGroupData | None, + Field(description='Observing group data to be created or updated.'), + ] = None target: Annotated[ dict[str, TargetData] | None, Field( - description='If creating or updating targets with this request, set the data here. Key is the unique identifier of the target and must be prefixed with the runid. Value is the target data and must be valid. Otherwise, the whole request will fail. Observing components can then reference the unique identifier (the map key) in this same request.' + description='If creating or updating targets with this request, set the data here.\n\n Key is the unique identifier of the target and must be prefixed with the runid.\n Value is the target data and must be valid. Otherwise, the whole request will fail.\n\n Observing components can then reference the unique identifier (the map key) in this same request.' ), ] = None observing_template: Annotated[ dict[str, ObservingTemplateData] | None, Field( - description='If creating or updating observing templates with this request, set the data here. Key is the unique identifier of the observing template and must be prefixed with the runid. Value is the observing template data and must be valid. Otherwise, the whole request will fail. Observing components can then reference the unique identifier (the map key) in this same request.' + description='If creating or updating observing templates with this request, set the data here.\n\n Key is the unique identifier of the observing template and must be prefixed with the runid.\n Value is the observing template data and must be valid. Otherwise, the whole request will fail.\n\n Observing components can then reference the unique identifier (the map key) in this same request.' + ), + ] = None + lock_version: Annotated[ + Int32Value | None, + Field( + description='If specified, the endpoint will use optimistic locking instead of last\n write wins. This should be set to the current version on entity.' ), ] = None - lock_version: Int32Value | None = None class SnrBlockDataSnrBlockObservingComponents(CFHTBaseModel): @@ -2439,13 +2646,13 @@ class SnrBlockData(CFHTBaseModel): cumulative_snr: Annotated[ float | None, Field( - description='the total snr achieved of all exposures of all observing components of this snr block' + description='the total snr achieved of all exposures\n of all observing components of this snr block' ), ] = None carry_forward_snr: Annotated[ float | None, Field( - description='if the program is copied to a new program possibly for a new semester the cumulative snr will be moved to the carry_forward_snr and the carry forward snr will be counted in the cumulative snr of the new program' + description='if the program is copied to a new program possibly for a new semester\n the cumulative snr will be moved to the carry_forward_snr and the\n carry forward snr will be counted in the cumulative snr of the new program' ), ] = None observing_components: list[SnrBlockDataSnrBlockObservingComponents] | None = None diff --git a/tests/cfht/test_models.py b/tests/cfht/test_models.py index 863c242..195f49d 100644 --- a/tests/cfht/test_models.py +++ b/tests/cfht/test_models.py @@ -59,24 +59,24 @@ def test_double_value_field_accepts_float(): def test_aliased_double_value_field_accepts_float(): - magnitude = TargetDataMagnitude(V=10.0) + magnitude = TargetDataMagnitude(v=10.0) assert magnitude.v == 10.0 - assert magnitude.api_dump() == {"V": {"value": 10.0}} + assert magnitude.api_dump() == {"v": {"value": 10.0}} def test_double_value_field_accepts_wrapped_api_input(): - magnitude = TargetDataMagnitude.model_validate({"V": {"value": 10.0}}) + magnitude = TargetDataMagnitude.model_validate({"v": {"value": 10.0}}) assert magnitude.v == 10.0 - null_magnitude = TargetDataMagnitude.model_validate({"V": {"value": None}}) + null_magnitude = TargetDataMagnitude.model_validate({"v": {"value": None}}) assert null_magnitude.v is None def test_double_value_field_accepts_float_assignment(): magnitude = TargetDataMagnitude() - magnitude.ab = 10.0 + magnitude.a_b = 10.0 - assert magnitude.ab == 10.0 - assert magnitude.api_dump() == {"AB": {"value": 10.0}} + assert magnitude.a_b == 10.0 + assert magnitude.api_dump() == {"a_b": {"value": 10.0}} diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index 2a0131a..63d7a7e 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -35,7 +35,7 @@ example_mag_by_instrument: dict[Instrument, str] = { Instrument.spirou: "h", Instrument.espadons: "v", - Instrument.megacam: "ab", + Instrument.megacam: "a_b", } @@ -279,7 +279,6 @@ def test_create_observing_group( facility.delete_target(target.token) -@pytest.mark.skip("response does not match schema") def test_get_exposures(program_facilities: list[CFHTFacility]): for facility in program_facilities: exposures = facility.exposures() From 4741a9e04ae30113c023b056ab2c53dce453b779 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Wed, 19 Aug 2026 09:50:03 -0700 Subject: [PATCH 32/33] fix notebook for new model defs --- examples/cfht.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cfht.ipynb b/examples/cfht.ipynb index 656dd45..f892146 100644 --- a/examples/cfht.ipynb +++ b/examples/cfht.ipynb @@ -82,7 +82,7 @@ "target_data = target_data_from_aeon(sidereal_target)\n", "# Fill in CFHT specific data\n", "target_data.token = f\"{facility.program_token}-{random.randint(1000000000, 9999999999)}\"\n", - "target_data.magnitude = TargetDataMagnitude(ab=10.0)\n", + "target_data.magnitude = TargetDataMagnitude(a_b=10.0)\n", "target_data.temperature_effective = 1234.5\n", "target_data.standard_star = False\n", "target_data.pointing_offset_token = f\"00AZ00-PO+{Instrument.megacam.value}+1\"" From 65ff765d7dd46e72317c427cd9998d1c07657ac6 Mon Sep 17 00:00:00 2001 From: Austin Riba Date: Fri, 21 Aug 2026 09:56:21 -0700 Subject: [PATCH 33/33] Update models from latest spec now passing online tests. --- examples/cfht.ipynb | 14 +++++------ src/aeonlib/cfht/models.py | 50 ++++++++++++++++++++++++++------------ tests/cfht/test_models.py | 8 +++--- tests/cfht/test_online.py | 2 +- 4 files changed, 46 insertions(+), 28 deletions(-) diff --git a/examples/cfht.ipynb b/examples/cfht.ipynb index f892146..90160ee 100644 --- a/examples/cfht.ipynb +++ b/examples/cfht.ipynb @@ -11,7 +11,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 1, "id": "08a7f23c-7c4e-4a1f-9481-562a2a391ad0", "metadata": {}, "outputs": [], @@ -35,7 +35,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 2, "id": "c10a85c8-613b-4a57-8de6-2c0c107f2b51", "metadata": {}, "outputs": [], @@ -66,7 +66,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 3, "id": "8707a1a0-fddc-41f0-b931-049c56703129", "metadata": {}, "outputs": [], @@ -82,7 +82,7 @@ "target_data = target_data_from_aeon(sidereal_target)\n", "# Fill in CFHT specific data\n", "target_data.token = f\"{facility.program_token}-{random.randint(1000000000, 9999999999)}\"\n", - "target_data.magnitude = TargetDataMagnitude(a_b=10.0)\n", + "target_data.magnitude = TargetDataMagnitude(ab=10.0)\n", "target_data.temperature_effective = 1234.5\n", "target_data.standard_star = False\n", "target_data.pointing_offset_token = f\"00AZ00-PO+{Instrument.megacam.value}+1\"" @@ -98,7 +98,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 4, "id": "7df70290-4fd6-4958-b59e-cb6aee52d32c", "metadata": {}, "outputs": [ @@ -106,7 +106,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "token='25BE25-5950669302' name='AEONLib CFHT Notebook Target' label=7 version=1 fixed_target=TargetDataFixedTarget(coordinate=SkyCoordinate(ra=320.11, dec=-42.0), proper_motion=FixedTargetProperMotion(ra_mas=None, dec_mas=None), computed_coordinate=None, estimated_radial_velocity_kmps=None) moving_target=None magnitude=TargetDataMagnitude(u=None, b=None, v=None, r=None, i=None, g=None, j=None, h=None, k=None, uu=None, gg=None, rr=None, ii=None, zz=None, ab=10.0) temperature_effective=1234.5 standard_star=None linked_target_identifiers=None finding_chart=[] pointing_offset_token='00AZ00-PO+MEGACAM+1' pointing_offset=PointingOffsetData(token='00AZ00-PO+MEGACAM+1', name='1', offset=OffsetCoordinate(ra_offset=None, dec_offset=None, exposure_number=None), label=None, version=None, user_token='SYSTEM', instrument=, is_system=True)\n" + "token='25BE25-4399809431' name='AEONLib CFHT Notebook Target' label=5 version=1 fixed_target=TargetDataFixedTarget(coordinate=SkyCoordinate(ra=320.11, dec=-42.0), proper_motion=FixedTargetProperMotion(ra_mas=None, dec_mas=None), computed_coordinate=None, estimated_radial_velocity_kmps=None) moving_target=None magnitude=TargetDataMagnitude(u=None, b=None, v=None, r=None, i=None, g=None, j=None, h=None, k=None, uu=None, gg=None, rr=None, ii=None, zz=None, ab=10.0) temperature_effective=1234.5 standard_star=None linked_target_identifiers=None finding_chart=[] pointing_offset_token='00AZ00-PO+MEGACAM+1' pointing_offset=PointingOffsetData(token='00AZ00-PO+MEGACAM+1', name='1', offset=OffsetCoordinate(ra_offset=None, dec_offset=None, exposure_number=None), label=None, version=None, user_token='SYSTEM', instrument=, is_system=True)\n" ] } ], @@ -131,7 +131,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 5, "id": "828e4200-232c-4e82-b69b-f10ed839a6eb", "metadata": {}, "outputs": [], diff --git a/src/aeonlib/cfht/models.py b/src/aeonlib/cfht/models.py index 33cedb3..50378f7 100644 --- a/src/aeonlib/cfht/models.py +++ b/src/aeonlib/cfht/models.py @@ -2,7 +2,7 @@ # ruff: noqa: E741 # generated by datamodel-codegen: # filename: cfhtopenapi.yaml -# timestamp: 2026-08-17T15:18:13+00:00 +# timestamp: 2026-08-21T16:42:09+00:00 from __future__ import annotations @@ -1131,34 +1131,51 @@ class TargetDataMagnitude(CFHTBaseModel): model_config = ConfigDict( validate_by_name=True, ) - u: Annotated[DoubleValue | None, Field(description='U-band magnitude')] = None - b: Annotated[DoubleValue | None, Field(description='B-band magnitude')] = None + u: Annotated[DoubleValue | None, Field(alias='U', description='U-band magnitude')] = ( + None + ) + b: Annotated[DoubleValue | None, Field(alias='B', description='B-band magnitude')] = ( + None + ) v: Annotated[ DoubleValue | None, Field( - description='V-band magnitude\n\n Required for targets observed with ESPaDonS.' + alias='V', + description='V-band magnitude\n\n Required for targets observed with ESPaDonS.', ), ] = None - r: Annotated[DoubleValue | None, Field(description='R-band magnitude')] = None - i: Annotated[DoubleValue | None, Field(description='I-band magnitude')] = None - g: Annotated[DoubleValue | None, Field(description='G-band magnitude')] = None - j: Annotated[DoubleValue | None, Field(description='J-band magnitude')] = None + r: Annotated[DoubleValue | None, Field(alias='R', description='R-band magnitude')] = ( + None + ) + i: Annotated[DoubleValue | None, Field(alias='I', description='I-band magnitude')] = ( + None + ) + g: Annotated[DoubleValue | None, Field(alias='G', description='G-band magnitude')] = ( + None + ) + j: Annotated[DoubleValue | None, Field(alias='J', description='J-band magnitude')] = ( + None + ) h: Annotated[ DoubleValue | None, Field( - description='H-band magnitude\n\n Required for targets observed with SPIRou.' + alias='H', + description='H-band magnitude\n\n Required for targets observed with SPIRou.', ), ] = None - k: Annotated[DoubleValue | None, Field(description='K-band magnitude')] = None + k: Annotated[DoubleValue | None, Field(alias='K', description='K-band magnitude')] = ( + None + ) uu: Annotated[DoubleValue | None, Field(description='u-band magnitude')] = None gg: Annotated[DoubleValue | None, Field(description='g-band magnitude')] = None rr: Annotated[DoubleValue | None, Field(description='r-band magnitude')] = None ii: Annotated[DoubleValue | None, Field(description='i-band magnitude')] = None zz: Annotated[DoubleValue | None, Field(description='z-band magnitude')] = None - a_b: Annotated[ + ab: Annotated[ DoubleValue | None, Field( - description='AB magnitude\n\n Required for targets observed with MegaCam.' + alias='AB', + description='AB magnitude\n\n Required for targets observed with MegaCam.', ), ] = None @@ -1881,7 +1898,7 @@ class ObservingGroupContext(CFHTBaseModel): description='*\n TimeAccounting for the observing group.\n\n api_visibility: readonly' ), ] = None - exposure_type: ExposureType | None = None + exposure_type: Annotated[ExposureType | None, Field(alias='exposureType')] = None use_group_dither_scheme: bool | None = None @@ -1907,7 +1924,7 @@ class ProgramData(CFHTBaseModel): abstract: str | None = None contact_info: ProgramDataContactInfo | None = None comment: list[ProgramDataComments] | None = None - sybase_pi_user_id: str | None = None + sybase_pi_user_id: Annotated[str | None, Field(alias='sybasePiUserId')] = None sub_totals: SubTotalsData | None = None token: Annotated[ str | None, @@ -2134,10 +2151,11 @@ class InstrumentConfigurationDataMegacamConfigurationData(CFHTBaseModel): minimum_exposures: int | None = None min_exptime_ms: int | None = None max_exptime_ms: int | None = None - mag_a_b: Annotated[ + mag_ab: Annotated[ float | None, Field( - description='TODO: interesting field here because associated with the target, but in the legacy mp snr mode, the mag is carried with the IC! so have to go back to this :(' + alias='magAB', + description='TODO: interesting field here because associated with the target, but in the legacy mp snr mode, the mag is carried with the IC! so have to go back to this :(', ), ] = None snr_mode: SnrMode | None = None diff --git a/tests/cfht/test_models.py b/tests/cfht/test_models.py index 195f49d..02c4fcd 100644 --- a/tests/cfht/test_models.py +++ b/tests/cfht/test_models.py @@ -62,7 +62,7 @@ def test_aliased_double_value_field_accepts_float(): magnitude = TargetDataMagnitude(v=10.0) assert magnitude.v == 10.0 - assert magnitude.api_dump() == {"v": {"value": 10.0}} + assert magnitude.api_dump() == {"V": {"value": 10.0}} def test_double_value_field_accepts_wrapped_api_input(): @@ -76,7 +76,7 @@ def test_double_value_field_accepts_wrapped_api_input(): def test_double_value_field_accepts_float_assignment(): magnitude = TargetDataMagnitude() - magnitude.a_b = 10.0 + magnitude.ab = 10.0 - assert magnitude.a_b == 10.0 - assert magnitude.api_dump() == {"a_b": {"value": 10.0}} + assert magnitude.ab == 10.0 + assert magnitude.api_dump() == {"AB": {"value": 10.0}} diff --git a/tests/cfht/test_online.py b/tests/cfht/test_online.py index 63d7a7e..93d92db 100644 --- a/tests/cfht/test_online.py +++ b/tests/cfht/test_online.py @@ -35,7 +35,7 @@ example_mag_by_instrument: dict[Instrument, str] = { Instrument.spirou: "h", Instrument.espadons: "v", - Instrument.megacam: "a_b", + Instrument.megacam: "ab", }