def click_open_manual(cls): logger.debug("open manual") Helper.explore_folder( "https://www.hydroffice.org/manuals/abc/user_manual_info.html#noaa-s-57-support-files-for-caris" )
def open_output_folder(self): Helper.explore_folder(self.output_folder)
def open_local_noaa_support_folder(self): if self.local_noaa_support_folder_present(): Helper.explore_folder(self.local_noaa_support_folder()) return Helper(lib_info=self._li).explore_package_folder()
def default_output_folder(cls): output_folder = Helper(lib_info=lib_info).package_folder() # create it if it does not exist if not os.path.exists(output_folder): os.makedirs(output_folder) return output_folder
def click_open_manual(cls): logger.debug("open manual") Helper.explore_folder( "https://www.hydroffice.org/manuals/qctools/user_manual_chart_triangle_rule.html" )
def link_activated(self, url): if "mailto:" in url: self.email_bug_report() else: Helper.explore_folder(url)
def click_open_manual(cls): logger.debug("open manual") Helper.explore_folder( "https://www.hydroffice.org/manuals/qctools/stable/user_manual_survey_sbdare_export.html" )
def _click_feature_scan(self, version): """abstract the feature scan calling mechanism""" self.parent_win.change_info_url(Helper(lib_info=lib_info).web_url(suffix="survey_feature_scan_%d" % version)) # library takes care of progress bar try: specs_version = None survey_area = 0 use_mhw = False mhw_value = 0.0 sorind = None sordat = None images_folder = None if version == 8: specs_version = self.toggle_specs_v8.value() if specs_version == 2017: specs_version = "2017" elif specs_version == 2018: specs_version = "2018" elif specs_version == 2019: specs_version = "2019" elif specs_version == 2020: specs_version = "2020" else: raise RuntimeError("unknown specs version: %s" % specs_version) toggle_survey_area = self.toggle_area_v8.value() if toggle_survey_area == 0: survey_area = survey_areas["Pacific Coast"] elif toggle_survey_area == 1: survey_area = survey_areas["Great Lakes"] elif toggle_survey_area == 2: survey_area = survey_areas["Atlantic Coast"] else: raise RuntimeError("unknown survey area: %s" % survey_area) use_mhw = self.use_mhw_v8.isChecked() mhw_str = self.mhw_value_v8.text() mhw_value = float(mhw_str) if self.check_sorind_v8.isChecked(): sorind = self.sorind_value_v8.text() is_valid = self.prj.check_sorind(value=sorind) if not is_valid: msg = "An invalid SORIND was entered!\n\nCheck: %s" % sorind # noinspection PyCallByClass QtWidgets.QMessageBox.critical(self, "Feature scan v%d[%s]" % (version, specs_version), msg, QtWidgets.QMessageBox.Ok) return if self.check_sordat_v8.isChecked(): sordat = self.sordat_value_v8.text() is_valid = self.prj.check_sordat(value=sordat) if not is_valid: msg = "An invalid SORDAT was entered!\n\nCheck: %s" % sordat # noinspection PyCallByClass QtWidgets.QMessageBox.critical(self, "Feature scan v%d[%s]" % (version, specs_version), msg, QtWidgets.QMessageBox.Ok) return elif version == 9: specs_version = self.toggle_specs_v9.value() if specs_version == 2017: specs_version = "2017" elif specs_version == 2018: specs_version = "2018" elif specs_version == 2019: specs_version = "2019" elif specs_version == 2020: specs_version = "2020" else: raise RuntimeError("unknown specs version: %s" % specs_version) toggle_survey_area = self.toggle_area_v9.value() if toggle_survey_area == 0: survey_area = survey_areas["Pacific Coast"] elif toggle_survey_area == 1: survey_area = survey_areas["Great Lakes"] elif toggle_survey_area == 2: survey_area = survey_areas["Atlantic Coast"] else: raise RuntimeError("unknown survey area: %s" % survey_area) if self.ask_multimedia_folder_v9.isChecked(): # ask for images folder # noinspection PyCallByClass images_folder = QtWidgets.QFileDialog.getExistingDirectory(self, "Select the folder with the images", QtCore.QSettings().value( "feature_scan_images_folder", ""), ) if images_folder == "": logger.debug('selecting multimedia folder: aborted') images_folder = None else: logger.debug("selected images folder: %s" % images_folder) QtCore.QSettings().setValue("feature_scan_images_folder", images_folder) use_mhw = self.use_mhw_v9.isChecked() mhw_value = 0.0 if use_mhw: mhw_str = self.mhw_value_v9.text() if mhw_str == "": msg = "The MHW field is empty! Enter a valid value or disable the WATLEV check." # noinspection PyArgumentList QtWidgets.QMessageBox.critical(self, "Feature scan v%d[%s]" % (version, specs_version), msg, QtWidgets.QMessageBox.Ok) return else: mhw_value = float(mhw_str) if self.check_sorind_v9.isChecked(): sorind = self.sorind_value_v9.text() is_valid = self.prj.check_sorind(value=sorind) if not is_valid: msg = "An invalid SORIND was entered!\n\nCheck: %s" % sorind # noinspection PyCallByClass QtWidgets.QMessageBox.critical(self, "Feature scan v%d[%s]" % (version, specs_version), msg, QtWidgets.QMessageBox.Ok) return if self.check_sordat_v9.isChecked(): sordat = self.sordat_value_v9.text() is_valid = self.prj.check_sordat(value=sordat) if not is_valid: msg = "An invalid SORDAT was entered!\n\nCheck: %s" % sordat # noinspection PyCallByClass QtWidgets.QMessageBox.critical(self, "Feature scan v%d[%s]" % (version, specs_version), msg, QtWidgets.QMessageBox.Ok) return else: RuntimeError("unknown Feature Scan version: %s" % version) self.prj.feature_scan(version=version, specs_version=specs_version, survey_area=survey_area, use_mhw=use_mhw, mhw_value=mhw_value, sorind=sorind, sordat=sordat, multimedia_folder=images_folder) # noinspection PyCallByClass QtWidgets.QMessageBox.information(self, "Feature scan v%d[%s]" % (version, specs_version), self.prj.scan_msg, QtWidgets.QMessageBox.Ok) except Exception as e: traceback.print_exc() # noinspection PyCallByClass QtWidgets.QMessageBox.critical(self, "Error", "While running survey's feature scan v%d, %s" % (version, e), QtWidgets.QMessageBox.Ok) return
def click_open_manual_v9(cls): logger.debug("open manual") Helper.explore_folder( "https://www.hydroffice.org/manuals/qctools/stable/user_manual_survey_detect_fliers.html" )
def click_open_manual(cls): logger.debug("open manual") Helper.explore_folder( "https://www.hydroffice.org/manuals/qctools/stable/user_manual_chart_scan_features.html" )
def save_to_db(self): """Save setup to db""" logger.info("*** > SETUP: saving ...") try: db = self.db # db.setup_version = self.setup_version # not overwrite the version since it implies different tables # db.active_setup_id = self.setup_id # obviously, unactivated to avoid db logic corruption # db.setup_status # only the current setup is visualized # db.setup_name = self.setup_name # input db.use_woa09 = self.use_woa09 db.use_woa13 = self.use_woa13 if db.setup_version > 1: db.use_woa18 = self.use_woa18 db.use_rtofs = self.use_rtofs if db.setup_version > 1: db.use_gomofs = self.use_gomofs db.ssp_extension_source = Helper.first_match( Dicts.atlases, self.ssp_extension_source) db.ssp_salinity_source = Helper.first_match( Dicts.atlases, self.ssp_salinity_source) db.ssp_temp_sal_source = Helper.first_match( Dicts.atlases, self.ssp_temp_sal_source) db.ssp_up_or_down = Helper.first_match(Dicts.ssp_directions, self.ssp_up_or_down) db.rx_max_wait_time = self.rx_max_wait_time db.use_sis4 = self.use_sis4 if db.setup_version > 1: db.use_sis5 = self.use_sis5 db.use_sippican = self.use_sippican db.use_mvp = self.use_mvp # output db.log_user = self.log_user db.log_server = self.log_server # client list db.delete_clients() for client in self.client_list.clients: logger.debug('- save: %s' % client.name) db.add_client(client_name=client.name, client_ip=client.ip, client_port=client.port, client_protocol=client.protocol) # listeners - sis4 db.sis4_listen_port = self.sis4_listen_port db.sis4_listen_timeout = self.sis4_listen_timeout # output - sis 4 and 5 db.sis_auto_apply_manual_casts = self.sis_auto_apply_manual_casts # listeners - sis5 if db.setup_version > 1: db.sis5_listen_ip = self.sis5_listen_ip db.sis5_listen_port = self.sis5_listen_port db.sis5_listen_timeout = self.sis5_listen_timeout # listeners - sippican db.sippican_listen_port = self.sippican_listen_port db.sippican_listen_timeout = self.sippican_listen_timeout # listeners - mvp db.mvp_ip_address = self.mvp_ip_address db.mvp_listen_port = self.mvp_listen_port db.mvp_listen_timeout = self.mvp_listen_timeout db.mvp_transmission_protocol = self.mvp_transmission_protocol db.mvp_format = self.mvp_format db.mvp_winch_port = self.mvp_winch_port db.mvp_fish_port = self.mvp_fish_port db.mvp_nav_port = self.mvp_nav_port db.mvp_system_port = self.mvp_system_port db.mvp_sw_version = self.mvp_sw_version db.mvp_instrument_id = self.mvp_instrument_id db.mvp_instrument = self.mvp_instrument # server db.server_source = self.server_source db.server_apply_surface_sound_speed = self.server_apply_surface_sound_speed # current settings db.current_project = self.current_project db.custom_projects_folder = self.custom_projects_folder db.custom_outputs_folder = self.custom_outputs_folder db.custom_woa09_folder = self.custom_woa09_folder db.custom_woa13_folder = self.custom_woa13_folder if db.setup_version > 1: db.custom_woa18_folder = self.custom_woa18_folder db.noaa_tools = self.noaa_tools db.default_institution = self.default_institution db.default_survey = self.default_survey db.default_vessel = self.default_vessel db.auto_apply_default_metadata = self.auto_apply_default_metadata db.close() except Exception as e: raise RuntimeError("while saving setup to db, %s" % e) logger.info("*** > SETUP: saved!")
def _click_submission_checks(self, version): """abstract the calling mechanism""" # sanity checks # - version if not isinstance(version, int): raise RuntimeError("passed invalid type for version: %s" % type(version)) if version not in [ 4, ]: raise RuntimeError("passed invalid version: %s" % version) # - list of folders (although the buttons should be never been enabled without a folder) if len(self.prj.submission_list) == 0: msg = "First add one or more submission folders!" # noinspection PyCallByClass QtWidgets.QMessageBox.warning(self, "Warning", msg, QtWidgets.QMessageBox.Ok) return self.parent_win.change_info_url( Helper(lib_info=lib_info).web_url( suffix="survey_submission_check_%d" % version)) # for each file in the project grid list msg = "Errors/warnings per submission folder:\n" opened_folders = list() for i, path in enumerate(self.prj.submission_list): logger.debug("submission folder: %s" % path) # switcher between different versions of find fliers if version == 4: saved = self._submission_checks(path=path, version=version, idx=i, total=len( self.prj.submission_list)) else: # this case should be never reached after the sanity checks raise RuntimeError("unknown Submission checks version: %s" % version) if not saved: # noinspection PyCallByClass QtWidgets.QMessageBox.critical( self, "Error", "While checking submission: %s" % path, QtWidgets.QMessageBox.Ok) continue msg += "- %s: %2d errors, %2d warnings \n" % ( self.prj.cur_root_name, self.prj.number_of_submission_errors(), self.prj.number_of_submission_warnings()) # open the output folder (if not already open) # print("output folder: %s" % self.prj.submission_output_folder) if self.prj.submission_output_folder not in opened_folders: self.prj.open_submission_output_folder() opened_folders.append(self.prj.submission_output_folder) # noinspection PyCallByClass QtWidgets.QMessageBox.information( self, "Submission checks v%d [HSSD %s]" % (version, self.prj.cur_submission_hssd), msg, QtWidgets.QMessageBox.Ok)
def click_open_manual_v4(cls): logger.debug("open manual") Helper.explore_folder( "https://www.hydroffice.org/manuals/qctools/stable/user_manual_survey_submission_checks.html" )
def generate_pdf(self, path: str, title: str = "Document", use_colors: bool = False, small: bool = False) -> bool: """Generate a multiple-page document, with passed title and list of strings""" # this function heavily relies on PySide for pdf creation, the import is local # to the function so that the class can still be used also without Pyside try: from PySide2 import QtGui from PySide2 import QtCore from PySide2 import QtPrintSupport except (ImportError, ValueError, IOError): logger.warning( "PySide2 is not properly installed, thus you cannot create a pdf file" ) self.display() return False # some preliminary tests if len(path) == 0: logger.warning("The passed file path is empty") return False path = os.path.abspath(path) if '.pdf' not in path.lower(): logger.warning( "The passed file name has not the pdf extension: %s" % path) return False path = Helper.truncate_too_long(path) if len(self.records) == 0: logger.warning("The passed string list is empty") return False # # INITIAL SETTINGS # logger.debug("output: %s" % path) # delete the passed filename if it already exists if os.path.exists(path): os.remove(path) # prepare some drawing tools blue_pen = QtGui.QPen(QtGui.QColor(30, 30, 255)) red_pen = QtGui.QPen(QtGui.QColor(255, 30, 30)) orange_pen = QtGui.QPen(QtGui.QColor(255, 165, 30)) green_pen = QtGui.QPen(QtGui.QColor(30, 200, 30)) gray_pen = QtGui.QPen(QtGui.QColor(120, 120, 120)) black_pen = QtGui.QPen(QtGui.QColor(30, 30, 30)) if small: big_font = QtGui.QFont("Arial", 9) normal_font = QtGui.QFont("Arial", 5, QtGui.QFont.Light) section_font = QtGui.QFont("Arial", 7, QtGui.QFont.Bold) check_font = QtGui.QFont("Arial", 6) small_font = QtGui.QFont("Arial", 4) else: big_font = QtGui.QFont("Arial", 10) normal_font = QtGui.QFont("Arial", 7, QtGui.QFont.Light) section_font = QtGui.QFont("Arial", 9, QtGui.QFont.Bold) check_font = QtGui.QFont("Arial", 8) small_font = QtGui.QFont("Arial", 6) lc_flags = QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter | QtCore.Qt.TextWordWrap cc_flags = QtCore.Qt.AlignCenter | QtCore.Qt.TextWordWrap # create the printer for pdf printer = QtPrintSupport.QPrinter( QtPrintSupport.QPrinter.HighResolution) printer.setCreator("HydrOffice") printer.setDocName("HydrOffice.pdf") printer.setOutputFormat(QtPrintSupport.QPrinter.PdfFormat) printer.setPageSize(QtPrintSupport.QPrinter.A4) printer.setOrientation(QtPrintSupport.QPrinter.Portrait) printer.setOutputFileName(path) page_rect = printer.pageRect() doc_width = page_rect.width() doc_height = page_rect.height() doc_margin = page_rect.x() # print(printer.getPageMargins(QtGui.QPrinter.DevicePixel)) # logger.info("Canvas: %sx%s" % (doc_width, doc_height)) # logger.info("Margin: %s" % doc_margin) # check if everything is ok if not printer.isValid(): logger.warning("the PDF printer is not valid") return False # # ACTUAL PRINTING # page_nr = 1 # a counter for pages section_nr = 0 first_section = True subsection_nr = 1 row_counter = 1 if small: row_height = 330 else: row_height = 220 # this represents the height of a single row of text max_rows = (doc_height - 2 * doc_margin) / row_height hor_pad = row_height / 2 # logger.info("rows for page: %i" % max_rows) # here we start painting to the printer painter = QtGui.QPainter() if not painter.begin(printer): logger.warning("not painter begin on printer") return False def print_page_template(): """Internal helper function that print borders, logo, time stamp, etc.""" # external border painter.setPen(gray_pen) border_area = QtCore.QRect(doc_margin, doc_margin, doc_width - 2 * doc_margin, doc_height - 2 * doc_margin) painter.drawRect(border_area) # make top-area top_area = QtCore.QRect(doc_margin, doc_margin, doc_width - 2 * doc_margin, row_height * 2) painter.drawRect(top_area) # logo hyo_logo_path = os.path.join(AppInfo().app_media_path, 'poweredby.png') if not os.path.exists(hyo_logo_path): raise RuntimeError("Unable to find logo: %s" % hyo_logo_path) hyo_logo = QtGui.QPixmap(hyo_logo_path) # print("logo size: %sx%s" % (hyo_logo.width(), hyo_logo.height())) logo_area = QtCore.QRect( doc_width / 2 - hyo_logo.width() / 2, doc_margin + (row_height * 2 - hyo_logo.height()) / 2, hyo_logo.width(), hyo_logo.height()) painter.drawPixmap(logo_area, hyo_logo) # make bottom-area bottom_area = QtCore.QRect(doc_margin, doc_height - doc_margin - row_height, doc_width - 2 * doc_margin, row_height) painter.drawRect(bottom_area) # time-stamp now_time = datetime.datetime.now() painter.setFont(small_font) painter.drawText( bottom_area, cc_flags, "time-stamp: %s, %s v.%s" % (now_time.strftime("%a, %d %b %Y %H:%M:%S"), self.lib_name, self.lib_version)) # page number page_area = QtCore.QRect(doc_width - doc_margin - 2 * row_height, doc_height - doc_margin - row_height, 2 * row_height, row_height) # painter.drawRect(page_area) painter.drawText(page_area, lc_flags, "Page %s" % page_nr) # set back to 'normal' font painter.setPen(black_pen) painter.setFont(normal_font) return True print_page_template() row_area = QtCore.QRect(doc_margin + hor_pad, doc_margin, doc_width - 2 * doc_margin - 2 * hor_pad, row_height) # painter.drawRect(row_area) alphabet = list(string.ascii_uppercase) first_page = True for content_item in self.records: # title document only for the first page if first_page: # document title if small: row_counter = 4 else: row_counter = 3 painter.setPen(blue_pen) painter.setFont(big_font) row_area.moveTo(row_area.x(), row_area.y() + row_counter * row_height) painter.drawText(row_area, cc_flags, title) # painter.drawRect(row_area) # row_counter += 1 # set back to 'normal' font painter.setPen(black_pen) painter.setFont(normal_font) first_page = False # manage a new page creation if row_counter >= (max_rows - 4): if not printer.newPage(): logger.warning( "Failed in flushing page to disk, disk full?") return False page_nr += 1 print_page_template() row_area = QtCore.QRect( doc_margin + hor_pad, doc_margin + row_height, doc_width - 2 * doc_margin - 2 * hor_pad, row_height) row_counter = 1 row_area.moveTo(row_area.x(), row_area.y() + row_height) # painter.drawRect(row_area) last_item = content_item.split(' ')[-1] first_item = content_item.split(' ')[0] if last_item.isdigit(): if int(last_item) > 0: # troubles -> red pen if use_colors: painter.setPen(red_pen) painter.drawText(row_area, lc_flags, "- " + content_item) if use_colors: painter.setPen(black_pen) else: # all good -> black pen painter.drawText(row_area, lc_flags, "- " + content_item) elif last_item == "[SKIP_REP]": # skip report for this item if use_colors: painter.setPen(gray_pen) painter.drawText(row_area, lc_flags, "- " + content_item.rsplit(' ', 1)[0]) if use_colors: painter.setPen(black_pen) elif last_item == "[SECTION]": # the string is a section separator if first_section: first_section = False else: section_nr += 1 # leave two empty rows row_area.moveTo(row_area.x(), row_area.y() + 2 * row_height) row_counter += 2 # write a numbered sections painter.setFont(section_font) painter.drawText( row_area, lc_flags, "%s. %s" % (alphabet[section_nr], content_item.rsplit(' ', 1)[0])) painter.setFont(normal_font) subsection_nr = 1 elif last_item == "[SKIP_SEC]": # the string is a section separator if use_colors: painter.setPen(gray_pen) if first_section: first_section = False else: section_nr += 1 # leave two empty rows row_area.moveTo(row_area.x(), row_area.y() + 2 * row_height) row_counter += 2 # write a numbered sections painter.setFont(section_font) painter.drawText( row_area, lc_flags, "%s. %s" % (alphabet[section_nr], content_item.rsplit(' ', 1)[0])) painter.setFont(normal_font) subsection_nr = 1 if use_colors: painter.setPen(black_pen) elif last_item == "[CHECK]" or last_item == "[TOTAL]": # the string is a section separator # leave an empty row row_area.moveTo(row_area.x(), row_area.y() + row_height) row_counter += 1 # write a numbered sections painter.setFont(check_font) painter.drawText( row_area, lc_flags, "%s.%s. %s" % (alphabet[section_nr], subsection_nr, content_item.rsplit(' ', 1)[0])) painter.setFont(normal_font) subsection_nr += 1 elif last_item == "[SKIP_CHK]": # the string is a section separator if use_colors: painter.setPen(gray_pen) # leave an empty row row_area.moveTo(row_area.x(), row_area.y() + row_height) row_counter += 1 # write a numbered sections painter.setFont(check_font) painter.drawText( row_area, lc_flags, "%s.%s. %s" % (alphabet[section_nr], subsection_nr, content_item.rsplit(' ', 1)[0])) painter.setFont(normal_font) subsection_nr += 1 if use_colors: painter.setPen(black_pen) elif last_item == "OK": # no issues, green ok if use_colors: painter.setPen(green_pen) painter.drawText(row_area, lc_flags, content_item) if use_colors: painter.setPen(black_pen) else: if first_item == "[WARNING]": if use_colors: painter.setPen(orange_pen) painter.drawText(row_area, lc_flags, content_item) if use_colors: painter.setPen(black_pen) elif first_item == "[ERROR]": if use_colors: painter.setPen(red_pen) painter.drawText(row_area, lc_flags, content_item) if use_colors: painter.setPen(black_pen) else: painter.drawText(row_area, lc_flags, content_item) row_counter += 1 # print("page %s, row %s" % (page_nr, row_counter)) # # FINISHING THE PRINTING # painter.end() return True
class TestABCLibHelper(unittest.TestCase): def setUp(self): self.h = Helper(lib_info=LibInfo()) @unittest.skipIf(Helper.is_linux(), "test not supported on Linux") def test_explore_folder(self): self.assertTrue(self.h.explore_folder(__file__)) self.assertFalse(self.h.explore_folder(__file__ + ".fake")) self.assertTrue(self.h.explore_folder(os.path.dirname(__file__))) self.assertFalse(self.h.explore_folder(os.path.dirname(__file__) + "fake")) def test_first_match(self): # fake dict a_dict = { "a": 1, "b": 99, "c": 1, } # test if it gives back the first matching key self.assertTrue(Helper.first_match(a_dict, 1) in ["a", "c"]) # test if it raises with a not-existing value with self.assertRaises(RuntimeError): Helper.first_match(a_dict, 2) def test_is_64bit_os(self): self.assertIsInstance(self.h.is_64bit_os(), bool) def test_is_64bit_python(self): self.assertIsInstance(self.h.is_64bit_python(), bool) def test_is_darwin_linux_windows(self): self.assertIsInstance(self.h.is_darwin(), bool) self.assertIsInstance(self.h.is_linux(), bool) self.assertIsInstance(self.h.is_windows(), bool) self.assertTrue(any([self.h.is_linux(), self.h.is_darwin(), self.h.is_windows()])) def test_is_pydro(self): self.assertIsInstance(self.h.is_pydro(), bool) def test_is_url(self): self.assertTrue(self.h.is_url("https://www.hydroffice.org")) self.assertTrue(self.h.is_url("http://www.hydroffice.org")) self.assertFalse(self.h.is_url("ftp://fake/url")) def test_python_path(self): self.assertTrue(os.path.exists(self.h.python_path())) def test_package_info(self): self.assertIsInstance(self.h.package_info(qt_html=True), str) self.assertIsInstance(self.h.package_info(qt_html=False), str) def test_package_folder(self): self.assertTrue(os.path.exists(self.h.package_folder())) def test_hydroffice_folder(self): self.assertTrue(os.path.exists(self.h.hydroffice_folder()))
def click_find_fliers_v9(self): """trigger the find fliers v9""" # sanity checks # - list of grids (although the buttons should be never been enabled without grids) if len(self.prj.grid_list) == 0: raise RuntimeError("the grid list is empty") height_mode = "auto" if self.set_height_ffv9.text() != "": height_mode = self.set_height_ffv9.text() ck = "c" if self.set_check_laplacian_ffv9.isChecked(): ck += "1" if self.set_check_curv_ffv9.isChecked(): ck += "2" if self.set_check_adjacent_ffv9.isChecked(): ck += "3" if self.set_check_slivers_ffv9.isChecked(): ck += "4" if self.set_check_isolated_ffv9.isChecked(): ck += "5" if self.set_check_edges_ffv9.isChecked(): ck += "6" ck += "f" if self.set_filter_fff_ffv9.isChecked(): ck += "1" if self.set_filter_designated_ffv9.isChecked(): ck += "2" self.parent_win.change_info_url( Helper(lib_info=lib_info).web_url( suffix="survey_find_fliers_9_fh_%s_%s" % (height_mode, ck))) self._parse_user_height() grid_list = self.prj.grid_list # pre checks if self.set_filter_fff_ffv9.isChecked(): if len(self.prj.s57_list) == 0: msg = "The 'Use Features from S57 File' option is active, but no S57 files have been selected!\n" \ "\n" \ "Do you want to continue with the analysis?" # noinspection PyCallByClass, PyArgumentList ret = QtWidgets.QMessageBox.warning( self, "Find Fliers v9 filters", msg, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if ret == QtWidgets.QMessageBox.No: return if self.set_filter_designated_ffv9.isChecked(): at_least_one_bag = False for grid_file in grid_list: if os.path.splitext(grid_file)[-1] == ".bag": at_least_one_bag = True if not at_least_one_bag: msg = "The 'Use Designated (SR BAG only)' option is active, " \ "but no BAG files have been selected!\n\n" \ "Do you want to continue with the analysis?" # noinspection PyCallByClass,PyArgumentList ret = QtWidgets.QMessageBox.warning( self, "Find Fliers v9 filters", msg, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if ret == QtWidgets.QMessageBox.No: return # for each file in the project grid list msg = "Potential fliers per input:\n" opened_folders = list() for i, grid_file in enumerate(grid_list): # we want to be sure that the label is based on the name of the new file input self.prj.clear_survey_label() # switcher between different versions of find fliers self._find_fliers(grid_file=grid_file, idx=(i + 1), total=len(grid_list)) # export the fliers saved = self._export_fliers() msg += "- %s: %d\n" % (self.prj.cur_grid_basename, self.prj.number_of_fliers()) # open the output folder (if not already open) if saved: if self.prj.fliers_output_folder not in opened_folders: self.prj.open_fliers_output_folder() opened_folders.append(self.prj.fliers_output_folder) self.prj.close_cur_grid() # noinspection PyCallByClass,PyArgumentList QtWidgets.QMessageBox.information(self, "Find fliers v9", msg, QtWidgets.QMessageBox.Ok)
def setUp(self): self.h = Helper(lib_info=LibInfo())
def click_open_manual_v2(cls): logger.debug("open manual") Helper.explore_folder( "https://www.hydroffice.org/manuals/qctools/stable/user_manual_survey_scan_designated.html" )
def on_export_profile_btn(self): logger.debug("export profiles clicked") if len(self.selected_writers) == 0: msg = "Select output formats before data export!" # noinspection PyCallByClass QtWidgets.QMessageBox.warning(self, "Export warning", msg, QtWidgets.QMessageBox.Ok) return settings = QtCore.QSettings() select_output_folder = self.selectFolder.isChecked() settings.setValue("select_output_folder", select_output_folder) output_folders = dict() # each writer may potentially have is own folder if select_output_folder: dlg = OutputFoldersDialog(main_win=self.main_win, lib=self.lib, writers=self.selected_writers, parent=self) dlg.exec_() output_folders = dlg.output_folders if len(output_folders) == 0: return # case where all the writers will write to the same folder if len(output_folders) == 0: for writer in self.selected_writers: output_folders[writer] = self.lib.outputs_folder settings.setValue("export_folder", self.lib.outputs_folder) logger.debug('output folder: %s' % self.lib.outputs_folder) # CARIS-specific check for file concatenation for writer in self.selected_writers: if writer == 'caris': caris_path = os.path.join(output_folders[writer], "CARIS", self.lib.current_project + ".svp") if os.path.exists(caris_path): msg = "An existing CARIS file is present in the output folder.\n\n" \ "Do you want to remove it to avoid possible profile duplications?" # noinspection PyCallByClass ret = QtWidgets.QMessageBox.question(self, "CARIS export", msg, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if ret == QtWidgets.QMessageBox.Yes: os.remove(caris_path) break # special case for Fugro ISS format force_writer_instrument_for_next_casts = None custom_writer_instrument = None # actually do the export current_project = None format_ok = False opened_folders = list() export_open_folder = self.openFolder.isChecked() settings.setValue("export_open_folder", export_open_folder) all_exported = True for pk in self._pks: success = self.lib.load_profile(pk, skip_atlas=True) if not success: # noinspection PyCallByClass QtWidgets.QMessageBox.warning(self, "Database", "Unable to load profile #%02d!" % pk, QtWidgets.QMessageBox.Ok) continue # special case: synthetic profile and NCEI skip_export = False for writer in self.selected_writers: if writer != 'ncei': continue if self.lib.ssp.l[0].meta.sensor_type == Dicts.sensor_types['Synthetic']: msg = "Attempt to export a synthetic profile in NCEI format!" # noinspection PyCallByClass QtWidgets.QMessageBox.warning(self, "Export warning", msg, QtWidgets.QMessageBox.Ok) skip_export = True continue if self.lib.current_project == 'default': msg = "The 'default' project cannot be used for NCEI export.\n\n" \ "Rename the project in the Database tab!" if self.lib.setup.noaa_tools: msg += "\n\nRecommend in project_survey format, e.g. OPR-P999-RA-17_H12345" # noinspection PyCallByClass QtWidgets.QMessageBox.warning(self, "Export warning", msg, QtWidgets.QMessageBox.Ok) skip_export = True continue if self.lib.setup.noaa_tools and self.lib.not_noaa_project(self.lib.current_project): if self.lib.not_noaa_project(current_project, format_ok): current_project, format_ok = self.lib.cb.ask_formatted_text(default=self.lib.noaa_project) if self.lib.not_noaa_project(current_project, format_ok): msg = "The project name cannot be used for NCEI export.\n\n" \ "Rename the project in the Database tab!\n\n" \ "Recommend \"project_survey\" format, e.g. OPR-P999-RA-17_H12345" # noinspection PyCallByClass QtWidgets.QMessageBox.warning(self, "Export warning", msg, QtWidgets.QMessageBox.Ok) skip_export = True continue if not self.lib.ssp.cur.meta.survey or \ not self.lib.ssp.cur.meta.vessel or \ not self.lib.ssp.cur.meta.institution: msg = "Survey, vessel, and institution metadata are mandatory for NCEI export.\n\n" \ "To fix the issue:\n" \ "- Load the profile (if not already loaded)\n" \ "- Set the missing values using the Metadata button on the Editor tool bar\n" # noinspection PyCallByClass QtWidgets.QMessageBox.warning(self, "Export warning", msg, QtWidgets.QMessageBox.Ok) skip_export = True continue # special case for Fugro ISS format with NCEI format if self.lib.ssp.cur.meta.probe_type == Dicts.probe_types['ISS']: logger.info("special case: NCEI and ISS format") if force_writer_instrument_for_next_casts is None: msg = "Enter the instrument type and model \n(if you don't know, leave it blank):" instrument, flag = self.lib.cb.ask_text_with_flag("ISS for NCEI", msg, flag_label="Apply to all the next profiles") logger.debug("user input for ISS: %s, %r" % (instrument, flag)) # if empty, we just use the sensor type if instrument is None or instrument == "": instrument = self.lib.ssp.cur.meta.sensor if flag: # to skip the user dialog for next casts force_writer_instrument_for_next_casts = instrument else: force_writer_instrument_for_next_casts = None custom_writer_instrument = instrument else: # user asked to apply to all the next profiles custom_writer_instrument = force_writer_instrument_for_next_casts if skip_export: all_exported = False continue self.progress.start(text="Exporting profile #%02d" % pk) try: self.progress.update(value=60) self.lib.export_data(data_paths=output_folders, data_formats=self.selected_writers, custom_writer_instrument=custom_writer_instrument) except RuntimeError as e: self.progress.end() msg = "Issue in exporting the data for profile #%02d.\nReason: %s" % (pk, e) # noinspection PyCallByClass QtWidgets.QMessageBox.critical(self, "Export error", msg, QtWidgets.QMessageBox.Ok) continue self.progress.end() # opening the output folder if export_open_folder: for output_folder in output_folders.values(): if output_folder not in opened_folders: Helper.explore_folder(output_folder) opened_folders.append(output_folder) if all_exported: msg = "Profiles successfully exported!" # noinspection PyCallByClass QtWidgets.QMessageBox.information(self, "Export profile", msg, QtWidgets.QMessageBox.Ok) else: msg = "At least one profile had issues in being exported!" # noinspection PyCallByClass QtWidgets.QMessageBox.warning(self, "Export profile", msg, QtWidgets.QMessageBox.Ok) self.accept()
def _click_scan_designated(self, version): """abstract the grid qa calling mechanism""" # sanity checks # - version if not isinstance(version, int): raise RuntimeError("passed invalid type for version: %s" % type(version)) if version not in [ 2, ]: raise RuntimeError("passed invalid Designated Scan version: %s" % version) if len(self.prj.s57_list) == 0: raise RuntimeError("the S57 list is empty") if len(self.prj.grid_list) == 0: raise RuntimeError("the grid list is empty") self.parent_win.change_info_url( Helper(lib_info=lib_info).web_url( suffix="survey_designated_scan_%d" % version)) # check for user input as neighborhood check if version == 2: self.neighborhood_dsv2 = self.set_neighborhood_dsv2.isChecked() else: # this case should be never reached after the sanity checks raise RuntimeError("unknown Designated Scan's version: %s" % version) # for each file in the project grid list msg = "Flagged features per input:\n" s57_list = self.prj.s57_list grid_list = self.prj.grid_list opened_folders = list() for i, s57_file in enumerate(s57_list): # print("s57: %s" % s57_file) # we want to be sure that the label is based on the name of the new file input self.prj.clear_survey_label() try: self.prj.read_feature_file(feature_path=s57_file) except Exception as e: # noinspection PyCallByClass QtWidgets.QMessageBox.critical( self, "Error", "While reading s57 file, %s" % e, QtWidgets.QMessageBox.Ok) self.parent_win.progress.setValue(100) return for j, grid_file in enumerate(grid_list): idx = (j + 1) * (i * len(s57_list)) total = len(s57_list) * len(grid_list) # switcher between different versions of find fliers if version == 2: self._scan_designated(grid_file=grid_file, version=version, idx=idx, total=total) else: # this case should be never reached after the sanity checks raise RuntimeError("unknown Grid QA version: %s" % version) logger.info("survey label: %s" % self.prj.survey_label) # open the output folder (if not already open) if self.prj.save_designated(): if self.prj.designated_output_folder not in opened_folders: self.prj.open_designated_output_folder() opened_folders.append( self.prj.designated_output_folder) msg += "- %s (S57: %s): %d\n" % \ (self.prj.cur_grid_basename, self.prj.cur_s57_basename, self.prj.number_of_designated()) # close the grid file self.prj.close_cur_grid() # noinspection PyCallByClass QtWidgets.QMessageBox.information(self, "Designated Scan v%d" % version, msg, QtWidgets.QMessageBox.Ok)
def _click_sbdare_export(self, version): """abstract the SBDARE export calling mechanism""" # sanity checks # - version if not isinstance(version, int): raise RuntimeError("passed invalid type for version: %s" % type(version)) if version not in [5]: raise RuntimeError("passed invalid Feature Scan version: %s" % version) # - list of grids (although the buttons should be never been enabled without grids) if len(self.prj.s57_list) == 0: raise RuntimeError("the S57 list is empty") self.parent_win.change_info_url( Helper(lib_info=lib_info).web_url( suffix="survey_sbdare_export_%d" % version)) # for each file in the project grid list msg = "Exported SBDARE features per input:\n" s57_list = self.prj.s57_list opened_folders = list() total = len(s57_list) for i, s57_file in enumerate(s57_list): self.parent_win.progress.start( title="SBDARE export v.%d" % version, text="Data loading [%d/%d]" % (i + 1, total), init_value=10) # we want to be sure that the label is based on the name of the new file input self.prj.clear_survey_label() self.parent_win.progress.update(value=20, text="SBDARE export v%d [%d/%d]" % (version, i + 1, total)) # switcher between different versions of SBDARE export if version in [5]: self._sbdare_export(feature_file=s57_file, version=version, idx=(i + 1), total=len(s57_list)) else: # this case should be never reached after the sanity checks raise RuntimeError("unknown SBDARE export version: %s" % version) self.parent_win.progress.update(value=40, text="SBDARE export v%d [%d/%d]" % (version, i + 1, total)) # export the flagged features logger.debug('exporting SBDARE features ...') saved = self.prj.save_sbdare() logger.debug('exporting SBDARE features: done') msg += "- %s: %d\n" % (self.prj.cur_s57_basename, self.prj.number_of_sbdare_features()) # open the output folder (if not already open) if saved: if self.prj.sbdare_output_folder not in opened_folders: self.prj.open_sbdare_output_folder() opened_folders.append(self.prj.sbdare_output_folder) warnings = self.prj.sbdare_warnings() msg += "\nWarnings: %d\n" % len(warnings) logger.info("Warnings: %d" % len(warnings)) for idx, warning in enumerate(warnings): logger.debug("#%02d: %s" % (idx, warning)) if idx == 9: msg += "- ... \n\n" \ "The remaining warnings are listed in the Command Shell.\n" continue if idx > 9: continue msg += "- %s\n" % warning self.parent_win.progress.end() # noinspection PyCallByClass QtWidgets.QMessageBox.information(self, "SBDARE export v%d" % version, msg, QtWidgets.QMessageBox.Ok)
def click_open_manual(cls): logger.debug("open manual") Helper.explore_folder("https://www.hydroffice.org/manuals/qctools/user_manual_survey_valsou_checks.html")
def open_output_folder(self) -> None: if self.output_folder: Helper.explore_folder(str(self.output_folder)) else: logger.warning('unable to define the output folder to open')
def _click_valsou_check(self, version): """abstract the feature scan calling mechanism""" # sanity checks # - version if not isinstance(version, int): raise RuntimeError("passed invalid type for version: %s" % type(version)) if version not in [7, ]: raise RuntimeError("passed invalid VALSOU check version: %s" % version) # - list of grids (although the buttons should be never been enabled without grids) if len(self.prj.s57_list) == 0: raise RuntimeError("the S57 list is empty") if len(self.prj.grid_list) == 0: raise RuntimeError("the grid list is empty") self.parent_win.change_info_url(Helper(lib_info=lib_info).web_url(suffix="survey_valsou_check_%d" % version)) # for each file in the project grid list msg = "Flagged features per input pair:\n" s57_list = self.prj.s57_list grid_list = self.prj.grid_list opened_folders = list() for i, s57_file in enumerate(s57_list): # print("s57: %s" % s57_file) # we want to be sure that the label is based on the name of the new file input self.prj.clear_survey_label() try: self.prj.read_feature_file(feature_path=s57_file) except Exception as e: # noinspection PyCallByClass QtWidgets.QMessageBox.critical(self, "Error", "While reading s57 file, %s" % e, QtWidgets.QMessageBox.Ok) self.parent_win.progress.setValue(100) return for j, grid_file in enumerate(grid_list): idx = (j + 1)*(i*len(s57_list)) total = len(s57_list)*len(grid_list) # switcher between different versions of find fliers if version in [7, ]: self._valsou_check(grid_file=grid_file, version=version, idx=idx, total=total) else: # this case should be never reached after the sanity checks raise RuntimeError("unknown VALSOU check version: %s" % version) logger.info("survey label: %s" % self.prj.survey_label) # de-confliction if version == 7 and self.set_overlap_fsv7.isChecked(): self.parent_win.progress.start(title="VALSOU check v.%d" % version, text="Deconflicting [%d/%d]" % (idx, total), init_value=90) self.prj.valsou_check_deconflict_v7() self.parent_win.progress.end() # open the output folder (if not already open) if self._export_valsou_check(): if self.prj.valsou_output_folder not in opened_folders: self.prj.open_valsou_output_folder() opened_folders.append(self.prj.valsou_output_folder) if self.prj.valsou_out_of_bbox: msg += "- %s VS. %s: %s\n" % (self.prj.cur_s57_basename, self.prj.cur_grid_basename, "no overlap") else: msg += "- %s VS. %s: %d\n" % (self.prj.cur_s57_basename, self.prj.cur_grid_basename, self.prj.number_of_valsou_features()) # close the grid file self.prj.close_cur_grid() # noinspection PyCallByClass QtWidgets.QMessageBox.information(self, "VALSOU check v%d" % version, msg, QtWidgets.QMessageBox.Ok)
def timestamp(self): return Helper.timestamp()
else: for bag_with_fliers in bag_with_fliers_list: report += (" . %s, %s\n" % (bag_with_fliers, nr_fliers_per_bag_dict[bag_with_fliers])) report += "- crashes while processing: %d/%d\n" % (len(bag_crash_list), nr_bag_todo) for bag_crash in bag_crash_list: report += (" . %s\n" % bag_crash) logger.info("\n%s" % report) # save on disk a final report if write_report_on_disk: report_path = os.path.join(output_folder, "Batch_Find_Fliers_Results.txt") try: # Trapping Exceptions like OSError (File permissions) with open(report_path, "w") as fod: fod.write(report) except Exception as e: logger.warning("issue in saving report on disk: %s (%s)" % (report_path, e)) # open the output folder if open_output_folder: Helper.explore_folder(output_folder) logger.debug("DONE!")
def open_bagchecks_output_folder(self): logger.info("open %s" % self.bagchecks_output_folder) Helper.explore_folder(self.bagchecks_output_folder)
def open_folder(self): Helper.explore_folder(self.data_folder)
def open_system_noaa_support_folder(cls): if cls.system_noaa_support_folder_present(): Helper.explore_folder(cls.system_noaa_support_folder()) return
def noaa_support_install(self): logger.debug("installing NOAA Support Files") noaa_support = NOAASupport(app_info=self._ai, lib_info=self._li, progress=QtProgress(parent=self)) msg = "The batch file must be executed as administrator\n" \ "with all other users logged off the system.\n" \ "Once executed, follow the instructions in the Windows shell.\n\n" \ "Do you want to continue with the installation?" # noinspection PyCallByClass,PyArgumentList ret = QtWidgets.QMessageBox.information( self, "Install files", msg, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if ret == QtWidgets.QMessageBox.No: return if not noaa_support.system_noaa_support_folder_present(): msg = "The support folder is not present!\n" \ "Did you copy it? Go to step #2." # noinspection PyCallByClass,PyArgumentList QtWidgets.QMessageBox.information(self, "Install files", msg, QtWidgets.QMessageBox.Ok) return if not noaa_support.system_batch_file_exists(): msg = "The batch file does not exist!\n" \ "Did you execute steps #1 and #2? Try to execute them again." # noinspection PyCallByClass,PyArgumentList QtWidgets.QMessageBox.information(self, "Install files", msg, QtWidgets.QMessageBox.Ok) return if not Helper.is_user_admin(): msg = "The app was not executed as admin!\n\n" \ "Do you want that this app executes the batch file?\n\n" \ "You will be prompted for permissions.\n" # noinspection PyCallByClass,PyArgumentList ret = QtWidgets.QMessageBox.information( self, "Install files", msg, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if ret == QtWidgets.QMessageBox.No: msg = "You have then two alternatives:\n" \ "#1: Close the app and re-open it using the option \"Run as administrator\"\n" \ "or:\n" \ "#2: Manually run as administrator the batch file at:\n" \ "- %s\n\n" \ "For option #2, do you want that the appFMG_8880" \ " open the folder with the batch file?" \ % (noaa_support.system_batch_file()) # noinspection PyCallByClass,PyArgumentList ret = QtWidgets.QMessageBox.information( self, "Install files", msg, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if ret == QtWidgets.QMessageBox.Yes: noaa_support.open_system_noaa_support_folder() return installed = noaa_support.exec_system_batch() if installed: msg = "Follow the instruction in the windows shell!" # noinspection PyCallByClass,PyArgumentList QtWidgets.QMessageBox.information(self, "Windows shell", msg, QtWidgets.QMessageBox.Ok) return else: msg = "Unable to install the support files.\n" \ "Try to manually run as administrator the batch file at: %s\n\n" \ % (noaa_support.system_batch_file()) # noinspection PyCallByClass,PyArgumentList QtWidgets.QMessageBox.critical(self, "Error", msg, QtWidgets.QMessageBox.Ok) noaa_support.open_system_noaa_support_folder() return else: installed = noaa_support.exec_system_batch() if installed: msg = "Success!" # noinspection PyCallByClass,PyArgumentList QtWidgets.QMessageBox.information(self, "Installation done!", msg, QtWidgets.QMessageBox.Ok) return else: msg = "Unable to install the support files.\n" \ "Try to manually run as administrator the batch file at: %s\n\n" \ % (noaa_support.system_batch_file()) # noinspection PyCallByClass,PyArgumentList QtWidgets.QMessageBox.critical(self, "Error", msg, QtWidgets.QMessageBox.Ok) noaa_support.open_system_noaa_support_folder() return