rda-python-icoads 1.0.19__tar.gz → 3.0.0__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.
Files changed (58) hide show
  1. rda_python_icoads-3.0.0/PKG-INFO +98 -0
  2. rda_python_icoads-3.0.0/README.md +81 -0
  3. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/pyproject.toml +4 -3
  4. rda_python_icoads-3.0.0/src/rda_python_icoads/checkicoads.py +224 -0
  5. rda_python_icoads-3.0.0/src/rda_python_icoads/cleanicoads.py +180 -0
  6. rda_python_icoads-3.0.0/src/rda_python_icoads/countattm.py +129 -0
  7. rda_python_icoads-3.0.0/src/rda_python_icoads/countattmvar.py +241 -0
  8. rda_python_icoads-3.0.0/src/rda_python_icoads/counticoads.py +155 -0
  9. rda_python_icoads-3.0.0/src/rda_python_icoads/countsst.py +225 -0
  10. rda_python_icoads-3.0.0/src/rda_python_icoads/fillicoads.py +175 -0
  11. rda_python_icoads-3.0.0/src/rda_python_icoads/fillinventory.py +153 -0
  12. rda_python_icoads-3.0.0/src/rda_python_icoads/fillitable.py +294 -0
  13. rda_python_icoads-3.0.0/src/rda_python_icoads/fillmonth.py +100 -0
  14. rda_python_icoads-3.0.0/src/rda_python_icoads/fixiidx.py +73 -0
  15. rda_python_icoads-3.0.0/src/rda_python_icoads/imma1_subset.py +773 -0
  16. rda_python_icoads-3.0.0/src/rda_python_icoads/maxsst.py +264 -0
  17. rda_python_icoads-3.0.0/src/rda_python_icoads/msg3_subset.py +344 -0
  18. rda_python_icoads-3.0.0/src/rda_python_icoads/msg_download.py +214 -0
  19. rda_python_icoads-3.0.0/src/rda_python_icoads/pg_imma.py +1793 -0
  20. rda_python_icoads-3.0.0/src/rda_python_icoads/writeicoads.py +171 -0
  21. rda_python_icoads-3.0.0/src/rda_python_icoads.egg-info/PKG-INFO +98 -0
  22. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/src/rda_python_icoads.egg-info/SOURCES.txt +1 -0
  23. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/src/rda_python_icoads.egg-info/entry_points.txt +1 -1
  24. rda_python_icoads-3.0.0/src/rda_python_icoads.egg-info/requires.txt +2 -0
  25. rda_python_icoads-1.0.19/PKG-INFO +0 -21
  26. rda_python_icoads-1.0.19/README.md +0 -5
  27. rda_python_icoads-1.0.19/src/rda_python_icoads/checkicoads.py +0 -222
  28. rda_python_icoads-1.0.19/src/rda_python_icoads/cleanicoads.py +0 -176
  29. rda_python_icoads-1.0.19/src/rda_python_icoads/countattm.py +0 -125
  30. rda_python_icoads-1.0.19/src/rda_python_icoads/countattmvar.py +0 -237
  31. rda_python_icoads-1.0.19/src/rda_python_icoads/counticoads.py +0 -153
  32. rda_python_icoads-1.0.19/src/rda_python_icoads/countsst.py +0 -221
  33. rda_python_icoads-1.0.19/src/rda_python_icoads/fillicoads.py +0 -171
  34. rda_python_icoads-1.0.19/src/rda_python_icoads/fillinventory.py +0 -149
  35. rda_python_icoads-1.0.19/src/rda_python_icoads/fillitable.py +0 -290
  36. rda_python_icoads-1.0.19/src/rda_python_icoads/fillmonth.py +0 -94
  37. rda_python_icoads-1.0.19/src/rda_python_icoads/fixiidx.py +0 -70
  38. rda_python_icoads-1.0.19/src/rda_python_icoads/imma1_subset.py +0 -778
  39. rda_python_icoads-1.0.19/src/rda_python_icoads/maxsst.py +0 -262
  40. rda_python_icoads-1.0.19/src/rda_python_icoads/msg3_subset.py +0 -345
  41. rda_python_icoads-1.0.19/src/rda_python_icoads/msg_download.py +0 -211
  42. rda_python_icoads-1.0.19/src/rda_python_icoads/writeicoads.py +0 -169
  43. rda_python_icoads-1.0.19/src/rda_python_icoads.egg-info/PKG-INFO +0 -21
  44. rda_python_icoads-1.0.19/src/rda_python_icoads.egg-info/requires.txt +0 -1
  45. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/LICENSE +0 -0
  46. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/MANIFEST.in +0 -0
  47. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/setup.cfg +0 -0
  48. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/src/rda_python_icoads/PgIMMA.py +0 -0
  49. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/src/rda_python_icoads/R3.0-stat_doc.pdf +0 -0
  50. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/src/rda_python_icoads/README_R3.0_Subset.html +0 -0
  51. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/src/rda_python_icoads/__init__.py +0 -0
  52. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/src/rda_python_icoads/msg +0 -0
  53. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/src/rda_python_icoads/msg3.0_subset_readme.txt +0 -0
  54. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/src/rda_python_icoads/msgsubset.f +0 -0
  55. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/src/rda_python_icoads/rdimma1_csv.f +0 -0
  56. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/src/rda_python_icoads.egg-info/dependency_links.txt +0 -0
  57. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/src/rda_python_icoads.egg-info/top_level.txt +0 -0
  58. {rda_python_icoads-1.0.19 → rda_python_icoads-3.0.0}/tests/test_icoads.py +0 -0
