1 | """ |
---|
2 | Arguments: |
---|
3 | :-p: project |
---|
4 | :--copy-config: <target directory path>: copy configuration files to target directory path; |
---|
5 | :--sum: <log file directory> create a summary of the results logged in the specified directory. |
---|
6 | :-f: <file>: check a single file; |
---|
7 | :-d: <directory>: check all the NetCDF files in a directory; |
---|
8 | :-D: <directory>: check all the NetCDF files in a directory tree (or latest versions if a recognised version managment system is defined for the project); |
---|
9 | :--ld: <log file directory>: ## directory to take log files; |
---|
10 | :-R: <record file name>: file name for file to take one record per file checked; |
---|
11 | :--cae: "catch all errors": will trap exceptions and record in log files, and then continue. Default is to stop after unrecognised exceptions. |
---|
12 | :--log: <single|multi>: Set log file management option -- see "Single log" and "Multi-log" below. |
---|
13 | :--blfmode: <mode>: Set mode for batch log file -- see log file modes |
---|
14 | :--blfms: Set milli-second mode for naming batch log files (instead of second mode); |
---|
15 | :--flfmode: <mode>: Set mode for file-level log file -- see log file modes |
---|
16 | :--aMap: Read in some attribute mappings and run tests with virtual substitutions; |
---|
17 | |
---|
18 | Log file modes |
---|
19 | -------------- |
---|
20 | |
---|
21 | Valid modes are: 'a': append |
---|
22 | 'n', 'np': new file, 'np': protect after closing (mode = 444) |
---|
23 | 'w', 'wo': write (overwrite if present), 'wo': protect after closing (mode = 444) |
---|
24 | |
---|
25 | Note that the log files generated in multi-log mode will re-use file names. If running with --flfmode set to 'n','np' or 'wo' it will be necessary to change or clear the target directory. The names of batch log files include the time, to the nearest second, when the process is started, so will not generally suffer from re-use. |
---|
26 | """ |
---|
27 | |
---|
28 | """ |
---|
29 | ceda_cc -p <project> [-f <NetCDF file>|-d <directory containing files>|-D <root of directory tree>] [other options] |
---|
30 | |
---|
31 | With the "-D" option, all files in the directory tree beneath the given diretory will be checked. With the "-d" option, only files in the given directory will be checked. |
---|
32 | """ |
---|
33 | import sys, os, string, time, logging |
---|
34 | |
---|
35 | class c4_init(object): |
---|
36 | |
---|
37 | def __init__(self,args=None): |
---|
38 | self.logByFile = True |
---|
39 | self.policyFileLogfileMode = 'w' |
---|
40 | self.policyBatchLogfileMode = 'np' |
---|
41 | self.policyBatchLogfileMs = False |
---|
42 | if args==None: |
---|
43 | args = sys.argv[1:] |
---|
44 | nn = 0 |
---|
45 | |
---|
46 | self.attributeMappingFile = None |
---|
47 | self.recordFile = 'Rec.txt' |
---|
48 | self.logDir = 'logs_02' |
---|
49 | self.errs = [] |
---|
50 | |
---|
51 | # Set default project to "CORDEX" |
---|
52 | self.project = "CORDEX" |
---|
53 | self.holdExceptions = False |
---|
54 | forceLogOrg = None |
---|
55 | argsIn = args[:] |
---|
56 | |
---|
57 | # Show help if no args or help requested |
---|
58 | if len(args) == 0 or args[0] in ('-h', '-help', '--help'): |
---|
59 | print __doc__ |
---|
60 | raise SystemExit(0) |
---|
61 | # The --copy-config option must be the first argument if it is present. |
---|
62 | elif args[0] == '--copy-config': |
---|
63 | if len(args) < 2: |
---|
64 | self.commandHints( argsIn ) |
---|
65 | args.pop(0) |
---|
66 | dest_dir = args.pop(0) |
---|
67 | config.copy_config(dest_dir) |
---|
68 | print 'Configuration directory copied to %s. Set CC_CONFIG_DIR to use this configuration.' % dest_dir |
---|
69 | print |
---|
70 | raise SystemExit(0) |
---|
71 | |
---|
72 | self.summarymode = args[0] == '--sum' |
---|
73 | if self.summarymode: |
---|
74 | return |
---|
75 | |
---|
76 | self.experimental = False |
---|
77 | self.forceNetcdfLib = None |
---|
78 | fltype = None |
---|
79 | argu = [] |
---|
80 | while len(args) > 0: |
---|
81 | next = args.pop(0) |
---|
82 | if next == '-f': |
---|
83 | flist = [args.pop(0),] |
---|
84 | self.logByFile = False |
---|
85 | fltype = '-f' |
---|
86 | self.source = flist[0] |
---|
87 | elif next == '--log': |
---|
88 | x = args.pop(0) |
---|
89 | assert x in ['single','multi','s','m'], 'unrecognised logging option (--log): %s' % (x) |
---|
90 | if x in ['multi','m']: |
---|
91 | forceLogOrg = 'multi' |
---|
92 | elif x in ['single','s']: |
---|
93 | forceLogOrg = 'single' |
---|
94 | elif next == '--experimental': |
---|
95 | self.experimental = True |
---|
96 | elif next == '--force-ncq': |
---|
97 | self.forceNetcdfLib = 'ncq3' |
---|
98 | elif next == '--force-cdms2': |
---|
99 | self.forceNetcdfLib = 'cdms2' |
---|
100 | elif next == '--force-pync4': |
---|
101 | self.forceNetcdfLib = 'netCDF4' |
---|
102 | elif next == '--force-scientific': |
---|
103 | self.forceNetcdfLib = 'Scientific' |
---|
104 | elif next == '--flfmode': |
---|
105 | lfmk = args.pop(0) |
---|
106 | assert lfmk in ['a','n','np','w','wo'], 'Unrecognised file logfile mode (--flfmode): %s' % lfmk |
---|
107 | self.policyFileLogfileMode = lfmk |
---|
108 | elif next == '--blfmode': |
---|
109 | lfmk = args.pop(0) |
---|
110 | assert lfmk in ['a','n','np','w','wo'], 'Unrecognised batch logfile mode (--blfmode): %s' % lfmk |
---|
111 | self.policyBatchLogfileMode = lfmk |
---|
112 | elif next == '--blfms': |
---|
113 | self.policyBatchLogfileMs = False |
---|
114 | elif next == '-d': |
---|
115 | fdir = args.pop(0) |
---|
116 | flist = glob.glob( '%s/*.nc' % fdir ) |
---|
117 | self.source = '%s/*.nc' % fdir |
---|
118 | elif next == '-D': |
---|
119 | flist = [] |
---|
120 | fdir = args.pop(0) |
---|
121 | for root, dirs, files in os.walk( fdir, followlinks=True ): |
---|
122 | for f in files: |
---|
123 | fpath = '%s/%s' % (root,f) |
---|
124 | if (os.path.isfile( fpath ) or os.path.islink( fpath )) and f[-3:] == '.nc': |
---|
125 | flist.append( fpath ) |
---|
126 | self.source = '%s/.....' % fdir |
---|
127 | elif next == '-R': |
---|
128 | self.recordFile = args.pop(0) |
---|
129 | elif next == '--ld': |
---|
130 | self.logDir = args.pop(0) |
---|
131 | elif next in ['--catchAllExceptions','--cae']: |
---|
132 | self.holdExceptions = True |
---|
133 | elif next == '--aMap': |
---|
134 | self.attributeMappingFile = args.pop(0) |
---|
135 | assert os.path.isfile( self.attributeMappingFile ), 'The token "--aMap" should be followed by the path or name of a file' |
---|
136 | elif next == "-p": |
---|
137 | self.project = args.pop(0) |
---|
138 | else: |
---|
139 | print 'Unused argument: %s' % next |
---|
140 | argu.append( next ) |
---|
141 | nn+=1 |
---|
142 | if nn != 0: |
---|
143 | print 'Unused arguments: ', argu |
---|
144 | self.commandHints( argsIn ) |
---|
145 | |
---|
146 | if self.project == 'CMIP5' and fltype != '-f': |
---|
147 | fl0 = [] |
---|
148 | for f in flist: |
---|
149 | if string.find( f, '/latest/' ) != -1: |
---|
150 | fl0.append(f) |
---|
151 | flist = fl0 |
---|
152 | |
---|
153 | if forceLogOrg != None: |
---|
154 | if forceLogOrg == 'single': |
---|
155 | self.logByFile = False |
---|
156 | else: |
---|
157 | self.logByFile = True |
---|
158 | |
---|
159 | if self.project[:2] == '__': |
---|
160 | self.source = 'dummy' |
---|
161 | flist = [] |
---|
162 | ss = 'abcdefgijk' |
---|
163 | ss = 'abcdefgijklmnopqrstuvwxyz' |
---|
164 | ss = 'abc' |
---|
165 | for i in range(10): |
---|
166 | v = 'v%s' % i |
---|
167 | for a in ss: |
---|
168 | for b in ss: |
---|
169 | flist.append( '%s_day_%s_%s_1900-1909.nc' % (v,a,b) ) |
---|
170 | flist.sort() |
---|
171 | fnl = [] |
---|
172 | for f in flist: |
---|
173 | fn = string.split(f, '/')[-1] |
---|
174 | fnl.append(fn) |
---|
175 | nd = 0 |
---|
176 | dupl = [] |
---|
177 | for k in range(len(fnl)-1): |
---|
178 | if fnl[k] == fnl[k-1]: |
---|
179 | nd += 1 |
---|
180 | dupl.append( fnl[k] ) |
---|
181 | self.dupDict = {} |
---|
182 | for f in dupl: |
---|
183 | self.dupDict[f] = 0 |
---|
184 | if nd != 0: |
---|
185 | self.errs.append( 'Duplicate file names encountered: %s' % nd ) |
---|
186 | self.errs.append( dupl ) |
---|
187 | self.flist = flist |
---|
188 | self.fnl = fnl |
---|
189 | if not os.path.isdir( self.logDir ): |
---|
190 | os.mkdir( self.logDir ) |
---|
191 | |
---|
192 | tstring1 = '%4.4i%2.2i%2.2i_%2.2i%2.2i%2.2i' % time.gmtime()[0:6] |
---|
193 | if self.policyBatchLogfileMs: |
---|
194 | t = time.time() |
---|
195 | ms = int( ( t-int(t) )*1000. ) |
---|
196 | tsring1 += '.%3.3i' % ms |
---|
197 | self.batchLogfile = '%s/qcBatchLog_%s.txt' % ( self.logDir,tstring1) |
---|
198 | ## default appending to myapp.log; mode='w' forces a new file (deleting old contents). |
---|
199 | self.logger = logging.getLogger('c4logger') |
---|
200 | if self.policyBatchLogfileMode in ['n','np']: |
---|
201 | assert not os.path.isfile( self.batchLogfile ), '%s exists and policy set to new file' % self.batchLogfile |
---|
202 | m = self.policyBatchLogfileMode[0] |
---|
203 | if m == 'n': |
---|
204 | m = 'w' |
---|
205 | if m == 'a': |
---|
206 | self.hdlr = logging.FileHandler(self.batchLogfile) |
---|
207 | else: |
---|
208 | self.hdlr = logging.FileHandler(self.batchLogfile,mode=m) |
---|
209 | formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') |
---|
210 | self.hdlr.setFormatter(formatter) |
---|
211 | self.logger.setLevel(logging.INFO) |
---|
212 | self.logger.addHandler(self.hdlr) |
---|
213 | |
---|
214 | self.attributeMappings = [] |
---|
215 | self.attributeMappingsLog = None |
---|
216 | if self.attributeMappingFile != None: |
---|
217 | for l in open( self.attributeMappingFile ).readlines(): |
---|
218 | if l[0] != '#': |
---|
219 | bb = string.split( string.strip(l), '|' ) |
---|
220 | assert len(bb) ==2, "Error in experimental module attributeMapping -- configuration line not scanned [%s]" % str(l) |
---|
221 | bits = string.split( bb[0], ';' ) |
---|
222 | cl = [] |
---|
223 | for b in bits: |
---|
224 | cl.append( string.split(b, '=' ) ) |
---|
225 | self.attributeMappings.append( ('am001',cl, string.split(bb[1],'=') ) ) |
---|
226 | self.attributeMappingsLog = open( 'attributeMappingsLog.txt', 'w' ) |
---|
227 | |
---|
228 | def commandHints(self, args): |
---|
229 | if args[0] in ['-h','--sum']: |
---|
230 | print 'Arguments look OK' |
---|
231 | elif args[0] == '--copy-config': |
---|
232 | print 'Usage [configuration copy]: ceda_cc --copy-config <target directory path>' |
---|
233 | else: |
---|
234 | if not( '-f' in args or '-d' in args or '-D' in args): |
---|
235 | print 'No file or target directory specified' |
---|
236 | print __doc__ |
---|
237 | raise SystemExit(0) |
---|
238 | |
---|
239 | |
---|
240 | def getFileLog( self, fn, flf=None ): |
---|
241 | if flf == None: |
---|
242 | tstring2 = '%4.4i%2.2i%2.2i' % time.gmtime()[0:3] |
---|
243 | if fn in self.dupDict.keys(): |
---|
244 | tag = '__%2.2i' % self.dupDict[fn] |
---|
245 | self.dupDict[fn] += 1 |
---|
246 | else: |
---|
247 | tag = '' |
---|
248 | self.fileLogfile = '%s/%s%s__qclog_%s.txt' % (self.logDir,fn[:-3],tag,tstring2) |
---|
249 | if self.policyFileLogfileMode in ['n','np']: |
---|
250 | assert not os.path.isfile( self.fileLogfile ), '%s exists and policy set to new file' % self.fileLogfile |
---|
251 | m = self.policyFileLogfileMode[0] |
---|
252 | if m == 'n': |
---|
253 | m = 'w' |
---|
254 | else: |
---|
255 | m = 'a' |
---|
256 | self.fileLogfile = flf |
---|
257 | |
---|
258 | self.fLogger = logging.getLogger('fileLog_%s_%s' % (fn,m)) |
---|
259 | if m == 'a': |
---|
260 | self.fHdlr = logging.FileHandler(self.fileLogfile) |
---|
261 | else: |
---|
262 | self.fHdlr = logging.FileHandler(self.fileLogfile,mode=m) |
---|
263 | fileFormatter = logging.Formatter('%(message)s') |
---|
264 | self.fHdlr.setFormatter(fileFormatter) |
---|
265 | self.fLogger.addHandler(self.fHdlr) |
---|
266 | self.fLogger.setLevel(logging.INFO) |
---|
267 | return self.fLogger |
---|
268 | |
---|
269 | def closeFileLog(self): |
---|
270 | self.fLogger.removeHandler(self.fHdlr) |
---|
271 | self.fHdlr.close() |
---|
272 | if self.policyFileLogfileMode in ['wo','np']: |
---|
273 | os.popen( 'chmod %s %s;' % (444, self.fileLogfile) ) |
---|
274 | |
---|
275 | def closeBatchLog(self): |
---|
276 | self.logger.removeHandler(self.hdlr) |
---|
277 | self.hdlr.close() |
---|
278 | if self.policyBatchLogfileMode in ['wo','np']: |
---|
279 | os.popen( 'chmod %s %s;' % (444, self.batchLogfile) ) |
---|