diff --git a/scheduler/core/calculations/groupinfo.py b/scheduler/core/calculations/groupinfo.py index d3785fe6..bdd7b2d4 100644 --- a/scheduler/core/calculations/groupinfo.py +++ b/scheduler/core/calculations/groupinfo.py @@ -28,8 +28,6 @@ class GroupInfo: 6. Scoring based on how the wind affects the group. 7. A list of indices of the time slots across the nights as to when the group can be scheduled. 8. The score assigned to the group. - - A group can be split if and only if it contains more than one observation. """ minimum_conditions: Conditions is_splittable: bool diff --git a/scheduler/core/components/optimizer/greedymax.py b/scheduler/core/components/optimizer/greedymax.py index 579971b4..ae5565e1 100644 --- a/scheduler/core/components/optimizer/greedymax.py +++ b/scheduler/core/components/optimizer/greedymax.py @@ -5,7 +5,6 @@ from dataclasses import dataclass from datetime import datetime, timedelta -from enum import Enum from typing import final, Dict, FrozenSet, List, Optional, Tuple import matplotlib.pyplot as plt diff --git a/scheduler/core/programprovider/ocs/__init__.py b/scheduler/core/programprovider/ocs/__init__.py index 4884cf57..cf467581 100644 --- a/scheduler/core/programprovider/ocs/__init__.py +++ b/scheduler/core/programprovider/ocs/__init__.py @@ -663,8 +663,10 @@ def autocorr_lag(x): # TODO: For now, we focus on instruments, and GMOS FPUs and dispersers exclusively. instrument_resources = frozenset([self._sources.origin.resource.lookup_resource(instrument)]) if 'GMOS' in instrument: - # Convert FPUs and dispersers to barcodes. - fpu_resources = frozenset([self._sources.origin.resource.fpu_to_barcode(site, fpu, instrument) for fpu in fpus]) + # Convert FPUs and dispersers to barcodes. Note that None might be contained in some of these + # sets, but we filter below to remove them. + fpu_resources = frozenset([self._sources.origin.resource.fpu_to_barcode(site, fpu, instrument) + for fpu in fpus]) disperser_resources = frozenset([self._sources.origin.resource.lookup_resource(disperser.split('_')[0]) for disperser in dispersers]) resources = frozenset([r for r in fpu_resources | disperser_resources | instrument_resources]) diff --git a/scheduler/services/resource/base.py b/scheduler/services/resource/base.py index 315a40da..98b2570c 100644 --- a/scheduler/services/resource/base.py +++ b/scheduler/services/resource/base.py @@ -15,7 +15,6 @@ from openpyxl import load_workbook from definitions import ROOT_DIR -from scheduler.core.meta import Singleton from .filters import * from .night_resource_configuration import NightConfiguration from .google_drive_downloader import GoogleDriveDownloader @@ -65,6 +64,11 @@ class ResourceService(ExternalService): 'PinholeC': 'PinholeC' }}) + # Constants for converting MDF to barcodes. + _instd = {'GMOS': '1', 'GMOS-N': '1', 'GMOS-S': '1', 'Flamingos2': '3'} + _semd = {'A': '0', 'B': '1'} + _progd = {'Q': '0', 'C': '1', 'L': '2', 'F': '3', 'S': '8', 'D': '9'} + def __init__(self, sites: FrozenSet[Site] = ALL_SITES): self._all_resources: Dict[str, Resource] = {} self._sites = sites @@ -102,16 +106,16 @@ def __init__(self, sites: FrozenSet[Site] = ALL_SITES): # The final output from this class: the configuration per night. self._night_configurations: Dict[Site, Dict[date, NightConfiguration]] = {site: {} for site in self._sites} - @staticmethod - def _mdf_to_barcode(mdfname: str, inst: str) -> Resource: + def _mdf_to_barcode(self, mdfname: str, inst: str) -> Optional[Resource]: """Legacy MOS mask barcode convention""" barcode = None - instd = {'GMOS': '1', 'GMOS-N': '1', 'GMOS-S': '1', 'Flamingos2': '3'} - semd = {'A': '0', 'B': '1'} - progd = {'Q': '0', 'C': '1', 'L': '2', 'F': '3', 'S': '8', 'D': '9'} - if inst in instd.keys(): - barcode = instd[inst] + semd[mdfname[6]] + progd[mdfname[7]] + mdfname[-6:-3] + mdfname[-2:] - return Resource(id=barcode) + if inst in ResourceService._instd.keys(): + # Collect the components of the string from the MDF name. + inst_id = ResourceService._instd[inst] + sem_id = ResourceService._semd[mdfname[6]] + progtype_id = ResourceService._progd[mdfname[7]] + barcode = f'{inst_id}{sem_id}{progtype_id}{mdfname[-6:-3]}{mdfname[-2:]}' + return self.lookup_resource(barcode) def _itcd_fpu_to_barcode_parser(self, r: List[str], site: Site) -> Set[str]: return {self._itcd_fpu_to_barcode[site][r[0].strip()].id} | {i.strip() for i in r[1:]} @@ -242,7 +246,7 @@ def _load_csv(self, site: Site, the new date. """ - def _process_file(f): + def _process_file(f) -> None: reader = csv.reader(f, delimiter=',') prev_row_date: Optional[date] = None diff --git a/tests/unit/scheduler/core/programprovider/ocs/test_ocs_api.py b/tests/unit/scheduler/core/programprovider/ocs/test_ocs_api.py index fbbbd940..22498fb4 100644 --- a/tests/unit/scheduler/core/programprovider/ocs/test_ocs_api.py +++ b/tests/unit/scheduler/core/programprovider/ocs/test_ocs_api.py @@ -8,10 +8,10 @@ from lucupy.helpers import dmsstr2deg from lucupy.minimodel import (AndGroup, AndOption, Atom, Band, CloudCover, Conditions, Constraints, ElevationType, GroupID, ImageQuality, Magnitude, MagnitudeBands, ObservationClass, ObservationID, - ObservationStatus, Priority, Program, ProgramID, ProgramMode, ProgramTypes, QAState, - Resource, ROOT_GROUP_ID, Semester, SemesterHalf, SetupTimeType, SiderealTarget, Site, - SkyBackground, TargetName, TargetType, TimeAccountingCode, TimeAllocation, TimingWindow, - TooType, WaterVapor, Wavelength) + ObservationMode, ObservationStatus, Priority, Program, ProgramID, ProgramMode, + ProgramTypes, QAState, Resource, ROOT_GROUP_ID, Semester, SemesterHalf, SetupTimeType, + SiderealTarget, Site, SkyBackground, TargetName, TargetType, TimeAccountingCode, + TimeAllocation, TimingWindow, TooType, WaterVapor, Wavelength) from lucupy.observatory.gemini.geminiobservation import GeminiObservation from lucupy.timeutils import sex2dec @@ -118,7 +118,8 @@ def create_minimodel_program() -> Program: qa_state=QAState.NONE, guide_state=True, resources=frozenset({gmosn, mirror}), - wavelengths=frozenset({Wavelength(0.475)}) + wavelengths=frozenset({Wavelength(0.475)}), + obs_mode=ObservationMode.IMAGING ) ] @@ -232,7 +233,8 @@ def create_minimodel_program() -> Program: qa_state=QAState.NONE, guide_state=True, resources=frozenset({gnirs}), - wavelengths=frozenset({Wavelength(2.2)}) + wavelengths=frozenset({Wavelength(2.2)}), + obs_mode=ObservationMode.IMAGING ) ] @@ -351,7 +353,8 @@ def create_minimodel_program() -> Program: qa_state=QAState.NONE, guide_state=True, resources=frozenset({gnirs}), - wavelengths=frozenset({Wavelength(2.2)}) + wavelengths=frozenset({Wavelength(2.2)}), + obs_mode=ObservationMode.IMAGING ) ] @@ -526,7 +529,8 @@ def create_minimodel_program() -> Program: qa_state=QAState.NONE, guide_state=True, resources=frozenset({gmosn, mirror}), - wavelengths=frozenset({Wavelength(0.475)}) + wavelengths=frozenset({Wavelength(0.475)}), + obs_mode=ObservationMode.IMAGING ) ]