rda-python-dbms 2.0.9__py3-none-any.whl → 3.0.0__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.
@@ -22,6 +22,7 @@ class PgChksum(PgCMD, PgSplit):
22
22
  super().__init__() # initialize parent class
23
23
  self.PGSUM = {
24
24
  'd' : [], # delay mode option, value array
25
+ 'e' : 0, # 1 for emailing dataset owners the list of their missing files
25
26
  's' : [], # specialist login name array for email notice, default to none
26
27
  'f' : [], # file name array, default to none
27
28
  'i' : [], # dataset id array, default for all
@@ -46,6 +47,7 @@ class PgChksum(PgCMD, PgSplit):
46
47
  'begin' : 0,
47
48
  'end' : 0
48
49
  }
50
+ self.MISSING = {} # dsid => list of missing file names with their location
49
51
  self.pgname = "pgchksum"
50
52
  self.pgcmd = None
51
53
 
@@ -61,7 +63,7 @@ class PgChksum(PgCMD, PgSplit):
61
63
  self.PGLOG['BCKGRND'] = 1 # processing in backgroup mode
62
64
  option = None
63
65
  elif option in self.PGSUM:
64
- if option in ['m', 'n', 'x']:
66
+ if option in ['e', 'm', 'n', 'x']:
65
67
  self.PGSUM[option] = 1
66
68
  option = None
67
69
  else:
@@ -80,12 +82,13 @@ class PgChksum(PgCMD, PgSplit):
80
82
  else:
81
83
  self.pglog(arg + ": value passed without leading Option", self.LGEREX)
82
84
  if not self.PGSUM['t']:
83
- print("Usage: pgchksum -t (W|S|Q) [-b] [-m] [-n] [-x] [-c FileCount] \\")
85
+ print("Usage: pgchksum -t (W|S|Q) [-b] [-e] [-m] [-n] [-x] [-c FileCount] \\")
84
86
  print(" [-d [Hostnames] [RerunCount]] [-f FileNames] [-i DatasetIDs] \\")
85
87
  print(" [-k SkipCount] [-o r|s] [-s Specialists]")
86
88
  print(" -b - background process and no screen output")
87
89
  print(" -c - File count to re-evaluate checksum; for all gathered files if not specified")
88
90
  print(" -d - delayed mode background/batch processes on RDA machines/DAV PBS nodes")
91
+ print(" -e - email the primary owner of each dataset a reminder listing its missing files")
89
92
  print(" -f - Re-evaluate the specify File names only")
90
93
  print(" -i - Dataset IDs the files belong; for all files if not specified")
91
94
  print(" -k - skip the given file count to re-evaluate checksum")
@@ -100,6 +103,8 @@ class PgChksum(PgCMD, PgSplit):
100
103
  self.pgexit(0)
101
104
  elif self.PGSUM['t'] not in 'QSW':
102
105
  self.pglog(self.PGSUM['t'] + ": Unknown File Type, must be one of 'QSW'", self.LGEREX)
106
+ if self.PGSUM['e'] and self.PGSUM['n']:
107
+ self.pglog("Option -e (email owners) cannot work with -n (no email)", self.LGEREX)
103
108
  # set different log file
104
109
  self.PGLOG['LOGFILE'] = self.pgname + '.log'
105
110
  self.set_suid(self.PGLOG['EUID'])
@@ -151,6 +156,7 @@ class PgChksum(PgCMD, PgSplit):
151
156
  self.evaluate_backfile_checksum(fcnt, pgrecs, cnts)
152
157
  self.dump_progress(fcnt, fcnt, cnts, self.PVALS['emlsum'])
153
158
  self.PVALS['end'] = int(time.time())
159
+ if self.PGSUM['e'] and self.MISSING: self.send_owner_emails()
154
160
  if not self.PGSUM['n']: self.send_check_email(cnts['cnt'], fcnt)
155
161
  if self.PGLOG['DSCHECK']:
156
162
  if self.PGLOG['ERRMSG']:
@@ -173,6 +179,43 @@ class PgChksum(PgCMD, PgSplit):
173
179
  else:
174
180
  self.pglog(subject, self.LOGWRN|self.SNDEML)
175
181
 
