ProjectFlask/FlaskMigrateExtension/MigrateSummaryDialog.py

changeset 35
65a377b7a52c
child 36
548dea93941c
equal deleted inserted replaced
34:a91c6a1eb23f 35:65a377b7a52c
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog showing a summary of all created.migrations.
8 """
9
10 from PyQt5.QtCore import pyqtSlot, Qt, QProcess, QEventLoop, QTimer
11 from PyQt5.QtGui import QGuiApplication
12 from PyQt5.QtWidgets import (
13 QDialog, QDialogButtonBox, QAbstractButton, QTreeWidgetItem
14 )
15
16 from E5Gui import E5MessageBox
17
18 from .Ui_MigrateSummaryDialog import Ui_MigrateSummaryDialog
19
20
21 class MigrateSummaryDialog(QDialog, Ui_MigrateSummaryDialog):
22 """
23 Class implementing a dialog showing a summary of all created.migrations.
24 """
25 def __init__(self, project, migrateProject, migrations="", parent=None):
26 """
27 Constructor
28
29 @param migrateProject reference to the migrate project extension
30 @type MigrateProject
31 @param project reference to the project object
32 @type Project
33 @param migrations directory path containing the migrations
34 @type str
35 @param parent reference to the parent widget
36 @type QWidget
37 """
38 super(MigrateSummaryDialog, self).__init__(parent)
39 self.setupUi(self)
40
41 self.__refreshButton = self.buttonBox.addButton(
42 self.tr("Refresh"), QDialogButtonBox.ActionRole)
43 self.__refreshButton.clicked.connect(self.showSummary)
44
45 self.__project = project
46 self.__migrateProject = migrateProject
47 self.__migrations = migrations
48 self.__process = None
49
50 def showSummary(self):
51 """
52 Public method to show the migrations summary.
53 """
54 workdir, env = self.__project.prepareRuntimeEnvironment()
55 if env is not None:
56 self.show()
57 self.raise_()
58
59 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
60 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
61 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True)
62 self.buttonBox.button(QDialogButtonBox.Cancel).setFocus(
63 Qt.OtherFocusReason)
64 QGuiApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
65
66 command = self.__project.getFlaskCommand()
67
68 self.__process = QProcess()
69 self.__process.setProcessEnvironment(env)
70 self.__process.setWorkingDirectory(workdir)
71
72 args = ["db", "history", "--indicate-current"]
73 if self.__migrations:
74 args += ["--directory", self.__migrations]
75
76 QGuiApplication.setOverrideCursor(Qt.WaitCursor)
77 self.__process.start(command, args)
78 ok = self.__process.waitForStarted(10000)
79 if ok:
80 ok = self.__process.waitForFinished(10000)
81 if ok:
82 out = str(self.__process.readAllStandardOutput(), "utf-8")
83 self.__processOutput(out)
84 else:
85 E5MessageBox.critical(
86 None,
87 self.tr("Migrations Summary"),
88 self.tr("""The Flask process did not finish within"""
89 """ 10 seconds."""))
90 else:
91 E5MessageBox.critical(
92 None,
93 self.tr("Migrations Summary"),
94 self.tr("""The Flask process could not be started."""))
95 for column in range(self.summaryWidget.columnCount()):
96 self.summaryWidget.resizeColumnToContents(column)
97 QGuiApplication.restoreOverrideCursor()
98
99 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False)
100 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
101 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
102 self.buttonBox.button(QDialogButtonBox.Close).setFocus(
103 Qt.OtherFocusReason)
104
105 def __processOutput(self, output):
106 """
107 Private method to process the flask output and populate the summary
108 list.
109
110 @param output output of the flask process
111 @type str
112 """
113 self.summaryWidget.clear()
114 self.upgradeButton.setEnabled(False)
115 self.downgradeButton.setEnabled(False)
116
117 lines = output.splitlines()
118 for line in lines:
119 isCurrent = False
120 oldRev, rest = line.split("->")
121 rest, message = rest.split(",", 1)
122 newRev, *labels = rest.split()
123 if labels:
124 labelList = [
125 label.replace("(", "").replace(")", "")
126 for label in labels
127 ]
128 labelsStr = ", ".join(labelList)
129 if "current" in labelList:
130 isCurrent = True
131 else:
132 labelsStr = ""
133
134 itm = QTreeWidgetItem(self.summaryWidget, [
135 oldRev.strip(),
136 newRev.strip(),
137 message.strip(),
138 labelsStr,
139 ])
140 if isCurrent:
141 font = itm.font(0)
142 font.setBold(True)
143 for column in range(self.summaryWidget.columnCount()):
144 itm.setFont(column, font)
145
146 @pyqtSlot()
147 def on_summaryWidget_itemSelectionChanged(self):
148 """
149 Private slot to handle the selection of an entry.
150 """
151 enable = bool(self.summaryWidget.selectedItems())
152 self.upgradeButton.setEnabled(enable)
153 self.downgradeButton.setEnabled(enable)
154
155 @pyqtSlot()
156 def on_upgradeButton_clicked(self):
157 """
158 Private slot to upgrade to the selected revision
159 """
160 itm = self.summaryWidget.selectedItems()[0]
161 rev = itm.text(1)
162 self.__migrateProject.upgradeDatabase(revision=rev)
163 self.showSummary()
164
165 @pyqtSlot()
166 def on_downgradeButton_clicked(self):
167 """
168 Private slot to downgrade to the selected revision
169 """
170 itm = self.summaryWidget.selectedItems()[0]
171 rev = itm.text(1)
172 self.__migrateProject.downgradeDatabase(revision=rev)
173 self.showSummary()
174
175 @pyqtSlot(QAbstractButton)
176 def on_buttonBox_clicked(self, button):
177 """
178 Private slot handling a button press of the button box
179
180 @param button reference to the pressed button
181 @type QAbstractButton
182 """
183 if button is self.buttonBox.button(QDialogButtonBox.Cancel):
184 self.__cancelProcess()
185
186 @pyqtSlot()
187 def __cancelProcess(self):
188 """
189 Private slot to terminate the current process.
190 """
191 if (
192 self.__process is not None and
193 self.__process.state() != QProcess.NotRunning
194 ):
195 self.__process.terminate()
196 QTimer.singleShot(2000, self.__process.kill)
197 self.__process.waitForFinished(3000)
198
199 self.__process = None

eric ide

mercurial