diff --git a/docs/installation.rst b/docs/installation.rst index 9075d450a..234d6b6c6 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -124,23 +124,9 @@ versions for dependencies. :: (mssenv) $ msui -Update ------- - -Builtin Update +Update Methods .............. -Since version 5.0 we provide a feature for updating MSS by the UI or the command line -After you started the MSS UI it informs you after a while if there is a new update available. -From the command line you can trigger this update feature by :: - - (mssenv) $ msui --update - - - -Other Methods -............. - For updating an existing MSS installation to the current version, it is best to install it into a new environment. If your current version is not far behind the new version you could try the `mamba update mss` as described. diff --git a/mslib/mscolab/app/__init__.py b/mslib/mscolab/app/__init__.py index 374383e1c..1e9fe719e 100644 --- a/mslib/mscolab/app/__init__.py +++ b/mslib/mscolab/app/__init__.py @@ -25,6 +25,7 @@ """ import os +import logging import sqlalchemy from flask_migrate import Migrate @@ -34,7 +35,11 @@ from flask import Flask, url_for from mslib.mscolab.conf import mscolab_settings from flask_sqlalchemy import SQLAlchemy -from mslib.utils import prefix_route +from mslib.utils import prefix_route, release_info + +message, update = release_info.check_for_new_release() +if update: + logging.warning(message) DOCS_SERVER_PATH = os.path.dirname(os.path.abspath(mslib.__file__)) diff --git a/mslib/mscolab/mscolab.py b/mslib/mscolab/mscolab.py index 445dc2f7c..e3b521872 100644 --- a/mslib/mscolab/mscolab.py +++ b/mslib/mscolab/mscolab.py @@ -45,7 +45,6 @@ from mslib.mscolab.server import APP from mslib.mscolab.utils import create_files from mslib.utils import setup_logging -from mslib.utils.qt import Worker, Updater def handle_start(args): @@ -358,7 +357,6 @@ def handle_sso_metadata_init(repo_exists): def main(): parser = argparse.ArgumentParser() parser.add_argument("-v", "--version", help="show version", action="store_true", default=False) - parser.add_argument("--update", help="Updates MSS to the newest version", action="store_true", default=False) subparsers = parser.add_subparsers(help='Available actions', dest='action') @@ -407,16 +405,6 @@ def main(): except git.exc.InvalidGitRepositoryError: repo_exists = False - updater = Updater() - if args.update: - updater.on_update_available.connect(lambda old, new: updater.update_mss()) - updater.on_log_update.connect(lambda s: print(s.replace("\n", ""))) - updater.on_status_update.connect(lambda s: print(s.replace("\n", ""))) - updater.run() - while Worker.workers: - list(Worker.workers)[0].wait() - sys.exit() - if args.action == "start": handle_start(args) diff --git a/mslib/msui/msui.py b/mslib/msui/msui.py index 0a4c4108e..b15bab1aa 100644 --- a/mslib/msui/msui.py +++ b/mslib/msui/msui.py @@ -42,7 +42,6 @@ from mslib.msui import constants from mslib.utils import setup_logging from mslib.msui.icons import icons -from mslib.utils.qt import Worker, Updater from mslib.utils.config import read_config_file from PyQt5 import QtGui, QtCore, QtWidgets @@ -62,7 +61,6 @@ def main(tutorial_mode=False): parser.add_argument("--debug", help="show debugging log messages on console", action="store_true", default=False) parser.add_argument("--logfile", help="Specify logfile location. Set to empty string to disable.", action="store", default=os.path.join(constants.MSUI_CONFIG_PATH, "msui.log")) - parser.add_argument("--update", help="Updates MSS to the newest version", action="store_true", default=False) args = parser.parse_args() @@ -74,16 +72,6 @@ def main(tutorial_mode=False): print("Version:", __version__) sys.exit() - if args.update: - updater = Updater() - updater.on_update_available.connect(lambda old, new: updater.update_mss()) - updater.on_log_update.connect(lambda s: print(s.replace("\n", ""))) - updater.on_status_update.connect(lambda s: print(s.replace("\n", ""))) - updater.run() - while Worker.workers: - list(Worker.workers)[0].wait() - sys.exit() - setup_logging(args) logging.info("MSS Version: %s", __version__) diff --git a/mslib/msui/msui_mainwindow.py b/mslib/msui/msui_mainwindow.py index f1ec9768b..99a0ea409 100644 --- a/mslib/msui/msui_mainwindow.py +++ b/mslib/msui/msui_mainwindow.py @@ -46,11 +46,11 @@ from mslib.msui import flighttrack as ft from mslib.msui import tableview, topview, sideview, linearview from mslib.msui import constants, editor, mscolab -from mslib.msui.updater import UpdaterUI from mslib.plugins.io.csv import load_from_csv, save_to_csv from mslib.msui.icons import icons, python_powered from mslib.utils.qt import get_open_filenames, get_save_filename, show_popup from mslib.utils.config import read_config_file, config_loader +from mslib.utils import release_info from PyQt5 import QtGui, QtCore, QtWidgets from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas @@ -408,6 +408,7 @@ def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) self.lblVersion.setText(f"Version: {__version__}") + self.lblNewVersion.setText(f"{release_info.check_for_new_release()[0]}") self.milestone_url = f'https://github.com/Open-MSS/MSS/issues?q=is%3Aclosed+milestone%3A{__version__[:-1]}' self.lblChanges.setText(f'New Features and Changes') blub = QtGui.QPixmap(python_powered()) @@ -532,10 +533,6 @@ def __init__(self, mscolab_data_dir=None, tutorial_mode=False, *args): self.mscolab.signal_render_new_permission.connect( lambda op_id, path: self.signal_render_new_permission.emit(op_id, path)) - # Don't start the updater during a test run of msui - if "pytest" not in sys.modules: - self.updater = UpdaterUI(self) - self.actionUpdater.triggered.connect(self.updater.show) self.openOperationsGb.hide() def bring_main_window_to_front(self): diff --git a/mslib/msui/qt5/ui_about_dialog.py b/mslib/msui/qt5/ui_about_dialog.py index 32d6a9f15..46725b0ff 100644 --- a/mslib/msui/qt5/ui_about_dialog.py +++ b/mslib/msui/qt5/ui_about_dialog.py @@ -2,9 +2,10 @@ # Form implementation generated from reading ui file 'ui_about_dialog.ui' # -# Created by: PyQt5 UI code generator 5.12.3 +# Created by: PyQt5 UI code generator 5.15.9 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. from PyQt5 import QtCore, QtGui, QtWidgets @@ -13,7 +14,7 @@ class Ui_AboutMSUIDialog(object): def setupUi(self, AboutMSUIDialog): AboutMSUIDialog.setObjectName("AboutMSUIDialog") - AboutMSUIDialog.resize(1052, 600) + AboutMSUIDialog.resize(1052, 771) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -82,6 +83,9 @@ def setupUi(self, AboutMSUIDialog): self.lblChanges.setObjectName("lblChanges") self.horizontalLayout_2.addWidget(self.lblChanges) self.verticalLayout_2.addLayout(self.horizontalLayout_2) + self.lblNewVersion = QtWidgets.QLabel(AboutMSUIDialog) + self.lblNewVersion.setObjectName("lblNewVersion") + self.verticalLayout_2.addWidget(self.lblNewVersion) spacerItem2 = QtWidgets.QSpacerItem(20, 10, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) self.verticalLayout_2.addItem(spacerItem2) self.lblLicense = QtWidgets.QLabel(AboutMSUIDialog) @@ -109,7 +113,7 @@ def setupUi(self, AboutMSUIDialog): self.verticalLayout.addLayout(self.verticalLayout_2) self.retranslateUi(AboutMSUIDialog) - self.btOK.clicked.connect(AboutMSUIDialog.accept) + self.btOK.clicked.connect(AboutMSUIDialog.accept) # type: ignore QtCore.QMetaObject.connectSlotsByName(AboutMSUIDialog) def retranslateUi(self, AboutMSUIDialog): @@ -120,25 +124,26 @@ def retranslateUi(self, AboutMSUIDialog): self.textBrowser.setHtml(_translate("AboutMSUIDialog", "\n" "\n" -"

Please read the reference documentation:

\n" -"


\n" -"

Bauer, R., Grooß, J.-U., Ungermann, J., Bär, M., Geldenhuys, M., and Hoffmann, L.: The Mission Support

\n" -"

System (MSS v7.0.4) and its use in planning for the SouthTRAC aircraft campaign, Geosci.

\n" -"

Model Dev., 15, 8983–8997, https://doi.org/10.5194/gmd-15-8983-2022, 2022.

\n" -"


\n" -"


\n" -"

Rautenhaus, M., Bauer, G., and Doernbrack, A.: A web service based tool to plan

\n" -"

atmospheric research flights, Geosci. Model Dev., 5,55-71, https://doi.org/10.5194/gmd-5-55-2012, 2012.

\n" -"


\n" -"

and the paper\'s Supplement (which includes a tutorial) before using the application. The documents are available at:

\n" -"


\n" -"

* http://www.geosci-model-dev.net/5/55/2012/gmd-5-55-2012.pdf

\n" -"

* http://www.geosci-model-dev.net/5/55/2012/gmd-5-55-2012-supplement.pdf

\n" -"

\n" -"

When using this software, please be so kind and acknowledge its use by citing the above mentioned reference documentation in publications, presentations, reports, etc. that you create. Thank you very much.

")) +"\n" +"

Please read the reference documentation:

\n" +"


\n" +"

Bauer, R., Grooß, J.-U., Ungermann, J., Bär, M., Geldenhuys, M., and Hoffmann, L.: The Mission Support

\n" +"

System (MSS v7.0.4) and its use in planning for the SouthTRAC aircraft campaign, Geosci.

\n" +"

Model Dev., 15, 8983–8997, https://doi.org/10.5194/gmd-15-8983-2022, 2022.

\n" +"


\n" +"


\n" +"

Rautenhaus, M., Bauer, G., and Doernbrack, A.: A web service based tool to plan

\n" +"

atmospheric research flights, Geosci. Model Dev., 5,55-71, https://doi.org/10.5194/gmd-5-55-2012, 2012.

\n" +"


\n" +"

and the paper\'s Supplement (which includes a tutorial) before using the application. The documents are available at:

\n" +"


\n" +"

* http://www.geosci-model-dev.net/5/55/2012/gmd-5-55-2012.pdf

\n" +"

* http://www.geosci-model-dev.net/5/55/2012/gmd-5-55-2012-supplement.pdf

\n" +"

\n" +"

When using this software, please be so kind and acknowledge its use by citing the above mentioned reference documentation in publications, presentations, reports, etc. that you create. Thank you very much.

")) self.lblVersion.setText(_translate("AboutMSUIDialog", "Version: --VERSION--")) self.lblChanges.setText(_translate("AboutMSUIDialog", "Changes: --CHANGES--")) + self.lblNewVersion.setText(_translate("AboutMSUIDialog", "Check for new Version: --NEW VERSION--")) self.lblLicense.setText(_translate("AboutMSUIDialog", "License: Apache License Version 2.0")) self.lblCopyright.setText(_translate("AboutMSUIDialog", "Copyright 2008-2014: Deutsches Zentrum fuer Luft- und Raumfahrt e.V.\n" "Copyright 2011-2014: Marc Rautenhaus\n" diff --git a/mslib/msui/qt5/ui_mainwindow.py b/mslib/msui/qt5/ui_mainwindow.py index 2b8056f50..ae68dfe21 100644 --- a/mslib/msui/qt5/ui_mainwindow.py +++ b/mslib/msui/qt5/ui_mainwindow.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'mslib/msui/ui/ui_mainwindow.ui' +# Form implementation generated from reading ui file 'ui_mainwindow.ui' # -# Created by: PyQt5 UI code generator 5.15.7 +# Created by: PyQt5 UI code generator 5.15.9 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -253,7 +253,6 @@ def setupUi(self, MSUIMainWindow): self.menuFile.addAction(self.actionQuit) self.menuHelp.addAction(self.actionShortcuts) self.menuHelp.addAction(self.actionMSColabHelp) - self.menuHelp.addAction(self.actionUpdater) self.menuHelp.addAction(self.actionOnlineHelp) self.menuHelp.addAction(self.actionAboutMSUI) self.menuHelp.addAction(self.actionSearch) diff --git a/mslib/msui/qt5/ui_updater_dialog.py b/mslib/msui/qt5/ui_updater_dialog.py deleted file mode 100644 index bf60607af..000000000 --- a/mslib/msui/qt5/ui_updater_dialog.py +++ /dev/null @@ -1,65 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'ui_updater_dialog.ui' -# -# Created by: PyQt5 UI code generator 5.12.3 -# -# WARNING! All changes made in this file will be lost! - - -from PyQt5 import QtCore, QtGui, QtWidgets - - -class Ui_Updater(object): - def setupUi(self, Updater): - Updater.setObjectName("Updater") - Updater.setWindowModality(QtCore.Qt.NonModal) - Updater.resize(854, 338) - self.verticalLayout = QtWidgets.QVBoxLayout(Updater) - self.verticalLayout.setObjectName("verticalLayout") - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.labelVersion = QtWidgets.QLabel(Updater) - self.labelVersion.setObjectName("labelVersion") - self.horizontalLayout.addWidget(self.labelVersion) - self.btUpdate = QtWidgets.QPushButton(Updater) - self.btUpdate.setEnabled(False) - self.btUpdate.setObjectName("btUpdate") - self.horizontalLayout.addWidget(self.btUpdate) - self.btRestart = QtWidgets.QPushButton(Updater) - self.btRestart.setEnabled(False) - self.btRestart.setObjectName("btRestart") - self.horizontalLayout.addWidget(self.btRestart) - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout.addItem(spacerItem) - self.label = QtWidgets.QLabel(Updater) - self.label.setOpenExternalLinks(True) - self.label.setObjectName("label") - self.horizontalLayout.addWidget(self.label) - self.verticalLayout.addLayout(self.horizontalLayout) - self.statusLabel = QtWidgets.QLabel(Updater) - self.statusLabel.setObjectName("statusLabel") - self.verticalLayout.addWidget(self.statusLabel) - self.output = QtWidgets.QPlainTextEdit(Updater) - font = QtGui.QFont() - font.setFamily("Sans Serif") - font.setStyleStrategy(QtGui.QFont.PreferDefault) - self.output.setFont(font) - self.output.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap) - self.output.setReadOnly(True) - self.output.setPlainText("") - self.output.setCenterOnScroll(False) - self.output.setObjectName("output") - self.verticalLayout.addWidget(self.output) - - self.retranslateUi(Updater) - QtCore.QMetaObject.connectSlotsByName(Updater) - - def retranslateUi(self, Updater): - _translate = QtCore.QCoreApplication.translate - Updater.setWindowTitle(_translate("Updater", "Updater")) - self.labelVersion.setText(_translate("Updater", "Newest Version: x.x.x")) - self.btUpdate.setText(_translate("Updater", "Update")) - self.btRestart.setText(_translate("Updater", "Restart MSUI")) - self.label.setText(_translate("Updater", "

Manual update instructions

")) - self.statusLabel.setText(_translate("Updater", "Nothing to do")) diff --git a/mslib/msui/ui/ui_about_dialog.ui b/mslib/msui/ui/ui_about_dialog.ui index c82ba442f..484e6912f 100644 --- a/mslib/msui/ui/ui_about_dialog.ui +++ b/mslib/msui/ui/ui_about_dialog.ui @@ -7,7 +7,7 @@ 0 0 1052 - 600 + 771 @@ -139,23 +139,23 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Please read the reference documentation:</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif';"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Bauer, R., Grooß, J.-U., Ungermann, J., Bär, M., Geldenhuys, M., and Hoffmann, L.: The Mission Support</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">System (MSS v7.0.4) and its use in planning for the SouthTRAC aircraft campaign, Geosci.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Model Dev., 15, 8983–8997, https://doi.org/10.5194/gmd-15-8983-2022, 2022.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif';"><br /></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif';"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Rautenhaus, M., Bauer, G., and Doernbrack, A.: A web service based tool to plan</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">atmospheric research flights, Geosci. Model Dev., 5,55-71, https://doi.org/10.5194/gmd-5-55-2012, 2012.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif';"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">and the paper's Supplement (which includes a tutorial) before using the application. The documents are available at:</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif';"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> * http://www.geosci-model-dev.net/5/55/2012/gmd-5-55-2012.pdf</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> * http://www.geosci-model-dev.net/5/55/2012/gmd-5-55-2012-supplement.pdf</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> When using this software, please be so kind and acknowledge its use by citing the above mentioned reference documentation in publications, presentations, reports, etc. that you create. Thank you very much.</span></p></body></html> +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">Please read the reference documentation:</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">Bauer, R., Grooß, J.-U., Ungermann, J., Bär, M., Geldenhuys, M., and Hoffmann, L.: The Mission Support</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">System (MSS v7.0.4) and its use in planning for the SouthTRAC aircraft campaign, Geosci.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">Model Dev., 15, 8983–8997, https://doi.org/10.5194/gmd-15-8983-2022, 2022.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><br /></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">Rautenhaus, M., Bauer, G., and Doernbrack, A.: A web service based tool to plan</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">atmospheric research flights, Geosci. Model Dev., 5,55-71, https://doi.org/10.5194/gmd-5-55-2012, 2012.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">and the paper's Supplement (which includes a tutorial) before using the application. The documents are available at:</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;"> * http://www.geosci-model-dev.net/5/55/2012/gmd-5-55-2012.pdf</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;"> * http://www.geosci-model-dev.net/5/55/2012/gmd-5-55-2012-supplement.pdf</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;"> </span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;"> When using this software, please be so kind and acknowledge its use by citing the above mentioned reference documentation in publications, presentations, reports, etc. that you create. Thank you very much.</span></p></body></html> @@ -193,6 +193,13 @@ p, li { white-space: pre-wrap; } + + + + Check for new Version: --NEW VERSION-- + + + diff --git a/mslib/msui/ui/ui_mainwindow.ui b/mslib/msui/ui/ui_mainwindow.ui index 9f6209063..737992ff4 100644 --- a/mslib/msui/ui/ui_mainwindow.ui +++ b/mslib/msui/ui/ui_mainwindow.ui @@ -403,7 +403,6 @@ Double click a operation to activate and view its description. - diff --git a/mslib/msui/ui/ui_updater_dialog.ui b/mslib/msui/ui/ui_updater_dialog.ui deleted file mode 100644 index 611b55719..000000000 --- a/mslib/msui/ui/ui_updater_dialog.ui +++ /dev/null @@ -1,107 +0,0 @@ - - - Updater - - - Qt::NonModal - - - - 0 - 0 - 854 - 338 - - - - Updater - - - - - - - - Newest Version: x.x.x - - - - - - - false - - - Update - - - - - - - false - - - Restart MSUI - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - <html><head/><body><p><a href="https://mss.readthedocs.io/en/stable/installation.html#install"><span style=" text-decoration: underline; color:#0000ff;">Manual update instructions</span></a></p></body></html> - - - true - - - - - - - - - Nothing to do - - - - - - - - Sans Serif - PreferDefault - - - - QPlainTextEdit::NoWrap - - - true - - - - - - false - - - - - - - - diff --git a/mslib/msui/updater.py b/mslib/msui/updater.py deleted file mode 100644 index b33822e94..000000000 --- a/mslib/msui/updater.py +++ /dev/null @@ -1,81 +0,0 @@ -""" - mslib.msui.updater - ~~~~~~~~~~~~~~~~~~~ - - This UI interface for the updater util, handles detection of an outdated mss version and automatic updating. - - This file is part of MSS. - - :copyright: Copyright 2021 May Bär - :copyright: Copyright 2021-2024 by the MSS team, see AUTHORS. - :license: APACHE-2.0, see LICENSE for details. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -""" -import logging - -from PyQt5 import QtCore, QtWidgets, QtGui - -from mslib.utils.qt import Updater -from mslib.msui.qt5 import ui_updater_dialog -from mslib import __version__ - - -class UpdaterUI(QtWidgets.QDialog, ui_updater_dialog.Ui_Updater): - """ - Checks for a newer versions of MSS and installs it. - Only works if conda is installed and MSS isn't inside a git repo. - """ - on_update_available = QtCore.pyqtSignal([str, str]) - - def __init__(self, parent=None): - super().__init__(parent) - self.setupUi(self) - self.hide() - self.labelVersion.setText(f"Newest Version: {__version__}") - self.updater = None - try: - self.updater = Updater() - except ModuleNotFoundError as ex: - # https://github.com/Open-MSS/MSS/issues/1409#issuecomment-1200946622 - logging.error("unexpected error in updater: %s %s in version: %s", type(ex), ex, __version__) - - if self.updater is not None: - monospace = QtGui.QFont("non-existent") - monospace.setStyleHint(QtGui.QFont.Monospace) - self.output.setFont(monospace) - self.updater.on_log_update.connect(lambda s: (self.output.insertPlainText(s), - self.output.verticalScrollBar().setSliderPosition( - self.output.verticalScrollBar().maximum()))) - self.updater.on_status_update.connect(self.statusLabel.setText) - self.updater.on_update_available.connect(self.notify_on_update) - self.updater.on_update_finished.connect(lambda: self.btRestart.setEnabled(True)) - self.btUpdate.clicked.connect(lambda: (self.updater.update_mss(), self.btUpdate.setEnabled(False))) - self.btRestart.clicked.connect(self.updater._restart_msui) - self.updater.run() - - def notify_on_update(self, old, new): - """ - Asks the user if they want to update MSS - """ - self.btUpdate.setEnabled(True) - self.labelVersion.setText(f"Newest Version: {new}") - if not self.updater.is_git_env: - ret = QtWidgets.QMessageBox.information( - self, "Mission Support System", - f"MSS can be updated from {old} to {new}\nDo you want to update?", - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, - QtWidgets.QMessageBox.No) - if ret == QtWidgets.QMessageBox.Yes: - self.show() - self.btUpdate.click() diff --git a/mslib/mswms/app/__init__.py b/mslib/mswms/app/__init__.py index dd7885619..bcc1d7dd1 100644 --- a/mslib/mswms/app/__init__.py +++ b/mslib/mswms/app/__init__.py @@ -25,11 +25,17 @@ """ import os +import logging import mslib from flask import Flask, url_for from mslib.mswms.gallery_builder import STATIC_LOCATION -from mslib.utils import prefix_route +from mslib.utils import prefix_route, release_info + + +message, update = release_info.check_for_new_release() +if update: + logging.warning(message) DOCS_SERVER_PATH = os.path.dirname(os.path.abspath(mslib.__file__)) diff --git a/mslib/mswms/mswms.py b/mslib/mswms/mswms.py index 987d6a7a2..b73fe9d5b 100644 --- a/mslib/mswms/mswms.py +++ b/mslib/mswms/mswms.py @@ -31,7 +31,6 @@ from mslib import __version__ from mslib.utils import setup_logging -from mslib.utils.qt import Updater, Worker from mslib.mswms.wms import app as application @@ -45,7 +44,6 @@ def main(): parser.add_argument("--debug", help="show debugging log messages on console", action="store_true", default=False) parser.add_argument("--logfile", help="If set to a name log output goes to that file", dest="logfile", default=None) - parser.add_argument("--update", help="Updates MSS to the newest version", action="store_true", default=False) subparsers = parser.add_subparsers(help='Available actions', dest='action') gallery = subparsers.add_parser("gallery", help="Subcommands surrounding the gallery") @@ -87,16 +85,6 @@ def main(): print("Version:", __version__) sys.exit() - updater = Updater() - if args.update: - updater.on_update_available.connect(lambda old, new: updater.update_mss()) - updater.on_log_update.connect(lambda s: print(s.replace("\n", ""))) - updater.on_status_update.connect(lambda s: print(s.replace("\n", ""))) - updater.run() - while Worker.workers: - list(Worker.workers)[0].wait() - sys.exit() - setup_logging(args) # keep the import after the version check. This creates all layers. diff --git a/mslib/utils/qt.py b/mslib/utils/qt.py index 8503b80c4..fd6441fb3 100644 --- a/mslib/utils/qt.py +++ b/mslib/utils/qt.py @@ -2,7 +2,7 @@ """ mslib.utils.msui_qt - ~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~ This module helps with qt @@ -30,14 +30,13 @@ import re import platform import sys -import subprocess import traceback from fslib.fs_filepicker import getSaveFileName, getOpenFileName, getExistingDirectory from PyQt5 import QtCore, QtWidgets, QtGui # noqa from mslib.utils.config import config_loader -from mslib.utils import FatalUserError, subprocess_startupinfo +from mslib.utils import FatalUserError def get_open_filename_qt(*args): @@ -431,159 +430,6 @@ def _update_gui(): window.requestUpdate() -class Updater(QtCore.QObject): - """ - Checks for a newer versions of MSS and provide functions to install it asynchronously. - Only works if conda is installed. - """ - on_update_available = QtCore.pyqtSignal([str, str]) - on_update_finished = QtCore.pyqtSignal() - on_log_update = QtCore.pyqtSignal([str]) - on_status_update = QtCore.pyqtSignal([str]) - - def __init__(self, parent=None): - super().__init__(parent) - self.is_git_env = False - self.new_version = None - self.old_version = None - # we are using the installer version of the env - self.conda_prefix = os.getenv("CONDA_PREFIX") - if self.conda_prefix is not None: - self.command = os.path.join(self.conda_prefix, 'bin', "conda") - mamba_cmd = os.path.join(self.conda_prefix, 'bin', 'mamba') - # Check if mamba is installed in the env - try: - subprocess.run([mamba_cmd], startupinfo=subprocess_startupinfo(), - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - self.command = mamba_cmd - except FileNotFoundError: - pass - else: - self.command = "conda" - - # pyqtSignals don't work without an application eventloop running - if QtCore.QCoreApplication.startingUp(): - self.on_update_available = NonQtCallback() - self.on_update_finished = NonQtCallback() - self.on_log_update = NonQtCallback() - self.on_status_update = NonQtCallback() - - def run(self): - """ - Starts the updater process - """ - Worker.create(self._check_version) - - def _check_version(self): - """ - Checks if conda search has a newer version of MSS - """ - # Don't notify on updates if mss is in a git repo, as you are most likely a developer - try: - git = subprocess.run(["git", "rev-parse", "--is-inside-work-tree"], - startupinfo=subprocess_startupinfo(), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, encoding="utf8") - if "true" in git.stdout: - self.is_git_env = True - except FileNotFoundError: - pass - - # Return if conda is not installed. conda is fallback of mamba - try: - subprocess.run([self.command], startupinfo=subprocess_startupinfo(), - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - except FileNotFoundError: - return - - self.on_status_update.emit("Checking for updates...") - - # Check if "search mss" yields a higher version than the currently running one - search = self._execute_command(f"{self.command} search mss") - self.new_version = search.split("\n")[-2].split()[1] - c_list = self._execute_command(f"{self.command} list -f mss") - self.old_version = c_list.split("\n")[-2].split()[1] - if any(c.isdigit() for c in self.new_version): - if self.new_version > self.old_version: - self.on_status_update.emit("Your version of MSS is outdated!") - self.on_update_available.emit(self.old_version, self.new_version) - else: - self.on_status_update.emit("Your MSS is up to date.") - - def _restart_msui(self): - """ - Restart msui with all the same parameters, not entirely - safe in case parameters change in higher versions, or while debugging - """ - command = [sys.executable.split(os.sep)[-1]] + sys.argv - if os.name == "nt" and not command[1].endswith(".py"): - command[1] += "-script.py" - os.execv(sys.executable, command) - - def _try_updating(self): - """ - Execute 'conda/mamba install mss=newest python -y' and return if it worked or not - """ - self.on_status_update.emit("Trying to update MSS...") - self._execute_command(f"{self.command} install mss={self.new_version} python -y") - if self._verify_newest_mss(): - return True - - return False - - def _update_mss(self): - """ - Try to install MSS' newest version - """ - if not self._try_updating(): - self.on_status_update.emit("Update failed. Please try it manually or by creating a new environment!") - else: - self.on_update_finished.emit() - self.on_status_update.emit("Update successful. Please restart MSS.") - - def _verify_newest_mss(self): - """ - Return if the newest mss exists in the environment or not - """ - verify = self._execute_command(f"{self.command} list -f mss") - if self.new_version in verify: - return True - - return False - - def _execute_command(self, command): - """ - Handles proper execution of conda subprocesses and logging - """ - process = subprocess.Popen(command.split(), - startupinfo=subprocess_startupinfo(), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - encoding="utf8") - self.on_log_update.emit(" ".join(process.args) + "\n") - - text = "" - for line in process.stdout: - self.on_log_update.emit(line) - text += line - - # Happens e.g. on connection errors during installation attempts - if "An unexpected error has occurred. Conda has prepared the above report" in text: - raise RuntimeError("Something went wrong! Can't safely continue to update.") - else: - return text - - def update_mss(self): - """ - Installs the newest mss version - """ - def on_failure(e: Exception): - self.on_status_update.emit("Update failed, please do it manually.") - self.on_log_update.emit(str(e)) - - Worker.create(self._update_mss, on_failure=on_failure) - - class NonQtCallback: """ Small mock of pyqtSignal to work without the QT eventloop. diff --git a/mslib/utils/release_info.py b/mslib/utils/release_info.py new file mode 100644 index 000000000..c2a5a64ea --- /dev/null +++ b/mslib/utils/release_info.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +""" + + mslib.utils.release_info + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Shows by a github API call information about the latest release + + This file is part of MSS. + + :copyright: Copyright 2024 Reimar Bauer + :copyright: Copyright 2024 by the MSS team, see AUTHORS. + :license: APACHE-2.0, see LICENSE for details. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import datetime +import logging +import requests + +from mslib.version import __version__ as installed_version + + +def get_latest_release(): + # GitHub API URL for the MSS Release + url = "https://api.github.com/repos/Open-MSS/MSS/releases/latest" + + try: + # Make a GET request to the GitHub API + response = requests.get(url, timeout=(1, 1)) + response.raise_for_status() # Raise an error for non-200 status codes + + # Extract the JSON response + release_data = response.json() + + # Extract the latest release tag and name + latest_release = { + 'tag_name': release_data['tag_name'], + 'release_name': release_data['name'], + 'published_at': release_data['published_at'], + 'url': release_data['html_url'] + } + return latest_release + + except requests.exceptions.RequestException as e: + logging.debug(f"Error fetching release data: {e}") + return None + + +def check_for_new_release(): + no_new_release_found = f"{datetime.date.today()}: No new release found." + try: + latest_release = get_latest_release() + except TimeoutError as e: + logging.debug(f"Error fetching release data: {e}") + latest_release = None + + if latest_release is None or latest_release['tag_name'] == installed_version: + return no_new_release_found, False + + github_url = f'{latest_release["url"]}' + return ' | '.join([ + f"New release found: {latest_release['release_name']} ({latest_release['tag_name']})", + f"Published at: {latest_release['published_at']}", + f"Release URL: {github_url}", + ]), True diff --git a/tests/_test_msui/test_updater.py b/tests/_test_msui/test_updater.py deleted file mode 100644 index 452ee88d7..000000000 --- a/tests/_test_msui/test_updater.py +++ /dev/null @@ -1,135 +0,0 @@ -# -*- coding: utf-8 -*- -""" - - tests._test_msui.test_updater - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This module provides pytest functions to tests msui.updater - - This file is part of MSS. - - :copyright: Copyright 2021 May Bär - :copyright: Copyright 2021-2024 by the MSS team, see AUTHORS. - :license: APACHE-2.0, see LICENSE for details. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -""" -import mock -import pytest -from PyQt5 import QtWidgets - -from mslib.msui.updater import UpdaterUI, Updater -from mslib.utils.qt import Worker - - -def no_conda(args=None, **named_args): - raise FileNotFoundError - - -class SubprocessDifferentVersionMock: - def __init__(self, args=None, **named_args): - self.returncode = 0 - self.args = args - if args and "list" in args and "mss" in args: - self.stdout = "*mss 0.0.0\n" - else: - self.stdout = "*mss 999.999.999\n" - - -class SubprocessSameMock: - def __init__(self, args=None, **named_args): - self.stdout = "*mss 999.999.999\n" - self.returncode = 0 - self.args = args - - -@mock.patch("mslib.utils.qt.Worker.start", Worker.run) -class Test_MSS_ShortcutDialog: - @pytest.fixture(autouse=True) - def setup(self, qtbot): - self.updater = Updater() - self.status = "" - self.update_available = False - self.update_finished = False - - def update_signal(old, new): - self.update_available = True - - def update_finished_signal(): - self.update_finished = True - - def status_signal(s): - self.status = s - - self.updater.on_update_available.connect(update_signal) - self.updater.on_status_update.connect(status_signal) - self.updater.on_update_finished.connect(update_finished_signal) - yield - - @mock.patch("subprocess.Popen", new=SubprocessDifferentVersionMock) - @mock.patch("subprocess.run", new=SubprocessDifferentVersionMock) - def test_update_recognised(self): - self.updater.run() - - assert self.updater.new_version == "999.999.999" - assert self.update_available - self.updater.new_version = "0.0.0" - - self.updater.update_mss() - assert self.status == "Update successful. Please restart MSS." - assert self.update_finished - - @mock.patch("subprocess.Popen", new=SubprocessSameMock) - @mock.patch("subprocess.run", new=SubprocessSameMock) - def test_no_update(self): - self.updater.run() - assert self.status == "Your MSS is up to date." - assert not self.update_available - assert not self.update_finished - - @mock.patch("subprocess.Popen", new=SubprocessDifferentVersionMock) - @mock.patch("subprocess.run", new=SubprocessDifferentVersionMock) - def test_update_failed(self): - self.updater.run() - assert self.updater.new_version == "999.999.999" - assert self.update_available - self.updater.new_version = "1000.1000.1000" - self.updater.update_mss() - assert self.status == "Update failed. Please try it manually or " \ - "by creating a new environment!" - - @mock.patch("subprocess.Popen", new=no_conda) - @mock.patch("subprocess.run", new=no_conda) - def test_no_conda(self): - self.updater.run() - assert self.updater.new_version is None and self.updater.old_version is None - assert not self.update_available - assert not self.update_finished - - @mock.patch("subprocess.Popen", new=no_conda) - @mock.patch("subprocess.run", new=no_conda) - def test_exception(self): - self.updater.new_version = "999.999.999" - self.updater.old_version = "999.999.999" - self.updater.update_mss() - assert self.status == "Update failed, please do it manually." - assert not self.update_finished - - @mock.patch("subprocess.Popen", new=SubprocessSameMock) - @mock.patch("subprocess.run", new=SubprocessSameMock) - @mock.patch("PyQt5.QtWidgets.QMessageBox.information", return_value=QtWidgets.QMessageBox.Yes) - def test_ui(self, mock): - ui = UpdaterUI() - ui.updater.on_update_available.emit("", "") - assert ui.statusLabel.text() == "Update successful. Please restart MSS." - assert ui.btRestart.isEnabled()