182
+ # email the primary owner(s) of each dataset the list of its missing files
183
+ def send_owner_emails(self):
184
+ for dsid in sorted(self.MISSING):
185
+ files = self.MISSING[dsid]
186
+ fcnt = len(files)
187
+ s = 's' if fcnt > 1 else ''
188
+ tjoin = "dsowner LEFT JOIN dssgrp ON specialist = logname"
189
+ pgrecs = self.pgmget(tjoin, "specialist, fstname",
190
+ "dsid = '{}' AND priority = 1 ORDER BY specialist".format(dsid), self.PVALS['emlerr'])
191
+ if not pgrecs or not pgrecs['specialist']:
192
+ self.pglog("{}: No primary owner found in dsowner to email {} missing {} file{}".format(dsid, fcnt, self.PVALS['TYPE'], s), self.PVALS['emllog'])
193
+ continue
194
+ owners = []
195
+ names = []
196
+ for i in range(len(pgrecs['specialist'])):
197
+ owners.append(pgrecs['specialist'][i])
198
+ names.append(pgrecs['fstname'][i] if pgrecs['fstname'][i] else pgrecs['specialist'][i])
199
+ subject = "PGCHKSUM: {} {} File{} Missing for {}".format(fcnt, self.PVALS['TYPE'], s, dsid)
200
+ msg = "Dear {},\n\n".format(' and '.join(names))
201
+ msg += ("Checking {} files of dataset {}, {} found the following {} file{} recorded in RDADB " +
202
+ "but missing from the archive location{}. Please restore the file{}, or remove/update " +
203
+ "the file record{} via dsarch if no longer needed:\n\n").format(
204
+ self.PVALS['TYPE'], dsid, self.pgname, fcnt, s, s, s, s)
205
+ for i in range(fcnt):
206
+ msg += "{}. {}\n".format(i+1, files[i])
207
+ msg += "\n{} on {}\n".format(self.pgcmd, self.PGLOG['HOSTNAME'])
208
+ receiver = ', '.join(o if o.find('@') > -1 else o + "@ucar.edu" for o in owners)
209
+ if self.send_python_email(subject, receiver, msg, None, '', self.PVALS['emlerr']):
210
+ self.pglog("{}: emailed {} about {} missing {} file{}".format(dsid, receiver, fcnt, self.PVALS['TYPE'], s), self.PVALS['emllog'])
211
+
212
+ # record a missing file for the dataset owner email
213
+ def add_missing_file(self, fname, pgrec, location):
214
+ if not self.PGSUM['e']: return
215
+ dsid = pgrec['dsid']
216
+ if dsid not in self.MISSING: self.MISSING[dsid] = []
217
+ self.MISSING[dsid].append("{} (Type {}, {})".format(fname, pgrec['type'], location))
218
+
176
219
  # get the checksum filelist for wfile
177
220
  def get_checksum_wfilelist(self):
178
221
  self.PVALS['TYPE'] = "Web"
@@ -197,7 +240,7 @@ class PgChksum(PgCMD, PgSplit):
197
240
  if self.PGLOG['DSCHECK']: self.set_dscheck_attribute("dsid", dsid, self.PVALS['emlerr'])
198
241
  if self.PGSUM['m']: self.PVALS['CND'] += "(checksum IS NULL OR checksum < ' ') "
199
242
  elif dscnt > 1:
200
- self.PVALS['CND'] = "dsid IN ({}) ".format(','.join(self.PGSUM['i']))
243
+ self.PVALS['CND'] = "dsid IN ('{}') ".format("','".join(self.PGSUM['i']))
201
244
  if self.PGSUM['o']: self.PVALS['CND'] += "ORDER BY {} ".format('wid' if self.PGSUM['o'] == 's' else "RANDOM()")
202
245
  if fcnt: self.PVALS['CND'] += "LIMIT {} ".format(fcnt)
203
246
  if self.PGSUM['k']:
@@ -206,6 +249,7 @@ class PgChksum(PgCMD, PgSplit):
206
249
  if dscnt == 1:
207
250
  wfrecs = self.pgmget_wfile(dsid, flds, self.PVALS['CND'])
208
251
  cnt = len(wfrecs['wid']) if wfrecs else 0