@@ -0,0 +1,98 @@
1
+ Metadata-Version: 2.4
2
+ Name: rda_python_icoads
3
+ Version: 3.0.0
4
+ Summary: RDA python package to manage RDA ICOADS datasets
5
+ Author-email: Zaihua Ji <zji@ucar.edu>
6
+ Project-URL: Homepage, https://github.com/NCAR/rda-python-icoads
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Requires-Python: >=3.7
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: rda_python_common>=3.0.14
15
+ Requires-Dist: rda_python_dsrqst>=2.0.4
16
+ Dynamic: license-file
17
+
18
+ # rda_python_icoads
19
+
20
+ RDA Python package to manage RDA ICOADS datasets.
21
+
22
+ ## Prerequisite
23
+
24
+ To dynamically generate a PDF readme fitting for each data subset, the Linux
25
+ commandline utility `wkhtmltopdf` must be installed. See
26
+ <https://wkhtmltopdf.org/index.html> for download and install instructions.
27
+
28
+ ## Programs
29
+
30
+ The package installs the following command-line utilities, all running as the
31
+ current user:
32
+
33
+ - `imma1_subset` — generate IMMA1-format ICOADS subset deliveries
34
+ - `msg3_subset` — generate MSG-format ICOADS R3 subsets
35
+ - `msg_download` — download MSG ICOADS source files
36
+ - `checkicoads` — check ICOADS archive integrity
37
+ - `cleanicoads` — clean up stale ICOADS files
38
+ - `counticoads`, `countattm`, `countattmvar`, `countsst` — record/attribute counters
39
+ - `fillicoads`, `fillinventory`, `fillitable`, `fillmonth` — populate ICOADS DB tables
40
+ - `fixiidx` — fix ICOADS indices
41
+ - `maxsst` — compute maximum SST values
42
+ - `writeicoads` — write ICOADS records
43
+
44
+ Run any program with no arguments to print its usage details.
45
+
46
+ ## Environment setup
47
+
48
+ Create a Python environment first; package installs in the next section run
49
+ inside whichever environment you activate here.
50
+
51
+ ### Option A — Python venv (DECS machines)
52
+
53
+ ```bash
54
+ python3 -m venv $ENVHOME # e.g. /glade/u/home/gdexdata/gdexmsenv
55
+ source $ENVHOME/bin/activate
56
+ ```
57
+
58
+ ### Option B — Conda (DAV/Casper)
59
+
60
+ ```bash
61
+ conda create --prefix $ENVHOME python=3.12 # e.g. /glade/work/gdexdata/conda-envs/pg-gdex
62
+ conda activate $ENVHOME
63
+ ```
64
+
65
+ ## Installing rda-python-icoads
66
+
67
+ Pick whichever install mode fits your workflow. All variants pull in the
68
+ transitive dependency (`rda_python_common`) automatically.
69
+
70
+ For local development, clone this repo alongside your project and install it
71
+ in editable mode so that changes are picked up without re-installing:
72
+
73
+ ```bash
74
+ git clone https://github.com/NCAR/rda-python-icoads.git
75
+ cd rda-python-icoads
76
+ pip install -e .
77
+ ```
78
+
79
+ To test a specific branch (e.g. an in-progress feature or fix branch), pass
80
+ `-b/--branch` to `git clone`:
81
+
82
+ ```bash
83
+ git clone -b <branch-name> https://github.com/NCAR/rda-python-icoads.git
84
+ cd rda-python-icoads
85
+ pip install -e .
86
+ ```
87
+
88
+ For a regular (non-editable) install from a checkout:
89
+
90
+ ```bash
91
+ pip install /path/to/rda-python-icoads
92
+ ```
93
+
94
+ For a production install on a system that uses the published distribution:
95
+
96
+ ```bash
97
+ pip install rda_python_icoads
98
+ ```
@@ -0,0 +1,81 @@
1
+ # rda_python_icoads
2
+
3
+ RDA Python package to manage RDA ICOADS datasets.
4
+
5
+ ## Prerequisite
6
+
7
+ To dynamically generate a PDF readme fitting for each data subset, the Linux
8
+ commandline utility `wkhtmltopdf` must be installed. See
9
+ <https://wkhtmltopdf.org/index.html> for download and install instructions.
10
+
11
+ ## Programs
12
+
13
+ The package installs the following command-line utilities, all running as the
14
+ current user:
15
+
16
+ - `imma1_subset` — generate IMMA1-format ICOADS subset deliveries
17
+ - `msg3_subset` — generate MSG-format ICOADS R3 subsets
18
+ - `msg_download` — download MSG ICOADS source files
19
+ - `checkicoads` — check ICOADS archive integrity
20
+ - `cleanicoads` — clean up stale ICOADS files
21
+ - `counticoads`, `countattm`, `countattmvar`, `countsst` — record/attribute counters
22
+ - `fillicoads`, `fillinventory`, `fillitable`, `fillmonth` — populate ICOADS DB tables
23
+ - `fixiidx` — fix ICOADS indices
24
+ - `maxsst` — compute maximum SST values
25
+ - `writeicoads` — write ICOADS records
26
+
27
+ Run any program with no arguments to print its usage details.
28
+
29
+ ## Environment setup
30
+
31
+ Create a Python environment first; package installs in the next section run
32
+ inside whichever environment you activate here.
33
+
34
+ ### Option A — Python venv (DECS machines)
35
+
36
+ ```bash
37
+ python3 -m venv $ENVHOME # e.g. /glade/u/home/gdexdata/gdexmsenv
38
+ source $ENVHOME/bin/activate
39
+ ```
40
+
41
+ ### Option B — Conda (DAV/Casper)
42
+
43
+ ```bash
44
+ conda create --prefix $ENVHOME python=3.12 # e.g. /glade/work/gdexdata/conda-envs/pg-gdex
45
+ conda activate $ENVHOME
46
+ ```
47
+
48
+ ## Installing rda-python-icoads
49
+
50
+ Pick whichever install mode fits your workflow. All variants pull in the
51
+ transitive dependency (`rda_python_common`) automatically.
52
+
53
+ For local development, clone this repo alongside your project and install it
54
+ in editable mode so that changes are picked up without re-installing:
55
+
56
+ ```bash
57
+ git clone https://github.com/NCAR/rda-python-icoads.git
58
+ cd rda-python-icoads
59
+ pip install -e .
60
+ ```
61
+
62
+ To test a specific branch (e.g. an in-progress feature or fix branch), pass
63
+ `-b/--branch` to `git clone`:
64
+
65
+ ```bash
66
+ git clone -b <branch-name> https://github.com/NCAR/rda-python-icoads.git
67
+ cd rda-python-icoads
68
+ pip install -e .
69
+ ```
70
+
71
+ For a regular (non-editable) install from a checkout:
72
+
73
+ ```bash
74
+ pip install /path/to/rda-python-icoads
75
+ ```
76
+
77
+ For a production install on a system that uses the published distribution:
78
+
79
+ ```bash
80
+ pip install rda_python_icoads
81
+ ```
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "rda_python_icoads"
9
- version = "1.0.19"
9
+ version = "3.0.0"
10
10
  authors = [
11
11
  { name="Zaihua Ji", email="zji@ucar.edu" },
12
12
  ]
