diff --git a/services/capability/test/test_capability_views.py b/services/capability/test/test_capability_views.py index f2f44f824bc62a5f97c601040f4712e235fb18ab..b6a741dacd031b2e6e3ae8ed31ba1fa11c75771e 100644 --- a/services/capability/test/test_capability_views.py +++ b/services/capability/test/test_capability_views.py @@ -53,6 +53,7 @@ def test_view_capability(test_config: Configurator, request_null_capability: Dum "paused": False, "state_machine": "None", "casa_recipe": None, + "default_casa_capo_key": None, } request_null_capability.matchdict["capability_name"] = "null" response = view_capability(request_null_capability) @@ -133,6 +134,7 @@ def test_create_capability(test_config: Configurator, request_null_capability: D "paused": False, "state_machine": "simple", "casa_recipe": None, + "default_casa_capo_key": None, } assert response.json_body == expected_response # Assert test capability has been added to list of capabilities (mocked) diff --git a/shared/workspaces/alembic/versions/eaca2da45ab6_capability_default_casa_capo_key.py b/shared/workspaces/alembic/versions/eaca2da45ab6_capability_default_casa_capo_key.py new file mode 100644 index 0000000000000000000000000000000000000000..17cde5706e5a60fa49b0c6fb134ff90459aac866 --- /dev/null +++ b/shared/workspaces/alembic/versions/eaca2da45ab6_capability_default_casa_capo_key.py @@ -0,0 +1,51 @@ +"""capability default casa capo key + +Revision ID: eaca2da45ab6 +Revises: 15d7b52aace4 +Create Date: 2024-06-11 22:33:12.112244 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'eaca2da45ab6' +down_revision = '15d7b52aace4' +branch_labels = None +depends_on = None + +NEW_COLUMN_NAME = "default_casa_capo_key" + +# Separating on a pipe to distinguish vla|alma values, this could probably be a JSON column +DEFAULT_CASA_CAPO_KEYS = { + "std_calibration": "homeForReprocessing", + "restore_cms": "homeForVlaRestore|homeForAlmaRestore", + "restore_basic_ms": "homeForDownloads", + "std_cms_imaging": "homeForVlaUserDrivenImaging|homeForAlmaUserDrivenImaging", + "std_restore_imaging": "homeForVlaUserDrivenImaging|homeForAlmaUserDrivenImaging", +} + + +def upgrade(): + op.add_column( + "capabilities", + sa.Column( + NEW_COLUMN_NAME, + sa.String, + comment="CAPO key pointing to the default CASA version for each capability", + ), + ) + + for k, v in DEFAULT_CASA_CAPO_KEYS.items(): + op.execute( + f""" + UPDATE capabilities + SET {NEW_COLUMN_NAME} = '{v}' + WHERE capability_name = '{k}' + """ + ) + + +def downgrade(): + op.drop_column("capabilities", NEW_COLUMN_NAME) diff --git a/shared/workspaces/workspaces/capability/schema.py b/shared/workspaces/workspaces/capability/schema.py index 541099c6e0d3859d07cf4b4d03973dca70cb7f24..c857e5303f3d9fb757dd60c138cb8d8875af04f6 100644 --- a/shared/workspaces/workspaces/capability/schema.py +++ b/shared/workspaces/workspaces/capability/schema.py @@ -1052,6 +1052,7 @@ class Capability(JSONSerializable): requires_qa = sa.Column("requires_qa", sa.Boolean, default=False) single_version_only = sa.Column("single_version_only", sa.Boolean, default=False) casa_recipe = sa.Column("casa_recipe", sa.String) + default_casa_capo_key = sa.Column("default_casa_capo_key", sa.JSON) templates = relationship("CapabilityTemplate", backref="capabilities", lazy="dynamic") requests = relationship("CapabilityRequest", back_populates="capability") state_machine = relationship("StateMachine", back_populates="capability", uselist=False) @@ -1097,6 +1098,7 @@ class Capability(JSONSerializable): "requires_qa": self.requires_qa, "single_version_only": self.single_version_only, "casa_recipe": self.casa_recipe, + "default_casa_capo_key": self.default_casa_capo_key, "paused": self.paused, "state_machine": self.state_machine.machine_type if self.state_machine else "None", } diff --git a/shared/workspaces/workspaces/system/services/casa_matrix_service.py b/shared/workspaces/workspaces/system/services/casa_matrix_service.py index 863a833a84e00af200a6f31f8dc3260ce4f3dd39..dd9949bdd779e468fcbfc83491e484e5ebab47d2 100644 --- a/shared/workspaces/workspaces/system/services/casa_matrix_service.py +++ b/shared/workspaces/workspaces/system/services/casa_matrix_service.py @@ -44,17 +44,6 @@ CASA_VERSION_REGEX = r"[a-zA-Z\-]+([\d]+(\.[\d]+){2}(-[\d]+){0,1}).*" PIPELINE_VERSION_REGEX = r".*([\d]{4}(\.[\d]+){3}).*" EL_SUFFIX_REGEX = r"el[0-9]+$" -DEFAULT_CAPABILITY = "std_calibration" -DEFAULT_TELESCOPE = "vla" - -CASA_DEFAULT_KEYS = { - "std_calibration": "homeForReprocessing", - "restore_cms": {"vla": "homeForVlaRestore", "alma": "homeForAlmaRestore"}, - "restore_basic_ms": "homeForDownloads", - "std_cms_imaging": {"vla": "homeForVlaUserDrivenImaging", "alma": "homeForAlmaUserDrivenImaging"}, - "std_restore_imaging": {"vla": "homeForVlaUserDrivenImaging", "alma": "homeForAlmaUserDrivenImaging"}, -} - def casa_version_from_path(path: str) -> str: """ @@ -217,8 +206,8 @@ class CasaMatrixService(CasaMatrixServiceIF): def get_version( self, version: str | None = None, - capability: str | None = DEFAULT_CAPABILITY, - telescope: str | None = DEFAULT_TELESCOPE, + capability: str | None = None, + telescope: str | None = None, ) -> dict[str, str]: """ Returns the CASA version that is valid for processing. @@ -237,8 +226,8 @@ class CasaMatrixService(CasaMatrixServiceIF): def get_versions( self, version: str | None = None, - capability: str | None = DEFAULT_CAPABILITY, - telescope: str | None = DEFAULT_TELESCOPE, + capability: str | None = None, + telescope: str | None = None, ) -> list[dict[str, str]]: """ Returns the CASA versions that are valid for processing as a dict. @@ -272,17 +261,18 @@ class CasaMatrixService(CasaMatrixServiceIF): # If the default is invalid, the newest version will be first in the list if versions[0]["version"] != version: default = self.get_default_version(capability, telescope) - for v in versions: - if v["version"] == default: - versions.insert(0, versions.pop(versions.index(v))) + if default: + for v in versions: + if v["version"] == default: + versions.insert(0, versions.pop(versions.index(v))) logger.debug(f"Found CASA versions for {capability}: {versions}") return versions def get_default_version( - self, capability: str = DEFAULT_CAPABILITY, telescope: str | None = DEFAULT_TELESCOPE - ) -> str: + self, capability: str | None, telescope: str | None + ) -> str | None: """ Gets the default CASA version from CAPO for the given capability (the std_calibration capability is default). @@ -290,14 +280,20 @@ class CasaMatrixService(CasaMatrixServiceIF): :param telescope: (Optional) The telescope to get the default (used for some capabilities) :return: The full path to the default CASA version as a string """ - key = CASA_DEFAULT_KEYS[capability] + if not capability: + return None - # Some capabilities have different paths per telescope - if isinstance(key, dict): - if telescope.lower() in key: - key = key[telescope.lower()] - else: - key = key[DEFAULT_TELESCOPE] + result = self.session.query(Capability.default_casa_capo_key).filter_by(name=capability).first() + keys = result[0].split("|") if result else None + + if not keys: + return None + + # Some capabilities have different paths per telescope (looking at you ALMA) + if len(keys) > 1 and telescope and telescope.lower() == 'alma': + key = keys[1] + else: + key = keys[0] # The CAPO setting is a full path to a CASA installation (this assumes it is installed) path = self.casa_settings[key] @@ -310,8 +306,8 @@ class CasaMatrixService(CasaMatrixServiceIF): :return: CASA recipe as a string, individual procedures delimited by a pipe character """ - result = self.session.query(Capability).filter_by(name=capability).first() - return result.casa_recipe if result else None + result = self.session.query(Capability.casa_recipe).filter_by(name=capability).first() + return result[0] if result else None def save_entity(self, entity: Base): """