NCCOOS Trac Projects: Top | Web | Platforms | Processing | Viz | Sprints | Sandbox | (Wind)

root/spongenet/trunk/spongenet/query.py

Revision 383 (checked in by cbc, 14 years ago)

Improved error processing and production query configuration.

Line 
1 #!/usr/bin/env python
2
3 """
4 Create CSV spreadsheets from sponge data NetCDF files.
5
6 Usage:
7
8    > python query.py path/to/config/file
9    > python query.py -t
10    > python query.py --test
11
12 Test silent import:
13
14 >>> import expand
15 """
16
17 __author__ = "Chris Calloway"
18 __email__ = "cbc@chriscalloway.org"
19 __copyright__ = "Copyright 2010 UNC-CH Department of Marine Science"
20 __license__ = "GPL2"
21
22 import warnings
23 warnings.simplefilter("ignore", DeprecationWarning)
24
25 import sys
26 import os
27 import glob
28 import csv
29 from pycdf import CDF, CDFError
30 import doctest
31 import unittest
32 from StringIO import StringIO
33
34 USAGE = "\n".join(__doc__.splitlines()[3:8])
35 TEST_PATH = os.path.join("tests", "query")
36
37
38 def _test():
39     """
40     Run doctests as unittest suite.
41
42     Test silent import:
43
44     >>> from expand import _test
45     """
46
47     suite = []
48     suite.append(doctest.DocTestSuite())
49     suite = unittest.TestSuite(suite)
50     unittest.TextTestRunner().run(suite)
51
52     return
53
54
55 def config_path():
56     """
57     Return the configuration file path from the command line.
58
59     Supply too few arguments on command line:
60
61     >>> save_stdout = sys.stdout
62     >>> temp_stdout = StringIO()
63     >>> sys.stdout = temp_stdout
64     >>> sys.argv = []
65     >>> _config_path = config_path()
66     >>> sys.stdout = save_stdout
67     >>> USAGE == "".join(temp_stdout.getvalue()[:-1].
68     ...                  splitlines(True)[1:])
69     True
70
71     Supply too many arguments on the command line:
72
73     >>> save_stdout = sys.stdout
74     >>> temp_stdout = StringIO()
75     >>> sys.stdout = temp_stdout
76     >>> sys.argv = ["", "", "",]
77     >>> _config_path = config_path()
78     >>> sys.stdout = save_stdout
79     >>> USAGE == "".join(temp_stdout.getvalue()[:-1].
80     ...                  splitlines(True)[1:])
81     True
82
83     Supply non-file argument:
84
85     >>> save_stdout = sys.stdout
86     >>> temp_stdout = StringIO()
87     >>> sys.stdout = temp_stdout
88     >>> _config_path = os.path.join(
89     ...                    os.path.dirname(
90     ...                        os.path.abspath(__file__)),
91     ...                    TEST_PATH)
92     >>> sys.argv = ["", _config_path]
93     >>> _config_path = config_path()
94     >>> sys.stdout = save_stdout
95     >>> USAGE == "".join(temp_stdout.getvalue()[:-1].
96     ...                  splitlines(True)[1:])
97     True
98
99     Supply nonexistent file argument:
100
101     >>> save_stdout = sys.stdout
102     >>> temp_stdout = StringIO()
103     >>> sys.stdout = temp_stdout
104     >>> _config_path = os.path.join(
105     ...                    os.path.dirname(
106     ...                        os.path.abspath(__file__)),
107     ...                    TEST_PATH, "xxxxx")
108     >>> sys.argv = ["", _config_path]
109     >>> _config_path = config_path()
110     >>> sys.stdout = save_stdout
111     >>> USAGE == "".join(temp_stdout.getvalue()[:-1].
112     ...                  splitlines(True)[1:])
113     True
114
115     Supply valid config path argument:
116
117     >>> _config_path = os.path.join(
118     ...                    os.path.dirname(
119     ...                        os.path.abspath(__file__)),
120     ...                    TEST_PATH, "config.py")
121     >>> sys.argv = ["", _config_path]
122     >>> _config_path == config_path()
123     True
124     """
125
126     path = None
127     try:
128         if len(sys.argv) == 2:
129             if sys.argv[1] == "-t" or sys.argv[1] == "--test":
130                 _test()
131             else:
132                 path = sys.argv[1]
133                 if not os.path.exists(path):
134                     raise IOError(path + \
135                                   " does not exist.")
136                 elif not os.path.isfile(path):
137                     raise IOError(path + \
138                                   " is not a file.")
139         else:
140             raise IOError("Incorrect number of arguments supplied.")
141     except IOError as e:
142         print e
143         print USAGE
144     return path
145
146
147 def config(path):
148     """
149     Return the configuration from a file.
150
151     Execute empty configuration:
152
153     >>> _config_path = os.path.join(
154     ...                    os.path.dirname(
155     ...                        os.path.abspath(__file__)),
156     ...                    TEST_PATH, "empty_config.py")
157     >>> ncdir,csvdir, \
158             ncfile_pattern,csvfile_pattern, \
159             location,platform,packages = \
160             config(_config_path)
161     >>> ncdir
162     >>> csvdir
163     >>> ncfile_pattern
164     >>> csvfile_pattern
165     >>> location
166     >>> platform
167     >>> packages
168
169     Execute nonexistent configuration:
170
171     >>> save_stdout = sys.stdout
172     >>> temp_stdout = StringIO()
173     >>> sys.stdout = temp_stdout
174     >>> _config_path = os.path.join(
175     ...                    os.path.dirname(
176     ...                        os.path.abspath(__file__)),
177     ...                    TEST_PATH, "xxxxx")
178     >>> ncdir,csvdir, \
179             ncfile_pattern,csvfile_pattern, \
180             location,platform,packages = \
181             config(_config_path)
182     >>> sys.stdout = save_stdout
183     >>> USAGE == "".join(temp_stdout.getvalue()[:-1].
184     ...                  splitlines(True)[1:])
185     True
186     >>> ncdir
187     >>> csvdir
188     >>> ncfile_pattern
189     >>> csvfile_pattern
190     >>> location
191     >>> platform
192     >>> packages
193
194     Execute bad configuration:
195
196     >>> save_stdout = sys.stdout
197     >>> temp_stdout = StringIO()
198     >>> sys.stdout = temp_stdout
199     >>> _config_path = os.path.join(
200     ...                    os.path.dirname(
201     ...                        os.path.abspath(__file__)),
202     ...                    TEST_PATH, "bad_config.py")
203     >>> ncdir,csvdir, \
204             ncfile_pattern,csvfile_pattern, \
205             location,platform,packages = \
206             config(_config_path)
207     >>> sys.stdout = save_stdout
208     >>> USAGE == "".join(temp_stdout.getvalue()[:-1].
209     ...                  splitlines(True)[1:])
210     True
211     >>> ncdir
212     >>> csvdir
213     >>> ncfile_pattern
214     >>> csvfile_pattern
215     >>> location
216     >>> platform
217     >>> packages
218
219     Execute valid configuration:
220
221     >>> _config_path = os.path.join(
222     ...                    os.path.dirname(
223     ...                        os.path.abspath(__file__)),
224     ...                    TEST_PATH, "config.py")
225     >>> ncdir,csvdir, \
226             ncfile_pattern,csvfile_pattern, \
227             location,platform,packages = \
228             config(_config_path)
229     >>> ncdir == os.path.join(os.path.dirname(os.path.abspath(__file__)),
230     ...                       TEST_PATH, "nc")
231     True
232     >>> csvdir == os.path.join(os.path.dirname(os.path.abspath(__file__)),
233     ...                        TEST_PATH, "csv")
234     True
235     >>> ncfile_pattern == "%(location)s_%(platform)s_%(package)s_" \
236                           "[0-9][0-9][0-9][0-9]_[0-9][0-9].nc"
237     True
238     >>> csvfile_pattern == "%(location)s_%(platform)s_%(month)s.csv"
239     True
240     >>> location == "location"
241     True
242     >>> platform == "platform"
243     True
244     >>> packages == (('system', ['voltage',
245     ...                          'memory',
246     ...                          'interval', ]),
247     ...              ('turbidity', ['turbidity', ]),
248     ...              ('optode_127', ['o2concentration',
249     ...                              'airsaturation',
250     ...                              'temperature',
251     ...                              'calphase',
252     ...                              'tcphase',
253     ...                              'c1rph',
254     ...                              'c2rph',
255     ...                              'c1amp',
256     ...                              'c2amp',
257     ...                              'rawtemp', ]),
258     ...              ('optode_167', ['o2concentration',
259     ...                              'airsaturation',
260     ...                              'temperature',
261     ...                              'calphase',
262     ...                              'tcphase',
263     ...                              'c1rph',
264     ...                              'c2rph',
265     ...                              'c1amp',
266     ...                              'c2amp',
267     ...                              'rawtemp', ]),
268     ...              ('optode_169', ['o2concentration',
269     ...                              'airsaturation',
270     ...                              'temperature',
271     ...                              'calphase',
272     ...                              'tcphase',
273     ...                              'c1rph',
274     ...                              'c2rph',
275     ...                              'c1amp',
276     ...                              'c2amp',
277     ...                              'rawtemp', ]),
278     ...              ('optode_170', ['o2concentration',
279     ...                              'airsaturation',
280     ...                              'temperature',
281     ...                              'calphase',
282     ...                              'tcphase',
283     ...                              'c1rph',
284     ...                              'c2rph',
285     ...                              'c1amp',
286     ...                              'c2amp',
287     ...                              'rawtemp', ]),
288     ...              ('optode_171', ['o2concentration',
289     ...                              'airsaturation',
290     ...                              'temperature',
291     ...                              'calphase',
292     ...                              'tcphase',
293     ...                              'c1rph',
294     ...                              'c2rph',
295     ...                              'c1amp',
296     ...                              'c2amp',
297     ...                              'rawtemp', ]),
298     ...              ('optode_172', ['o2concentration',
299     ...                              'airsaturation',
300     ...                              'temperature',
301     ...                              'calphase',
302     ...                              'tcphase',
303     ...                              'c1rph',
304     ...                              'c2rph',
305     ...                              'c1amp',
306     ...                              'c2amp',
307     ...                              'rawtemp', ]),
308     ...              ('optode_173', ['o2concentration',
309     ...                              'airsaturation',
310     ...                              'temperature',
311     ...                              'calphase',
312     ...                              'tcphase',
313     ...                              'c1rph',
314     ...                              'c2rph',
315     ...                              'c1amp',
316     ...                              'c2amp',
317     ...                              'rawtemp', ]),
318     ...              ('optode_175', ['o2concentration',
319     ...                              'airsaturation',
320     ...                              'temperature',
321     ...                              'calphase',
322     ...                              'tcphase',
323     ...                              'c1rph',
324     ...                              'c2rph',
325     ...                              'c1amp',
326     ...                              'c2amp',
327     ...                              'rawtemp', ]),
328     ...              ('optode_176', ['o2concentration',
329     ...                              'airsaturation',
330     ...                              'temperature',
331     ...                              'calphase',
332     ...                              'tcphase',
333     ...                              'c1rph',
334     ...                              'c2rph',
335     ...                              'c1amp',
336     ...                              'c2amp',
337     ...                              'rawtemp', ]),
338     ...              ('optode_178', ['o2concentration',
339     ...                              'airsaturation',
340     ...                              'temperature',
341     ...                              'calphase',
342     ...                              'tcphase',
343     ...                              'c1rph',
344     ...                              'c2rph',
345     ...                              'c1amp',
346     ...                              'c2amp',
347     ...                              'rawtemp', ]),
348     ...              ('optode_179', ['o2concentration',
349     ...                              'airsaturation',
350     ...                              'temperature',
351     ...                              'calphase',
352     ...                              'tcphase',
353     ...                              'c1rph',
354     ...                              'c2rph',
355     ...                              'c1amp',
356     ...                              'c2amp',
357     ...                              'rawtemp', ]),
358     ...              ('conductivity', ['conductivity',
359     ...                                'temperature', ]),
360     ...              ('pressure', ['pressure',
361     ...                            'temperature', ]),
362     ...              ('current', ['abs_speed',
363     ...                           'direction',
364     ...                           'v',
365     ...                           'u',
366     ...                           'heading',
367     ...                           'tiltx',
368     ...                           'tilty',
369     ...                           'std_speed',
370     ...                           'strength',
371     ...                           'pings', ]),
372     ...             )
373     True
374     """
375
376     namespace = {}
377     try:
378         execfile(path, globals(), namespace)
379     except IOError as e:
380         print e
381         print USAGE
382     except SyntaxError as e:
383         print e
384         print USAGE
385     return (namespace.get("NCDIR"),
386             namespace.get("CSVDIR"),
387             namespace.get("NCFILE_PATTERN"),
388             namespace.get("CSVFILE_PATTERN"),
389             namespace.get("LOCATION"),
390             namespace.get("PLATFORM"),
391             namespace.get("PACKAGES"),
392            )
393
394
395 def create(csvpath, ncdir, ncfile_pattern,
396            location, platform, month, packages):
397     """
398     Create a specific month of CSV spreadsheet from sponge data NetCDF files.
399     """
400
401     columns = ["time", ]
402     varmap = [("time", None), ]
403
404     # Get all the column headings and variable arrays
405     for package, variables in packages:
406         ncpath = ncfile_pattern % {"location": location,
407                                    "platform": platform,
408                                    "package": package, }
409         ncpath = ncpath[:-34] + month + ".nc"
410         ncpath = os.path.join(ncdir, ncpath)
411         try:
412             variables.append("time")
413             cdf = CDF(ncpath)
414             for variable in variables:
415                 var = ".".join([package, variable])
416                 try:
417                     varmap.append((var, cdf.var(variable).get()))
418                     if variable != "time":
419                         columns.append(var)
420                 except CDFError:
421                     print "Bypassing variable", var
422         except CDFError:
423             print "Bypassing package", package
424     varmap = dict(varmap)
425
426     # Sanity check the time arrays
427     time_list = [var for var in varmap.keys() if var.endswith(".time")]
428     time_list = zip(time_list[:-1],time_list[1:])
429     for time0,time1 in time_list:
430         if any(varmap[time0] != varmap[time1]):
431             raise CDFError("Time arrays are not equivalent.")
432     varmap["time"] = varmap[time0]
433
434     # Create the rows.
435     rows = [[] for time in varmap["time"]]
436     for column in columns:
437         for pos in range(len(rows)):
438             rows[pos].append(varmap[column][pos])
439
440     # Output the CSV file.
441     handle = open(csvpath, "wb")
442     writer = csv.writer(handle)
443     writer.writerow(columns)
444     writer.writerows(rows)
445     handle.close()
446
447     return
448
449
450 def query(ncdir, csvdir,
451           ncfile_pattern, csvfile_pattern,
452           location, platform, packages):
453     """
454     Create CSV spreadsheets from sponge data NetCDF files.
455     """
456
457     # Find all monthly NetCDF files for location/platform
458     ncfile_patterns = [(package,
459                         ncfile_pattern % {"location":location,
460                                           "platform":platform,
461                                           "package":package, },
462                        ) for package, variables in packages]
463     paths = dict([(package,
464                    [month
465                     for month in glob.glob(os.path.join(ncdir, pattern))
466                     if os.path.isfile(month)],
467                   ) for package, pattern in ncfile_patterns])
468
469     # Find all months for location/platform
470     months = list(set([os.path.splitext(path)[0][-7:]
471                        for package, variables in packages
472                        for path in paths[package]]))
473     months.sort()
474
475     # Create a directory for monthly CSV files.
476     if not os.path.exists(csvdir):
477         os.mkdir(csvdir, 0755)
478     elif not os.path.isdir(csvdir):
479         raise IOError("CSV directory name " + \
480                        csvdir + \
481                        " exists and is not a directory.")
482
483     # Create a CSV file for each month
484     for month in months:
485         path = csvfile_pattern % {"location": location,
486                                   "platform": platform,
487                                   "month": month}
488         path = os.path.join(csvdir, path)
489         if not os.path.exists(path):
490             print "=" * 40
491             print "Creating", path
492             try:
493                 create(path, ncdir, ncfile_pattern,
494                        location, platform, month, packages)
495             except CDFError:
496                 print "Time array mismatch amongst", month, "packages."
497         elif not os.path.isfile(path):
498             raise IOError("CSV file name " + \
499                           path + \
500                           " exists and is not a file.")
501         elif month == months[-1]:
502             # Always create last month
503             print "=" * 40
504             print "Creating", path
505             create(path + "new", ncdir, ncfile_pattern,
506                    location, platform, month, packages)
507             os.remove(path)
508             os.rename(path + "new", path)
509
510     return
511
512
513 def _main():
514     """
515     Run module as script.
516
517     Supply incomplete config file:
518
519     >>> save_stdout = sys.stdout
520     >>> temp_stdout = StringIO()
521     >>> sys.stdout = temp_stdout
522     >>> _config_path = os.path.join(
523     ...                    os.path.dirname(
524     ...                        os.path.abspath(__file__)),
525     ...                    TEST_PATH, "incomplete_config.py")
526     >>> sys.argv = ["", _config_path]
527     >>> _main()
528     >>> sys.stdout = save_stdout
529     >>> USAGE == "".join(temp_stdout.getvalue()[:-1].
530     ...                  splitlines(True)[1:])
531     True
532     """
533
534     _config_path = config_path()
535     if _config_path:
536         _config = config(_config_path)
537         if any(_config):
538             if all(_config):
539                 query(*_config)
540             else:
541                 print "Config file", _config_path, "does not contain all necessary parameters."
542                 print USAGE
543
544     return
545
546 if __name__ == "__main__":
547     _main()
Note: See TracBrowser for help on using the browser.