scc_access/scc_access.py

changeset 30
4669876326d4
parent 28
2d90204710af
parent 29
3e3e5bda6b77
child 31
020e80de1f64
equal deleted inserted replaced
28:2d90204710af 30:4669876326d4
1 import requests 1 import requests
2 requests.packages.urllib3.disable_warnings()
3
4 import sys
5 2
6 try: 3 try:
7 import urllib.parse as urlparse # Python 3 4 import urllib.parse as urlparse # Python 3
8 except ImportError: 5 except ImportError:
9 import urlparse # Python 2 6 import urlparse # Python 2
17 import sys 14 import sys
18 import time 15 import time
19 import urlparse 16 import urlparse
20 from zipfile import ZipFile 17 from zipfile import ZipFile
21 18
22 import requests
23 import yaml 19 import yaml
24 20
25 requests.packages.urllib3.disable_warnings() 21 requests.packages.urllib3.disable_warnings()
26
27 logger = logging.getLogger(__name__) 22 logger = logging.getLogger(__name__)
28 23
29 # The regex to find the measurement id from the measurement page 24 # The regex to find the measurement id from the measurement page
30 # This should be read from the uploaded file, but would require an extra NetCDF module. 25 # This should be read from the uploaded file, but would require an extra NetCDF module.
31 regex = "<h3>Measurement (?P<measurement_id>.{12}) <small>" 26 regex = "<h3>Measurement (?P<measurement_id>.{12}) <small>"
38 version no check is performed, and no feedback is given if the upload 33 version no check is performed, and no feedback is given if the upload
39 was successful. If everything is setup correctly, it will work. 34 was successful. If everything is setup correctly, it will work.
40 """ 35 """
41 36
42 def __init__(self, auth, output_dir, base_url): 37 def __init__(self, auth, output_dir, base_url):
38
43 self.auth = auth 39 self.auth = auth
44 self.output_dir = output_dir 40 self.output_dir = output_dir
45 self.base_url = base_url 41 self.base_url = base_url
46 self.session = requests.Session() 42 self.session = requests.Session()
47 self.construct_urls() 43 self.session.auth = auth
48 44 self.session.verify = False
49 def construct_urls(self):
50 """ Construct all URLs needed for processing. """
51 # Construct the absolute URLs
52 self.login_url = urlparse.urljoin(self.base_url, 'accounts/login/') 45 self.login_url = urlparse.urljoin(self.base_url, 'accounts/login/')
53 self.upload_url = urlparse.urljoin(self.base_url, 'data_processing/measurements/quick/') 46 self.upload_url = urlparse.urljoin(self.base_url, 'data_processing/measurements/quick/')
54 self.download_preprocessed_pattern = urlparse.urljoin(self.base_url, 'data_processing/measurements/{0}/download-preprocessed/') 47 self.download_preprocessed_pattern = urlparse.urljoin(self.base_url,
55 self.download_optical_pattern = urlparse.urljoin(self.base_url, 'data_processing/measurements/{0}/download-optical/') 48 'data_processing/measurements/{0}/download-preprocessed/')
56 self.download_graph_pattern = urlparse.urljoin(self.base_url, 'data_processing/measurements/{0}/download-plots/') 49 self.download_optical_pattern = urlparse.urljoin(self.base_url,
50 'data_processing/measurements/{0}/download-optical/')
51 self.download_graph_pattern = urlparse.urljoin(self.base_url,
52 'data_processing/measurements/{0}/download-plots/')
57 self.delete_measurement_pattern = urlparse.urljoin(self.base_url, 'admin/database/measurements/{0}/delete/') 53 self.delete_measurement_pattern = urlparse.urljoin(self.base_url, 'admin/database/measurements/{0}/delete/')
58 self.api_base_url = urlparse.urljoin(self.base_url, 'api/v1/') 54 self.api_base_url = urlparse.urljoin(self.base_url, 'api/v1/')
55 self.list_measurements_pattern = urlparse.urljoin(self.base_url, 'data_processing/measurements/')
59 56
60 def login(self, credentials): 57 def login(self, credentials):
61 """ Login the the website. """ 58 """ Login the the website. """
62 logger.debug("Attempting to login to SCC, username %s." % credentials[0]) 59 logger.debug("Attempting to login to SCC, username %s." % credentials[0])
63 self.login_credentials = {'username': credentials[0], 60 login_credentials = {'username': credentials[0],
64 'password': credentials[1]} 61 'password': credentials[1]}
65 62
66 logger.debug("Accessing login page at %s." % self.login_url) 63 logger.debug("Accessing login page at %s." % self.login_url)
67 64
68 # Get upload form 65 # Get upload form
69 login_page = self.session.get(self.login_url, auth=self.auth, verify=False) 66 login_page = self.session.get(self.login_url)
70 67
68 # TODO: Do we need this? Mortiz removed it.
71 if login_page.status_code != 200: 69 if login_page.status_code != 200:
72 logger.error('Could not access login pages. Status code %s' % login_page.status_code) 70 logger.error('Could not access login pages. Status code %s' % login_page.status_code)
73 sys.exit(1) 71 sys.exit(1)
74 72
75 logger.debug("Submiting credentials.") 73 logger.debug("Submiting credentials.")
76
77 # Submit the login data 74 # Submit the login data
78 login_submit = self.session.post(self.login_url, 75 login_submit = self.session.post(self.login_url,
79 data=self.login_credentials, 76 data=login_credentials,
80 headers={'X-CSRFToken': login_page.cookies['csrftoken'], 77 headers={'X-CSRFToken': login_page.cookies['csrftoken'],
81 'referer': self.login_url}, 78 'referer': self.login_url})
82 verify=False,
83 auth=self.auth)
84 return login_submit 79 return login_submit
85 80
86 def logout(self): 81 def logout(self):
87 pass 82 pass
88 83
89 def upload_file(self, filename, system_id, rs_filename=None): 84 def upload_file(self, filename, system_id, rs_filename=None):
90 """ Upload a filename for processing with a specific system. If the 85 """ Upload a filename for processing with a specific system. If the
91 upload is successful, it returns the measurement id. """ 86 upload is successful, it returns the measurement id. """
92 # Get submit page 87 # Get submit page
93 upload_page = self.session.get(self.upload_url, 88 upload_page = self.session.get(self.upload_url)
94 auth=self.auth,
95 verify=False)
96 89
97 # Submit the data 90 # Submit the data
98 upload_data = {'system': system_id} 91 upload_data = {'system': system_id}
99 files = {'data': open(filename, 'rb')} 92 files = {'data': open(filename, 'rb')}
100 93
105 98
106 upload_submit = self.session.post(self.upload_url, 99 upload_submit = self.session.post(self.upload_url,
107 data=upload_data, 100 data=upload_data,
108 files=files, 101 files=files,
109 headers={'X-CSRFToken': upload_page.cookies['csrftoken'], 102 headers={'X-CSRFToken': upload_page.cookies['csrftoken'],
110 'referer': self.upload_url,}, 103 'referer': self.upload_url})
111 verify=False,
112 auth=self.auth)
113 104
114 if upload_submit.status_code != 200: 105 if upload_submit.status_code != 200:
115 logger.warning("Connection error. Status code: %s" % upload_submit.status_code) 106 logger.warning("Connection error. Status code: %s" % upload_submit.status_code)
116 return False 107 return False
117 108
129 """ Downloads some files from the download_url to the specified 120 """ Downloads some files from the download_url to the specified
130 subdir. This method is used to download preprocessed file, optical 121 subdir. This method is used to download preprocessed file, optical
131 files etc. 122 files etc.
132 """ 123 """
133 # Get the file 124 # Get the file
134 request = self.session.get(download_url, auth=self.auth, 125 request = self.session.get(download_url, stream=True)
135 verify=False, 126
136 stream=True) 127 if not request.ok:
128 raise Exception("Could not download files for measurement '%s'" % measurement_id)
137 129
138 # Create the dir if it does not exist 130 # Create the dir if it does not exist
139 local_dir = os.path.join(self.output_dir, measurement_id, subdir) 131 local_dir = os.path.join(self.output_dir, measurement_id, subdir)
140 if not os.path.exists(local_dir): 132 if not os.path.exists(local_dir):
141 os.makedirs(local_dir) 133 os.makedirs(local_dir)
178 170
179 def rerun_processing(self, measurement_id, monitor=True): 171 def rerun_processing(self, measurement_id, monitor=True):
180 measurement, status = self.get_measurement(measurement_id) 172 measurement, status = self.get_measurement(measurement_id)
181 173
182 if measurement: 174 if measurement:
183 request = self.session.get(measurement.rerun_processing_url, auth=self.auth, 175 request = self.session.get(measurement.rerun_processing_url, stream=True)
184 verify=False,
185 stream=True)
186 176
187 if request.status_code != 200: 177 if request.status_code != 200:
188 logger.error( 178 logger.error(
189 "Could not rerun processing for %s. Status code: %s" % (measurement_id, request.status_code)) 179 "Could not rerun processing for %s. Status code: %s" % (measurement_id, request.status_code))
190 return 180 return
199 measurement, status = self.get_measurement(measurement_id) 189 measurement, status = self.get_measurement(measurement_id)
200 190
201 if measurement: 191 if measurement:
202 logger.debug("Attempting to rerun all processing through %s." % measurement.rerun_all_url) 192 logger.debug("Attempting to rerun all processing through %s." % measurement.rerun_all_url)
203 193
204 request = self.session.get(measurement.rerun_all_url, auth=self.auth, 194 request = self.session.get(measurement.rerun_all_url, stream=True)
205 verify=False,
206 stream=True)
207 195
208 if request.status_code != 200: 196 if request.status_code != 200:
209 logger.error("Could not rerun pre processing for %s. Status code: %s" % 197 logger.error("Could not rerun pre processing for %s. Status code: %s" %
210 (measurement_id, request.status_code)) 198 (measurement_id, request.status_code))
211 return 199 return
212 200
213 if monitor: 201 if monitor:
214 self.monitor_processing(measurement_id) 202 self.monitor_processing(measurement_id)
215 203
216 def process(self, filename, system_id, rs_filename=None): 204 def process(self, filename, system_id, monitor, rs_filename=None):
217 """ Upload a file for processing and wait for the processing to finish. 205 """ Upload a file for processing and wait for the processing to finish.
218 If the processing is successful, it will download all produced files. 206 If the processing is successful, it will download all produced files.
219 """ 207 """
220 logger.info("--- Processing started on %s. ---" % datetime.datetime.now()) 208 logger.info("--- Processing started on %s. ---" % datetime.datetime.now())
221 # Upload file 209 # Upload file
222 logger.info("--- Uploading file") 210 logger.info("--- Uploading file")
223 measurement_id = self.upload_file(filename, system_id, rs_filename=rs_filename) 211 measurement_id = self.upload_file(filename, system_id, rs_filename=rs_filename)
224 212
225 logger.info("--- Monitoring processing") 213 logger.info("--- Monitoring processing")
226 measurement = self.monitor_processing(measurement_id) 214 if monitor:
227 return measurement 215 return self.monitor_processing(measurement_id)
216 return None
228 217
229 def monitor_processing(self, measurement_id): 218 def monitor_processing(self, measurement_id):
230 """ Monitor the processing progress of a measurement id""" 219 """ Monitor the processing progress of a measurement id"""
231 220
232 # try to deal with error 404 221 # try to deal with error 404
251 sys.exit(1) 240 sys.exit(1)
252 241
253 logger.info('measurement %s found', measurement_id) 242 logger.info('measurement %s found', measurement_id)
254 243
255 if measurement is not None: 244 if measurement is not None:
256 error_count = 0
257 while measurement.is_running: 245 while measurement.is_running:
258 logger.info("Measurement is being processed (status: %s, %s, %s). Please wait.", measurement.upload, measurement.pre_processing, measurement.processing) 246 logger.info("Measurement is being processed (status: %s, %s, %s). Please wait.", measurement.upload, measurement.pre_processing, measurement.processing)
259 time.sleep(10) 247 time.sleep(10)
260 measurement, status = self.get_measurement(measurement_id) 248 measurement, status = self.get_measurement(measurement_id)
261 249
273 261
274 def get_status(self, measurement_id): 262 def get_status(self, measurement_id):
275 """ Get the processing status for a measurement id through the API. """ 263 """ Get the processing status for a measurement id through the API. """
276 measurement_url = urlparse.urljoin(self.api_base_url, 'measurements/?id__exact=%s' % measurement_id) 264 measurement_url = urlparse.urljoin(self.api_base_url, 'measurements/?id__exact=%s' % measurement_id)
277 265
278 response = self.session.get(measurement_url, 266 response = self.session.get(measurement_url)
279 auth=self.auth,
280 verify=False)
281 267
282 response_dict = response.json() 268 response_dict = response.json()
283 269
284 if response_dict['objects']: 270 if response_dict['objects']:
285 measurement_list = response_dict['objects'] 271 measurement_list = response_dict['objects']
290 return None 276 return None
291 277
292 def get_measurement(self, measurement_id): 278 def get_measurement(self, measurement_id):
293 measurement_url = urlparse.urljoin(self.api_base_url, 'measurements/%s/' % measurement_id) 279 measurement_url = urlparse.urljoin(self.api_base_url, 'measurements/%s/' % measurement_id)
294 280
295 response = self.session.get(measurement_url, 281 response = self.session.get(measurement_url)
296 auth=self.auth, 282
297 verify=False) 283 if not response.ok:
298
299 # maybe the measurements isn't already available on the database.
300 if response.status_code == 404:
301 return None, 404
302
303 if response.status_code != 200:
304 logger.error('Could not access API. Status code %s.' % response.status_code) 284 logger.error('Could not access API. Status code %s.' % response.status_code)
305 return None, response.status_code 285 return None, response.status_code
306 286
307 response_dict = response.json() 287 response_dict = response.json()
308 288
327 if measurement is None: 307 if measurement is None:
328 logger.warning("Nothing to delete.") 308 logger.warning("Nothing to delete.")
329 return None 309 return None
330 310
331 # Go the the page confirming the deletion 311 # Go the the page confirming the deletion
332 delete_url = self.delete_measurement_pattern.format(measurement.id) 312 delete_url = self.delete_measurement_pattern.format(measurement_id)
333 313
334 confirm_page = self.session.get(delete_url, 314 confirm_page = self.session.get(delete_url)
335 auth=self.auth,
336 verify=False)
337 315
338 # Check that the page opened properly 316 # Check that the page opened properly
339 if confirm_page.status_code != 200: 317 if confirm_page.status_code != 200:
340 logger.warning("Could not open delete page. Status: {0}".format(confirm_page.status_code)) 318 logger.warning("Could not open delete page. Status: {0}".format(confirm_page.status_code))
341 return None 319 return None
342 320
343 # Delete the measurement 321 # Delete the measurement
344 delete_page = self.session.post(delete_url, 322 delete_page = self.session.post(delete_url,
345 auth=self.auth,
346 verify=False,
347 data={'post': 'yes'}, 323 data={'post': 'yes'},
348 headers={'X-CSRFToken': confirm_page.cookies['csrftoken'], 324 headers={'X-CSRFToken': confirm_page.cookies['csrftoken'],
349 'referer': delete_url} 325 'referer': delete_url}
350 ) 326 )
351 if delete_page.status_code != 200: 327 if delete_page.status_code != 200:
357 return True 333 return True
358 334
359 def available_measurements(self): 335 def available_measurements(self):
360 """ Get a list of available measurement on the SCC. """ 336 """ Get a list of available measurement on the SCC. """
361 measurement_url = urlparse.urljoin(self.api_base_url, 'measurements') 337 measurement_url = urlparse.urljoin(self.api_base_url, 'measurements')
362 response = self.session.get(measurement_url, 338 response = self.session.get(measurement_url)
363 auth=self.auth,
364 verify=False)
365 response_dict = response.json() 339 response_dict = response.json()
366 340
367 measurements = None 341 measurements = None
368 if response_dict: 342 if response_dict:
369 measurement_list = response_dict['objects'] 343 measurement_list = response_dict['objects']
372 else: 346 else:
373 logger.warning("No response received from the SCC when asked for available measurements.") 347 logger.warning("No response received from the SCC when asked for available measurements.")
374 348
375 return measurements 349 return measurements
376 350
351 def list_measurements(self, station=None, system=None, start=None, stop=None, upload_status=None,
352 processing_status=None, optical_processing=None):
353
354 # Need to set to empty string if not specified, we won't get any results
355 params = {
356 "station": station if station is not None else "",
357 "system": system if system is not None else "",
358 "stop": stop if stop is not None else "",
359 "start": start if start is not None else "",
360 "upload_status": upload_status if upload_status is not None else "",
361 "preprocessing_status": processing_status if processing_status is not None else "",
362 "optical_processing_status": optical_processing if optical_processing is not None else ""
363 }
364 resp = self.session.get(self.list_measurements_pattern, params=params).text
365 tbl_rgx = re.compile(r'<table id="measurements">(.*?)</table>', re.DOTALL)
366 entry_rgx = re.compile(r'<tr>(.*?)</tr>', re.DOTALL)
367 measurement_rgx = re.compile(
368 r'.*?<td><a[^>]*>(\w+)</a>.*?<td>.*?<td>([\w-]+ [\w:]+)</td>.*<td data-order="([-]?\d+),([-]?\d+),([-]?\d+)".*',
369 re.DOTALL)
370 matches = tbl_rgx.findall(resp)
371 if len(matches) != 1:
372 return []
373
374 ret = []
375 for entry in entry_rgx.finditer(matches[0]):
376 m = measurement_rgx.match(entry.string[entry.start(0):entry.end(0)])
377 if m:
378 name, date, upload, preproc, optical = m.groups()
379 ret.append(
380 Measurement(self.base_url, {"id": name, "upload": int(upload), "pre_processing": int(preproc),
381 "processing": int(optical)}))
382
383 return ret
384
377 def measurement_id_for_date(self, t1, call_sign='bu', base_number=0): 385 def measurement_id_for_date(self, t1, call_sign='bu', base_number=0):
378 """ Give the first available measurement id on the SCC for the specific 386 """ Give the first available measurement id on the SCC for the specific
379 date. 387 date.
380 """ 388 """
381 date_str = t1.strftime('%Y%m%d') 389 date_str = t1.strftime('%Y%m%d')
382 search_url = urlparse.urljoin(self.api_base_url, 'measurements/?id__startswith=%s' % date_str) 390 search_url = urlparse.urljoin(self.api_base_url, 'measurements/?id__startswith=%s' % date_str)
383 391
384 response = self.session.get(search_url, 392 response = self.session.get(search_url)
385 auth=self.auth,
386 verify=False)
387 393
388 response_dict = response.json() 394 response_dict = response.json()
389 395
390 measurement_id = None 396 measurement_id = None
391 397
478 measurement = scc.process(filename, system_id, rs_filename=rs_filename) 484 measurement = scc.process(filename, system_id, rs_filename=rs_filename)
479 scc.logout() 485 scc.logout()
480 return measurement 486 return measurement
481 487
482 488
483 def delete_measurement(measurement_id, settings): 489 def delete_measurement(measurement_ids, settings):
484 """ Shortcut function to delete a measurement from the SCC. """ 490 """ Shortcut function to delete measurements from the SCC. """
485 logger.info("Deleting %s" % measurement_id)
486
487 scc = SCC(settings['basic_credentials'], settings['output_dir'], settings['base_url']) 491 scc = SCC(settings['basic_credentials'], settings['output_dir'], settings['base_url'])
488 scc.login(settings['website_credentials']) 492 scc.login(settings['website_credentials'])
489 scc.delete_measurement(measurement_id) 493 for m_id in measurement_ids:
494 logger.info("Deleting %s" % m_id)
495 scc.delete_measurement(m_id)
490 scc.logout() 496 scc.logout()
491 497
492 498
493 def rerun_all(measurement_id, monitor, settings): 499 def rerun_all(measurement_ids, monitor, settings):
494 """ Shortcut function to delete a measurement from the SCC. """ 500 """ Shortcut function to rerun measurements from the SCC. """
495 logger.info("Rerunning all products for %s" % measurement_id)
496 501
497 scc = SCC(settings['basic_credentials'], settings['output_dir'], settings['base_url']) 502 scc = SCC(settings['basic_credentials'], settings['output_dir'], settings['base_url'])
498 scc.login(settings['website_credentials']) 503 scc.login(settings['website_credentials'])
499 scc.rerun_all(measurement_id, monitor) 504 for m_id in measurement_ids:
505 logger.info("Rerunning all products for %s" % m_id)
506 scc.rerun_all(m_id, monitor)
500 scc.logout() 507 scc.logout()
501 508
502 509
503 def rerun_processing(measurement_id, monitor, settings): 510 def rerun_processing(measurement_ids, monitor, settings):
504 """ Shortcut function to delete a measurement from the SCC. """ 511 """ Shortcut function to delete a measurement from the SCC. """
505 logger.info("Rerunning (optical) processing for %s" % measurement_id)
506 512
507 scc = SCC(settings['basic_credentials'], settings['output_dir'], settings['base_url']) 513 scc = SCC(settings['basic_credentials'], settings['output_dir'], settings['base_url'])
508 scc.login(settings['website_credentials']) 514 scc.login(settings['website_credentials'])
509 scc.rerun_processing(measurement_id, monitor) 515 for m_id in measurement_ids:
516 logger.info("Rerunning (optical) processing for %s" % m_id)
517 scc.rerun_processing(m_id, monitor)
510 scc.logout() 518 scc.logout()
511 519
512 520
513 def import_settings(config_file_path): 521 def list_measurements(settings, station=None, system=None, start=None, stop=None, upload_status=None,
522 preprocessing_status=None,
523 optical_processing=None):
524 """List all available measurements"""
525 scc = SCC(settings['basic_credentials'], settings['output_dir'], settings['base_url'])
526 scc.login(settings['website_credentials'])
527 ret = scc.list_measurements(station=station, system=system, start=start, stop=stop, upload_status=upload_status,
528 processing_status=preprocessing_status, optical_processing=optical_processing)
529 for entry in ret:
530 print("%s" % entry.id)
531 scc.logout()
532
533
534 def download_measurements(measurement_ids, download_preproc, download_optical, download_graph, settings):
535 """Download all measurements for the specified IDs"""
536 scc = SCC(settings['basic_credentials'], settings['output_dir'], settings['base_url'])
537 scc.login(settings['website_credentials'])
538 for m_id in measurement_ids:
539 if download_preproc:
540 logger.info("Downloading preprocessed files for '%s'" % m_id)
541 scc.download_preprocessed(m_id)
542 logger.info("Complete")
543 if download_optical:
544 logger.info("Downloading optical files for '%s'" % m_id)
545 scc.download_optical(m_id)
546 logger.info("Complete")
547 if download_graph:
548 logger.info("Downloading profile graph files for '%s'" % m_id)
549 scc.download_graphs(m_id)
550 logger.info("Complete")
551
552
553 def settings_from_path(config_file_path):
514 """ Read the configuration file. 554 """ Read the configuration file.
515 555
516 The file should be in YAML syntax.""" 556 The file should be in YAML syntax."""
517 557
518 if not os.path.isfile(config_file_path): 558 if not os.path.isfile(config_file_path):
519 logger.error("Wrong path for configuration file (%s)" % config_file_path) 559 raise argparse.ArgumentTypeError("Wrong path for configuration file (%s)" % config_file_path)
520 sys.exit(1)
521 560
522 with open(config_file_path) as yaml_file: 561 with open(config_file_path) as yaml_file:
523 try: 562 try:
524 settings = yaml.safe_load(yaml_file) 563 settings = yaml.safe_load(yaml_file)
525 logger.debug("Read settings file(%s)" % config_file_path) 564 logger.debug("Read settings file(%s)" % config_file_path)
526 except: 565 except Exception:
527 logger.error("Could not parse YAML file (%s)" % config_file_path) 566 raise argparse.ArgumentTypeError("Could not parse YAML file (%s)" % config_file_path)
528 sys.exit(1)
529 567
530 # YAML limitation: does not read tuples 568 # YAML limitation: does not read tuples
531 settings['basic_credentials'] = tuple(settings['basic_credentials']) 569 settings['basic_credentials'] = tuple(settings['basic_credentials'])
532 settings['website_credentials'] = tuple(settings['website_credentials']) 570 settings['website_credentials'] = tuple(settings['website_credentials'])
533 return settings 571 return settings
534 572
535 573
574 # Setup for command specific parsers
575 def setup_delete(parser):
576 def delete_from_args(parsed):
577 delete_measurement(parsed.IDs, parsed.config)
578
579 parser.add_argument("IDs", nargs="+", help="measurement IDs to delete.")
580 parser.set_defaults(execute=delete_from_args)
581
582
583 def setup_rerun_all(parser):
584 def rerun_all_from_args(parsed):
585 rerun_all(parsed.IDs, parsed.process, parsed.config)
586
587 parser.add_argument("IDs", nargs="+", help="Measurement IDs to rerun.")
588 parser.add_argument("-p", "--process", help="Wait for the results of the processing.",
589 action="store_true")
590 parser.set_defaults(execute=rerun_all_from_args)
591
592
593 def setup_rerun_processing(parser):
594 def rerun_processing_from_args(parsed):
595 rerun_processing(parsed.IDs, parsed.process, parsed.config)
596
597 parser.add_argument("IDs", nargs="+", help="Measurement IDs to rerun the processing on.")
598 parser.add_argument("-p", "--process", help="Wait for the results of the processing.",
599 action="store_true")
600 parser.set_defaults(execute=rerun_processing_from_args)
601
602
603 def setup_process_file(parser):
604 def process_file_from_args(parsed):
605 process_file(parsed.file, parsed.system, parsed.process, parsed.config, parsed.radiosounding)
606
607 parser.add_argument("filename", help="Measurement file name or path.")
608 parser.add_argument("system", help="Processing system id.")
609 parser.add_argument("--radiosounding", default=None, help="Radiosounding file name or path")
610 parser.add_argument("-p", "--process", help="Wait for the results of the processing.",
611 action="store_true")
612 parser.set_defaults(execute=process_file_from_args)
613
614
615 def setup_upload_file(parser):
616 def upload_file_from_args(parsed):
617 upload_file(parsed.file, parsed.system, parsed.config, parsed.radiosounding)
618
619 parser.add_argument("filename", help="Measurement file name or path.")
620 parser.add_argument("system", help="Processing system id.")
621 parser.add_argument("--radiosounding", default=None, help="Radiosounding file name or path")
622
623 parser.set_defaults(execute=upload_file_from_args)
624
625
626 def setup_list_measurements(parser):
627 def list_measurements_from_args(parsed):
628 list_measurements(parsed.config, station=parsed.station, system=parsed.system, start=parsed.start,
629 stop=parsed.stop,
630 upload_status=parsed.upload_status, preprocessing_status=parsed.preprocessing_status,
631 optical_processing=parsed.optical_processing_status)
632
633 def status(arg):
634 if -127 <= int(arg) <= 127:
635 return arg
636 else:
637 raise argparse.ArgumentTypeError("Status must be between -127 and 127")
638
639 def date(arg):
640 if re.match(r'\d{4}-\d{2}-\d{2}', arg):
641 return arg
642 else:
643 raise argparse.ArgumentTypeError("Date must be in format 'YYYY-MM-DD'")
644
645 parser.add_argument("--station", help="Filter for only the selected station")
646 parser.add_argument("--system", help="Filter for only the selected station")
647 parser.add_argument("--start", help="Filter for only the selected station", type=date)
648 parser.add_argument("--stop", help="Filter for only the selected station", type=date)
649 parser.add_argument("--upload-status", help="Filter for only the selected station", type=status)
650 parser.add_argument("--preprocessing-status", help="Filter for only the selected station", type=status)
651 parser.add_argument("--optical-processing-status", help="Filter for only the selected station", type=status)
652 parser.set_defaults(execute=list_measurements_from_args)
653
654
655 def setup_download_measurements(parser):
656 def download_measurements_from_args(parsed):
657 preproc = parsed.download_preprocessed
658 optical = parsed.download_optical
659 graphs = parsed.download_profile_graphs
660 if not preproc and not graphs:
661 optical = True
662 download_measurements(parsed.IDs, preproc, optical, graphs, parsed.config)
663
664 parser.add_argument("IDs", help="Measurement IDs that should be downloaded.", nargs="+")
665 parser.add_argument("--download-preprocessed", action="store_true", help="Download preprocessed files.")
666 parser.add_argument("--download-optical", action="store_true",
667 help="Download optical files (default if no other download is used).")
668 parser.add_argument("--download-profile-graphs", action="store_true", help="Download profile graph files.")
669 parser.set_defaults(execute=download_measurements_from_args)
670
671
536 def main(): 672 def main():
537 # Define the command line arguments. 673 # Define the command line arguments.
538 parser = argparse.ArgumentParser() 674 parser = argparse.ArgumentParser()
539 parser.add_argument("config", help="Path to configuration file") 675 subparsers = parser.add_subparsers()
540 parser.add_argument("filename", nargs='?', help="Measurement file name or path.", default='') 676
541 parser.add_argument("system", nargs='?', help="Processing system id.", default=0) 677 delete_parser = subparsers.add_parser("delete", help="Deletes a measurement.")
542 parser.add_argument("-p", "--process", help="Wait for the results of the processing.", 678 rerun_all_parser = subparsers.add_parser("rerun-all", help="Rerun a measurement.")
543 action="store_true") 679 rerun_processing_parser = subparsers.add_parser("rerun-processing",
544 parser.add_argument("--delete", help="Measurement ID to delete.") 680 help="Rerun processing routings for a measurement.")
545 parser.add_argument("--rerun-all", help="Measurement ID to rerun.") 681 process_file_parser = subparsers.add_parser("process-file", help="Process a file.")
546 parser.add_argument("--rerun-processing", help="Measurement ID to rerun processing routines.") 682 upload_file_parser = subparsers.add_parser("upload-file", help="Upload a file.")
547 683 list_parser = subparsers.add_parser("list", help="List all measurements.")
548 # others files 684 download_parser = subparsers.add_parser("download", help="Download selected measurements.")
549 parser.add_argument("--radiosounding", default=None, help="Radiosounding file name or path") 685
686 setup_delete(delete_parser)
687 setup_rerun_all(rerun_all_parser)
688 setup_rerun_processing(rerun_processing_parser)
689 setup_process_file(process_file_parser)
690 setup_upload_file(upload_file_parser)
691 setup_list_measurements(list_parser)
692 setup_download_measurements(download_parser)
550 693
551 # Verbosity settings from http://stackoverflow.com/a/20663028 694 # Verbosity settings from http://stackoverflow.com/a/20663028
552 parser.add_argument('-d', '--debug', help="Print debugging information.", action="store_const", 695 parser.add_argument('-d', '--debug', help="Print debugging information.", action="store_const",
553 dest="loglevel", const=logging.DEBUG, default=logging.INFO, 696 dest="loglevel", const=logging.DEBUG, default=logging.INFO,
554 ) 697 )
555 parser.add_argument('-s', '--silent', help="Show only warning and error messages.", action="store_const", 698 parser.add_argument('-s', '--silent', help="Show only warning and error messages.", action="store_const",
556 dest="loglevel", const=logging.WARNING 699 dest="loglevel", const=logging.WARNING
557 ) 700 )
558 701
702 home = os.path.expanduser("~")
703 default_config_location = os.path.abspath(os.path.join(home, ".scc_access.yaml"))
704 parser.add_argument("-c", "--config", help="Path to the config file.", type=settings_from_path,
705 default=default_config_location)
706
559 args = parser.parse_args() 707 args = parser.parse_args()
560 708
561 # Get the logger with the appropriate level 709 # Get the logger with the appropriate level
562 logging.basicConfig(format='%(levelname)s: %(message)s', level=args.loglevel) 710 logging.basicConfig(format='%(levelname)s: %(message)s', level=args.loglevel)
563 711
564 settings = import_settings(args.config) 712 # Dispatch to appropriate function
565 713 args.execute(args)
566 # If the arguments are OK, try to log-in to SCC and upload. 714
567 if args.delete: 715
568 # If the delete is provided, do nothing else 716 # When running through terminal
569 delete_measurement(args.delete, settings) 717 if __name__ == '__main__':
570 elif args.rerun_all: 718 main()
571 rerun_all(args.rerun_all, args.process, settings)
572 elif args.rerun_processing:
573 rerun_processing(args.rerun_processing, args.process, settings)
574 else:
575 if (args.filename == '') or (args.system == 0):
576 parser.error('Provide a valid filename and system parameters.\nRun with -h for help.\n')
577
578 if args.process:
579 process_file(args.filename, args.system, settings, rs_filename=args.radiosounding)
580 else:
581 upload_file(args.filename, args.system, settings, rs_filename=args.radiosounding)

mercurial