Commit efb7eaae22c3d28829648e143c7e029b8956c0f3

Authored by Jürgen Knödlseder
1 parent d726da04

Refactor cscaldb script and add unit test.

cscripts/Makefile.am
... ... @@ -9,6 +9,9 @@ pythonscripts = __init__.py \
9 9 csfindobs.py csiactcopy.py csiactdata.py csiactobs.py \
10 10 obsutils.py
11 11  
  12 +# Parfiles to be distributed in $(prefix)/syspfiles
  13 +parfiles = cscaldb.par
  14 +
12 15 # Scripts for which symbolic links will be created in bindir
13 16 linkscripts = cspull cssens cstsdist cscaldb csresmap \
14 17 csobsdef csspec cslightcrv csobsinfo \
... ... @@ -16,12 +19,14 @@ linkscripts = cspull cssens cstsdist cscaldb csresmap \
16 19 csobs2caldb \
17 20 csfindobs csiactcopy csiactdata csiactobs
18 21  
19   -# Distribute scripts
  22 +# Distribute scripts and parfiles
20 23 EXTRA_DIST = $(binscripts) $(pythonscripts)
21 24 binscriptsdir = $(bindir)
22 25 binscripts_DATA = $(binscripts)
23 26 pythonscriptsdir = $(pyexecdir)/cscripts
24 27 pythonscripts_DATA = $(pythonscripts)
  28 +pfilesdir = $(prefix)/syspfiles
  29 +pfiles_DATA = $(parfiles)
25 30  
26 31 # Add hook rule to create symbolic links for cscripts
27 32 install-data-hook:
... ...
cscripts/cscaldb.par 0 → 100644
  1 +#############################################################################
  2 +# #
  3 +# CTA Science Analysis Tools #
  4 +# #
  5 +# ------------------------------------------------------------------------- #
  6 +# #
  7 +# File: cscaldb.par #
  8 +# Version: 1.1.0 #
  9 +# #
  10 +# Author: Jurgen Knodlseder #
  11 +# Jurgen.Knodlseder@irap.omp.eu #
  12 +# IRAP #
  13 +# #
  14 +# Purpose: Parameter file for the cscaldb script #
  15 +# #
  16 +#############################################################################
  17 +#
  18 +# Standard parameters
  19 +#====================
  20 +chatter, i, h, 2,0,4, "Chattiness of output"
  21 +clobber, b, h, yes,,, "Overwrite existing output files with new output files?"
  22 +debug, b, h, no,,, "Debugging mode activated"
  23 +mode, s, h, "ql",,, "Mode of automatic parameters"
  24 +logfile, f, h, "cscaldb.log",,, "Log filename"
... ...
cscripts/cscaldb.py
... ... @@ -2,7 +2,7 @@
2 2 # ==========================================================================
3 3 # This script dumps all available calibrations into the console.
4 4 #
5   -# Copyright (C) 2014-2015 Juergen Knoedlseder
  5 +# Copyright (C) 2014-2016 Juergen Knoedlseder
6 6 #
7 7 # This program is free software: you can redistribute it and/or modify
8 8 # it under the terms of the GNU General Public License as published by
... ... @@ -25,32 +25,28 @@ import glob
25 25 import os
26 26  
27 27  
28   -# ============ #
29   -# cspull class #
30   -# ============ #
  28 +# ============= #
  29 +# cscaldb class #
  30 +# ============= #
31 31 class cscaldb(ctools.cscript):
32 32 """
33   - This class dumps the content of the ctools calibration database.
34   - It derives from the ctools.cscript class which provides support for
35   - parameter files, command line arguments, and logging. In that way
36   - the Python script behaves just as a regular ctool.
  33 + The cscaldb class logs the content of the ctools calibration database
  34 + into the log file. Optionally, if the "debug" parameter is set to "yes"
  35 + the calibration database is also logged into the console.
37 36 """
38 37 def __init__(self, *argv):
39 38 """
40 39 Constructor.
41 40 """
42 41 # Set name
43   - self.name = "cscaldb"
44   - self.version = "1.0.0"
45   -
46   - # Make sure that parfile exists
47   - file = self.parfile()
  42 + self._name = "cscaldb"
  43 + self._version = "1.0.0"
48 44  
49 45 # Initialise application
50 46 if len(argv) == 0:
51   - ctools.cscript.__init__(self, self.name, self.version)
  47 + ctools.cscript.__init__(self, self._name, self._version)
52 48 elif len(argv) ==1:
53   - ctools.cscript.__init__(self, self.name, self.version, *argv)
  49 + ctools.cscript.__init__(self, self._name, self._version, *argv)
