rda-python-icoads 3.0.1__py3-none-any.whl → 3.0.3__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.
@@ -29,7 +29,8 @@ class FillIcoads(PgIMMA):
29
29
  'names' : None,
30
30
  'files' : [],
31
31
  'dates' : [],
32
- 'dtlen' : 0
32
+ 'dtlen' : 0,
33
+ 'zipped' : 0
33
34
  }
34
35
 
35
36
  #
@@ -111,12 +112,125 @@ class FillIcoads(PgIMMA):
111
112
  fcnt = 0
112
113
  tcounts = [0]*self.TABLECOUNT
113
114
  for file in self.PVALS['files']:
115
+ pfile = self.get_partial_month_file(file) if addinventory else None
116
+ if pfile:
117
+ fcnt += 1
118
+ acnts = self.process_imma_file(pfile, addinventory)
119
+ for i in range(self.TABLECOUNT): tcounts[i] += acnts[i]
120
+ if self.PVALS['zipped']: self.pgsystem("gzip " + pfile, self.LOGWRN, 5)
114
121
  fcnt += 1
115
122
  acnts = self.process_imma_file(file, addinventory)
116
123
  for i in range(self.TABLECOUNT): tcounts[i] += acnts[i]
117
124
 
118
125
  if fcnt > 1: self.pglog("{} ({}) filled for {} files".format('/'.join(map(str, tcounts)), self.PVALS['names'], fcnt), self.LOGWRN)
119
126
 
127
+ #
128
+ # get the first data date in a given imma file
129
+ #
130
+ def get_imma_file_date(self, fname):
131
+
132
+ fdate = None
133
+ IMMA = open(fname, 'r', encoding = 'latin_1')
134
+ line = IMMA.readline()
135
+ self.identify_attm_name(line) # the date offset depends on the attm name
136
+ while line:
137
+ if not (self.PVALS['uatti'] and line[0:2] == self.PVALS['uatti']):
138
+ fdate = self.get_imma_date(line)
139
+ if fdate: break
140
+ line = IMMA.readline()
141
+ IMMA.close()
142
+
143
+ return fdate
144
+
145
+ #
146
+ # get the latest date filled into the inventory
147
+ #
148
+ def get_max_filled_date(self):
149
+
150
+ pgrec = self.pgget(f"{self.CNTLSC}.inventory", "max(date) mdate", "", self.LGEREX)
151
+
152
+ return self.format_date(pgrec['mdate']) if pgrec and pgrec['mdate'] else None
153
+
154
+ #
155
+ # locate a data file, gunzip it if only the gzipped one is on file
156
+ #
157
+ def locate_imma_file(self, fname):
158
+
159
+ self.PVALS['zipped'] = 0
160
+ if op.isfile(fname): return fname
161
+
162
+ zfile = fname + ".gz"
163
+ if op.isfile(zfile):
164
+ self.pgsystem("gunzip " + zfile, self.LOGWRN, 5)
165
+ if op.isfile(fname):
166
+ self.PVALS['zipped'] = 1
167
+ return fname
168
+
169
+ return None
170
+
171
+ #
172
+ # a month is filled up only if its last day is in the inventory; if an earlier month is
173
+ # filled partially, return the name of its data file, to fill up the missing dates before
174
+ # the given file is filled
175
+ #
176
+ def get_partial_month_file(self, fname):
177
+
178
+ mdate = self.get_max_filled_date()
179
+ if not mdate: return None # nothing filled yet
180
+
181
+ edate = self.enddate(mdate, 0, 'M')
182
+ if mdate == edate: return None # the latest filled month is filled up
183
+
184
+ fdate = self.get_imma_file_date(fname)
185
+ if not fdate or self.diffdate(fdate, edate) <= 0: return None # the given file holds the partial month
186
+
187
+ pgrec = self.pgget(f"{self.CNTLSC}.inventory", "fname", "date = '{}'".format(mdate), self.LGEREX)
188
+ pfile = self.locate_imma_file(pgrec['fname']) if pgrec and pgrec['fname'] else None
189
+ if not pfile:
190
+ self.pglog("{}: month is filled through {} only, and its data file is not found in {} to "
191
+ "fill it up before filling {}".format(mdate[0:7], mdate, op.abspath('.'), fdate[0:7]), self.LGEREX)
192
+
193
+ # the leading '>' propagates the note up through the calling processes, into the email report
194
+ self.pglog("> {}: filled through {} only, fill it up from {} before filling {}".format(
195
+ mdate[0:7], mdate, pfile, fdate[0:7]), self.LOGWRN)
196
+
197
+ return pfile
198
+
199
+ #
200
+ # skip the dates that are filled already if the month of the given file is resumed
201
+ #
202
+ def get_resume_date(self, fname, fdate, bdate):
203
+
204
+ mdate = self.get_max_filled_date()
205
+ if not mdate: return bdate # nothing filled yet
206
+ if self.diffdate(mdate, fdate) < 0: return bdate # none of this file is filled yet
207
+ if self.diffdate(mdate, self.enddate(fdate, 0, 'M')) > 0: return bdate # refilling an earlier month
208
+
209
+ rdate = self.adddate(mdate, 0, 0, 1)
210
+ if bdate and self.diffdate(bdate, rdate) >= 0: return bdate
211
+
212
+ self.pglog("> {}: filled through {} already, continue filling from {}".format(
213
+ op.basename(fname), mdate, rdate), self.LOGWRN)
214
+
215
+ return rdate
216
+
217
+ #
218
+ # log an error, without stopping, if the month of the given file is not filled up, so that
219
+ # a partial fill is reported by the calling process and the month is resumed on a rerun
220
+ #
221
+ def check_month_filled(self, fname, fdate, bdate):
222
+
223
+ edate = self.enddate(fdate, 0, 'M')
224
+ pgrec = self.pgget(f"{self.CNTLSC}.inventory", "max(date) mdate",
225
+ "date BETWEEN '{}-01' AND '{}'".format(fdate[0:7], edate), self.LGEREX)
226
+ mdate = self.format_date(pgrec['mdate']) if pgrec and pgrec['mdate'] else None
227
+ if mdate == edate:
228
+ if bdate: self.pglog("> {}: filled up through {}".format(fdate[0:7], edate), self.LOGWRN)
229
+ return
230
+
231
+ self.pglog("{}: PARTIAL fill of {}, filled through {} of {}; rerun to fill the remaining dates".format(
232
+ op.basename(fname), fdate[0:7], (mdate if mdate else 'nothing'), edate), self.LOGERR)
233
+
120
234
  #
