| 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 |
| 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) |
|