217 lines
6.9 KiB
Python
217 lines
6.9 KiB
Python
import sys
|
|
from os import getenv, stat
|
|
import hashlib
|
|
import subprocess
|
|
from datetime import datetime
|
|
from contextlib import contextmanager
|
|
|
|
import requests
|
|
from numpy import linspace
|
|
from scipy.interpolate import interp1d
|
|
|
|
from gui_qt.Qt import QtGui, QtWidgets, QtCore
|
|
|
|
|
|
@contextmanager
|
|
def busy_cursor():
|
|
try:
|
|
cursor = QtGui.QCursor(QtCore.Qt.ForbiddenCursor)
|
|
QtWidgets.QApplication.setOverrideCursor(cursor)
|
|
yield
|
|
|
|
finally:
|
|
QtWidgets.QApplication.restoreOverrideCursor()
|
|
|
|
|
|
class RdBuCMap:
|
|
# taken from Excel sheet from colorbrewer.org
|
|
_rdbu = [
|
|
(103, 0, 31),
|
|
(178, 24, 43),
|
|
(214, 96, 77),
|
|
(244, 165, 130),
|
|
(253, 219, 199),
|
|
(247, 247, 247),
|
|
(209, 229, 240),
|
|
(146, 197, 222),
|
|
(67, 147, 195),
|
|
(33, 102, 172),
|
|
(5, 48, 97)
|
|
]
|
|
|
|
def __init__(self, vmin=-1., vmax=1.):
|
|
self.min = vmin
|
|
self.max = vmax
|
|
|
|
self.spline = [interp1d(linspace(self.max, self.min, num=11), [rgb[i] for rgb in RdBuCMap._rdbu])
|
|
for i in range(3)]
|
|
|
|
def color(self, val: float):
|
|
if val > self.max:
|
|
col = QtGui.QColor.fromRgb(*RdBuCMap._rdbu[0])
|
|
elif val < self.min:
|
|
col = QtGui.QColor.fromRgb(*RdBuCMap._rdbu[-1])
|
|
else:
|
|
col = QtGui.QColor.fromRgb(*(float(self.spline[i](val)) for i in range(3)))
|
|
|
|
return col
|
|
|
|
|
|
class UpdateDialog(QtWidgets.QDialog):
|
|
host = 'mirror.infra.pkm'
|
|
bucket = 'nmreval'
|
|
version = 'NMReval-latest-x86_64'
|
|
|
|
def __init__(self, filename: str = None, parent=None):
|
|
super().__init__(parent=parent)
|
|
self._init_ui()
|
|
|
|
if filename is None:
|
|
filename = getenv('APPIMAGE')
|
|
self._appfile = filename
|
|
self._url_zsync = f'http://{self.host}/{self.bucket}/{self.version}.AppImage.zsync'
|
|
|
|
self.process = QtCore.QProcess(self)
|
|
|
|
self.look_for_updates()
|
|
|
|
def _init_ui(self):
|
|
self.setWindowTitle('Updates')
|
|
|
|
layout = QtWidgets.QVBoxLayout()
|
|
|
|
self.label = QtWidgets.QLabel()
|
|
layout.addWidget(self.label)
|
|
|
|
layout.addSpacing(10)
|
|
|
|
self.dialog_button = QtWidgets.QDialogButtonBox()
|
|
self.dialog_button.accepted.connect(self.accept)
|
|
self.dialog_button.rejected.connect(self.reject)
|
|
layout.addWidget(self.dialog_button)
|
|
|
|
self.setLayout(layout)
|
|
|
|
def look_for_updates(self):
|
|
# Download zsync file of latest Appimage, look for SHA-1 hash and compare with hash of AppImage
|
|
m_time_zsync, checksum_zsync = self.get_zsync()
|
|
m_time_file, checksum_file = self.get_appimage_info()
|
|
|
|
if m_time_zsync is None:
|
|
label_text = '<p>Retrieval of version information failed.</p>' \
|
|
'<p>Please try later (or complain to people that it does not work).</p>'
|
|
dialog_bttns = QtWidgets.QDialogButtonBox.Close
|
|
else:
|
|
label_text = f'<p>Found most recent update: {m_time_zsync.strftime("%d %B %Y %H:%M")}</p>'
|
|
|
|
if m_time_file is None:
|
|
label_text += 'No AppImage file found, press Ok to downlaod latest version.'
|
|
dialog_bttns = QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Close
|
|
else:
|
|
label_text += f'<p>Date of used AppImage: {m_time_file.strftime("%d %B %Y %H:%M")}</p>'
|
|
|
|
if not ((checksum_file is not None) and (checksum_zsync is not None)):
|
|
label_text += 'Could not determine if this version is newer, please update manually (if necessary).'
|
|
dialog_bttns = QtWidgets.QDialogButtonBox.Close
|
|
elif checksum_file != checksum_zsync:
|
|
label_text += f'<p>Newer version available. <b>Update?</b></p>'
|
|
dialog_bttns = QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Cancel
|
|
else:
|
|
label_text += f'<p>Version is already the newest</p>'
|
|
dialog_bttns = QtWidgets.QDialogButtonBox.Close
|
|
|
|
self.label.setText(label_text)
|
|
self.dialog_button.setStandardButtons(dialog_bttns)
|
|
|
|
def get_zsync(self):
|
|
m_time_zsync = None
|
|
checksum_zsync = None
|
|
zsync_file = None
|
|
try:
|
|
response = requests.get(self._url_zsync)
|
|
if response.status_code == requests.codes['\o/']:
|
|
zsync_file = response.content
|
|
except Exception as e:
|
|
pass
|
|
|
|
if zsync_file is not None:
|
|
for line in zsync_file.split(b'\n'):
|
|
try:
|
|
kw, val = line.split(b': ')
|
|
if kw == b'MTime':
|
|
m_time_zsync = datetime.strptime(str(val, encoding='utf-8'), '%a, %d %b %Y %H:%M:%S %z').astimezone(None)
|
|
elif kw == b'SHA-1':
|
|
checksum_zsync = str(val, encoding='utf-8')
|
|
|
|
except ValueError:
|
|
# stop when empty line is reached
|
|
break
|
|
|
|
return m_time_zsync, checksum_zsync
|
|
|
|
def get_appimage_info(self):
|
|
if self._appfile is None:
|
|
return None, None
|
|
|
|
stat_mtime = stat(self._appfile).st_mtime
|
|
m_time_file = datetime.fromtimestamp(stat_mtime).replace(microsecond=0)
|
|
with open(self._appfile, 'rb') as f:
|
|
checksum_file = hashlib.sha1(f.read()).hexdigest()
|
|
|
|
return m_time_file, checksum_file
|
|
|
|
def update_appimage(self):
|
|
if self._appfile is None:
|
|
args = [self._url_zsync]
|
|
else:
|
|
args = ['-i', self._appfile, self._url_zsync]
|
|
|
|
self.process.readyReadStandardOutput.connect(self.onReadyReadStandardOutput)
|
|
self.process.readyReadStandardError.connect(self.onReadyReadStandardOutput)
|
|
self.process.start('zsync', args)
|
|
|
|
if not self.process.waitForFinished():
|
|
return False
|
|
|
|
return True
|
|
|
|
def onReadyReadStandardOutput(self):
|
|
result = self.process.readAllStandardOutput().data().decode()
|
|
self.label.setText(result)
|
|
|
|
|
|
def accept(self):
|
|
if self.update_appimage():
|
|
_ = QtWidgets.QMessageBox.information(self, 'Updates', 'Download finished. Execute AppImage for new version.')
|
|
super().accept()
|
|
|
|
|
|
|
|
def open_bug_report():
|
|
form_entries = {
|
|
'description': 'Please state the nature of the medical emergency.',
|
|
'title': 'Everything is awesome?',
|
|
'assign[0]': 'dominik',
|
|
'subscribers[0]': 'dominik',
|
|
'tag': 'nmreval',
|
|
'priority': 'normal',
|
|
'status': 'open',
|
|
}
|
|
full_url = 'https://chaos3.fkp.physik.tu-darmstadt.de/maniphest/task/edit/?'
|
|
|
|
for k, v in form_entries.items():
|
|
full_url += f'{k}={v}&'
|
|
full_url.replace(' ', '+')
|
|
|
|
import webbrowser
|
|
webbrowser.open(full_url)
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
app = QtWidgets.QApplication([])
|
|
w = UpdateDialog()
|
|
w.show()
|
|
|
|
sys.exit(app.exec())
|