121
235
  # read icoads record from given file name and save them into IVADDB
122
236
  #
@@ -125,19 +239,27 @@ class FillIcoads(PgIMMA):
125
239
  iname = fname if addinventory else None
126
240
  self.pglog("Record IMMA records in File '{}' into IVADDB".format(fname), self.WARNLG)
127
241
 
128
- IMMA = open(fname, 'r', encoding = 'latin_1')
129
242
  acounts = [0]*self.TABLECOUNT
130
243
  records = {}
244
+ bdate = self.PVALS['dates'][0] if self.PVALS['dtlen'] > 0 else None
245
+ edate = self.PVALS['dates'][1] if self.PVALS['dtlen'] > 1 else None
246
+
247
+ fdate = self.get_imma_file_date(fname) if iname else None
248
+ if fdate: bdate = self.get_resume_date(fname, fdate, bdate)
249
+
250
+ IMMA = open(fname, 'r', encoding = 'latin_1')
131
251
 
132
252
  # get the first valid date and do initialization
253
+ cdate = None
133
254
  line = IMMA.readline()
134
255
  self.identify_attm_name(line) # check and record standalone attm name
135
256
  while line:
136
- idate = cdate = self.get_imma_date(line)
137
- if cdate and (self.PVALS['dtlen'] == 0 or self.diffdate(cdate, self.PVALS['dates'][0]) >= 0):
138
- self.init_indices_for_date(cdate, iname)
139
- records = self.get_imma_records(cdate, line, records)
140
- break
257
+ if not (self.PVALS['uatti'] and line[0:2] == self.PVALS['uatti']): # a standalone attm line holds no date
258
+ cdate = self.get_imma_date(line)
259
+ if cdate and (not bdate or self.diffdate(cdate, bdate) >= 0):
260
+ self.init_indices_for_date(cdate, iname)
261
+ records = self.get_imma_records(cdate, line, records)
262
+ break
141
263
  line = IMMA.readline()