252
+ if cnt > 0: wfrecs['dsid'] = [dsid]*cnt # wfile_{dsid} has no dsid column
209
253
  else:
210
254
  pgrecs = self.pgmget('wfile', 'wid, dsid', self.PVALS['CND'])
211
255
  cnt = len(pgrecs['wid']) if pgrecs else 0
@@ -245,7 +289,7 @@ class PgChksum(PgCMD, PgSplit):
245
289
  self.PVALS['CND'] = "dsid = '{}'".format(dsid)
246
290
  if self.PGLOG['DSCHECK']: self.set_dscheck_attribute("dsid", dsid, self.PVALS['emlerr'])
247
291
  elif cnt > 1:
248
- self.PVALS['CND'] = "dsid IN ({})".format(','.join(self.PGSUM['i']))
292
+ self.PVALS['CND'] = "dsid IN ('{}')".format("','".join(self.PGSUM['i']))
249
293
  if self.PGSUM['m']:
250
294
  if self.PVALS['CND']: self.PVALS['CND'] += " AND "
251
295
  self.PVALS['CND'] += "(checksum IS NULL OR checksum < ' ')"
@@ -279,7 +323,7 @@ class PgChksum(PgCMD, PgSplit):
279
323
  self.PVALS['CND'] = "dsid = '{}' AND ".format(dsid)
280
324
  if self.PGLOG['DSCHECK']: self.set_dscheck_attribute("dsid", dsid, self.PVALS['emlerr'])
281
325
  elif cnt > 1:
282
- self.PVALS['CND'] = "dsid IN ({}) AND ".format(','.join(self.PGSUM['i']))
326
+ self.PVALS['CND'] = "dsid IN ('{}') AND ".format("','".join(self.PGSUM['i']))
283
327
  if self.PGSUM['m']:
284
328
  self.PVALS['CND'] += "(checksum IS NULL OR checksum < ' ') AND "
285
329
  self.PVALS['CND'] += "status = 'A'"
@@ -376,6 +420,7 @@ class PgChksum(PgCMD, PgSplit):
376
420
  self.evaluate_file_status('DSK', fname, pgrec, info, cnts)
377
421
  else:
378
422
  self.pglog("{}: Type {} {} file is missing on Disk".format(fname, pgrec['type'], self.PVALS['TYPE']), self.PVALS['emllog'])
423
+ self.add_missing_file(fname, pgrec, 'on Disk')
379
424
  cnts['ecnt'] += 1
380
425
 
381
426
  # evaluate an object file
@@ -385,6 +430,7 @@ class PgChksum(PgCMD, PgSplit):
385
430
  self.evaluate_file_status('OBS', "{}-{}".format(bucket, fname), pgrec, info, cnts)
386
431
  else:
387
432
  self.pglog("{}: Type {} {} file is missing in Object Store".format(fname, pgrec['type'], self.PVALS['TYPE']), self.PVALS['emllog'])
433
+ self.add_missing_file("{}-{}".format(bucket, fname), pgrec, 'in Object Store')
388
434
  cnts['ecnt'] += 1
389
435
 
390
436
  # evaluate a quasar file
@@ -394,6 +440,7 @@ class PgChksum(PgCMD, PgSplit):
394
440
  self.evaluate_file_status('QSR', "{}-{}".format(endpoint, fname), pgrec, info, cnts)
395
441
  else:
396
442
  self.pglog("{}: Type {} {} file is missing on Quasar".format(fname, pgrec['type'], self.PVALS['TYPE']), self.PVALS['emllog'])
443
+ self.add_missing_file("{}-{}".format(endpoint, fname), pgrec, 'on Quasar')
397
444
  cnts['ecnt'] += 1
398
445
 
399
446
  # evaluate a file status
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rda_python_dbms
3
- Version: 2.0.9
3
+ Version: 3.0.0
4
4
  Summary: RDA Python Package to manage PostreSQL databases
5
5
  Author-email: Zaihua Ji <zji@ucar.edu>
6
6
  Project-URL: Homepage, https://github.com/NCAR/rda-python-dbms