54 50 else:
55 51 raise TypeError("Invalid number of arguments given.")
56 52  
... ... @@ -68,34 +64,97 @@ class cscaldb(ctools.cscript):
68 64 # Return
69 65 return
70 66  
71   - def parfile(self):
  67 + def _get_parameters(self):
72 68 """
73   - Check if parfile exists. If parfile does not exist then create a
74   - default parfile. This kluge avoids shipping the cscript with a parfile.
  69 + Get parameters from parfile.
75 70 """
76   - # Set parfile name
77   - parfile = self.name+".par"
  71 + # Return
  72 + return
78 73  
79   - try:
80   - pars = gammalib.GApplicationPars(parfile)
81   - except:
82   - # Signal if parfile was not found
83   - sys.stdout.write("Parfile "+parfile+" not found. Create default parfile.\n")
  74 + def _get_missions(self, caldb):
  75 + """
  76 + Extract mission names from a calibration database.
84 77  
85   - # Create default parfile
86   - pars = gammalib.GApplicationPars()
87   - pars.append_standard()
88   - pars.save(parfile)
  78 + Args:
  79 + caldb: Calibration database.
89 80  
90   - # Return
91   - return
  81 + Returns:
  82 + A list of mission names.
  83 + """
  84 + # Initialise mission list
  85 + missions = []
  86 +
  87 + # Extract missions
  88 + paths = glob.glob(caldb.rootdir()+"/data/*")
  89 + paths.sort()
  90 + for path in paths:
  91 + missions.append(os.path.basename(path))
92 92  
93   - def get_parameters(self):
  93 + # Sort missions
  94 + missions.sort()
  95 +
  96 + # Return missions
  97 + return missions
  98 +
  99 + def _get_instruments(self, caldb, mission):
94 100 """
95   - Get parameters from parfile.
  101 + Extract instrument names from a calibration database.
  102 +
  103 + Args:
  104 + caldb: Calibration database.
  105 + mission: Mission name.
  106 +
  107 + Returns:
  108 + A list of instrument names.
96 109 """
97   - # Return
98   - return
  110 + # Initialise instrument list
  111 + instruments = []
  112 +
  113 + # Extract instruments
  114 + paths = glob.glob(caldb.rootdir()+"/data/"+mission+"/*")
  115 + paths.sort()
  116 + for path in paths:
  117 + instruments.append(os.path.basename(path))
  118 +
  119 + # Sort instruments
  120 + instruments.sort()
  121 +
  122 + # Return instruments
  123 + return instruments
  124 +
  125 + def _get_response_names(self, calibrations):
  126 + """
  127 + Extract response names from a calibrations FITS table and return
  128 + them in form of a list.
  129 +
  130 + Args:
  131 + calibrations: FITS table containing the calibrations.
  132 +
  133 + Returns:
  134 + A list of response names.
  135 + """
  136 + # Initialise response name list
  137 + names = []
  138 +
  139 + # Extract response names from calibrations and append them to the
  140 + # response name list
  141 + nrows = calibrations.length()
  142 + ncols = calibrations.number()
  143 + for row in range(nrows):
  144 + for col in range(ncols):
  145 + cal = calibrations.string(row, col)
  146 + istart = cal.find("NAME(")
  147 + if istart != -1:
  148 + istop = cal.find(")")
  149 + name = cal[5:istop]
  150 + if names.count(name) == 0:
  151 + names.append(name)
  152 +
  153 + # Sort response names
  154 + names.sort()
  155 +
  156 + # Return response names
  157 + return names
