rda-python-common 3.0.6__tar.gz → 3.0.9__tar.gz
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.
- {rda_python_common-3.0.6/src/rda_python_common.egg-info → rda_python_common-3.0.9}/PKG-INFO +1 -1
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/pyproject.toml +1 -1
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/pg_dbi.py +103 -23
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/pg_log.py +3 -9
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/pg_opt.py +3 -3
- {rda_python_common-3.0.6 → rda_python_common-3.0.9/src/rda_python_common.egg-info}/PKG-INFO +1 -1
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/LICENSE +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/README.md +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/setup.cfg +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/PgCMD.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/PgDBI.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/PgFile.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/PgLOG.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/PgLock.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/PgOPT.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/PgSIG.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/PgSplit.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/PgUtil.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/__init__.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/pg_cmd.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/pg_file.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/pg_lock.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/pg_password.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/pg_sig.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/pg_split.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/pg_util.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/pgpassword.py +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common/pgpassword.usg +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common.egg-info/SOURCES.txt +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common.egg-info/dependency_links.txt +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common.egg-info/entry_points.txt +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common.egg-info/requires.txt +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common.egg-info/top_level.txt +0 -0
- {rda_python_common-3.0.6 → rda_python_common-3.0.9}/test/test_common.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rda_python_common
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.9
|
|
4
4
|
Summary: RDA Python common library codes shared by other RDA python packages
|
|
5
5
|
Author-email: Zaihua Ji <zji@ucar.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/NCAR/rda-python-common
|
|
@@ -85,6 +85,7 @@ class PgDBI(PgLOG):
|
|
|
85
85
|
curtran (int): Transaction counter: 0 = idle, >0 = inside a transaction.
|
|
86
86
|
NMISSES (list): Cached list of scientist IDs (userno) not found in the DB.
|
|
87
87
|
LMISSES (list): Cached list of login names not found in the DB.
|
|
88
|
+
USRWARN (int): Set to 1 once the 'run filluser' reminder has been logged.
|
|
88
89
|
TABLES (dict): Cache of table-field default info keyed by table name.
|
|
89
90
|
SEQUENCES (dict): Cache of sequence field names keyed by table name.
|
|
90
91
|
SPECIALIST (dict): Cache of specialist records keyed by dataset ID.
|
|
@@ -125,6 +126,7 @@ class PgDBI(PgLOG):
|
|
|
125
126
|
self.curtran = 0 # 0 - no transaction, 1 - in transaction
|
|
126
127
|
self.NMISSES = [] # array of mising userno
|
|
127
128
|
self.LMISSES = [] # array of mising logname
|
|
129
|
+
self.USRWARN = 0 # 1 - reminder for incomplete user records given already
|
|
128
130
|
self.TABLES = {} # record table field information
|
|
129
131
|
self.SEQUENCES = {} # record table sequence fielnames
|
|
130
132
|
self.SPECIALIST = {} # hash array refrences to specialist info of dsids
|
|
@@ -1699,6 +1701,82 @@ class PgDBI(PgLOG):
|
|
|
1699
1701
|
ret = self.pgget('information_schema.tables', None, condition, logact)
|
|
1700
1702
|
return (self.SUCCESS if ret else self.FAILURE)
|
|
1701
1703
|
|
|
1704
|
+
def dssgrp_user_info(self, userno, logname = None):
|
|
1705
|
+
"""Build a partial dssdb.user record from dssdb.dssgrp.
|
|
1706
|
+
|
|
1707
|
+
Fallback for when the UCAR People API is unreachable, so a usable user record
|
|
1708
|
+
can still be added for a DECS group member. Fields absent from dssgrp are set
|
|
1709
|
+
to the values implied by DECS membership; the record is marked stat_flag 'M'
|
|
1710
|
+
so that 'filluser -i' completes it once the People API is available again.
|
|
1711
|
+
|
|
1712
|
+
Args:
|
|
1713
|
+
userno (int): Scientist number; pass 0 to look up by logname instead.
|
|
1714
|
+
logname (str | None): UCAR login name; used when userno is 0.
|
|
1715
|
+
|
|
1716
|
+
Returns:
|
|
1717
|
+
dict | None: Partial mapping of column_name → value suitable for
|
|
1718
|
+
pgadd('dssdb.user'), or None when not in dssgrp.
|
|
1719
|
+
"""
|
|
1720
|
+
cond = "userno = {}".format(userno) if userno else "logname = '{}'".format(logname)
|
|
1721
|
+
pgrec = self.pgget("dssgrp", "logname, userno, lstname, fstname, midinit, phoneno", cond, self.PGDBI['ERRLOG'])
|
|
1722
|
+
if not pgrec: return None
|
|
1723
|
+
if not pgrec['userno']: del pgrec['userno'] # leave NULL; 0 is never looked up
|
|
1724
|
+
email = pgrec['logname'] + '@ucar.edu'
|
|
1725
|
+
pgrec['org_type'] = 'DSS'
|
|
1726
|
+
pgrec['org_name'] = 'NCAR'
|
|
1727
|
+
pgrec['country'] = 'UNITED.STATES'
|
|
1728
|
+
pgrec['email'] = pgrec['ucaremail'] = email
|
|
1729
|
+
pgrec['stat_flag'] = 'M'
|
|
1730
|
+
return pgrec
|
|
1731
|
+
|
|
1732
|
+
def add_missed_user(self, userno, logname = None):
|
|
1733
|
+
"""Add a dssdb.user record for a user missing from the table.
|
|
1734
|
+
|
|
1735
|
+
Tries the UCAR People API first, then dssgrp, and finally falls back to a bare
|
|
1736
|
+
stub. A reminder to run filluser is logged once per process whenever the added
|
|
1737
|
+
record is incomplete (stat_flag 'M').
|
|
1738
|
+
|
|
1739
|
+
Args:
|
|
1740
|
+
userno (int): Scientist number; pass 0 to add by logname instead.
|
|
1741
|
+
logname (str | None): UCAR login name; used when userno is 0.
|
|
1742
|
+
|
|
1743
|
+
Returns:
|
|
1744
|
+
int: The new user.uid, or 0 if the insert fails.
|
|
1745
|
+
"""
|
|
1746
|
+
pgrec = self.ucar_user_info(userno, logname)
|
|
1747
|
+
if not pgrec: pgrec = self.dssgrp_user_info(userno, logname)
|
|
1748
|
+
if not pgrec:
|
|
1749
|
+
pgrec = {'userno': userno} if userno else {'logname': logname}
|
|
1750
|
+
pgrec['stat_flag'] = 'M'
|
|
1751
|
+
if pgrec['stat_flag'] == 'M': self.incomplete_user_warning()
|
|
1752
|
+
return self.pgadd("dssdb.user", pgrec, (self.PGDBI['EXITLG']|self.AUTOID))
|
|
1753
|
+
|
|
1754
|
+
def incomplete_user_warning(self):
|
|
1755
|
+
"""Log a one-time reminder to run filluser for incomplete dssdb.user records."""
|
|
1756
|
+
if self.USRWARN: return
|
|
1757
|
+
self.USRWARN = 1
|
|
1758
|
+
self.pglog("Incomplete record in dssdb.user; run 'filluser -i' later to complete it", self.LGWNEM)
|
|
1759
|
+
|
|
1760
|
+
def reopen_user_record(self, pgrec):
|
|
1761
|
+
"""Reactivate a closed dssdb.user record and return its uid.
|
|
1762
|
+
|
|
1763
|
+
Clears until_date and marks the record incomplete (stat_flag 'M') so that
|
|
1764
|
+
'filluser -i' refreshes it. Records that are not closed are returned as is.
|
|
1765
|
+
Only call this for a present-day lookup; reopening on a historical lookup
|
|
1766
|
+
would wrongly reactivate a user who has since left.
|
|
1767
|
+
|
|
1768
|
+
Args:
|
|
1769
|
+
pgrec (dict): Record holding at least 'uid' and 'until_date'.
|
|
1770
|
+
|
|
1771
|
+
Returns:
|
|
1772
|
+
int: The uid of the record.
|
|
1773
|
+
"""
|
|
1774
|
+
if pgrec['until_date']:
|
|
1775
|
+
record = {'until_date': None, 'stat_flag': 'M'}
|
|
1776
|
+
self.pgupdt("dssdb.user", record, "uid = {}".format(pgrec['uid']), self.PGDBI['ERRLOG'])
|
|
1777
|
+
self.pglog("Closed user.uid = {} is reopened".format(pgrec['uid']), self.LGWNEM)
|
|
1778
|
+
return pgrec['uid']
|
|
1779
|
+
|
|
1702
1780
|
def check_user_uid(self, userno, date = None):
|
|
1703
1781
|
"""Return the user.uid for a scientist ID, adding a record if missing.
|
|
1704
1782
|
|
|
@@ -1716,6 +1794,7 @@ class PgDBI(PgLOG):
|
|
|
1716
1794
|
"""
|
|
1717
1795
|
if not userno: return 0
|
|
1718
1796
|
if type(userno) is str: userno = int(userno)
|
|
1797
|
+
istoday = (date is None)
|
|
1719
1798
|
if date is None:
|
|
1720
1799
|
datecond = "until_date IS NULL"
|
|
1721
1800
|
date = 'today'
|
|
@@ -1727,11 +1806,9 @@ class PgDBI(PgLOG):
|
|
|
1727
1806
|
self.pglog("{}: Scientist ID NOT on file for {}".format(userno, date), self.LGWNEM)
|
|
1728
1807
|
self.NMISSES.append(userno)
|
|
1729
1808
|
# check again if a user is on file with different date range
|
|
1730
|
-
pgrec = self.pgget("dssdb.user", "uid", "userno = {}".format(userno), self.PGDBI['ERRLOG'])
|
|
1731
|
-
if pgrec: return pgrec['uid']
|
|
1732
|
-
|
|
1733
|
-
if not pgrec: pgrec = {'userno': userno, 'stat_flag': 'M'}
|
|
1734
|
-
uid = self.pgadd("dssdb.user", pgrec, (self.PGDBI['EXITLG']|self.AUTOID))
|
|
1809
|
+
pgrec = self.pgget("dssdb.user", "uid, until_date", "userno = {}".format(userno), self.PGDBI['ERRLOG'])
|
|
1810
|
+
if pgrec: return self.reopen_user_record(pgrec) if istoday else pgrec['uid']
|
|
1811
|
+
uid = self.add_missed_user(userno)
|
|
1735
1812
|
if uid: self.pglog("{}: Scientist ID Added as user.uid = {}".format(userno, uid), self.LGWNEM)
|
|
1736
1813
|
return uid
|
|
1737
1814
|
|
|
@@ -1748,6 +1825,7 @@ class PgDBI(PgLOG):
|
|
|
1748
1825
|
int: user.uid on success, 0 if logname is falsy or insert fails.
|
|
1749
1826
|
"""
|
|
1750
1827
|
if not logname: return 0
|
|
1828
|
+
istoday = (not date)
|
|
1751
1829
|
if not date:
|
|
1752
1830
|
date = 'today'
|
|
1753
1831
|
datecond = "until_date IS NULL"
|
|
@@ -1759,11 +1837,9 @@ class PgDBI(PgLOG):
|
|
|
1759
1837
|
self.pglog("{}: UCAR Login Name NOT on file for {}".format(logname, date), self.LGWNEM)
|
|
1760
1838
|
self.LMISSES.append(logname)
|
|
1761
1839
|
# check again if a user is on file with different date range
|
|
1762
|
-
pgrec = self.pgget("dssdb.user", "uid", "logname = '{}'".format(logname), self.PGDBI['ERRLOG'])
|
|
1763
|
-
if pgrec: return pgrec['uid']
|
|
1764
|
-
|
|
1765
|
-
if not pgrec: pgrec = {'logname': logname, 'stat_flag': 'M'}
|
|
1766
|
-
uid = self.pgadd("dssdb.user", pgrec, (self.PGDBI['EXITLG']|self.AUTOID))
|
|
1840
|
+
pgrec = self.pgget("dssdb.user", "uid, until_date", "logname = '{}'".format(logname), self.PGDBI['ERRLOG'])
|
|
1841
|
+
if pgrec: return self.reopen_user_record(pgrec) if istoday else pgrec['uid']
|
|
1842
|
+
uid = self.add_missed_user(0, logname)
|
|
1767
1843
|
if uid: self.pglog("{}: UCAR Login Name Added as user.uid = {}".format(logname, uid), self.LGWNEM)
|
|
1768
1844
|
return uid
|
|
1769
1845
|
|
|
@@ -1796,6 +1872,9 @@ class PgDBI(PgLOG):
|
|
|
1796
1872
|
'email': "ucaremail",
|
|
1797
1873
|
'phone': "phoneno"
|
|
1798
1874
|
}
|
|
1875
|
+
# pgperson/pgusername are installed by rda_python_metrics; skip quietly when the
|
|
1876
|
+
# commands are not on this host so that callers can fall back to dssgrp
|
|
1877
|
+
if not self.valid_command("pgperson"): return None
|
|
1799
1878
|
buf = self.pgsystem("pgperson " + ("-uid {}".format(userno) if userno else "-username {}".format(logname)), self.LOGWRN, 20)
|
|
1800
1879
|
if not buf: return None
|
|
1801
1880
|
pgrec = {}
|
|
@@ -1807,23 +1886,24 @@ class PgDBI(PgLOG):
|
|
|
1807
1886
|
if key == 'upid' and 'upid' in pgrec: break # get one record only
|
|
1808
1887
|
pgrec[matches[key]] = val
|
|
1809
1888
|
if not pgrec: return None
|
|
1889
|
+
# the People API omits keys it has no value for, so read them defensively
|
|
1810
1890
|
if userno:
|
|
1811
1891
|
pgrec['userno'] = userno
|
|
1812
|
-
elif pgrec
|
|
1892
|
+
elif pgrec.get('userno'):
|
|
1813
1893
|
pgrec['userno'] = userno = int(pgrec['userno'])
|
|
1814
|
-
if pgrec
|
|
1815
|
-
if pgrec
|
|
1816
|
-
|
|
1817
|
-
|
|
1894
|
+
if pgrec.get('upid'): pgrec['upid'] = int(pgrec['upid'])
|
|
1895
|
+
if pgrec.get('stat_flag'): pgrec['stat_flag'] = 'A' if pgrec['stat_flag'] == "True" else 'C'
|
|
1896
|
+
email = pgrec.get('email')
|
|
1897
|
+
if email and re.search(r'(@|\.)ucar\.edu$', email, re.I):
|
|
1898
|
+
email = pgrec['email'] = pgrec.get('ucaremail')
|
|
1818
1899
|
pgrec['org_name'] = 'NCAR'
|
|
1819
|
-
country = pgrec
|
|
1820
|
-
pgrec['country'] = self.set_country_code(
|
|
1821
|
-
if pgrec
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
buf = self.pgsystem("pgusername {}".format(pgrec['logname']), self.LOGWRN, 20)
|
|
1900
|
+
country = pgrec.get('country')
|
|
1901
|
+
if email or country: pgrec['country'] = self.set_country_code(email, country)
|
|
1902
|
+
val = "NCAR" if pgrec.get('division') else None
|
|
1903
|
+
pgrec['org_type'] = self.get_org_type(val, email)
|
|
1904
|
+
logname = pgrec.get('logname')
|
|
1905
|
+
if not logname or not self.valid_command("pgusername"): return pgrec
|
|
1906
|
+
buf = self.pgsystem("pgusername {}".format(logname), self.LOGWRN, 20)
|
|
1827
1907
|
if not buf: return pgrec
|
|
1828
1908
|
for line in buf.split('\n'):
|
|
1829
1909
|
ms = re.match(r'^(.+)<=>(.*)$', line)
|
|
@@ -124,7 +124,8 @@ class PgLOG:
|
|
|
124
124
|
'SETUID': '', # the login name for suid if it is different to the CURUID
|
|
125
125
|
'FILEMODE': 0o664, # default 8-base file mode
|
|
126
126
|
'EXECMODE': 0o775, # default 8-base executable file mode or directory mode
|
|
127
|
-
# COMMONUSER and ADMINUSER are set
|
|
127
|
+
# COMMONUSER and ADMINUSER are set in set_common_pglog() via SETPGLOG,
|
|
128
|
+
# which lets environment variables COMMONUSER/ADMINUSER override the defaults
|
|
128
129
|
'SUDOGDEX': 0, # 1 to allow sudo to self.PGLOG['COMMONUSER']
|
|
129
130
|
'HOSTNAME': '', # current host name the process in running on
|
|
130
131
|
'OBJCTSTR': "object",
|
|
@@ -183,13 +184,6 @@ class PgLOG:
|
|
|
183
184
|
self.set_common_pglog()
|
|
184
185
|
self.OUTPUT = None
|
|
185
186
|
|
|
186
|
-
def SETPGLOG(self, key, default):
|
|
187
|
-
"""Set ``self.PGLOG[key]`` from environment variable ``PG<key>`` or
|
|
188
|
-
fall back to ``default`` if the variable is unset. Used to make
|
|
189
|
-
per-environment overrides (e.g. PGCOMMONUSER, PGADMINUSER) survive
|
|
190
|
-
package upgrades."""
|
|
191
|
-
self.PGLOG[key] = os.environ.get('PG' + key, default)
|
|
192
|
-
|
|
193
187
|
def open_output(self, outfile=None):
|
|
194
188
|
"""Open the result output destination.
|
|
195
189
|
|
|
@@ -1435,7 +1429,7 @@ class PgLOG:
|
|
|
1435
1429
|
|
|
1436
1430
|
Called automatically by :meth:`__init__`.
|
|
1437
1431
|
"""
|
|
1438
|
-
# resolve common/admin user from environment (
|
|
1432
|
+
# resolve common/admin user from environment (COMMONUSER / ADMINUSER)
|
|
1439
1433
|
self.SETPGLOG("COMMONUSER", "gdexdata")
|
|
1440
1434
|
self.SETPGLOG("ADMINUSER", "zji")
|
|
1441
1435
|
self.PGLOG['RDAUSER'] = self.PGLOG['COMMONUSER']
|
|
@@ -577,9 +577,9 @@ class PgOPT(PgFile):
|
|
|
577
577
|
if 'LN' not in self.params: self.pglog("Could not get user login name", self.PGOPT['extlog'])
|
|
578
578
|
self.validate_dataset()
|
|
579
579
|
if self.OPTS[self.PGOPT['CACT']][2] > 0: self.validate_dsowner(aname)
|
|
580
|
-
|
|
581
|
-
if not
|
|
582
|
-
self.PGOPT['UID'] =
|
|
580
|
+
uid = self.get_user_uid(self.params['LN'])
|
|
581
|
+
if not uid: self.pglog("Could not get user.uid for " + self.params['LN'], self.PGOPT['extlog'])
|
|
582
|
+
self.PGOPT['UID'] = uid
|
|
583
583
|
self.open_output(self.params['OF'] if 'OF' in self.params else None)
|
|
584
584
|
|
|
585
585
|
def set_sudo_uid(self, aname, uid):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rda_python_common
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.9
|
|
4
4
|
Summary: RDA Python common library codes shared by other RDA python packages
|
|
5
5
|
Author-email: Zaihua Ji <zji@ucar.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/NCAR/rda-python-common
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common.egg-info/requires.txt
RENAMED
|
File without changes
|
{rda_python_common-3.0.6 → rda_python_common-3.0.9}/src/rda_python_common.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|