@@ -10,7 +10,7 @@ rda_python_dbms/pg_ddllib.py,sha256=-zawllfUCe7QZNvza-u5TAYKP4yilC9K6TetHhoGenU,
10
10
  rda_python_dbms/pg_schema.py,sha256=Vsf5fKENYIOgMKE3yS0TkXhg-aeHZ2nYzpvltFAcdFY,5520
11
11
  rda_python_dbms/pg_seq.py,sha256=wNxqsyIjcTplJErPirZ-VkZ8HS4mgQUwPz8Xwd12kYA,4914
12
12
  rda_python_dbms/pgbackup.py,sha256=IuP3nQEEVR86YlSZuGyqsdz1-O9rgET76FnWB7iL3xk,4249
13
- rda_python_dbms/pgchksum.py,sha256=8jfBncptxp57Gr2t66Kk9MFt5Z_aXfozOGZPaohKGwM,21802
13
+ rda_python_dbms/pgchksum.py,sha256=ePHdh2MQ3q8102Nm7znXPg8ToI-ohGnyg3f-ipIYWu8,24921
14
14
  rda_python_dbms/pgcmptables.py,sha256=LQ4KK4JuQs2z3nBdINWYgyiGu2MF9RZjS5_cAktbs6k,47123
15
15
  rda_python_dbms/pgdbdump.py,sha256=8d-1z16Y2DyJnQqex2wFu5svChKgncPnzdQ-iUftHg8,3831
16
16
  rda_python_dbms/pgdbrestore.py,sha256=d12hl1r6usK1oGIFAPVWUB_KkIH04PPlugxVSnLOX1g,3110
@@ -954,9 +954,9 @@ rda_python_dbms/tables/obsua/wind_5v2_0.json,sha256=NehMFE-GMNlD5Bh_fiacfSOmZWkr
954
954
  rda_python_dbms/tables/obsua/wind_60v2_0.json,sha256=jOyQgD5ph7YBMuQfvYz1Ln6X_-_Vdhl36HBexOHZ1dA,1067
955
955
  rda_python_dbms/tables/obsua/wind_8v2_0.json,sha256=5wtCThmFeOyiAGH_Qv3_jrXjhjo9b87ehlpI9QQY2SM,1064
956
956
  rda_python_dbms/tables/obsua/wind_9v2_0.json,sha256=7OKFUicOn15LL_WuF1VE_L8DjxsHmRP4uBnopuMIeTg,1064
957
- rda_python_dbms-2.0.9.dist-info/licenses/LICENSE,sha256=Uky6fiiVw7cakFVRtVpzg-mvqOQ-WcA_H8lHqhPRg58,1101
958
- rda_python_dbms-2.0.9.dist-info/METADATA,sha256=8jhKTRIo91_CXYgIdyMBwXbCZQ1VOpS-1ghgC6NxVbI,617
959
- rda_python_dbms-2.0.9.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
960
- rda_python_dbms-2.0.9.dist-info/entry_points.txt,sha256=iti6iZS2OEeBqO5aHlWeMFcbuwN_2assidP0qeSkQ6E,346
961
- rda_python_dbms-2.0.9.dist-info/top_level.txt,sha256=4T1ShNUMyiqmwDIuDL7pDOm_S-6zWWW3Vy6OdAzYA4c,16
962
- rda_python_dbms-2.0.9.dist-info/RECORD,,
957
+ rda_python_dbms-3.0.0.dist-info/licenses/LICENSE,sha256=Uky6fiiVw7cakFVRtVpzg-mvqOQ-WcA_H8lHqhPRg58,1101
958
+ rda_python_dbms-3.0.0.dist-info/METADATA,sha256=B9DrL7R_MmsAlJg1nRTq0jUBKjhPMFfITFGqlX5kqIE,617
959
+ rda_python_dbms-3.0.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
960
+ rda_python_dbms-3.0.0.dist-info/entry_points.txt,sha256=iti6iZS2OEeBqO5aHlWeMFcbuwN_2assidP0qeSkQ6E,346
961
+ rda_python_dbms-3.0.0.dist-info/top_level.txt,sha256=4T1ShNUMyiqmwDIuDL7pDOm_S-6zWWW3Vy6OdAzYA4c,16
962
+ rda_python_dbms-3.0.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (82.0.1)
2
+ Generator: setuptools (84.2.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5