99 158  
100 159 def execute(self):
101 160 """
... ... @@ -116,23 +175,18 @@ class cscaldb(ctools.cscript):
116 175 self.log.cout(True)
117 176  
118 177 # Get parameters
119   - self.get_parameters()
  178 + self._get_parameters()
120 179  
121 180 # Write input parameters into logger
122 181 if self.logTerse():
123 182 self.log_parameters()
124 183 self.log("\n")
125 184  
126   - # Get the calibration database path
  185 + # Get the calibration database
127 186 caldb = gammalib.GCaldb()
128 187  
129   - # Gather missions
130   - missions = []
131   - paths = glob.glob(caldb.rootdir()+"/data/*")
132   - paths.sort()
133   - for path in paths:
134   - head, tail = os.path.split(path)
135   - missions.append(tail)
  188 + # Extract mission names from the calibration database
  189 + missions = self._get_missions(caldb)
136 190  
137 191 # Loop over missions
138 192 for mission in missions:
... ... @@ -146,41 +200,26 @@ class cscaldb(ctools.cscript):
146 200 self.log("\n")
147 201 self.log.header1("Mission: "+mission)
148 202  
149   - # Gather instruments
150   - instruments = []
151   - paths = glob.glob(caldb.rootdir()+"/data/"+mission+"/*")
152   - paths.sort()
153   - for path in paths:
154   - head, tail = os.path.split(path)
155   - instruments.append(tail)
  203 + # Extract instruments
  204 + instruments = self._get_instruments(caldb, mission)
156 205  
157 206 # Loop over instruments
158 207 for instrument in instruments:
159 208  
160 209 # Write mission into logger
161 210 if self.logTerse():
162   - self.log.header3("Response functions in database \""+instrument+"\"")
  211 + self.log.header3("Response functions in database \""+
  212 + instrument+"\"")
163 213  
164   - # Open calibration index file
165   - cifname = caldb.rootdir()+"/data/"+mission+"/"+instrument+"/caldb.indx"
166   - fits = gammalib.GFits(cifname)
167   - cif = fits["CIF"]
168   - cals = cif["CAL_CBD"]
  214 + # Open calibration index file and retrieve calibrations
  215 + filename = "/data/"+mission+"/"+instrument+"/caldb.indx"
  216 + cifname = caldb.rootdir() + filename
  217 + fits = gammalib.GFits(cifname)
  218 + cif = fits["CIF"]
  219 + cals = cif["CAL_CBD"]
169 220  
170 221 # Extract response names
171   - names = []
172   - nrows = cals.length()
173   - ncols = cals.number()
174   - for row in range(nrows):
175   - for col in range(ncols):
176   - cal = cals.string(row, col)
177   - istart = cal.find("NAME(")
178   - if istart != -1:
179   - istop = cal.find(")")
180   - name = cal[5:istop]
181   - if names.count(name) == 0:
182   - names.append(name)
183   - names.sort()
  222 + names = self._get_response_names(cals)
184 223  
185 224 # Print response name
186 225 if self.logTerse():
... ... @@ -197,7 +236,7 @@ class cscaldb(ctools.cscript):
197 236 # ======================== #
198 237 if __name__ == '__main__':
199 238 """
200   - Dumps calibration database into log file.
  239 + Logs calibration database into the log file.