142
264
 
143
265
  line = IMMA.readline()
@@ -152,7 +274,7 @@ class FillIcoads(PgIMMA):
152
274
  for i in range(self.TABLECOUNT): acounts[i] += acnts[i]
153
275
  records = {}
154
276
  cdate = idate
155
- if self.PVALS['dtlen'] == 2 and self.diffdate(cdate, self.PVALS['dates'][1]) > 0: break
277
+ if edate and self.diffdate(cdate, edate) > 0: break
156
278
  self.init_indices_for_date(cdate, iname)
157
279
  records = self.get_imma_records(idate, line, records)
158
280
  line = IMMA.readline()
@@ -165,6 +287,8 @@ class FillIcoads(PgIMMA):
165
287
 
166
288
  self.pglog("{} ({}) filled from {}".format(' '.join(map(str, acounts)), self.PVALS['names'], op.basename(fname)), self.LOGWRN)
167
289
 
290
+ if fdate and self.PVALS['dtlen'] == 0: self.check_month_filled(fname, fdate, bdate)
291
+
168
292
  return acounts
169
293
 
170
294
  # main function to execute this script
@@ -233,11 +233,15 @@ class FillItable(PgIMMA):
233
233
  else:
234
234
  self.pglog("{}({}): index counted already between {} and {}".format(var, val, pgrec['imin'], pgrec['imax']), self.LOGWRN)
235
235
 
236
- cnt = 0
236
+ cnt = ncnt = 0
237
237
  for val in records:
238
238
  pgrec = records[val]
239
+ if not pgrec:
240
+ ncnt += 1 # counted already; nothing to add
241
+ continue
239
242
  cnt += self.pgupdt(vtable, pgrec, "{} = {}".format(var, val), self.LGEREX)
240
243
 
244
+ if ncnt: self.pglog("{}: nothing to add for {} values counted already".format(vtable, ncnt), self.LOGWRN)
241
245
  self.pglog("{} of {} values recounted in table '{}'".format(cnt, vcnt, vtable), self.LOGWRN)
242
246
 
243
247
  #
@@ -24,7 +24,7 @@ class FillMonth(PgUtil):
24
24
  def __init__(self):
25
25
  super().__init__()
