diff --git a/.gitattributes b/.gitattributes index af2242b14cd72f4805ed8df8780c5d3a27a98cc2..87d79ca583e4d6597cd2e331e7d91efedf5e9dd4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1087,6 +1087,9 @@ CEP/Pipeline/recipes/sip/helpers/WritableParmDB.py -text CEP/Pipeline/recipes/sip/helpers/__init__.py eol=lf CEP/Pipeline/recipes/sip/helpers/data_quality.py eol=lf CEP/Pipeline/recipes/sip/helpers/metadata.py eol=lf +CEP/Pipeline/recipes/sip/helpers/test/CMakeLists.txt -text +CEP/Pipeline/recipes/sip/helpers/test/t_metadata.py -text +CEP/Pipeline/recipes/sip/helpers/test/t_metadata.sh -text CEP/Pipeline/recipes/sip/master/__init__.py eol=lf CEP/Pipeline/recipes/sip/master/copier.py -text CEP/Pipeline/recipes/sip/master/deprecated/bbs.py eol=lf diff --git a/CEP/Pipeline/recipes/sip/CMakeLists.txt b/CEP/Pipeline/recipes/sip/CMakeLists.txt index e52af408f9598e286becfc725e886711e1ed31a0..87cdd00cf499e483d4c98183c457abe45b49490a 100644 --- a/CEP/Pipeline/recipes/sip/CMakeLists.txt +++ b/CEP/Pipeline/recipes/sip/CMakeLists.txt @@ -143,3 +143,4 @@ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/tasks.cfg.CEP4.in ${CMAKE_CURRENT_BINARY_DIR}/tasks.cfg.CEP4) +add_subdirectory(helpers/test/) diff --git a/CEP/Pipeline/recipes/sip/helpers/metadata.py b/CEP/Pipeline/recipes/sip/helpers/metadata.py index 247761de26ea56938e31469d793e1797a7b8226b..fdf83778f1ecbca87d9ab6073ebcef66cfe93387 100644 --- a/CEP/Pipeline/recipes/sip/helpers/metadata.py +++ b/CEP/Pipeline/recipes/sip/helpers/metadata.py @@ -79,6 +79,65 @@ def to_parset(data, prefix=''): result.replace(fullkey, str(value)) return result +class StorageWriterTypes: + ''' + type definitions for the used storage writer for the dataproducts, + and method to get the type and version from an MS table + ''' + CASA = 'CASA' # any default casa storage manager in MS datasets + LOFAR = 'LOFAR' # the lofar storage manager in MS datasets + DYSCO = 'DYSCO' # the dyscostorage manager in MS datasets + HDF5DEFAULT = 'HDF5DEFAULT' # normal hdf5 + UNKNOWN = 'UNKNOWN' # miscellaneous/unknown + + UNKNOWN_VERSION = 'UNKNOWN' + + @staticmethod + def get_type_and_version(main_table, logger=None): + ''' + tries to determine the used StorageWriterType and version from a MS main table + :param main_table: the main table of a casacore Measurement Set + :param logger: a logging.logger instance + :return: a tuple of (StorageWriterTypes.<constant>, '<version>') + ''' + try: + dminfo = main_table.getdminfo('DATA') + dataManagerType = dminfo.get('TYPE') + + if dataManagerType in ['StandardStMan', 'IncrementalStMan', + 'TiledColumnStMan', 'TiledCellStMan', 'TiledShapeStMan']: + return StorageWriterTypes.CASA, StorageWriterTypes.get_casa_version() + + if dataManagerType == 'LofarStMan': + try: + version = dminfo['SPEC']['version'] + except: + version = StorageWriterTypes.UNKNOWN_VERSION + return StorageWriterTypes.LOFAR, version + + if dataManagerType == 'DyscoStMan': + version = os.environ.get('DYSCO_VERSION', StorageWriterTypes.UNKNOWN_VERSION) + return StorageWriterTypes.DYSCO, StorageWriterTypes.get_dysco_version() + + except Exception as e: + if logger: + logger.error('Could not determine the used storageWriter type and version: %s', e) + + return StorageWriterTypes.UNKNOWN, StorageWriterTypes.UNKNOWN_VERSION + + @staticmethod + def get_casa_version(): + ''' + :return the casa version from the environment, or unknown + ''' + return os.environ.get('CASACORE_VERSION', StorageWriterTypes.UNKNOWN_VERSION) + + @staticmethod + def get_dysco_version(): + ''' + :return the dysco version from the environment, or unknown + ''' + return os.environ.get('DYSCO_VERSION', StorageWriterTypes.UNKNOWN_VERSION) class DataProduct(object): @@ -91,7 +150,10 @@ class DataProduct(object): 'fileFormat' : "", 'filename' : "", 'location' : "", - 'percentageWritten' : 0 + 'percentageWritten' : 0, + # by default the type of storagewriter is unknown and overridden in each subclass if needed + 'storageWriter': StorageWriterTypes.UNKNOWN, + 'storageWriterVersion': StorageWriterTypes.UNKNOWN_VERSION } self.logger = logger @@ -163,16 +225,21 @@ class Correlated(DataProduct): pyrap.tables.taql('calc ctod($startTime s)')[0] .replace('/', '-', 2).replace('/', 'T') ) + + sw_type, sw_version = StorageWriterTypes.get_type_and_version(main, self.logger) + self._data.update({ 'percentageWritten' : 100, + 'storageWriter': sw_type, + 'storageWriterVersion': sw_version, 'startTime' : startTimeString, 'duration' : endTime - startTime, 'integrationInterval' : exposure, 'centralFrequency' : spw.getcell('REF_FREQUENCY', 0), - 'channelWidth' : spw.getcell('RESOLUTION', 0)[0], + 'channelWidth' : spw.getcell('RESOLUTION', [0])[0], 'channelsPerSubband' : spw.getcell('NUM_CHAN', 0), # Assume subband name has format 'SB-nn' - 'subband' : int(spw.getcell('NAME', 0)[3:]), + 'subband' : int(spw.getcell('NAME', 'SB000')[3:]), 'stationSubband' : 0 ### NOT CORRECT! ### }) except Exception, error: @@ -202,11 +269,12 @@ class InstrumentModel(DataProduct): Collect instrument model metadata from the Measurement Set `filename`. """ super(InstrumentModel, self).collect(filename) + self._data['storageWriter'] = StorageWriterTypes.CASA + self._data['storageWriterVersion'] = StorageWriterTypes.get_casa_version() if self._data['size'] > 0: self._data['percentageWritten'] = 100 - class SkyImage(DataProduct): """ Class representing the metadata associated with a sky image. @@ -242,6 +310,16 @@ class SkyImage(DataProduct): """ super(SkyImage, self).collect(filename) try: + if filename.endswith('.h5'): + self._data['storageWriter'] = StorageWriterTypes.HDF5DEFAULT + self._data['storageWriterVersion'] = StorageWriterTypes.UNKNOWN_VERSION + elif filename.endswith('.IM'): + self._data['storageWriter'] = StorageWriterTypes.CASA + self._data['storageWriterVersion'] = StorageWriterTypes.get_casa_version() + else: + self._data['storageWriter'] = StorageWriterTypes.UNKNOWN + self._data['storageWriterVersion'] = StorageWriterTypes.UNKNOWN_VERSION + image = pyrap.images.image(filename) coord = image.coordinates() beaminfo = image.imageinfo()['restoringbeam'] diff --git a/CEP/Pipeline/recipes/sip/helpers/test/CMakeLists.txt b/CEP/Pipeline/recipes/sip/helpers/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f566fde1ae2292337318648b14ed1805886363b5 --- /dev/null +++ b/CEP/Pipeline/recipes/sip/helpers/test/CMakeLists.txt @@ -0,0 +1,6 @@ +# $Id$ +include(LofarCTest) + +lofar_add_test(t_metadata) + + diff --git a/CEP/Pipeline/recipes/sip/helpers/test/t_metadata.py b/CEP/Pipeline/recipes/sip/helpers/test/t_metadata.py new file mode 100755 index 0000000000000000000000000000000000000000..c62fee90f82bc9b25376f9abefba7436e41b85cc --- /dev/null +++ b/CEP/Pipeline/recipes/sip/helpers/test/t_metadata.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python + +# Copyright (C) 2017 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it +# and/or modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. + +import unittest +from lofarpipe.recipes.helpers.metadata import * +from numpy import * +import mock + +import logging +logger = logging.getLogger(__name__) + +class AbstractMockTable: + '''a mocked version of the pyrap.tables.table class, + can be used with dependency injection in the tests''' + def __init__(self, *args, **kwargs): + pass + + def getkeyword(self, *args, **kwargs): + return '' + + def getcell(self, name, default=None): + return default + + def nrows(self): + return 0 + + def getdminfo(self, columnname): + return {} + +class LofarMockTable(AbstractMockTable): + def getdminfo(self, columnname): + assert columnname == 'DATA' + # return a real world example of datamanager info from a MS with a LofarStMan + return {'NAME': 'LofarStMan', + 'SEQNR': 0, + 'SPEC': {'alignment': 512, + 'bigEndian': False, + 'maxNrSample': 3056.0, + 'nbaseline': 741, + 'nrBytesPerNrValidSamples': 2, + 'startTime': 5022530520.500695, + 'timeInterval': 1.00139008, + 'useSeqnrFile': True, + 'version': 3}, + 'TYPE': 'LofarStMan'} + +class DyscoMockTable(AbstractMockTable): + def getdminfo(self, columnname): + assert columnname == 'DATA' + # return a real world example of datamanager info from a MS with a DyscoStMan + return {'NAME': 'DyscoData', + 'SEQNR': 3, + 'SPEC': {'dataBitCount': 10, + 'distribution': 'TruncatedGaussian', + 'distributionTruncation': 2.5, + 'normalization': 'AF', + 'studentTNu': 0.0, + 'weightBitCount': 12}, + 'TYPE': 'DyscoStMan'} + +class CasaTiledMockTable(AbstractMockTable): + def getdminfo(self, columnname): + assert columnname == 'DATA' + # return a real world example of datamanager info from a MS with a TiledColumnStMan + return {'NAME': 'TiledFlag', + 'SEQNR': 4, + 'SPEC': {'ActualMaxCacheSize': 0, + 'DEFAULTTILESHAPE': array([4, 4, 65536], dtype=int32), + 'HYPERCUBES': {'*1': {'BucketSize': 131072, + 'CellShape': array([4, 4], dtype=int32), + 'CubeShape': array([4, 4, 5993949], dtype=int32), + 'ID': {}, + 'TileShape': array([4, 4, 65536], dtype=int32)}}, + 'MAXIMUMCACHESIZE': 0, + 'SEQNR': 4}, + 'TYPE': 'TiledColumnStMan'} + +class CasaStandardMockTable(AbstractMockTable): + def getdminfo(self, columnname): + assert columnname == 'DATA' + # return a real world example of datamanager info from a MS with a StandardStMan + return {'NAME': 'SSMVar', + 'SEQNR': 0, + 'SPEC': {'ActualCacheSize': 2, + 'BUCKETSIZE': 32768, + 'IndexLength': 11830, + 'PERSCACHESIZE': 2}, + 'TYPE': 'StandardStMan'} + + +# for some reason, the casa and dysco versions are 'encoded' in the running environment +# define the here and set them for this test in the enviroment +CASA_VERSION = "2.2.0" +DYSCO_VERSION = "1.01" +os.environ['CASACORE_VERSION'] = CASA_VERSION +os.environ['DYSCO_VERSION'] = DYSCO_VERSION + + +class StorageWriterTypesTest(unittest.TestCase): + ''' + Tests the StorageWriterTypes class + ''' + + def test_get_type_and_version_casa_standard(self): + main = CasaStandardMockTable() + sw_type, sw_version = StorageWriterTypes.get_type_and_version(main) + self.assertEqual(StorageWriterTypes.CASA, sw_type) + self.assertEqual(CASA_VERSION, sw_version) + + def test_get_type_and_version_casa_tiled(self): + main = CasaTiledMockTable() + sw_type, sw_version = StorageWriterTypes.get_type_and_version(main) + self.assertEqual(StorageWriterTypes.CASA, sw_type) + self.assertEqual(CASA_VERSION, sw_version) + + def test_get_type_and_version_dysco(self): + main = DyscoMockTable() + sw_type, sw_version = StorageWriterTypes.get_type_and_version(main) + self.assertEqual(StorageWriterTypes.DYSCO, sw_type) + self.assertEqual(DYSCO_VERSION, sw_version) + + def test_get_type_and_version_lofar(self): + main = LofarMockTable() + sw_type, sw_version = StorageWriterTypes.get_type_and_version(main) + self.assertEqual(StorageWriterTypes.LOFAR, sw_type) + self.assertEqual(3, sw_version) + + def test_get_type_and_version_unknown(self): + main = AbstractMockTable() + sw_type, sw_version = StorageWriterTypes.get_type_and_version(main) + self.assertEqual(StorageWriterTypes.UNKNOWN, sw_type) + self.assertEqual(StorageWriterTypes.UNKNOWN_VERSION, sw_version) + + +class MetaDataTest(unittest.TestCase): + ''' + Tests the creation of correct meta data parsets + ''' + + def test_correlated_casa_standard(self): + with mock.patch('pyrap.tables.table', new=CasaStandardMockTable): + dataproduct_metadata = Correlated(logger=None, filename='casa-standard') + metadata_parset = dataproduct_metadata.as_parameterset() + logger.info('casa_standard metadata parset:\n%s', metadata_parset) + self.assertEqual(StorageWriterTypes.CASA, metadata_parset.getString('storageWriter')) + self.assertEqual(CASA_VERSION, metadata_parset.getString('storageWriterVersion')) + + def test_correlated_casa_tiled(self): + with mock.patch('pyrap.tables.table', new=CasaTiledMockTable): + dataproduct_metadata = Correlated(logger=None, filename='casa-tiled') + metadata_parset = dataproduct_metadata.as_parameterset() + logger.info('casa_lofar metadata parset:\n%s', metadata_parset) + self.assertEqual(StorageWriterTypes.CASA, metadata_parset.getString('storageWriter')) + self.assertEqual(CASA_VERSION, metadata_parset.getString('storageWriterVersion')) + + def test_correlated_lofar(self): + with mock.patch('pyrap.tables.table', new=LofarMockTable): + dataproduct_metadata = Correlated(logger=None, filename='lofar') + metadata_parset = dataproduct_metadata.as_parameterset() + logger.info('lofar metadata parset:\n%s', metadata_parset) + self.assertEqual(StorageWriterTypes.LOFAR, metadata_parset.getString('storageWriter')) + self.assertEqual('3', metadata_parset.getString('storageWriterVersion')) + + def test_correlated_dysco(self): + with mock.patch('pyrap.tables.table', new=DyscoMockTable): + dataproduct_metadata = Correlated(logger=None, filename='dysco') + metadata_parset = dataproduct_metadata.as_parameterset() + logger.info('dysco metadata parset:\n%s', metadata_parset) + self.assertEqual(StorageWriterTypes.DYSCO, metadata_parset.getString('storageWriter')) + self.assertEqual(DYSCO_VERSION, metadata_parset.getString('storageWriterVersion')) + + def test_correlated_unknown(self): + with mock.patch('pyrap.tables.table', new=AbstractMockTable): + dataproduct_metadata = Correlated(logger=None, filename='foo.bar') + metadata_parset = dataproduct_metadata.as_parameterset() + logger.info('unknown metadata parset:\n%s', metadata_parset) + self.assertEqual(StorageWriterTypes.UNKNOWN, metadata_parset.getString('storageWriter')) + self.assertEqual(StorageWriterTypes.UNKNOWN_VERSION, metadata_parset.getString('storageWriterVersion')) + + def test_instrument_model(self): + with mock.patch('pyrap.images.image'): + dataproduct_metadata = InstrumentModel(logger=None, filename='foo.INST') + metadata_parset = dataproduct_metadata.as_parameterset() + logger.info('instrument model metadata parset:\n%s', metadata_parset) + self.assertEqual(StorageWriterTypes.CASA, metadata_parset.getString('storageWriter')) + self.assertEqual(CASA_VERSION, metadata_parset.getString('storageWriterVersion')) + + def test_skyimage_h5(self): + with mock.patch('pyrap.images.image'): + dataproduct_metadata = SkyImage(logger=None, filename='foo.h5') + metadata_parset = dataproduct_metadata.as_parameterset() + logger.info('instrument model metadata parset:\n%s', metadata_parset) + self.assertEqual(StorageWriterTypes.HDF5DEFAULT, metadata_parset.getString('storageWriter')) + self.assertEqual(StorageWriterTypes.UNKNOWN_VERSION, metadata_parset.getString('storageWriterVersion')) + + def test_skyimage_casa(self): + with mock.patch('pyrap.images.image'): + dataproduct_metadata = SkyImage(logger=None, filename='foo.IM') + metadata_parset = dataproduct_metadata.as_parameterset() + logger.info('instrument model metadata parset:\n%s', metadata_parset) + self.assertEqual(StorageWriterTypes.CASA, metadata_parset.getString('storageWriter')) + self.assertEqual(CASA_VERSION, metadata_parset.getString('storageWriterVersion')) + + def test_skyimage_other(self): + with mock.patch('pyrap.images.image'): + dataproduct_metadata = SkyImage(logger=None, filename='foo.fits') + metadata_parset = dataproduct_metadata.as_parameterset() + logger.info('instrument model metadata parset:\n%s', metadata_parset) + self.assertEqual(StorageWriterTypes.UNKNOWN, metadata_parset.getString('storageWriter')) + self.assertEqual(StorageWriterTypes.UNKNOWN_VERSION, metadata_parset.getString('storageWriterVersion')) + + +if __name__ == '__main__': + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.DEBUG) + unittest.main() + diff --git a/CEP/Pipeline/recipes/sip/helpers/test/t_metadata.sh b/CEP/Pipeline/recipes/sip/helpers/test/t_metadata.sh new file mode 100755 index 0000000000000000000000000000000000000000..f5b276f5ce3931415e054a13e4ae816f3577573b --- /dev/null +++ b/CEP/Pipeline/recipes/sip/helpers/test/t_metadata.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +./runctest.sh t_metadata