201 240 """
202 241 # Create instance of application
203 242 app = cscaldb(sys.argv)
... ...
pyext/Makefile.am
1 1 #############################################################################
2 2 # Makefile for ctools Python extension module #
3 3 # ------------------------------------------------------------------------- #
4   -# Copyright (C) 2011-2015 Juergen Knoedlseder #
  4 +# Copyright (C) 2011-2016 Juergen Knoedlseder #
5 5 # ------------------------------------------------------------------------- #
6 6 # #
7 7 # This program is free software: you can redistribute it and/or modify #
... ... @@ -46,6 +46,7 @@ testscripts = ctools/tests/__init__.py \
46 46 ../test/test_ctbutterfly.py \
47 47 ../test/test_ctulimit.py \
48 48 ../test/test_cterror.py \
  49 + ../test/test_cscaldb.py \
49 50 ../test/test_pipelines.py
50 51 testdata = ../test/data/crab.xml \
51 52 ../test/data/crab_events.fits.gz \
... ...
test/test_cscaldb.py 0 → 100755
  1 +#! /usr/bin/env python
  2 +# ==========================================================================
  3 +# This scripts performs unit tests for the cscaldb script.
  4 +#
  5 +# Copyright (C) 2016 Juergen Knoedlseder
  6 +#
  7 +# This program is free software: you can redistribute it and/or modify
  8 +# it under the terms of the GNU General Public License as published by
  9 +# the Free Software Foundation, either version 3 of the License, or
  10 +# (at your option) any later version.
  11 +#
  12 +# This program is distributed in the hope that it will be useful,
  13 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 +# GNU General Public License for more details.
  16 +#
  17 +# You should have received a copy of the GNU General Public License
  18 +# along with this program. If not, see <http://www.gnu.org/licenses/>.
  19 +#
  20 +# ==========================================================================
  21 +import gammalib
  22 +import cscripts
  23 +
  24 +
  25 +# ============================= #
  26 +# Test class for cscaldb script #
  27 +# ============================= #
  28 +class Test(gammalib.GPythonTestSuite):
  29 + """
  30 + Test class for cscaldb script.
  31 + """
  32 + # Constructor
  33 + def __init__(self):
  34 + """
  35 + Constructor.
  36 + """
  37 + # Call base class constructor
  38 + gammalib.GPythonTestSuite.__init__(self)
  39 +
  40 + # Return
  41 + return
  42 +
  43 + # Set test functions
  44 + def set(self):
  45 + """
  46 + Set all test functions.
  47 + """
  48 + # Set test name
  49 + self.name("cscaldb")
  50 +
  51 + # Append tests
  52 + self.append(self._test_functional, "Test cscaldb functionality")
  53 +
  54 + # Return
  55 + return
  56 +
  57 + # Test cscaldb functionality
  58 + def _test_functional(self):
  59 + """
  60 + Test cscaldb functionality.
  61 + """
  62 + # Set-up cscaldb
  63 + caldb = cscripts.cscaldb()
  64 +
  65 + # Run script
  66 + self.test_try("Run cscaldb")
  67 + try:
  68 + caldb.run()
  69 + self.test_try_success()
  70 + except:
  71 + msg = "Exception occured in cscaldb."
  72 + self.test_try_failure(msg)
  73 +
  74 + # Return
  75 + return
... ...
test/test_cscripts.sh.in
... ... @@ -47,11 +47,30 @@ export CALDB=$PWD/caldb
47 47 rm -rf $PFILES
48 48 mkdir -p $PFILES
49 49 cp -r ../src/*/*.par pfiles/
  50 +cp -r ../cscripts/*.par pfiles/
50 51 rm -rf $CALDB
51 52 mkdir -p $CALDB
52 53  
53 54  
54 55 #
  56 +# Test cscaldb
  57 +# ============
  58 +$ECHO -n "Test cscaldb: "
  59 +chmod +x @abs_top_srcdir@/cscripts/cscaldb.py
  60 +@PYTHON_COVERAGE@@abs_top_srcdir@/cscripts/cscaldb.py \
  61 + chatter=4 > cscaldb.log
  62 +$ECHO -n "."
  63 +if [ -s "cscaldb.log" ]
  64 +then
  65 + $ECHO -n "."
  66 +else
  67 + $ECHO " cscaldb.log file is not valid"
  68 + exit 1
  69 +fi
  70 +$ECHO " ok"
  71 +
  72 +
  73 +#
55 74 # Test csobs2caldb
56 75 # ================
57 76 $ECHO -n "Test csobs2caldb: "
... ...
test/test_python.py
... ... @@ -37,6 +37,7 @@ import test_ctcubemask
37 37 import test_ctbutterfly
38 38 import test_ctulimit
39 39 import test_cterror
  40 +import test_cscaldb
40 41 import test_pipelines
41 42  
42 43  
... ... @@ -65,6 +66,7 @@ def test(installed=False):
65 66 suite_ctbutterfly = test_ctbutterfly.Test()
66 67 suite_ctulimit = test_ctulimit.Test()
67 68 suite_cterror = test_cterror.Test()
  69 + suite_cscaldb = test_cscaldb.Test()
68 70 suite_pipelines = test_pipelines.Test()
69 71  
70 72 # Setup unit tests
... ... @@ -82,6 +84,7 @@ def test(installed=False):
82 84 suite_ctbutterfly.set()
83 85 suite_ctulimit.set()
84 86 suite_cterror.set()
  87 + suite_cscaldb.set()
85 88 suite_pipelines.set()
86 89  
87 90 # Append tests to container
... ... @@ -99,6 +102,7 @@ def test(installed=False):
99 102 suites.append(suite_ctbutterfly)
100 103 suites.append(suite_ctulimit)
101 104 suites.append(suite_cterror)
  105 + suites.append(suite_cscaldb)
102 106 suites.append(suite_pipelines)
103 107  
104 108 # If we have an installed version then create a temporary
... ... @@ -113,11 +117,11 @@ def test(installed=False):
113 117 # Get test directory
114 118 import inspect
115 119 testdir = inspect.getfile(ctools.tests)
116   - head, tail = os.path.split(testdir)
  120 + dirname = os.path.dirname(testdir)
117 121  
118 122 # Copy over test data and irf
119   - os.system("cp -r %s %s" % (head+"/data", "data"))
120   - os.system("cp -r %s %s" % (head+"/irf", "irf"))
  123 + os.system("cp -r %s %s" % (dirname+"/data", "data"))
  124 + os.system("cp -r %s %s" % (dirname+"/irf", "irf"))
121 125  
122 126 # Set PFILES environment variable
123 127 try:
... ... @@ -129,6 +133,7 @@ def test(installed=False):
129 133 # Copy over pfiles
130 134 if not installed:
131 135 os.system("cp -r ../src/*/*.par pfiles/")
  136 + os.system("cp -r ../cscripts/*.par pfiles/")
132 137 else:
133 138 os.system("cp -r %s/syspfiles/*.par pfiles/" % (os.environ['CTOOLS']))
134 139  
... ...