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

root/spongenet/trunk/spongenet/query.py

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

Format timestamp column.

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