@@ -20,7 +20,8 @@ classifiers = [
20
20
  "Development Status :: 5 - Production/Stable",
21
21
  ]
22
22
  dependencies = [
23
- "rda_python_common",
23
+ "rda_python_common>=3.0.14",
24
+ "rda_python_dsrqst>=2.0.4",
24
25
  ]
25
26
 
26
27
  [tool.setuptools]
@@ -55,7 +56,7 @@ where = ["src"]
55
56
  "fillinventory" = "rda_python_icoads.fillinventory:main"
56
57
  "fillitable" = "rda_python_icoads.fillitable:main"
57
58
  "fillmonth" = "rda_python_icoads.fillmonth:main"
58
- "fixidx" = "rda_python_icoads.fixidx:main"
59
+ "fixiidx" = "rda_python_icoads.fixiidx:main"
59
60
  "msg_download" = "rda_python_icoads.msg_download:main"
60
61
  "maxsst" = "rda_python_icoads.maxsst:main"
61
62
  "writeicoads" = "rda_python_icoads.writeicoads:main"
@@ -0,0 +1,224 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ ##################################################################################
4
+ #
5
+ # Title : checkicoads
6
+ # Author : Zaihua Ji, zji@ucar.edu
7
+ # Date : 12/30/2020
8
+ # 2025-03-03 transferred to package rda_python_icoads from
9
+ # https://github.com/NCAR/rda-icoads.git
10
+ # 2026-09-04 convert to class CheckIcoads
11
+ # Purpose : check and compare ICOADS data files and IVADDB records
12
+ #
13
+ # Github : https://github.com/NCAR/rda-python-icoads.git
14
+ #
15
+ ##################################################################################
16
+
17
+ import sys
18
+ import os
19
+ import re
20
+ from os import path as op
21
+ from .pg_imma import PgIMMA
22
+
23
+ class CheckIcoads(PgIMMA):
24
+
25
+ def __init__(self):
26
+ super().__init__()
27
+ self.PVALS = {
28
+ 'bdate' : None,
29
+ 'edate' : None,
30
+ 'bmdate' : [],
31
+ 'emdate' : [],
32
+ 'fname' : [],
33
+ 'flag' : [], # 1 - file exists, 2 - db records exist, 3 - both
34
+ 'mproc' : 10,
35
+ 'fpattern' : "IMMA1_R3.0.0_<YYYY-MM>",
36
+ 'readall' : 0
37
+ }
38
+
39
+ #
40
+ # main function to run dsarch
41
+ #
42
+ def main(self):
43
+
44
+ option = ''
45
+ argv = sys.argv[1:]
46
+
47
+ for arg in argv:
48
+ if arg == "-b":
49
+ self.PGLOG['BCKGRND'] = 1
50
+ elif arg == "-a":
51
+ self.PVALS['readall'] = 1
52
+ elif arg == "-f":
53
+ option = 'f'
54
+ elif arg == "-m":
55
+ option = 'm'
56
+ elif re.match(r'^-', arg):
57
+ self.pglog(arg + ": Invalid Option", self.LGWNEX)
58
+ elif option:
59
+ if option == 'f':
60
+ self.PVALS['fpattern'] = arg
61
+ elif option == 'm':
62
+ self.PVALS['mproc'] = arg
63
+ option = ''
64
+ elif not self.PVALS['bdate']:
65
+ self.PVALS['bdate'] = arg
66
+ elif not self.PVALS['edate']:
67
+ self.PVALS['edate'] = arg
68
+ else:
69
+ self.pglog(arg + ": Invalid parameter", self.LGWNEX)
70
+
71
+ self.PGLOG['LOGFILE'] = "icoads.log"
72
+ self.ivaddb_dbname()
73
+
74
+ if not (self.PVALS['bdate'] and self.PVALS['edate']):
75
+ pgrec = self.pgget("cntldb.inventory", "min(date) bdate, max(date) edate", '', self.LGEREX)
76
+ print("Usage: checkicoads [-a] [-m mproc] [-f FilePattern] BeginDate EndDate")
77
+ print(" Default FilePattern is " + self.PVALS['fpattern'])
78
+ print(" Option -a - read all attms, including multi-line ones, such as IVAD and REANQC")
79
+ print(" Option -m - start up to given number of processes, one for each month (Default to 10)")
80
+ print(" Set BeginDate and EndDate between '{}' and '{}'".format(pgrec['bdate'], pgrec['edate']))
81
+ sys.exit(0)
82
+
83
+ if self.diffdate(self.PVALS['bdate'], self.PVALS['edate']) > 0:
84
+ tmpdate = self.PVALS['bdate']
85
+ self.PVALS['bdate'] = self.PVALS['edate']
86
+ self.PVALS['edate'] = tmpdate
87
+
88
+ self.cmdlog("checkicoads {}".format(' '.join(argv)))
89
+ self.check_imma_data()
90
+ self.cmdlog()
91
+ sys.exit(0)
92
+
93
+ #
94
+ # check imma data
95
+ #
96
+ def check_imma_data(self):
97
+
98
+ mcnt = self.init_months()
99
+ if mcnt == 1: self.PVALS['mproc'] = 1
100
+ if self.PVALS['mproc'] > 1:
101
+ self.start_none_daemon('writeicoads', '', self.PGLOG['CURUID'], self.PVALS['mproc'], 300, 1)
102
+
103
+ for midx in range(mcnt):
104
+ fname = self.PVALS['fname'][midx]
105
+ if op.isfile(fname +".cnt"): continue # monthly file counted already
106
+ if self.PVALS['mproc'] > 1:
107
+ stat = self.start_child("checkicoads_{}".format(midx), self.LOGWRN, 1) # try to start a child process
108
+ if stat <= 0:
109
+ sys.exit(1) # something wrong
110
+ elif self.PGSIG['PPID'] > 1:
111
+ self.check_imma_file(fname, midx)
112
+ sys.exit(0) # stop child process
113
+ else:
114
+ self.pgdisconnect(0) # disconnect database for reconnection
115
+ continue # continue for next midx
116
+ else:
117
+ self.check_imma_file(fname, midx)
118
+
119
+ if self.PVALS['mproc'] > 1: self.check_child(None, 0, self.LOGWRN, 1)
120
+
121
+ self.dump_final_counts()
122
+
123
+ #
124
+ # compare icoads records from given file name and IVADDB
125
+ #
126
+ def check_imma_file(self, fname, midx):
127
+
128
+ self.pglog("Count IMMA records in File '{}'".format(fname), self.WARNLG)
129
+ flag = self.PVALS['flag'][midx]
130
+
131
+ acnts = [0]*self.TABLECOUNT
132
+ acounts = [0]*self.TABLECOUNT
133
+
134
+ if flag&1:
135
+ IMMA = open(fname, 'r')
136
+ line = IMMA.readline()
137
+ while line:
138
+ if self.PVALS['readall'] and re.match(r'^98', line):
139
+ self.get_imma_multiple_counts(line, acnts)
140
+ else:
141
+ self.get_imma_counts(line, acnts)
142
+ line.IMMA.readline()
143
+ IMMA.close()
144
+ for i in range(self.TABLECOUNT): acounts[i] = acnts[i]
145
+
146
+ if flag&2:
147
+ self.pglog("Count IMMA records in in IVADDB", self.WARNLG)
148
+ cdate = bdate = self.PVALS['bmdate'][midx]
149
+ edate = self.PVALS['emdate'][midx]
150
+ while cdate <= edate:
151
+ acnts = self.count_imma_records(cdate, 0, self.PVALS['readall'])
152
+ cdate = self.adddate(cdate, 0, 0, 1)
153
+ if acnts:
154
+ for i in range(self.TABLECOUNT): acounts[i] -= acnts[i]
155
+
156
+ self.dump_monthly_counts(fname, acounts)
157
+
158
+ #
159
+ # dump monthly counts
160
+ #
161
+ def dump_monthly_counts(self, fname, acounts):
162
+
163
+ oname = fname + ".cnt"
164
+ IMMA = open(oname, 'w')
165
+ IMMA.write("{}, {}\n".format(fname, ', '.join(acounts)))
166
+ IMMA.close()
167
+
168
+ #
169
+ # concat all monthly counts into one
170
+ #
171
+ def dump_final_counts(self):
172
+
173
+ fname = "ICOADS_DIFF_COUNTS.csv"
174
+
175
+ IMMA = open(fname, 'w')
176
+ IMMA.write("FileName, {}\n".format(', '.join(self.IMMA_NAMES)))
177
+ IMMA.close()
178
+ self.pgsystem("cat *.cnt >> " + fname)
179
+
180
+ #
181
+ # initialize the month list
182
+ #
183
+ def init_months(self):
184
+
185
+ seps = ["<" , ">"]; # temporal pattern delimiters
186
+ match = "[^" + seps[1] + "]+"
187
+
188
+ ms = re.search(r'{}({}){}'.format(seps[0], match, seps[1]), self.PVALS['fpattern'])
189
+ if ms:
190
+ tpattern = ms.group(1)
191
+ treplace = "{}{}{}".format(seps[0], tpattern, seps[1])
192
+ else:
193
+ self.pglog(self.PVALS['fpattern'] + ": Not temporal pattern found to get month list", self.LGEREX)
194
+
195
+ bdate = self.PVALS['bdate']
196
+ done = midx = 0
197
+ while True:
198
+ edate = self.enddate(bdate, 0, 'M')
199
+ if self.diffdate(self.PVALS['edate'], edate) <= 0:
200
+ edate = self.PVALS['edate']
201
+ done = 1
202
+ mdate = self.format_date(bdate, tpattern)
203
+ fname = self.PVALS['fpattern'].replace(treplace, mdate)
204
+ flag = 0
205
+ if op.isfile(fname): flag += 1
206
+ if self.pgget("cntldb.inventory", "", "date BETWEEN '{}' AND '{}'".format(bdate, edate), self.LGEREX):
207
+ flag += 2
208
+ if flag:
209
+ self.PVALS['bmdate'].append(bdate)
210
+ self.PVALS['emdate'].append(edate)
211
+ self.PVALS['fname'].append(fname)
212
+ self.PVALS['flag'].append(flag)
213
+ midx += 1
214
+ if done: break
215
+ bdate = self.adddate(edate, 0, 0, 1)
216
+
217
+ return midx
218
+
219
+ # main function to execute this script
220
+ def main():
221
+ CheckIcoads().main()
222
+
223
+ # call main() to start program
224
+ if __name__ == "__main__": main()
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ ##################################################################################
4
+ #
5
+ # Title : cleanicoads
6
+ # Author : Zaihua Ji, zji@ucar.edu
7
+ # Date : 12/30/2020
8
+ # 2025-03-03 transferred to package rda_python_icoads from
9
+ # https://github.com/NCAR/rda-icoads.git
10
+ # 2026-09-04 convert to class CleanIcoads
11
+ # Purpose : clean up one or all IMMA1 attms in IVADDB for given period
12
+ #
13
+ # Github : https://github.com/NCAR/rda-python-icoads.git
14
+ #
15
+ ##################################################################################
16
+
17
+ import sys
18
+ import os
19
+ import re
20
+ from os import path as op
21
+ from .pg_imma import PgIMMA
22
+
23
+ class CleanIcoads(PgIMMA):
24
+
25
+ def __init__(self):
26
+ super().__init__()
27
+ self.PVALS = {
28
+ 'bdate' : None,
29
+ 'edate' : None,
30
+ 'aname' : None,
31
+ 'tinfo' : {},
32
+ 'tcnt' : 0,
33
+ 'dcnd' : None,
34
+ 'uatti' : '',
35
+ 'names' : None
36
+ }
37
+
38
+ #
39
+ # main function to run dsarch
40
+ #
41
+ def main(self):
42
+
43
+ option = ''
44
+ files = []
45
+ leaduid = 0
46
+ chkexist = 0
47
+ readall = 0
48
+ argv = sys.argv[1:]
49
+
50
+ for arg in argv:
51
+ if arg == "-b":
52
+ self.PGLOG['BCKGRND'] = 1
53
+ elif arg == "-a":
54
+ option = 'a'
55
+ elif re.match(r'^-', arg):
56
+ self.pglog(arg + ": Invalid Option", self.LGWNEX)
57
+ elif option:
58
+ self.PVALS['aname'] = arg
59
+ option = ''
60
+ elif not self.PVALS['bdate']:
61
+ self.PVALS['bdate'] = arg
62
+ elif not self.PVALS['edate']:
63
+ self.PVALS['edate'] = arg
64
+ else:
65
+ self.pglog(arg + ": Invalid parameter", self.LGWNEX)
66
+
67
+ if not self.PVALS['bdate']:
68
+ print("Usage: cleanicoads [-a ATTNAME] BDATE EDATE")
69
+ print(" Option -a - clean a single attm for given attm name")
70
+ self.pgexit()
71
+
72
+ self.PGLOG['LOGFILE'] = "icoads.log"
73
+ self.set_scname(dbname = 'ivaddb', scname = self.IVADSC, lnname = 'ivaddb', dbhost = self.PGLOG['PMISCHOST'])
74
+ self.cmdlog("cleanicoads {}".format(' '.join(argv)))
75
+ self.set_table_info()
76
+ self.clean_imma_data()
77
+ self.cmdlog()
78
+ self.pgexit()
79
+
80
+ #
81
+ # set the table index list
82
+ #
83
+ def set_table_info(self):
84
+
85
+ table = f"{self.CNTLSC}.inventory"
86
+ if self.PVALS['edate']:
87
+ self.PVALS['dcnd'] = "date BETWEEN '{}' AND '{}'".format(self.PVALS['bdate'], self.PVALS['edate'])
88
+ else:
89
+ self.PVALS['dcnd'] = "date >= '{}'".format(self.PVALS['bdate'])
90
+
91
+ self.PVALS['tinfo'] = self.pgmget(table, "tidx, min(miniidx) bidx, max(maxiidx) eidx", self.PVALS['dcnd'] + " GROUP BY tidx", self.LGEREX)
92
+ self.PVALS['tcnt'] = len(self.PVALS['tinfo']['tidx']) if self.PVALS['tinfo'] else 0
93
+
94
+ if not self.PVALS['tcnt']:
95
+ self.pglog("{}: No data found in IVADDB for {}".format(table, self.PVALS['dcnd']), self.LGEREX)
96
+
97
+ #
98
+ # clean up imma data
99
+ #
100
+ def clean_imma_data(self):
101
+
102
+ table = f"{self.CNTLSC}.inventory"
103
+
104
+ for i in range(self.PVALS['tcnt']):
105
+ tidx = self.PVALS['tinfo']['tidx'][i]
106
+ cnd = "iidx BETWEEN {} AND {}".format(self.PVALS['tinfo']['bidx'][i], self.PVALS['tinfo']['eidx'][i])
107
+ if self.PVALS['aname']:
108
+ self.clean_one_attm_for_tidx(self.PVALS['aname'], tidx, cnd)
109
+ else:
110
+ self.clean_imma_data_for_tidx(tidx, cnd)
111
+
112
+ if not self.PVALS['aname']:
113
+ cnt = self.pgdel(table, self.PVALS['dcnd'], self.LGEREX)
114
+ s = 's' if cnt > 1 else ''
115
+ self.pglog("{}: {} record{} deleted for {}".format(table, cnt, s, self.PVALS['dcnd']), self.LOGWRN)
116
+
117
+ #
118
+ # clean up imma data for table index
119
+ #
120
+ def clean_imma_data_for_tidx(self, tidx, cnd):
121
+
122
+ self.pglog("Clean IMMA data for table index {}...".format(tidx), self.LOGWRN)
123
+
124
+ for i in range(self.TABLECOUNT):
125
+ aname = self.IMMA_NAMES[i]
126
+ self.clean_one_attm_for_tidx(aname, tidx, cnd)
127
+
128
+ #
129
+ # clean up one attm data for table index
130
+ #
131
+ def clean_one_attm_for_tidx(self, aname, tidx, cnd):
132
+
133
+ table = f"{self.IVADSC}.{aname}_{tidx}"
134
+ if not self.pgcheck(table): return 0 # not record to delete
135
+
136
+ if aname == 'iuida': self.clean_itidx_for_tidx(table, cnd)
137
+
138
+ cnt = self.pgdel(table, cnd, self.LGEREX)
139
+ s = 's' if cnt > 1 else ''
140
+ self.pglog("{}: {} record{} deleted for {}".format(table, cnt, s, cnd), self.LOGWRN)
141
+
142
+ cnt = self.pgget(table, "", "", self.LGEREX)
143
+ self.clean_iattm_for_tidx(aname, tidx, cnt)
144
+
145
+ #
146
+ # clean up table itidx for table index
147
+ #
148
+ def clean_itidx_for_tidx(self, table, cnd):
149
+
150
+ tname = f"{self.CNTLSC}.itidx"
151
+ uids = self.pgmget(table, "distinct (substring(uid, 1, 2)) uida", cnd, self.LGEREX)
152
+ ucnt = len(uids['uida']) if uids else 0
153
+ for i in range(ucnt):
154
+ table = "{}_{}".format(tname, uids['uida'][i].lower())
155
+ if not self.pgcheck(table): continue
156
+ cnt = self.pgdel(table, cnd, self.LOGWRN)
157
+ s = 's' if cnt > 1 else ''
158
+ self.pglog("{}: {} record{} deleted".format(table, cnt, s), self.LOGWRN)
159
+
160
+ #
161
+ # clean up table iattm for table index
162
+ #
163
+ def clean_iattm_for_tidx(self, aname, tidx, cnt):
164
+
165
+ table = f"{self.CNTLSC}.iattm"
166
+ cnd = "attm = '{}' AND tidx = {}".format(aname, tidx)
167
+ pgrec = {'count' : cnt}
168
+ self.pgupdt(table, pgrec, cnd, self.LGWNEX)
169
+ self.pglog("{}: Set count to {} for {}".format(table, cnt, cnd), self.LOGWRN)
170
+
171
+ table += "_daily"
172
+ cnd += " AND " + self.PVALS['dcnd']
173
+ self.pgdel(table, self.PVALS['dcnd'], self.LGWNEX)
174
+
175
+ # main function to execute this script
176
+ def main():
177
+ CleanIcoads().main()
178
+
179
+ # call main() to start program
180
+ if __name__ == "__main__": main()