From 1739141e600d82766a079d57c36038518c96f2fa Mon Sep 17 00:00:00 2001 From: chausman <chausman@nrao.edu> Date: Fri, 9 Jul 2021 16:33:53 -0600 Subject: [PATCH] refactoring casa-envoy --- .../pexable/casa_envoy/casa_envoy/auditor.py | 81 ++++--- .../casa_envoy/casa_envoy/interfaces.py | 19 +- .../casa_envoy/casa_envoy/launchers.py | 224 +++++++++++------- .../pexable/casa_envoy/casa_envoy/palaver.py | 101 ++------ .../casa_envoy/test/image-metadata.json | 1 + .../pexable/casa_envoy/test/test.json | 3 +- .../pexable/casa_envoy/test/test_auditor.py | 99 +++++--- .../casa_envoy/test/test_casa_envoy.py | 57 +++-- .../pexable/casa_envoy/test/test_launchers.py | 99 ++++---- 9 files changed, 382 insertions(+), 302 deletions(-) diff --git a/apps/cli/executables/pexable/casa_envoy/casa_envoy/auditor.py b/apps/cli/executables/pexable/casa_envoy/casa_envoy/auditor.py index 078367eb8..f6c85c0d2 100644 --- a/apps/cli/executables/pexable/casa_envoy/casa_envoy/auditor.py +++ b/apps/cli/executables/pexable/casa_envoy/casa_envoy/auditor.py @@ -14,49 +14,57 @@ from casa_envoy.interfaces import AuditorIF from casa_envoy.schema import AbstractTextFile -def get_fields_for(filename: str) -> list: - metadata_list = ["fileSetIds", "workflowName", "systemId", "productLocator", "destinationDirectory"] +def get_fields_for(product_type: str, filename: str) -> list: + cal_metadata_list = [ + "fileSetIds", + "workflowName", + "systemId", + "creationTime", + "productLocator", + "destinationDirectory", + ] + img_metadata_list = [ + "fileSetIds", + "workflowName", + "systemId", + "creationTime", + "productLocator", + "destinationDirectory", + "cms_path", + "sdmId", + ] + ppr_list = ["RootDirectory", "RelativePath", "SdmIdentifier"] if ".xml" in filename: return ppr_list - if ".json" in filename: - return metadata_list + if ".json" in filename and "cal" in product_type: + return cal_metadata_list + elif ".json" in filename and "img" in product_type: + return img_metadata_list -def get_xml_content(filename: str): - with open(filename) as file: - return BeautifulSoup(file.read(), "xml") +def get_xml_content(file: AbstractTextFile): + return BeautifulSoup(file.content, "xml") -def get_value_for(filename: str, key: str) -> str: - if ".xml" in filename: - ppr_content = get_xml_content(filename) +def get_value_for(file: AbstractTextFile, key: str) -> str: + if ".xml" in file.filename: + ppr_content = get_xml_content(file) return ppr_content.find(key).string - if ".json" in filename: - with open(filename) as file: - metadata = json.loads(file.read()) - return metadata[key] + if ".json" in file.filename: + content = json.loads(file.content) + return content[key] class AuditFiles(AuditorIF): - def __init__(self, files: List[str], settings: Dict[str, str]): + def __init__(self, files: List[AbstractTextFile], settings: Dict[str, str]): + self.logger = logging.getLogger("casa_envoy") self.files = files self.settings = settings - self.logger = logging.getLogger("casa_envoy") - - def read_file(self, filename: str) -> AbstractTextFile: - if os.path.isfile(filename): - with open(filename) as file: - if ".json" in filename: - metadata = json.loads(file.read()) - return AbstractTextFile(filename, json.dumps(metadata)) - else: - if ".xml" in filename: - ppr = file.read() - return AbstractTextFile(filename, ppr) + self.product_type = settings.get("product_type") def check_required_fields(self, file: AbstractTextFile, fields: list) -> bool: missing = [] @@ -83,7 +91,7 @@ class AuditFiles(AuditorIF): shutil.copy(ppr.filename, "./working") os.chdir("./working") - parsed_xml = get_xml_content(ppr.filename) + parsed_xml = get_xml_content(ppr) parsed_xml.find("RootDirectory").string = self.settings["rootDirectory"] parsed_xml.find("RelativePath").string = self.settings["processingDirectory"] @@ -97,15 +105,16 @@ class AuditFiles(AuditorIF): invalid_files = [] for file in self.files: self.logger.info(f"Auditing file {file}...") - f = self.read_file(file) - if f.filename == "PPR.xml": + if file.filename == "PPR.xml": self.logger.info("Correcting PPR.xml for condor processing...") - f = self.correct_for_condor(f) + file = self.correct_for_condor(file) - valid = self.check_required_fields(file=f, fields=get_fields_for(f.filename)) + valid = self.check_required_fields( + file=file, fields=get_fields_for(self.product_type, file.filename) + ) if not valid: - invalid_files.append(f.filename) + invalid_files.append(file.filename) if len(invalid_files) != 0: self.logger.info(f"INVALID FILE FOUND: {invalid_files}") @@ -115,7 +124,7 @@ class AuditFiles(AuditorIF): class AuditDirectories(AuditorIF): - def __init__(self, ppr: str, settings: Dict[str, str]): + def __init__(self, ppr: AbstractTextFile, settings: Dict[str, str]): self.logger = logging.getLogger("casa_envoy") self.rootDirectory = settings["rootDirectory"] self.relative_path = settings["processingDirectory"] @@ -142,4 +151,6 @@ class AuditDirectories(AuditorIF): self.logger.info("FAILURE: data not found in rawdata/") return False else: - self.logger.info("DIRECTORY ERROR: A directory is missing from the processing root directory.") + self.logger.info( + "DIRECTORY ERROR: A directory is missing from the processing root directory." + ) diff --git a/apps/cli/executables/pexable/casa_envoy/casa_envoy/interfaces.py b/apps/cli/executables/pexable/casa_envoy/casa_envoy/interfaces.py index 597a2f128..6721bdb07 100644 --- a/apps/cli/executables/pexable/casa_envoy/casa_envoy/interfaces.py +++ b/apps/cli/executables/pexable/casa_envoy/casa_envoy/interfaces.py @@ -1,27 +1,30 @@ """ Interfaces for casa_envoy """ +import abc from abc import ABC +from typing import Dict -class CasaLauncherIF(ABC): +class LauncherIF(ABC): """ - Generic CASA Launcher methods. + Generic Launcher methods. Should be implemented for any type of CASA processing. """ - def run(self): - raise NotImplementedError - def setup_environment(self, parameters: dict): - raise NotImplementedError + @abc.abstractmethod + def launch_casa(self): + pass - def check_logs(self, parent_path: str): - raise NotImplementedError + @abc.abstractmethod + def run_audit(self, parameters: Dict[str, str]): + pass class AuditorIF(ABC): """ Generic functionality implementation for auditor classes """ + def audit(self): raise NotImplementedError diff --git a/apps/cli/executables/pexable/casa_envoy/casa_envoy/launchers.py b/apps/cli/executables/pexable/casa_envoy/casa_envoy/launchers.py index 911e4b473..ccc21a1b3 100644 --- a/apps/cli/executables/pexable/casa_envoy/casa_envoy/launchers.py +++ b/apps/cli/executables/pexable/casa_envoy/casa_envoy/launchers.py @@ -4,106 +4,84 @@ import glob import sys import os import subprocess +from typing import Dict -from casa_envoy.interfaces import CasaLauncherIF -from casa_envoy.schema import AbstractTextFile +import json + +from casa_envoy.auditor import AuditFiles, AuditDirectories +from casa_envoy.interfaces import LauncherIF +from casa_envoy.schema import AbstractTextFile -def get_base_environment(parameters: dict): - os.environ["SCIPIPE_ROOTDIR"] = parameters["rootDirectory"] - os.environ["CASA_HOME"] = parameters["homeForReprocessing"] - os.environ["LANG"] = "en_US.UTF-8" +def get_abs_file(filename: str) -> AbstractTextFile: + with open(filename) as file: + if ".json" in filename: + content = json.loads(file.read()) + elif ".xml" in filename: + content = file.read() -def check_processing_env(logger: logging.Logger): - logger.info("Checking processing environment:") - env1 = os.environ.get("SCIPIPE_ROOTDIR") - logger.info(f"SCIPIPE_ROOTDIR: {env1}") - env2 = os.environ.get("CASA_HOME") - logger.info(f"CASA_HOME: {env2}") - env3 = os.environ.get("PPR_FILENAME") - logger.info(f"PPR_FILENAME: {env3}") - env4 = os.environ.get("LANG") - logger.info(f"LANG: {env4}") - if "None" in [env1, env2, env3, env4]: - logger.info("Environment setup Failed!") - sys.exit(1) - else: - logger.info("Environment ready for processing") + return AbstractTextFile(filename, content) -class CalibrationLauncher(CasaLauncherIF): - def __init__(self, ppr: AbstractTextFile, metadata: AbstractTextFile): +class CasaLauncher: + def __init__(self, parameters: dict): self.logger = logging.getLogger("casa_envoy") - self.ppr = ppr - self.metadata = metadata + self.parameters = parameters - def run(self): - self.logger.info("RUNNING CASA CALIBRATION!") - os.chdir("./working") - result = subprocess.Popen( - "PYTHONPATH='' xvfb-run -e ${PWD}/xvfb-run.err.txt -d -s \"-screen 0 800x600x16\" " - "${CASA_HOME}/bin/casa --pipeline --nogui --nologger -c " - "${CASA_HOME}/pipeline/pipeline/runvlapipeline.py ${PPR_FILENAME} || true", - shell=True, - executable="/bin/bash", - stdout=sys.stdout, - stderr=sys.stderr, - ) - return result.communicate() - - def setup_environment(self, parameters: dict): - get_base_environment(parameters) - os.environ["PPR_FILENAME"] = str(self.ppr) - - check_processing_env(self.logger) - - def check_logs(self, parent_path: str): - self.logger.info("CHECKING CASA CALIBRATION LOGS!") - # make sure we are in the correct directory to find log file - if not os.getcwd().endswith("/working"): - os.chdir(parent_path + "/working") + def setup_environment(self): + os.environ["SCIPIPE_ROOTDIR"] = self.parameters["rootDirectory"] + os.environ["CASA_HOME"] = self.parameters["homeForReprocessing"] + os.environ["LANG"] = "en_US.UTF-8" + os.environ["PPR_FILENAME"] = self.parameters["ppr"] - casa_logs = glob.glob("casa-*.log") + self.check_processing_env() - for file in casa_logs: - if re.match("^.*SEVERE\sflagmanager.*$", open(file).read()): - self.logger.error("CASA ERROR!") - else: - self.logger.info("CASA Success!") + def check_processing_env(self): + self.logger.info("Checking processing environment:") + env_list = ["SCIPIPE_ROOTDIR", "CASA_HOME", "PPR_FILENAME", "LANG"] + result_list = [] -class ImagingLauncher(CasaLauncherIF): - def __init__(self, ppr: AbstractTextFile, metadata: AbstractTextFile): - self.logger = logging.getLogger("casa_envoy") - self.ppr = ppr - self.metadata = metadata + for var in env_list: + env = os.environ.get(var) + result_list.append(env) + self.logger.info(f"{var}: {env}") + + if "None" in result_list: + self.logger.info("Environment setup Failed!") + sys.exit(1) + else: + self.logger.info("Environment ready for processing") def run(self): - self.logger.info("RUNNING CASA IMAGING!") - os.chdir("./working") - result = subprocess.Popen( - "PYTHONPATH='' xvfb-run -e ${PWD}/xvfb-run.err.txt -d -s \"-screen 0 800x600x16\" " - "${CASA_HOME}/bin/casa --pipeline --nogui --nologger -c " - "${CASA_HOME}/pipeline/pipeline/runvlapipeline.py ${PPR_FILENAME} || true", - shell=True, - executable="/bin/bash", - stdout=sys.stdout, - stderr=sys.stderr, - ) - return result.communicate() - - def setup_environment(self, parameters: dict): - get_base_environment(parameters) - os.environ["PPR_FILENAME"] = str(self.ppr) - - check_processing_env(self.logger) - - def check_logs(self, parent_path: str): - self.logger.info("CHECKING CASA IMAGING LOGS!") + self.setup_environment() + + if self.parameters.get("useCasa"): + self.logger.info("RUNNING CASA!") + os.chdir("./working") + result = subprocess.Popen( + "PYTHONPATH='' xvfb-run -e ${PWD}/xvfb-run.err.txt -d -s \"-screen 0 800x600x16\" " + "${CASA_HOME}/bin/casa --pipeline --nogui --nologger -c " + "${CASA_HOME}/pipeline/pipeline/runvlapipeline.py ${PPR_FILENAME} || true", + shell=True, + executable="/bin/bash", + stdout=sys.stdout, + stderr=sys.stderr, + ) + return result.communicate() + else: + self.logger.info("RUNNING VELA!") + run_type = self.parameters.get("product_type") + metadata = self.parameters.get("metadata") + ppr = self.parameters.get("ppr") + subprocess.run(["./vela", run_type, metadata, ppr]) + + def check_logs(self): + self.logger.info("CHECKING CASA LOGS!") # make sure we are in the correct directory to find log file if not os.getcwd().endswith("/working"): - os.chdir(parent_path + "/working") + os.chdir(self.parameters.get("parent_path") + "/working") casa_logs = glob.glob("casa-*.log") @@ -112,3 +90,83 @@ class ImagingLauncher(CasaLauncherIF): self.logger.error("CASA ERROR!") else: self.logger.info("CASA Success!") + + +class CalibrationLauncher(LauncherIF): + def __init__(self, parameters: dict): + self.logger = logging.getLogger("casa_envoy") + self.parameters = parameters + self.ppr = get_abs_file(parameters.get("ppr")) + self.metadata = get_abs_file(parameters.get("metadata")) + + def launch_casa(self): + if self.check_calibratable(): + self.run_audit(self.parameters) + CasaLauncher(self.parameters).run() + else: + self.logger.error("ERROR: Provided SPL is not type execution block!") + sys.exit(1) + + def check_calibratable(self) -> bool: + spl = self.metadata.content["productLocator"] + if "execblock" in spl: + return True + else: + self.logger.info("SPL ERROR: This product locator is not calibratable!") + return False + + def run_audit(self, parameters: Dict[str, str]): + dir_audit = AuditDirectories(self.ppr, parameters).audit() + if dir_audit: + self.logger.info("Directory audit successful!") + else: + self.logger.error("FAILURE: directory structure audit was unsuccessful!") + sys.exit(1) + + audit = AuditFiles([self.ppr, self.metadata], parameters).audit() + if audit: + self.logger.info("File audit successful!") + else: + self.logger.error("FAILURE: file audit was unsuccessful!") + sys.exit(1) + + +class ImagingLauncher(LauncherIF): + def __init__(self, parameters: dict): + self.logger = logging.getLogger("casa_envoy") + self.parameters = parameters + self.ppr = get_abs_file(parameters.get("ppr")) + self.metadata = get_abs_file(parameters.get("metadata")) + + def launch_casa(self): + if self.check_imageable(): + self.run_audit(self.parameters) + CasaLauncher(self.parameters).run() + else: + self.logger.error("ERROR: CMS information missing or incorrect!") + sys.exit(1) + + def check_imageable(self) -> bool: + content = self.metadata.content + cms_name = content["cmsName"] + cms_path = content["calibrationSourceDirectory"] + if cms_name is not None and cms_path is not None and cms_name[-3:] == ".ms": + return True + else: + self.logger.info("CMS ERROR: Imaging requires a valid CMS name and location!") + return False + + def run_audit(self, parameters: Dict[str, str]): + dir_audit = AuditDirectories(self.ppr, parameters).audit() + if dir_audit: + self.logger.info("Directory audit successful!") + else: + self.logger.info("FAILURE: directory structure audit was unsuccessful!") + sys.exit(1) + + audit = AuditFiles([self.ppr, self.metadata], parameters).audit() + if audit: + self.logger.info("File audit successful!") + else: + self.logger.info("FAILURE: file audit was unsuccessful!") + sys.exit(1) diff --git a/apps/cli/executables/pexable/casa_envoy/casa_envoy/palaver.py b/apps/cli/executables/pexable/casa_envoy/casa_envoy/palaver.py index 0aa55ca27..8be5536d8 100644 --- a/apps/cli/executables/pexable/casa_envoy/casa_envoy/palaver.py +++ b/apps/cli/executables/pexable/casa_envoy/casa_envoy/palaver.py @@ -5,14 +5,10 @@ palaver definition: an unnecessarily elaborate or complex procedure import argparse import logging import pathlib -import subprocess import sys import os -import json -from typing import Dict from pycapo import CapoConfig -from casa_envoy.auditor import AuditDirectories, AuditFiles from casa_envoy.launchers import CalibrationLauncher, ImagingLauncher """ @@ -24,20 +20,27 @@ logger.setLevel(logging.INFO) logger.addHandler(logging.StreamHandler(sys.stdout)) -def _get_settings(cwd: pathlib.Path): +def _get_settings(cwd: pathlib.Path, args: list): use_casa = CapoConfig().getboolean("edu.nrao.archive.workspaces.ProcessingSettings.useCasa") casa_home = ( CapoConfig().settings("edu.nrao.archive.workflow.config.CasaVersions").homeForReprocessing ) + parent_path = cwd root_dir = str(cwd.parent) processing_dir = str(cwd.stem) + metadata = args[0] + ppr = args[1] + return { "useCasa": use_casa, "homeForReprocessing": casa_home, "rootDirectory": root_dir, "processingDirectory": processing_dir, + "parent_path": parent_path, + "metadata": metadata, + "ppr": ppr, } @@ -63,87 +66,23 @@ def arg_parser() -> argparse.ArgumentParser: return parser -def check_calibratable(metadata_filename: str) -> bool: - with open(metadata_filename) as json_meta: - metadata = json.loads(json_meta.read()) - spl = metadata["productLocator"] - if "execblock" in spl: - return True - else: - logger.info("SPL ERROR: This product locator is not calibratable!") - return False - - -def check_imagable(metadata_filename: str) -> bool: - with open(metadata_filename) as json_meta: - metadata = json.loads(json_meta.read()) - cms_name = metadata["cmsName"] - cms_path = metadata["calibrationSourceDirectory"] - if cms_name is not None and cms_path is not None and cms_name[-3:] == ".ms": - return True - else: - logger.info("CMS ERROR: Imaging requires a valid CMS name and location!") - return False - - -def run_audit(ppr: str, metadata: str, settings: Dict[str, str]): - dir_audit = AuditDirectories(ppr, settings).audit() - if dir_audit: - logger.info("Directory audit successful!") - else: - logger.info("FAILURE: directory structure audit was unsuccessful!") - sys.exit(1) - - audit = AuditFiles([ppr, metadata], settings).audit() - if audit: - logger.info("File audit successful!") - else: - logger.info("FAILURE: file audit was unsuccessful!") - sys.exit(1) - - def main(): args = arg_parser().parse_args() path = os.getcwd() - settings = _get_settings(pathlib.Path(path)) if args.standard_cal is not None: - metadata = args.standard_cal[0] - ppr = args.standard_cal[1] - - run_audit(ppr, metadata, settings) - if check_calibratable(metadata): - if settings.get("useCasa"): - launcher = CalibrationLauncher(ppr, metadata) - launcher.setup_environment(settings) - launcher.run() - launcher.check_logs(parent_path=path) - # make sure we return to the parent directory after processing - os.chdir(path) - else: - logger.info("RUNNING VELA!") - subprocess.run(["./vela", "--standard-cal", metadata, ppr]) - else: - logger.error("ERROR: Provided SPL is not type execution block!") - sys.exit(1) + parameters = _get_settings(pathlib.Path(path), args.standard_cal) + parameters["product_type"] = "standard-cal" + + CalibrationLauncher(parameters).launch_casa() + # make sure we return to the parent directory after processing + os.chdir(path) elif args.standard_img is not None: - metadata = args.standard_img[0] - ppr = args.standard_img[1] - - run_audit(ppr, metadata, settings) - if check_imagable(metadata): - if settings.get("useCasa"): - launcher = ImagingLauncher(ppr, metadata) - launcher.setup_environment(settings) - launcher.run() - launcher.check_logs(parent_path=path) - # return to parent directory after processing - os.chdir(path) - else: - logger.info("RUNNING VELA!") - subprocess.run(["./vela", "--standard-img", metadata, ppr]) - else: - logger.error("ERROR: CMS information missing or incorrect!") - sys.exit(1) + parameters = _get_settings(pathlib.Path(path), args.standard_img) + parameters["product_type"] = "standard-img" + + ImagingLauncher(parameters).launch_casa() + # return to parent directory after processing + os.chdir(path) diff --git a/apps/cli/executables/pexable/casa_envoy/test/image-metadata.json b/apps/cli/executables/pexable/casa_envoy/test/image-metadata.json index e2b19ffab..340c903ac 100644 --- a/apps/cli/executables/pexable/casa_envoy/test/image-metadata.json +++ b/apps/cli/executables/pexable/casa_envoy/test/image-metadata.json @@ -2,6 +2,7 @@ "fileSetIds": "brain_000.58099.67095825232", "workflowName": "std_cms_imaging", "systemId": "4", + "creationTime": "2021-07-01T21:14:00", "productLocator": "uid://evla/execblock/ec082e65-452d-4fec-ad88-f5b4af1f9e36", "projectMetadata": { "projectCode": "Operations", diff --git a/apps/cli/executables/pexable/casa_envoy/test/test.json b/apps/cli/executables/pexable/casa_envoy/test/test.json index dad157e97..e84dbb8e0 100644 --- a/apps/cli/executables/pexable/casa_envoy/test/test.json +++ b/apps/cli/executables/pexable/casa_envoy/test/test.json @@ -2,6 +2,7 @@ "fileSetIds": "brain_001.58099.678886747686", "workflowName": "std_calibration", "systemId": "5", + "creationTime": "2021-05-06T17:34:36", "productLocator": "uid://evla/execblock/9cb0964b-ad6b-40ed-bd87-d08c502503e2", "projectMetadata": { "projectCode": "Operations", @@ -10,4 +11,4 @@ "observer": "VLA Operations" }, "destinationDirectory": "/tmp/workspaces_tmp/testing" -} \ No newline at end of file +} diff --git a/apps/cli/executables/pexable/casa_envoy/test/test_auditor.py b/apps/cli/executables/pexable/casa_envoy/test/test_auditor.py index af33bfdfc..87b69ed65 100644 --- a/apps/cli/executables/pexable/casa_envoy/test/test_auditor.py +++ b/apps/cli/executables/pexable/casa_envoy/test/test_auditor.py @@ -13,104 +13,139 @@ from casa_envoy.auditor import ( ) from casa_envoy.schema import AbstractTextFile -settings = { +cal_settings = { "useCasa": False, "homeForReprocessing": "/home/casa/packages/pipeline/current", "rootDirectory": "/tmp/workspaces_tmp/", - "processingDirectory": "tmpiox5trbp" + "processingDirectory": "tmpiox5trbp", + "metadata": "test/test.json", + "ppr": "test/PPR.xml", + "product_type": "standard-cal", } test_ppr = AbstractTextFile(filename="test/PPR.xml", content=Path("test/PPR.xml").read_text()) +test_cal_metadata = AbstractTextFile( + filename="test/test.json", content=Path("test/test.json").read_text() +) + +img_settings = { + "useCasa": False, + "homeForReprocessing": "/home/casa/packages/pipeline/current", + "rootDirectory": "/tmp/workspaces_tmp/", + "processingDirectory": "tmpiox5trbp", + "metadata": "test/image-metadata.json", + "ppr": "test/cmsimage-PPR.xml", + "product_type": "standard-img", +} +test_img_ppr = AbstractTextFile( + filename="test/cmsimage-PPR.xml", content=Path("test/cmsimage-PPR.xml").read_text() +) +test_img_metadata = AbstractTextFile( + filename="test/image-metadata.json", content=Path("test/image-metadata.json").read_text() +) def test_get_fields_for(): - fields = ["fileSetIds", "workflowName", "systemId", "productLocator", "destinationDirectory"] - result = get_fields_for("test/test.json") + product_type = "standard-cal" + fields = [ + "fileSetIds", + "workflowName", + "systemId", + "creationTime", + "productLocator", + "destinationDirectory", + ] + result = get_fields_for(product_type=product_type, filename=test_cal_metadata.filename) assert result == fields fields2 = ["RootDirectory", "RelativePath", "SdmIdentifier"] - result2 = get_fields_for("test/PPR.xml") + result2 = get_fields_for(product_type=product_type, filename=test_ppr.filename) assert result2 == fields2 + product_type2 = "standard-img" + img_fields = [ + "fileSetIds", + "workflowName", + "systemId", + "creationTime", + "productLocator", + "destinationDirectory", + "cms_path", + "sdmId", + ] + result = get_fields_for(product_type=product_type2, filename=test_img_metadata.filename) + assert result == img_fields + def test_get_xml_content(): - content = get_xml_content("test/PPR.xml") + content = get_xml_content(test_ppr) assert content.find("RelativePath").string == "tmpiox5trbp" assert content.find("SdmIdentifier").string == "brain_001.58099.678886747686" + assert content.find("CreationTime").string == "2021-05-06T17:34:36" def test_get_value_for(): - value = get_value_for("test/PPR.xml", "RelativePath") + value = get_value_for(test_ppr, "RelativePath") assert value == "tmpiox5trbp" - value = get_value_for("test/PPR.xml", "SdmIdentifier") + value = get_value_for(test_ppr, "SdmIdentifier") assert value == "brain_001.58099.678886747686" - value = get_value_for("test/test.json", "workflowName") + value = get_value_for(test_cal_metadata, "workflowName") assert value == "std_calibration" - value = get_value_for("test/test.json", "productLocator") + value = get_value_for(test_cal_metadata, "productLocator") assert value == "uid://evla/execblock/9cb0964b-ad6b-40ed-bd87-d08c502503e2" class TestAuditFiles: - def test_read_file(self): - file = AuditFiles(["test/test.json", "test/PPR.xml"], settings).read_file("test/test.json") - assert file.filename == "test/test.json" - - file2 = AuditFiles(["test/test.json", "test/PPR.xml"], settings).read_file("test/PPR.xml") - assert file2.filename == "test/PPR.xml" - def test_check_required_fields(self): - file = AuditFiles(["test/test.json", "test/PPR.xml"], settings).read_file("test/test.json") + audit_files = AuditFiles([test_cal_metadata, test_ppr], cal_settings) fields = [ "fileSetIds", "workflowName", "systemId", + "creationTime", "productLocator", "destinationDirectory", ] - result = AuditFiles(["test/test.json", "test/PPR.xml"], settings).check_required_fields(file, fields) + result = audit_files.check_required_fields(test_cal_metadata, fields) assert result is True fieldsF = [ "fileSetIds", "workflowName", "systemId", + "creationTime", "productLocator", "destinationDirectory", "FakeField", ] - resultF = AuditFiles(["test/test.json", "test/PPR.xml"], settings).check_required_fields( - file, fieldsF - ) + resultF = audit_files.check_required_fields(test_cal_metadata, fieldsF) assert resultF is False - file2 = AuditFiles(["test/test.json", "test/PPR.xml"], settings).read_file("test/PPR.xml") fields2 = ["RootDirectory", "RelativePath", "SdmIdentifier"] - result2 = AuditFiles(["test/test.json", "test/PPR.xml"], settings).check_required_fields( - file2, fields2 - ) + result2 = audit_files.check_required_fields(test_ppr, fields2) assert result2 is True fields2F = ["RootDirectory", "RelativePath", "SdmIdentifier", "FakeField"] - result2F = AuditFiles(["test/test.json", "test/PPR.xml"], settings).check_required_fields( - file2, fields2F - ) + result2F = audit_files.check_required_fields(test_ppr, fields2F) assert result2F is False @patch("os.chdir") @patch("shutil.copy") def test_correct_for_condor(self, mock_copy, mock_os): - ppr = AuditFiles(["test/test.json", "test/PPR.xml"], settings).correct_for_condor(ppr=test_ppr) + ppr = AuditFiles([test_cal_metadata, test_ppr], cal_settings).correct_for_condor( + ppr=test_ppr + ) assert ppr.filename == "test/PPR.xml" def test_audit(self): - result = AuditFiles(["test/test.json", "test/PPR.xml"], settings=settings).audit() + result = AuditFiles([test_cal_metadata, test_ppr], cal_settings).audit() assert result is True class TestAuditDirectories: def test_audit(self): - result = AuditDirectories("test/PPR.xml", settings).audit() + result = AuditDirectories(test_ppr, cal_settings).audit() assert result is False diff --git a/apps/cli/executables/pexable/casa_envoy/test/test_casa_envoy.py b/apps/cli/executables/pexable/casa_envoy/test/test_casa_envoy.py index 4089a39f0..c0ff840ec 100644 --- a/apps/cli/executables/pexable/casa_envoy/test/test_casa_envoy.py +++ b/apps/cli/executables/pexable/casa_envoy/test/test_casa_envoy.py @@ -4,32 +4,59 @@ Tests for casa_envoy.palaver import argparse from unittest.mock import patch, MagicMock -from casa_envoy.palaver import check_calibratable, check_imagable, _get_settings +import casa_envoy.palaver as palaver expected_settings = { "useCasa": False, "homeForReprocessing": "/home/casa/packages/pipeline/current", "rootDirectory": "/lustre/aoc/cluster/pipeline/docker/workspaces/spool", "processingDirectory": "tmpo1ca1pp_", + "parent_path": "/lustre/aoc/cluster/pipeline/docker/workspaces/spool/tmpo1ca1pp_", + "metadata": "test/test.json", + "ppr": "test/PPR.xml", + "product_type": "standard-cal", } args = argparse.Namespace() -def test_check_calibratable(): - check = check_calibratable("test/test.json") - assert check is True +class TestPalaver: + def test_get_settings(self): + args.standard_cal = ["test/test.json", "test/PPR.xml"] + with patch( + "pathlib.Path.cwd", + MagicMock( + return_value="/lustre/aoc/cluster/pipeline/docker/workspaces/spool/tmpo1ca1pp_" + ), + ) as cwd: + settings = palaver._get_settings(cwd, args.standard_cal) + assert settings["useCasa"] == expected_settings["useCasa"] + assert settings["homeForReprocessing"] == expected_settings["homeForReprocessing"] + assert settings["metadata"] == expected_settings["metadata"] + assert settings["ppr"] == expected_settings["ppr"] -def test_check_imagable(): - check = check_imagable("test/image-metadata.json") - assert check is True + args.standard_cal = None + @patch("os.chdir") + @patch("os.getcwd") + def test_main_cal(self, mock_cwd, mock_chdir): + args.standard_cal = ["test/test.json", "test/PPR.xml"] -def test_get_settings(): - with patch( - "pathlib.Path.cwd", - MagicMock(return_value="/lustre/aoc/cluster/pipeline/docker/workspaces/spool/tmpo1ca1pp_"), - ) as cwd: - settings = _get_settings(cwd) - assert settings["useCasa"] == expected_settings["useCasa"] - assert settings["homeForReprocessing"] == expected_settings["homeForReprocessing"] + with patch("argparse.ArgumentParser.parse_args", MagicMock(return_value=args)) as mock_args: + with patch("casa_envoy.launchers.CalibrationLauncher.launch_casa") as cal_launcher: + palaver.main() + assert cal_launcher.call_count == 1 + + args.standard_cal = None + + @patch("os.chdir") + @patch("os.getcwd") + def test_main_img(self, mock_cwd, mock_chdir): + args.standard_img = ["test/image-metadata.json", "test/cmsimage-PPR.xml"] + + with patch("argparse.ArgumentParser.parse_args", MagicMock(return_value=args)) as mock_args: + with patch("casa_envoy.launchers.ImagingLauncher.launch_casa") as img_launcher: + palaver.main() + assert img_launcher.call_count == 1 + + args.standard_img = None diff --git a/apps/cli/executables/pexable/casa_envoy/test/test_launchers.py b/apps/cli/executables/pexable/casa_envoy/test/test_launchers.py index dfe285a1c..b3f714042 100644 --- a/apps/cli/executables/pexable/casa_envoy/test/test_launchers.py +++ b/apps/cli/executables/pexable/casa_envoy/test/test_launchers.py @@ -1,68 +1,73 @@ import os from unittest.mock import mock_open, patch -from casa_envoy.launchers import CalibrationLauncher, ImagingLauncher +from casa_envoy.launchers import CalibrationLauncher, ImagingLauncher, CasaLauncher +cal_parameters = { + "useCasa": False, + "homeForReprocessing": "/home/casa/packages/pipeline/current", + "rootDirectory": "/tmp/workspaces_tmp/", + "processingDirectory": "tmpiox5trbp", + "metadata": "test/test.json", + "ppr": "test/PPR.xml", + "product_type": "standard-cal", +} +img_parameters = { + "useCasa": False, + "homeForReprocessing": "/home/casa/packages/pipeline/current", + "rootDirectory": "/tmp/workspaces_tmp/", + "processingDirectory": "tmpiox5trbp", + "metadata": "test/image-metadata.json", + "ppr": "test/cmsimage-PPR.xml", + "product_type": "standard-img", +} -class TestCalibrationLauncher: - @patch("subprocess.Popen") + +class TestCasaLauncher: + def test_setup_environment(self): + CasaLauncher(parameters=cal_parameters).setup_environment() + assert os.environ.get("SCIPIPE_ROOTDIR") == cal_parameters["rootDirectory"] + assert os.environ.get("CASA_HOME") == cal_parameters["homeForReprocessing"] + assert os.environ.get("PPR_FILENAME") == "test/PPR.xml" + + @patch("subprocess.run") @patch("os.chdir") def test_run(self, mock_os, mock_subprocess): - CalibrationLauncher("test/PPR.xml", "test/test.json").run() + CasaLauncher(parameters=cal_parameters).run() assert mock_subprocess.call_count == 1 - def test_setup_environment(self): - parameters = { - "useCasa": False, - "homeForReprocessing": "/home/casa/packages/pipeline/current", - "rootDirectory": "/tmp/workspaces_tmp/", - "processingDirectory": "tmpiox5trbp", - } - CalibrationLauncher("PPR.xml", "test.json").setup_environment(parameters=parameters) - assert os.environ.get("SCIPIPE_ROOTDIR") == parameters["rootDirectory"] - assert os.environ.get("CASA_HOME") == parameters["homeForReprocessing"] - assert os.environ.get("PPR_FILENAME") == "PPR.xml" - @patch("builtins.open") @patch("glob.glob") @patch("os.chdir") @patch("os.getcwd") def test_check_logs(self, mock_os_cwd, mock_os_dir, mock_glob, mock_open): - CalibrationLauncher("PPR.xml", "test.json").check_logs(parent_path=".") + CasaLauncher(parameters=cal_parameters).check_logs() assert mock_os_cwd.call_count == 1 assert mock_os_dir.call_count == 0 assert mock_glob.call_count == 1 -class TestImagingLauncher: - @patch("os.chdir") - def test_run(self, mock_os): - with patch("subprocess.Popen") as sp: - ImagingLauncher("test/cmsimage-PPR.xml", "test/image-metadata.json").run() - assert sp.call_count == 1 +class TestCalibrationLauncher: + @patch("casa_envoy.launchers.CalibrationLauncher.run_audit") + @patch("casa_envoy.launchers.CasaLauncher.run") + def test_launch_casa(self, mock_run, mock_audit): + CalibrationLauncher(parameters=cal_parameters).launch_casa() + assert mock_run.call_count == 1 + assert mock_audit.call_count == 1 - def test_setup_environment(self): - parameters = { - "useCasa": False, - "homeForReprocessing": "/home/casa/packages/pipeline/current", - "rootDirectory": "/tmp/workspaces_tmp/", - "processingDirectory": "tmpiox5trbp", - } - ppr = "cmsimage-PPR.xml" - metadata = "image-metadata.json" - ImagingLauncher(ppr, metadata).setup_environment(parameters=parameters) - assert os.environ.get("SCIPIPE_ROOTDIR") == parameters["rootDirectory"] - assert os.environ.get("CASA_HOME") == parameters["homeForReprocessing"] - assert os.environ.get("PPR_FILENAME") == ppr + def test_check_calibratable(self): + check = CalibrationLauncher(parameters=cal_parameters).check_calibratable() + assert check is True - @patch("builtins.open") - @patch("glob.glob") - @patch("os.chdir") - @patch("os.getcwd") - def test_check_logs(self, mock_os_cwd, mock_os_dir, mock_glob, mock_open): - ppr = "cmsimage-PPR.xml" - metadata = "image-metadata.json" - ImagingLauncher(ppr, metadata).check_logs(parent_path=".") - assert mock_os_cwd.call_count == 1 - assert mock_os_dir.call_count == 0 - assert mock_glob.call_count == 1 + +class TestImagingLauncher: + @patch("casa_envoy.launchers.CalibrationLauncher.run_audit") + @patch("casa_envoy.launchers.CasaLauncher.run") + def test_launch_casa(self, mock_run, mock_audit): + CalibrationLauncher(parameters=img_parameters).launch_casa() + assert mock_run.call_count == 1 + assert mock_audit.call_count == 1 + + def test_check_imageable(self): + check = ImagingLauncher(parameters=img_parameters).check_imageable() + assert check is True -- GitLab