26
26
  self.CMDS = {
27
- 'filename' : "IMMA1_R3.0.2_",
27
+ 'filenames' : ["IMMA1_R3.0.2_", "IMMA1_R3.0.3_"], # IMMA1_R3.0.3_ since 2025-08
28
28
  'fillicoads' : "fillicoads -i ",
29
29
  'fillitable' : "fillitable -t -v dck pt sid -r ",
30
30
  'cdmsmonth' : "cdmsmonth "
@@ -64,17 +64,31 @@ class FillMonth(PgUtil):
64
64
  self.cmdlog()
65
65
  sys.exit(0)
66
66
 
67
+ #
68
+ # locate the monthly IMMA1 file, gunzip it if only the gzipped one is on file
69
+ #
70
+ def get_monthly_file(self, smonth):
71
+
72
+ files = [fname + smonth for fname in self.CMDS['filenames']]
73
+
74
+ for file in files:
75
+ if op.isfile(file): return file
76
+
77
+ for file in files:
78
+ zfile = file + ".gz"
79
+ if op.isfile(zfile):
80
+ self.pgsystem("gunzip " + zfile, self.LGEREX, 5)
81
+ return file
82
+
83
+ self.pglog("Miss monthly IMMA1 file, gzipped or not, in {}: {}".format(
84
+ op.abspath('.'), ', '.join(files)), self.LGEREX)
85
+
67
86
  #
68
87
  # fill monthly IMMA1 data to IVADDB
69
88
  #
70
89
  def fill_monthly_data(self, smonth, srange):
71
90
 
72
- file = self.CMDS['filename'] + smonth
73
-
74
- if not op.isfile(file):
75
- # unzip file
76
- cmd = "gunzip {}.gz".format(file)
77
- self.pgsystem(cmd, self.LGWNEX, 5)
91
+ file = self.get_monthly_file(smonth)
78
92
 
79
93
  # fillicoads
80
94
  cmd = self.CMDS['fillicoads'] + file
@@ -375,7 +375,7 @@ class PgIMMA(PgOPT):
375
375
  self.UIDOFFSET = 4
376
376
  self.UIDLENGTH = 15
377
377
  atti = line[15:17]
378
- CURRN3 = int(line[12])
378
+ self.CURRN3 = int(line[12])
379
379
  else:
380
380
  atti = None
381
381
 
@@ -622,8 +622,28 @@ class PgIMMA(PgOPT):
622
622
  #
623
623
  # add multiple imma records into different tables in RDADB
624
624
  #
625
+ # all writes for one date are wrapped in a single transaction, so an interrupted
626
+ # fill can never leave an inventory record behind without its data; the date is
627
+ # then simply refilled when the file is processed again
628
+ #
625
629
  def add_imma_records(self, cdate, records):
626
630
 
631
+ self.starttran()
632
+ try:
633
+ acnts = self.add_date_records(cdate, records)
634
+ except BaseException:
635
+ self.aborttran()
636
+ self.pglog("{}: records rolled back for the date".format(cdate), self.LOGWRN)
637
+ raise
638
+ self.endtran()
639
+
640
+ return acnts
641
+
642
+ #
643
+ # add all records of one date into the attm and the control tables
644
+ #
645
+ def add_date_records(self, cdate, records):
646
+
627
647
  if self.INVENTORY and self.IMMA_NAMES[0] in records: # add counting record into inventory table
628
648
  rcnt = len(records[self.IMMA_NAMES[0]]['iidx'])
629
649
  if rcnt > 0: self.INVENTORY = self.add_inventory_record(self.INVENTORY['fname'], cdate, rcnt, self.INVENTORY)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rda_python_icoads
3
- Version: 3.0.1
3
+ Version: 3.0.3
4
4
  Summary: RDA python package to manage RDA ICOADS datasets
5
5
  Author-email: Zaihua Ji <zji@ucar.edu>
6
6
  Project-URL: Homepage, https://github.com/NCAR/rda-python-icoads
@@ -8,10 +8,10 @@ rda_python_icoads/countattm.py,sha256=HACWjl1J1gR-wObSAY_8WtJh6RGZG_b1dwE9NGr6ZF
8
8
  rda_python_icoads/countattmvar.py,sha256=5Xrg6zPxy0ckDUE0WYFVF9oDZRBOCTEQq0WQFd5VxRE,8571
9
9
  rda_python_icoads/counticoads.py,sha256=7SMu2HJBvUSmmThkNitAdvdyk7aO9dWKvdSv6VGBLng,5179
10
10
  rda_python_icoads/countsst.py,sha256=3jLrZUqlrZBVt9ib7nBO6n1D6jPL0T9Esu_elntHMDg,7170
11
- rda_python_icoads/fillicoads.py,sha256=DzveAwULO8dDnooSMmEiGiS8k0lIa1qW9G7kTmyeH-k,6082
11
+ rda_python_icoads/fillicoads.py,sha256=kPk4SVaZhfA7DKSZEyiM0_ipsvq4Ct8ERN9J_e8KXxA,11146
12
12
  rda_python_icoads/fillinventory.py,sha256=cDcfjPAF2Ir991xO9PNMiGSB9boLCUj8txKAw3D_yzI,4617
13
- rda_python_icoads/fillitable.py,sha256=LRXXF0YbSKp40Yerwt_aIsDO7fsPQDvhARWoBJ6bHPI,10957
14
- rda_python_icoads/fillmonth.py,sha256=WDP6m6rWW59mCPnQ-ruAeLFnw2U8Dlm6OOFAGEnA1h0,2912
13
+ rda_python_icoads/fillitable.py,sha256=QpatUrSbIHcs-BCL8dBfZCM4FnQWwhArWnENhmvBZUQ,11178
14
+ rda_python_icoads/fillmonth.py,sha256=jeelzcSFic29-R5JoaKr87iDygha0b9AaHSRbxxPluY,3405
15
15
  rda_python_icoads/fixiidx.py,sha256=5k6Intm4Zw5mAik-W-lnj167_YloP5THqaY96TMP4Mw,2333
16
16
  rda_python_icoads/imma1_subset.py,sha256=IX8vAdViFtcG3ai0_FQSro9038D2cRDcQdezUrNA6Kg,27566
17
17
  rda_python_icoads/maxsst.py,sha256=TroXmOKvy2VS0l6ILPJmVfVvDTynma_yoilaVDMXCZ8,9108
@@ -20,12 +20,12 @@ rda_python_icoads/msg3.0_subset_readme.txt,sha256=Nb3rFq4lkxBDhSzS5XLCgCkZpozUGe
20
20
  rda_python_icoads/msg3_subset.py,sha256=MXnV-bFDFef5O2vafOxVcrt97wRGu4zmfy8EXbbwxS0,14629
21
21
  rda_python_icoads/msg_download.py,sha256=G_MawrQtpmsQOPTgDIcq94K4-BySj7xPTsQydd2UVYo,7636
22
22
  rda_python_icoads/msgsubset.f,sha256=KuM2WAk0ZFEH8LBhHKiGWt8zaW_0ARJXYV2kiD5w5xs,21253
23
- rda_python_icoads/pg_imma.py,sha256=JBNBcCCnZCOcozi0rok149ahY2j0I7pX2lzGbmcvVe4,65727
23
+ rda_python_icoads/pg_imma.py,sha256=RDl2h-JScZxozVLHhebrEuAAUWtbU0_7BoJKZpxMhI8,66378
24
24
  rda_python_icoads/rdimma1_csv.f,sha256=_hOHvA0UiTykCIsj7ZYkmooNmUq29-p7ZnyjeSyvt8U,22443
25
25
  rda_python_icoads/writeicoads.py,sha256=HQqTp4m2erTV1bOkkBJZwDH6ispJPutPM3ZA8Y_giKI,5841
26
- rda_python_icoads-3.0.1.dist-info/licenses/LICENSE,sha256=1dck4EAQwv8QweDWCXDx-4Or0S8YwiCstaso_H57Pno,1097
27
- rda_python_icoads-3.0.1.dist-info/METADATA,sha256=OClItbEyC0u-NRGPY2O9T8a0u6E7ZLNwE0WykxXy6zw,3032
28
- rda_python_icoads-3.0.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
29
- rda_python_icoads-3.0.1.dist-info/entry_points.txt,sha256=RrLvvQNiBddv_LCYU9b49Zm3fpSl53DBOUXi22SVcE0,776
30
- rda_python_icoads-3.0.1.dist-info/top_level.txt,sha256=PjJe4PwUIiNxPlNg2d8JNPY1SLSu-_GHAdXe-hUVU1U,18
31
- rda_python_icoads-3.0.1.dist-info/RECORD,,
26
+ rda_python_icoads-3.0.3.dist-info/licenses/LICENSE,sha256=1dck4EAQwv8QweDWCXDx-4Or0S8YwiCstaso_H57Pno,1097
27
+ rda_python_icoads-3.0.3.dist-info/METADATA,sha256=nZybIybbtjmcxeiFOE1J48lhKGrsiISeWik6X23GnKI,3032
28
+ rda_python_icoads-3.0.3.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
29
+ rda_python_icoads-3.0.3.dist-info/entry_points.txt,sha256=RrLvvQNiBddv_LCYU9b49Zm3fpSl53DBOUXi22SVcE0,776
30
+ rda_python_icoads-3.0.3.dist-info/top_level.txt,sha256=PjJe4PwUIiNxPlNg2d8JNPY1SLSu-_GHAdXe-hUVU1U,18
31
+ rda_python_icoads-3.0.3.dist-info/RECORD,,