source: flex_extract.git/test/Regression/Mars_request/test_cmp_mars_requests.py @ 6275f7b

ctbtodev
Last change on this file since 6275f7b was 6275f7b, checked in by Anne Philipp <anne.philipp@…>, 5 years ago

completed testcase for mars request comparison

  • Property mode set to 100644
File size: 10.1 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3"""Comparison of the created MARS requests of two flex_extract versions.
4
5There will be comparisons for the given standard control files in the
6"Controls" - directory. The result of the comparison is stored in the
7"Log" - directory with an individual timestamp in the format %Y-%m-%d_%H-%M-%S.
8(E.g. log_2018-11-23_12-42-29)
9The MARS request files are named such that it contains the name of the
10corresponding control files "<control-identifier>.csv" (e.g. EA5_mr.csv).
11They are stored in the corresponding version directory and have the same
12name in both versions.
13
14The script should be called like:
15
16    python test_cmp_mars_requests.py <old_version_number> <new_version_number>
17
18Note
19----
20The MARS request files from the older version have to be in place already.
21The request files of the new/current version will be generated automatically
22with the "run_local.sh" script.
23It is necessary to have a directory named after the version number of
24flex_extract. For example: "7.0.3" and "7.1".
25
26Example
27-------
28    python test_cmp_mars_requests.py 7.0.4 7.1
29"""
30
31# ------------------------------------------------------------------------------
32# MODULES
33# ------------------------------------------------------------------------------
34import os
35import sys
36import pandas as pd
37import subprocess
38import shutil
39from datetime import datetime
40
41sys.path.append('../../../source/python')
42import _config
43from  mods.tools import init128
44
45# ------------------------------------------------------------------------------
46# FUNCTION
47# ------------------------------------------------------------------------------
48def test_mr_column_equality(mr_old, mr_new):
49    '''Check if old and new version of MARS requests have the same
50    amount of columns.
51
52    If the number is not equal and/or the column names are not equal
53    an error message is stored in global variable "err_msg".
54
55    Parameters
56    ----------
57    mr_old : :obj:`pandas DataFrame`
58        Contains the mars requests from the old version of
59        flex_extract.
60
61    mr_new : :obj:`pandas DataFrame`
62        Contains the mars requests from the new version of
63        flex_extract.
64
65    Return
66    ------
67    bool
68        True if successful, False otherwise.
69    '''
70    global err_msg
71    if (len(mr_old.columns.values) == len(mr_new.columns.values) and
72        sorted(mr_old.columns.values) == sorted(mr_new.columns.values)):
73        return True
74    else:
75        err_msg = 'Unequal number and/or column names!\n'
76        return False
77
78
79def test_mr_number_equality(mr_old, mr_new):
80    '''Check if old and new version have the same number of requests.
81
82    If the number is not equal an error message is stored in
83    global variable "err_msg".
84
85    Parameters
86    ----------
87    mr_old : :obj:`pandas DataFrame`
88        Contains the mars requests from the old version of
89        flex_extract.
90
91    mr_new : :obj:`pandas DataFrame`
92        Contains the mars requests from the new version of
93        flex_extract.
94
95    Return
96    ------
97    bool
98        True if successful, False otherwise.
99    '''
100    global err_msg
101    if len(mr_new.index) == len(mr_old.index):
102        return True
103    else:
104        err_msg = 'Unequal number of mars requests!\n'
105        return False
106
107def test_mr_content_equality(mr_old, mr_new):
108    '''Check if old and new version have the same request contents.
109
110    If the content in a column is not equal an error message is stored in
111    global variable "err_msg", recording the column.
112
113    Parameters
114    ----------
115    mr_old : :obj:`pandas DataFrame`
116        Contains the mars requests from the old version of
117        flex_extract.
118
119    mr_new : :obj:`pandas DataFrame`
120        Contains the mars requests from the new version of
121        flex_extract.
122
123    Return
124    ------
125    bool
126        True if successful, False otherwise.
127    '''
128    global err_msg
129    lresult = None
130    columns = list(mr_new.columns.values)
131    del columns[columns.index('target')]
132    mr_new = trim_all_columns(mr_new)
133    mr_old = trim_all_columns(mr_old)
134    for col in columns:
135        if mr_new[col].equals(mr_old[col]):
136            lresult = True
137        else:
138            err_msg += 'Unconsistency happend to be in column: ' + col + '\n'
139            print mr_new[col]
140            print mr_old[col]
141            return False
142    return lresult
143
144
145def trim_all_columns(df):
146    """
147    Trim whitespace from ends of each value across all series in dataframe
148    """
149    trim_strings = lambda x: x.strip() if isinstance(x, str) else x
150    return df.applymap(trim_strings)
151
152def convert_param_numbers(mr_old):
153    """
154    Convert the numbers parameter into integers with 3 digits.
155    """
156
157    if str(mr_old).strip() != "OFF" and mr_old != None and '/' in str(mr_old) :
158        numbers = mr_old.split('/')
159        number = str(int(numbers[0])).zfill(3)+'/TO/'+str(int(numbers[2])).zfill(3)
160
161        return number
162
163    return mr_old
164
165def convert_param_step(mr_old):
166    """
167    For pure forecast with steps greater than 23 hours, the older versions
168    writes out a list of steps instead with the syntax 'to' and 'by'.
169    e.g. 000/003/006/009/012/015/018/021/024/027/030/033/036
170   
171    Convert this to 0/to/36/by/3
172    """
173
174    #if 'to' in str(mr_old) and 'by' in str(mr_old):
175    #    steps = mr_old.split('/')
176    #    step = []
177    #    for i in range(int(steps[0]),int(steps[2]),int(steps[4])):
178    #        step.append(str(int(i)).zfill(2))
179    #    return '/'.join(step)
180   
181    if not mr_old.isdigit() and 'to' not in mr_old.lower():
182        if int(mr_old.split('/')[-1]) > 23:
183   
184            steps = mr_old.split('/')
185            dtime = int(steps[1]) - int(steps[0])
186           
187            nsteps = str(int(steps[1]))+'/to/'+str(int(steps[-1]))+'/by/'+str(int(dtime))
188            return nsteps
189           
190   
191    return mr_old
192
193def to_param_id(pars, table):
194    '''Transform parameter names to parameter ids with ECMWF grib table 128.
195
196    Parameters
197    ----------
198    pars : str
199        Addpar argument from CONTROL file in the format of
200        parameter names instead of ids. The parameter short
201        names are sepearted with "/" and they are passed as
202        one single string.
203
204    table : dict
205        Contains the ECMWF grib table 128 information.
206        The key is the parameter number and the value is the
207        short name of the parameter.
208
209    Return
210    ------
211    ipar : list of int
212        List of addpar parameters from CONTROL file transformed to
213        parameter ids in the format of integer.
214    '''
215    if not pars:
216        return []
217    if not isinstance(pars, str):
218        pars=str(pars)
219
220    cpar = pars.upper().split('/')
221    spar = []
222    for par in cpar:
223        par = par.strip()
224        for k, v in table.items():
225            if par.isdigit():
226                par = str(int(par)).zfill(3)
227            if par == k or par == v:
228                spar.append(k + '.128')
229                break
230        else:
231            print('\n\Warning: par ' + par + ' not found in table 128\n\n”')
232
233    return '/'.join(str(l) for l in spar)
234
235
236
237if __name__ == '__main__':
238
239    # basic values for paths and versions
240    control_path = 'Controls'
241    log_path = 'Log'
242    old_dir = sys.argv[1] # e.g. '7.0.4'
243    new_dir = sys.argv[2] # e.g. '7.1'
244    mr_filename = 'mars_requests.csv'
245
246    # have to be set to "True" in the beginnning
247    # so it only fails if a test fails
248    lfinal = True
249
250    # prepare log file for this test run
251    currenttime = datetime.now()
252    time_str = currenttime.strftime('%Y-%m-%d_%H-%M-%S')
253    logfile = os.path.join(log_path, 'log_' + time_str)
254    with open(logfile, 'aw') as f:
255        f.write('Compare mars requests between version ' + old_dir +
256                ' and version ' + new_dir + ' : \n')
257
258    # list all controlfiles
259    controls =  os.listdir(control_path)
260
261    # loop over controlfiles
262    for c in controls:
263        # empty error message for every controlfile
264        err_msg = ''
265
266        # start flex_extract with controlfiles to get mars_request files
267        shutil.copy(os.path.join(control_path,c), _config.PATH_CONTROLFILES)
268        subprocess.check_output(['run_local.sh', new_dir, c])
269        os.remove(os.path.join(_config.PATH_CONTROLFILES,c))
270
271        # cut-of "CONTROL_" string and mv mars_reqeust file
272        # to a name including control specific name
273        mr_name = c.split('CONTROL_')[1] + '.csv'
274        shutil.move(os.path.join(new_dir,mr_filename), os.path.join(new_dir,mr_name))
275
276        # read both mr files (old & new)
277        mr_old = pd.read_csv(os.path.join(old_dir, mr_name))
278        mr_new = pd.read_csv(os.path.join(new_dir, mr_name))
279
280        mr_old.columns = mr_old.columns.str.strip()
281        mr_new.columns = mr_new.columns.str.strip()
282
283        # convert names in old to ids
284        table128 = init128(_config.PATH_GRIBTABLE)
285        #print mr_old['param']
286
287        # some format corrections are necessary to compare older versions with 7.1
288        mr_old['param'] = mr_old['param'].apply(to_param_id, args=[table128])
289        mr_old['number'] = mr_old['number'].apply(convert_param_numbers) 
290        if '142' in mr_old.ix[0,'param']: # if flux request
291            mr_old.ix[0,'step'] = convert_param_step(mr_old.ix[0,'step'])
292
293        print 'Results: ', c
294
295        # do tests on mr files
296        lcoleq = test_mr_column_equality(mr_old, mr_new)
297        lnoeq = test_mr_number_equality(mr_old, mr_new)
298        lcoeq = test_mr_content_equality(mr_old, mr_new)
299
300        # check results for mr file
301        lfinal = lfinal and lcoleq and lnoeq and lcoeq
302
303        # write out result to logging file
304        with open(logfile, 'aw') as f:
305            if lcoleq and lnoeq and lcoeq:
306                f.write('... ' + c + ' ... OK!' + '\n')
307            else:
308                f.write('... ' + c + ' ... FAILED!' + '\n')
309                if err_msg:
310                    f.write('... \t' + err_msg + '\n')
311
312        exit
313
314    # exit with success or error status
315    if lfinal:
316        sys.exit(0) # 'SUCCESS'
317    else:
318        sys.exit(1) # 'FAIL'
Note: See TracBrowser for help on using the repository browser.
hosted by ZAMG