rda-python-icoads 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rda-python-icoads might be problematic. Click here for more details.

@@ -0,0 +1,764 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ ##################################################################################
4
+ #
5
+ # Title : imma1_subset
6
+ # Author : Zaihua Ji, zji@ucar.edu
7
+ # Date : 01/04/2021
8
+ # 2025-02-18 transferred to package rda_python_icoads from
9
+ # https://github.com/NCAR/rda-icoads.git
10
+ # Purpose : process ICOADS requests under control of dsrqst
11
+ # for data in PostgreSQL IVADDB in IMMA1 format
12
+ #
13
+ # Github: https://github.com/NCAR/rda-python-icoads.git
14
+ #
15
+ ##################################################################################
16
+
17
+ import sys
18
+ import os
19
+ import re
20
+ import glob
21
+ from os import path as op
22
+ from rda_python_common import PgLOG
23
+ from rda_python_common import PgDBI
24
+ from rda_python_common import PgSIG
25
+ from rda_python_common import PgUtil
26
+ from rda_python_common import PgFile
27
+ from rda_python_common import PgOPT
28
+ from rda_python_dsrqst import PgSubset
29
+ from . import PgIMMA
30
+
31
+ IDSID = 'd548000'
32
+ VAR2DB = {'IS' : "ics"}
33
+ PVALS = {
34
+ 'codedir' : op.dirname(op.abspath(__file__)) + '/',
35
+ 'rdimma1' : "rdimma1_csv.f",
36
+ 'readhtml' : "README_R3.0_Subset.html",
37
+ 'readme' : "readme_imma1.",
38
+ 'html2pdf' : "wkhtmltopdf",
39
+ 'resol' : 0.02,
40
+ 'trmcnt' : 0, # count of trim variables selected
41
+ 'dates' : [], # [bdate, edate]
42
+ 'lats' : [], # [slat, nlat]
43
+ 'lons' : [], # [wlon, elon]
44
+ 'flts' : [],
45
+ 'vars' : [],
46
+ 'fmts' : {},
47
+ 'rinfo' : {},
48
+ 'vinfo' : {},
49
+ 'tidx' : [],
50
+ 'facnt' : 0, # full attm count, for Reanqc and Ivad attms
51
+ 'bdate' : [],
52
+ 'edate' : [],
53
+ 'fachar' : 97, # chr(97) = 'a'
54
+ # hash array if specified
55
+ 'pts' : [],
56
+ 'dcks' : [],
57
+ 'sids' : [],
58
+ 'iopts' : 0,
59
+ }
60
+
61
+ FAVARS = {} # hash of ireanqc and iivad, values are selected source name-var
62
+ FSFLDS = {} # hash of selected data variables in FACNDS
63
+ FSSRCS = {} # hash of ireanqc and iivad, values are array of selected source names
64
+ FSCNDS = {} # hash of ireanqc and iivad, values are array of conditions for variables in FSFLDS
65
+ FACNDS = {
66
+ 'ERA-20C' : 'dprp = 1',
67
+ 'CERA-20C' : 'dprp = 2',
68
+ 'FS01' : "arci = 'FS01'",
69
+ 'BKT' : "arci = ' BKT'"
70
+ } # additional condition for full attm
71
+ FAFLDS = {'d' : [0, 18], 'w' : [0, 20], 'slp' : [0, 25], 'at' : [0, 29]}
72
+ FASRCS = {
73
+ 'ERA-20C' : ['d', 'w', 'slp', 'at'],
74
+ 'CERA-20C' : ['d', 'w', 'slp', 'at'],
75
+ 'FS01' : ['w'],
76
+ 'BKT' : ['at']
77
+ }
78
+
79
+ TRIMS = {'sst' : 0, 'at' : 0, 'd' : 0, 'w' : 0, 'slp' : 0, 'wbt' : 0, 'dpt' : 0, 'rh' : 0}
80
+
81
+ # Optional attms for subset. Append var-list to Replace <OPTATTMS> in the README template file
82
+ OPTATTMS = {
83
+ 'headline' : "<h2>Details for optional selections</h2>\n<ul>\n",
84
+ 'iimmt5' : "<li>P/V <i>Immt</i>, <a href=\"http://rda.ucar.edu/datasets/" + IDSID + "/docs/R3.0-imma1.pdf#page=37\">Table C5, page 37</a>\n<ul><li><i>\n",
85
+ 'imodqc' : "<li>P/V <i>Mod-qc</i>, <a href=\"http://rda.ucar.edu/datasets/" + IDSID + "/docs/R3.0-imma1.pdf#page=38\">Table C6, page 38</a>\n<ul><li><i>\n",
86
+ 'imetavos' : "<li>P/V <i>Meta-vos</i>, <a href=\"http://rda.ucar.edu/datasets/" + IDSID + "/docs/R3.0-imma1.pdf#page=39\">Table C7, page 39</a><ul><li><i>\n",
87
+ 'inocn' : "<li>P/V <i>Nocn</i>, <a href=\"http://rda.ucar.edu/datasets/" + IDSID + "/docs/R3.0-imma1.pdf#page=40\">Table C8, page 40</a><ul><li><i>\n",
88
+ 'iecr' : "<li>P/V <i>Ecr</i>, <a href=\"http://rda.ucar.edu/datasets/" + IDSID + "/docs/R3.0-imma1.pdf#page=41\">Table C9, page 41</a><ul><li><i>\n",
89
+ 'ireanqc' : "<li>P/V <i>Rean-qc</i>, <a href=\"http://rda.ucar.edu/datasets/" + IDSID + "/docs/R3.0-imma1.pdf#page=42\">Table C95, page 42</a><ul>\n",
90
+ 'iivad' : "<li>P/V <i>Ivad</i>, <a href=\"http://rda.ucar.edu/datasets/" + IDSID + "/docs/R3.0-imma1.pdf#page=43\">Table C96, page 43</a><ul>\n"
91
+ }
92
+
93
+ PGRQST = PGFILE = None
94
+ pgcmd = 'imma1_subset'
95
+ PSTEP = 32
96
+
97
+ #
98
+ # main function to run dsarch
99
+ #
100
+ def main():
101
+
102
+ global PGRQST, PGFILE
103
+
104
+ PgLOG.PGLOG['LOGFILE'] = "icoads.log"
105
+ argv = sys.argv[1:]
106
+ option = rdir = None
107
+ fidx = ridx = 0
108
+
109
+ for arg in argv:
110
+ if arg == "-b":
111
+ PgLOG.PGLOG['BCKGRND'] = 1
112
+ elif arg == "-d":
113
+ PgLOG.PGLOG['DBGLEVEL'] = 1000
114
+ elif arg == "-f":
115
+ option = 'f'
116
+ elif re.match(r'^-', arg):
117
+ PgLOG.pglog(arg + ": Unknown Option", PgLOG.LGEREX)
118
+ elif option and option == 'f':
119
+ fidx = int(arg)
120
+ option = None
121
+ elif re.match(r'^(\d+)$', arg):
122
+ if ridx == 0:
123
+ ridx = int(arg)
124
+ else:
125
+ PgLOG.pglog("{}: Request Index ({}) given already".format(arg, ridx), PgLOG.LGEREX)
126
+ else:
127
+ if rdir: PgLOG.pglog("{}: Request Directory ({}) given already".format(arg, rdir), PgLOG.LGEREX)
128
+ rdir = arg
129
+
130
+ PgLOG.cmdlog("{} {}".format(pgcmd, ' '.join(argv)))
131
+ PgDBI.dssdb_scname()
132
+ if fidx:
133
+ PGFILE = PgDBI.pgget('wfrqst', '*', "findex = {}".format(fidx), PgLOG.LGEREX)
134
+ if ridx == 0: ridx = PGFILE['rindex']
135
+ PGRQST = PgSubset.valid_subset_request(ridx, rdir, IDSID, PgLOG.LGWNEX)
136
+ if not rdir: rdir = PgLOG.join_paths(PgLOG.PGLOG['RQSTHOME'], PGRQST['rqstid'])
137
+ rstr = "{}-Rqst{}".format(PGRQST['dsid'], ridx)
138
+ if fidx > 0: rstr += "-" + PGFILE['wfile']
139
+
140
+ if fidx:
141
+ process_subset_file(ridx, fidx, rdir, rstr)
142
+ else:
143
+ process_subset_request(ridx, rdir, rstr)
144
+
145
+ PgLOG.cmdlog()
146
+ sys.exit(0)
147
+
148
+ #
149
+ # set the table info array
150
+ #
151
+ def get_table_info(dates):
152
+
153
+ bdate = PgUtil.validate_date(dates[0])
154
+ edate = PgUtil.validate_date(dates[1])
155
+ cnd = "bdate <= '{}' AND edate >= '{}'".format(edate, bdate)
156
+ pgrecs = PgDBI.pgmget('itable', "tidx, bdate, edate", cnd + " ORDER BY tidx", PgLOG.LGEREX)
157
+ tcnt = len(pgrecs['tidx']) if pgrecs else 0
158
+ if not tcnt: PgLOG.pglog("No table index found in dssdb.itable for " + cnd, PgLOG.LGEREX)
159
+ PVALS['tidx'] = pgrecs['tidx']
160
+ PVALS['bdate'] = [bdate]
161
+ PVALS['bdate'].extend([str(d) for d in pgrecs['bdate'][1:]])
162
+ PVALS['edate'] = [str(d) for d in pgrecs['edate'][:-1]]
163
+ PVALS['edate'].append(edate)
164
+
165
+ return tcnt
166
+
167
+ #
168
+ # get the float format string
169
+ #
170
+ def get_float_format(resl):
171
+
172
+ prec = 0
173
+ while resl < 1:
174
+ prec += 1
175
+ resl *=10
176
+
177
+ return r'{:.%df}' % prec if prec else ''
178
+
179
+ #
180
+ # set variable information
181
+ #
182
+ def set_var_info():
183
+
184
+ vars = PVALS['vars']
185
+ flts = PVALS['flts']
186
+ anames = []
187
+ tcodes = []
188
+ avars = {}
189
+ ovars = {}
190
+ uvars = {}
191
+ dvars = {}
192
+ alens = {}
193
+ aprecs = {}
194
+ fmts = {}
195
+ maxs = {}
196
+ fscnts = {}
197
+
198
+ vcnt = len(vars)
199
+ pname = ''
200
+ for i in range(vcnt):
201
+ uvar = ovar = vars[i].upper()
202
+ if ovar in VAR2DB:
203
+ var = VAR2DB[ovar]
204
+ else:
205
+ var = ovar.lower()
206
+
207
+ if var in TRIMS:
208
+ TRIMS[var] = 1
209
+ PVALS['trmcnt'] +=1
210
+
211
+ vinfo = PVALS['vinfo'][var] = PgIMMA.name2number(var)
212
+ aname = vinfo[2]
213
+ if aname != pname:
214
+ anames.append(aname)
215
+ avars[aname] = []
216
+ ovars[aname] = []
217
+ uvars[aname] = []
218
+ dvars[aname] = []
219
+ aprecs[aname] = []
220
+ imma = PgIMMA.IMMAS[aname]
221
+ attm = imma[3]
222
+ tcodes.append("C{}".format(vinfo[0]))
223
+ pname = aname
224
+
225
+ avars[aname].append(var)
226
+ ovars[aname].append(ovar)
227
+ prec = attm[var][2]
228
+ aprecs[aname].append(prec)
229
+ vfld = attm[var]
230
+ vlen = len(vfld)
231
+ if prec > 0 and prec < 1:
232
+ fmts[var] = get_float_format(prec)
233
+ if vlen > 5:
234
+ unit = vfld[5]
235
+ if unit.find('deg') > -1: unit.replace('deg', '&deg;')
236
+ uvar += "({})".format(unit)
237
+ if vlen > 6: maxs[var] = vfld[6]
238
+ uvars[aname].append(uvar)
239
+ if vlen > 4 and vfld[4] == 0:
240
+ dvars[aname].append(var)
241
+ if var in FAFLDS: FSFLDS[var] = FAFLDS[var]
242
+
243
+ set_full_attm_info("iuida", anames, tcodes, avars, aprecs)
244
+
245
+ aname = "ireanqc"
246
+ fscnts[aname] = set_full_attm_count(aname, "fnr")
247
+ if fscnts[aname]:
248
+ set_full_attm_info(aname, anames, tcodes, avars, aprecs)
249
+ PVALS['facnt'] += 1
250
+
251
+ aname = "iivad"
252
+ fscnts[aname] = set_full_attm_count(aname, "fni")
253
+ if fscnts[aname]:
254
+ set_full_attm_info(aname, anames, tcodes, avars, aprecs)
255
+ PVALS['facnt'] += 1
256
+
257
+ # create headline and optional document
258
+ vhead = vcore = optattms = ''
259
+ facnt = len(anames)
260
+ acnt = facnt - PVALS['facnt'] - 1
261
+ for i in range(acnt):
262
+ aname = anames[i]
263
+ vinfo = ','.join(ovars[aname])
264
+ if vhead: vhead += ","
265
+ vhead += vinfo
266
+ uinfo = ','.join(uvars[aname])
267
+ if aname in OPTATTMS:
268
+ optattms += OPTATTMS[aname] + PgLOG.break_long_string(uinfo, 60, "<br>", 20, ",") + "</i></li></ul></li>\n"
269
+ elif aname == 'icoreloc':
270
+ vcore = uinfo
271
+ if 'icorereg' not in ovars:
272
+ PVALS['rinfo']['C0LIST'] = PgLOG.break_long_string(vcore, 60, "<br>", 20, ",")
273
+ elif aname == 'icorereg':
274
+ vcore += ',' + uinfo
275
+ PVALS['rinfo']['C0LIST'] = PgLOG.break_long_string(vcore, 60, "<br>", 20, ",")
276
+ else:
277
+ lkey = tcodes[i] + "LIST"
278
+ PVALS['rinfo'][lkey] = PgLOG.break_long_string(uinfo, 60, "<br>", 20, ",")
279
+
280
+ # add attm Uida variables to
281
+ vinfo = ','.join(avars['iuida'])
282
+ vhead += "," + vinfo.upper()
283
+ acnt += 1
284
+
285
+ for i in range(acnt, facnt):
286
+ aname = anames[i]
287
+ optattms += OPTATTMS[aname]
288
+ if fscnts[aname] > 1:
289
+ j = PVALS['fachar']
290
+ for favar in FAVARS[aname]:
291
+ fachar = chr(j)
292
+ j += 1
293
+ vinfo = ''
294
+ for var in avars[aname]:
295
+ if vinfo: vinfo += ","
296
+ vinfo += "{}-{}".format(fachar, var.upper())
297
+ vhead += "," + vinfo
298
+ optattms += "<li><i>" + PgLOG.break_long_string("{} => {}: {}".format(favar, fachar, vinfo), 60, "<br>", 20, ",") + "</i></li>\n"
299
+ else:
300
+ favar = FAVARS[aname][0]
301
+ vinfo = ''
302
+ for var in avars[aname]:
303
+ if vinfo: vinfo += ","
304
+ vinfo += var.upper()
305
+ vhead += "," + vinfo
306
+ optattms += "<li><i>" + PgLOG.break_long_string("{}: {}".format(favar, vinfo), 60, "<br>", 20, ",") + "</i></li>\n"
307
+ optattms += "</ul></li>\n"
308
+
309
+ PVALS['vhead'] = vhead
310
+ if optattms: PVALS['rinfo']['OPTATTMS'] = "{}{}</ul>".format(OPTATTMS['headline'], optattms)
311
+ PVALS['anames'] = anames
312
+ PVALS['avars'] = avars
313
+ PVALS['aprecs'] = aprecs
314
+ PVALS['dvars'] = dvars
315
+ PVALS['fmts'] = fmts
316
+ PVALS['maxs'] = maxs
317
+
318
+ wlon = int(100*PVALS['lons'][0])
319
+ elon = int(100*PVALS['lons'][1])
320
+ slat = int(100*PVALS['lats'][0])
321
+ nlat = int(100*PVALS['lats'][1])
322
+ if wlon == 0 and elon == 36000:
323
+ loncnd = ''
324
+ elif wlon == elon:
325
+ loncnd = "lon = {}".format(elon)
326
+ elif wlon > elon:
327
+ loncnd = "(lon >= {} OR lon <= {})".format(wlon, elon)
328
+ elif wlon > 0 and elon < 36000:
329
+ loncnd = " lon between {} AND {}".format(wlon, elon)
330
+ elif elon < 36000:
331
+ loncnd = "lon <= {}".format(elon)
332
+ elif wlon > 0:
333
+ loncnd = "lon >= {}".format(wlon)
334
+ else:
335
+ loncnd = ''
336
+
337
+ if slat == -9000 and nlat == 9000:
338
+ latcnd = ''
339
+ elif slat == nlat:
340
+ latcnd = "lat = {}".format(slat)
341
+ elif slat > -9000 and nlat < 9000:
342
+ latcnd = " lat between {} AND {}".format(slat, nlat)
343
+ elif nlat < 9000:
344
+ latcnd = "lat <= {}".format(nlat)
345
+ else:
346
+ latcnd = "lat >= {}".format(slat)
347
+
348
+ if latcnd and loncnd:
349
+ PVALS['spcnd'] = "{} AND {}".format(latcnd, loncnd)
350
+ elif latcnd:
351
+ PVALS['spcnd'] = latcnd
352
+ elif loncnd:
353
+ PVALS['spcnd'] = loncnd
354
+ else:
355
+ PVALS['spcnd'] = ''
356
+
357
+ PVALS['fopts'] = {'OPDN' : flts[0], 'OPPT' : flts[1], 'OPSE' : flts[2],
358
+ 'OPCQ' : flts[3], 'OPTF' : flts[4], 'OP11' : flts[5]}
359
+
360
+ #
361
+ # set counts for the included full attms
362
+ #
363
+ def set_full_attm_count(aname, cname):
364
+
365
+ if aname not in FSSRCS: return 0
366
+
367
+ vcnt = 0
368
+ for sname in FSSRCS[aname]:
369
+ fv = []
370
+ fn = []
371
+ for var in FASRCS[sname]:
372
+ if var in FSFLDS and FSFLDS[var]:
373
+ fv.append("{}-{}".format(sname, var.upper()))
374
+ fn.append(" AND {} AND {} = {}".format(FACNDS[sname], cname, FSFLDS[var][1]))
375
+ vcnt += 1
376
+ if fv: FAVARS[aname] = fv
377
+ if fn: FSCNDS[aname] = fn
378
+
379
+ return vcnt
380
+
381
+ #
382
+ # set information for the included full attms
383
+ #
384
+ def set_full_attm_info(aname, anames, tcodes, avars, aprecs):
385
+
386
+ anames.append(aname)
387
+ imma = PgIMMA.IMMAS[aname]
388
+ attm = imma[3]
389
+ avars[aname] = PgIMMA.order_attm_variables(attm)
390
+ if aname not in aprecs: aprecs[aname] = []
391
+ tcodes.append("C" + imma[1])
392
+ for var in avars[aname]:
393
+ aprecs[aname].append(attm[var][2])
394
+
395
+ #
396
+ # process a validated subset request
397
+ #
398
+ def process_subset_request(ridx, rdir, rstr):
399
+
400
+ ptcnt = PGRQST['ptcount']
401
+ if ptcnt > 0 and check_request_built(ridx, ptcnt, rstr) != 1: return
402
+
403
+ get_subset_info(rstr)
404
+
405
+ if ptcnt > 0:
406
+ PgFile.change_local_directory(rdir, PgLOG.LOGWRN)
407
+ build_final_files(ridx, rstr)
408
+ else:
409
+ tcnt = get_table_info(PVALS['dates'])
410
+ for i in range(tcnt):
411
+ bdate = PVALS['edate'][i]
412
+ edate = PVALS['edate'][i]
413
+ tidx = PVALS['tidx'][i]
414
+ pgrec = {}
415
+ pgrec['data_format'] = 'ASCII'
416
+ pgrec['disp_order'] = i+1
417
+ pgrec['command'] = PVALS['pgcmd'] + " -f -FI"
418
+ pgrec['cmd_detail'] = "dates={} {}&tidx={}".format(bdate, edate, tidx)
419
+ fname = "ICOADS_R3.0_Rqst{}_{}-{}.csv".format(ridx, bdate, edate)
420
+ PgSubset.add_request_file(ridx, fname, pgrec, PgLOG.LGEREX)
421
+
422
+ #
423
+ # process a validated subset request file
424
+ #
425
+ def process_subset_file(ridx, fidx, rdir, rstr):
426
+
427
+ if PGFILE['status'] == 'O':
428
+ PgLOG.pglog(rstr + ': Request File is built already', PgLOG.LOGWRN)
429
+ return
430
+
431
+ PgFile.change_local_directory(rdir, PgLOG.LOGWRN)
432
+ get_subset_info(rstr)
433
+ set_var_info()
434
+ cinfo = PGFILE['cmd_detail']
435
+ tidx = 0
436
+ dates = []
437
+ for line in cinfo.split("&"):
438
+ ms = re.search(r'([-\w]+)=(.+)', line)
439
+ if not ms: continue
440
+ token = ms.group(1)
441
+ pstring = ms.group(2)
442
+ if token == "dates": # Date Limits
443
+ dates = pstring.split(' ')
444
+ elif token == 'tidx':
445
+ tidx = int(pstring)
446
+
447
+ if not (tidx and dates):
448
+ PgLOG.pglog(rstr + ': Miss tidx or date range to build request', PgLOG.LOGWRN)
449
+
450
+ subset_table_index(PGFILE['wfile'], tidx, dates[0], dates[1])
451
+
452
+ #
453
+ # build range dates for subsetting
454
+ #
455
+ def build_table_file(fd, tidx, bdate, edate, atables):
456
+
457
+ anames = PVALS['anames']
458
+ facnt = len(anames)
459
+ acnt = facnt - PVALS['facnt'] - 1
460
+
461
+ # query on the icoreloc
462
+ aname = anames[0]
463
+ qcnd = "date BETWEEN '{}' AND '{}'".format(bdate, edate)
464
+ if PVALS['spcnd']: qcnd += " AND " + PVALS['spcnd']
465
+ tname = "{}_{}".format(aname, tidx)
466
+ pgrecs = PgDBI.pgmget(tname, "*", qcnd, PgLOG.LGEREX)
467
+ rcnt = len(pgrecs['iidx']) if pgrecs else 0
468
+ for r in range(rcnt):
469
+ pgrec = {'icoreloc' : PgUtil.onerecord(pgrecs, r)}
470
+ # quey for all attms
471
+ qcnd = "iidx = {}".format(pgrec['icoreloc']['iidx'])
472
+ for a in range(1, acnt):
473
+ aname = anames[a]
474
+ if atables[aname]:
475
+ tname = "{}_{}".format(aname, tidx)
476
+ pgrec[aname] = PgDBI.pgget(tname, "*", qcnd, PgLOG.LGEREX)
477
+ else:
478
+ pgrec[aname] = None
479
+
480
+ # process trimming
481
+ if 'icorereg' not in pgrec:
482
+ aname = "icorereg"
483
+ tname = "icorereg_{}".format(tidx)
484
+ pgrec[aname] = PgDBI.pgget(tname, "*", qcnd, PgLOG.LGEREX)
485
+
486
+ if 'iicoads' not in pgrec:
487
+ aname = "iicoads"
488
+ tname = "iicoads_{}".format(tidx)
489
+ pgrec[aname] = PgDBI.pgget(tname, "*", qcnd, PgLOG.LGEREX)
490
+ elif PVALS['iopts']:
491
+ if not_match_iopts(pgrec['iicoads']): continue
492
+
493
+ values = PgIMMA.TRIMQC2(pgrec, PVALS['fopts'])
494
+ if not values: continue
495
+
496
+ if PVALS['trmcnt'] > 0:
497
+ for var in values:
498
+ if not TRIMS[var] or values[var]: continue
499
+ if var == 'rh':
500
+ if pgrec['iimmt5'] and var in pgrec['iimmt5']:
501
+ pgrec['iimmt5'][var] = None
502
+ elif pgrec['icorereg'] and var in pgrec['icorereg']:
503
+ pgrec['icorereg'][var] = None
504
+
505
+ #skip empty record
506
+ valid = 0
507
+ for a in range(acnt):
508
+ aname = anames[a]
509
+ record = pgrec[aname]
510
+ dvars = PVALS['dvars'][aname]
511
+ if not (dvars and record): continue
512
+ for var in dvars:
513
+ if var in record and record[var] != None:
514
+ if isinstance(record[var], int) or len(record[var]) > 0:
515
+ valid = 1
516
+ break
517
+ if valid: break
518
+
519
+ if not valid: continue
520
+
521
+ buf = ''
522
+ # save each attm values
523
+ for a in range(acnt):
524
+ aname = anames[a]
525
+ buf += join_attm_fields(aname, pgrec[aname]) + ","
526
+
527
+ # get and save Uida attm values
528
+ aname = "iuida"
529
+ tname = "iuida_{}".format(tidx)
530
+ record = PgDBI.pgget(tname, "*", qcnd, PgLOG.LGEREX)
531
+ buf += join_attm_fields(aname, record)
532
+
533
+ # get and save full attm values
534
+ for a in range(acnt+1, facnt):
535
+ aname = anames[a]
536
+ tname = "{}_{}".format(aname, tidx)
537
+ for cnd in FSCNDS[aname]:
538
+ if atables[aname]:
539
+ record = PgDBI.pgget(tname, "*", qcnd + cnd, PgLOG.LGEREX)
540
+ else:
541
+ record = None
542
+
543
+ buf += ',' + join_attm_fields(aname, record)
544
+
545
+ buf += "\n"
546
+ fd.write(buf)
547
+
548
+ #
549
+ # check if not matching options
550
+ #
551
+ def not_match_iopts(pgrec):
552
+
553
+ if PVALS['pts'] and not (pgrec['pt'] and pgrec['pt'] in PVALS['pts']): return 1
554
+ if PVALS['dcks'] and not (pgrec['dck'] and pgrec['dck'] in PVALS['dcks']): return 1
555
+ if PVALS['sids'] and not (pgrec['sid'] and pgrec['sid'] in PVALS['sids']): return 1
556
+
557
+ return 0 # matched
558
+
559
+ #
560
+ # join the attm fields to generate a string
561
+ #
562
+ def join_attm_fields(aname, record):
563
+
564
+ fmts = PVALS['fmts']
565
+ maxs = PVALS['maxs']
566
+ vars = PVALS['avars'][aname]
567
+ precs = PVALS['aprecs'][aname]
568
+ vcnt = len(vars)
569
+ if not record: return ','.join(['']*vcnt)
570
+
571
+ sret = ''
572
+ for v in range(vcnt):
573
+ if v: sret += ','
574
+ var = vars[v]
575
+ val = record[var]
576
+ if val is None: continue
577
+ fmt = '{}'
578
+ if precs[v] == 0:
579
+ if var == 'id' and val.find(',') > -1: fmt = '"{}"'
580
+ elif precs[v] != 1:
581
+ if var not in maxs or val < maxs[var]:
582
+ val *= precs[v]
583
+ if var in fmts: fmt = fmts[var]
584
+ sret += fmt.format(val)
585
+
586
+ return sret
587
+
588
+ #
589
+ # check if all table files are built
590
+ #
591
+ def check_table_filename(ridx, tidx, edate):
592
+
593
+ fname = "Rqst{}-t{:03}.*-{}".format(ridx, tidx, edate.replace('-', ''))
594
+ return True if glob.glob(fname) else False
595
+
596
+ #
597
+ # check if all request data files are built
598
+ # return 0 - not yet;
599
+ # 1 - all data files are built;
600
+ # 2 - all files done including help files
601
+ #
602
+ def check_request_built(ridx, ptcnt, rstr):
603
+
604
+ cnd = "rindex = {} AND status = 'O'".format(ridx)
605
+ if ptcnt > 1:
606
+ pcnt = PgDBI.pgget('ptrqst', '', cnd)
607
+ if pcnt < ptcnt:
608
+ PgLOG.pglog('{}: {}/{} Request Partitions not finished yet'.format(rstr, (ptcnt-pcnt), ptcnt), PgLOG.LOGWRN)
609
+ return 0
610
+ wfcnt = PGRQST['fcount']
611
+ fcnt = PgDBI.pgget('wfrqst', '', cnd)
612
+ if fcnt < wfcnt:
613
+ PgLOG.pglog('{}: {}/{} Request Files not finished yet'.format(rstr, (wfcnt-fcnt), wfcnt), PgLOG.LOGWRN)
614
+ return 0
615
+ fname = PVALS['rdimma1']
616
+ cnd += " AND wfile = '{}'".format(fname)
617
+ if op.isfile(fname) and PgDBI.pgget("wfrqst", "", cnd):
618
+ PgLOG.pglog('{}: Request built already'.format(rstr), PgLOG.LOGWRN)
619
+ return 2
620
+ return 1
621
+
622
+ #
623
+ # subset data and save in fname
624
+ #
625
+ def subset_table_index(fname, tidx, bdate, edate):
626
+
627
+ atables = {}
628
+ PgDBI.ivaddb_scname()
629
+
630
+ for aname in PVALS['anames']:
631
+ atables[aname] = PgDBI.pgget('cntldb.iattm', "", "tidx = {} AND attm = '{}'".format(tidx, aname), PgLOG.LGEREX)
632
+
633
+ dstep = int(PgUtil.diffdate(edate, bdate)/PSTEP)
634
+ if dstep == 0: dstep = 1
635
+ PgLOG.pgsystem("echo '{}' > {}".format(PVALS['vhead'], fname), PgLOG.LGWNEX, 1029)
636
+ fd = open(fname, 'a')
637
+ while bdate <= edate:
638
+ pdate = PgUtil.adddate(bdate, 0, 0, dstep)
639
+ if pdate > edate: pdate = edate
640
+ build_table_file(fd, tidx, bdate, pdate, atables)
641
+ bdate = PgUtil.adddate(pdate, 0, 0, 1)
642
+
643
+ fd.close()
644
+
645
+ #
646
+ # build the final subset files
647
+ #
648
+ def build_final_files(ridx, rstr):
649
+
650
+ PgDBI.dssdb_scname()
651
+
652
+ fcnt = PgDBI.pghet('wfrqst', '', "rindex = {} AND type = 'D'".format(ridx))
653
+ fname = write_readme()
654
+ pgrec = {'status' : 'O', 'type' : 'O', 'data_format' : 'PDF'}
655
+ fcnt += 1
656
+ pgrec['disp_order'] = fcnt
657
+ PgSubset.add_request_file(ridx, fname, pgrec, PgLOG.LGEREX)
658
+
659
+ fname = PVALS['rdimma1']
660
+ pgrec = {'status' : 'O', 'type' : 'S', 'data_format' : 'FORTRAN'}
661
+ fcnt += 1
662
+ pgrec['disp_order'] = fcnt
663
+ PgFile.local_copy_local(fname, PVALS['codedir'] + fname, PgLOG.LGEREX)
664
+ PgSubset.add_request_file(ridx, fname, pgrec, PgLOG.LGEREX)
665
+
666
+ #
667
+ # process reqest info, create command file and the input file list
668
+ #
669
+ def get_subset_info(rstr):
670
+
671
+ rinfo = PGRQST['rinfo'] if PGRQST['rinfo'] else PGRQST['note']
672
+ cnt = 0
673
+ for line in rinfo.split("&"):
674
+ ms = re.search(r'([-\w]+)=(.+)', line)
675
+ if not ms: continue
676
+ token = ms.group(1)
677
+ pstring = ms.group(2)
678
+ if token == "dates": # Date Limits
679
+ PVALS['rinfo']['DATES'] = pstring
680
+ PVALS['dates'] = pstring.split(' ')
681
+ if len(PVALS['dates']) == 2: cnt += 1
682
+ elif token == 'lats':
683
+ PVALS['lats'] = PgSubset.get_latitudes(pstring, PVALS['resol'])
684
+ PVALS['rinfo']['LATS'] = "{}, {}".format(PVALS['lats'][0], PVALS['lats'][1])
685
+ cnt += 1
686
+ elif token == 'lons':
687
+ PVALS['lons'] = PgSubset.get_longitudes(pstring, PVALS['resol'])
688
+ PVALS['rinfo']['LONS'] = "{}, {}".format(PVALS['lons'][0], PVALS['lons'][1])
689
+ cnt += 1
690
+ elif token == 'flts': # Filter Options
691
+ sflts = pstring.split(' ')
692
+ PVALS['flts'] = [int(sflt) for sflt in sflts]
693
+ PVALS['rinfo']['FLTLIST'] = "</td><td>".join(sflts)
694
+ cnt += 1
695
+ elif token == 'vars': # Variable Names
696
+ PVALS['vars'] = pstring.split(', ')
697
+ cnt += 1
698
+ elif token == 'pts': # platform ids
699
+ PVALS['iopts'] += 1
700
+ PVALS['pts'] = list(map(int, pstring.split(', ')))
701
+ cnt += 1
702
+ elif token == 'dcks': # Deck ids
703
+ PVALS['iopts'] += 1
704
+ PVALS['dcks'] = list(map(int, pstring.split(', ')))
705
+ cnt += 1
706
+ elif token == 'sids': # source ids
707
+ PVALS['iopts'] += 1
708
+ PVALS['sids'] = list(map(int, pstring.split(', ')))
709
+ cnt += 1
710
+ elif token == 'Rean-qc':
711
+ FSSRCS['ireanqc'] = pstring.split(', ')
712
+ elif token == 'Ivad':
713
+ FSSRCS['iivad'] = pstring.split(', ')
714
+
715
+ if cnt < 5: PgLOG.pglog(rstr + ": Incomplete request control information", PgLOG.LGEREX)
716
+
717
+ #
718
+ # write a HTML readme file, and convert it to PDF format
719
+ #
720
+ def write_readme():
721
+
722
+ user = PgDBI.get_ruser_names(PGRQST['email'])
723
+ readme = PVALS['readme'] + PGRQST['rqstid'].lower()
724
+ rinfo = PVALS['rinfo']
725
+ readtmp = PVALS['codedir'] + PVALS['readhtml']
726
+
727
+ PgLOG.pglog("Create Readme file " + readme, PgLOG.LOGWRN)
728
+ readhtml = readme + ".html"
729
+ URM = open(readhtml, 'w')
730
+ RHTML = open(readtmp, 'r')
731
+ rinfo['CURDATE'] = PgUtil.curdate("D Month YYYY")
732
+ rinfo['ACCDATE'] = PgUtil.curdate()
733
+ rinfo['USER'] = "{} [{}]".format(user['name'], PGRQST['email'])
734
+
735
+ line = RHTML.readline()
736
+ while line:
737
+ if re.match(r'^#', line): continue # skip comment line
738
+ ms = re.search(r'__(\w+)__', line)
739
+ if ms:
740
+ key = ms.group(1)
741
+ rep = "__{}__".format(key)
742
+ if key in rinfo:
743
+ line = line.replace(rep, rinfo[key])
744
+ else:
745
+ line = line.replace(rep, '')
746
+ URM.write(line)
747
+ line = RHTML.readline()
748
+
749
+ RHTML.close()
750
+ URM.close()
751
+
752
+ readpdf = readme + ".pdf"
753
+ PgLOG.pgsystem("{} {} {}".format(PVALS['html2pdf'], readhtml, readpdf), PgLOG.LOGWRN, 35)
754
+ if not PgFile.check_local_file(readpdf):
755
+ PgLOG.pglog("{}: Error convert {} to {}".format(PVALS['html2pdf'], readhtml, readpdf), PgLOG.LOGWRN)
756
+
757
+ PgFile.delete_local_file(readhtml, PgLOG.LGWNEX)
758
+
759
+ return readpdf
760
+
761
+ #
762
+ # call main() to start program
763
+ #
764
+ if __name__ == "__main__": main()