ProjectPyramid/MigrateSummaryDialog.py

branch
eric7
changeset 148
dcbd3a96f03c
child 156
62170c2682a3
equal deleted inserted replaced
147:eb28b4b6f7f5 148:dcbd3a96f03c
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2020 - 2021 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 PyQt6.QtCore import pyqtSlot, Qt, QProcess, QEventLoop, QTimer
11 from PyQt6.QtGui import QGuiApplication
12 from PyQt6.QtWidgets import (
13 QDialog, QDialogButtonBox, QAbstractButton, QTreeWidgetItem,
14 QAbstractItemView
15 )
16
17 from EricGui.EricOverrideCursor import EricOverrideCursor, EricOverridenCursor
18 from EricWidgets import EricMessageBox
19
20 from .Ui_MigrateSummaryDialog import Ui_MigrateSummaryDialog
21
22
23 class MigrateSummaryDialog(QDialog, Ui_MigrateSummaryDialog):
24 """
25 Class implementing a dialog showing a summary of all created.migrations.
26 """
27 def __init__(self, project, parent=None):
28 """
29 Constructor
30
31 @param project reference to the project object
32 @type Project
33 @param parent reference to the parent widget
34 @type QWidget
35 """
36 super().__init__(parent)
37 self.setupUi(self)
38
39 self.__refreshButton = self.buttonBox.addButton(
40 self.tr("Refresh"), QDialogButtonBox.ButtonRole.ActionRole)
41 self.__refreshButton.clicked.connect(self.showSummary)
42
43 self.__project = project
44
45 self.__process = None
46 self.__currentItemIndex = 1000000
47 self.__currentRevision = ""
48
49 def showSummary(self):
50 """
51 Public method to show the migrations summary.
52 """
53 projectPath = self.__project.projectPath()
54
55 self.show()
56 self.raise_()
57
58 self.buttonBox.button(
59 QDialogButtonBox.StandardButton.Close).setEnabled(False)
60 self.buttonBox.button(
61 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
62 self.buttonBox.button(
63 QDialogButtonBox.StandardButton.Cancel).setEnabled(True)
64 self.buttonBox.button(
65 QDialogButtonBox.StandardButton.Cancel).setFocus(
66 Qt.FocusReason.OtherFocusReason)
67 QGuiApplication.processEvents(
68 QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
69
70 command = self.__project.getAlembicCommand()
71
72 self.__process = QProcess()
73 self.__process.setWorkingDirectory(projectPath)
74
75 args = ["-c", "development.ini", "history", "--indicate-current"]
76
77 with EricOverrideCursor():
78 self.__process.start(command, args)
79 ok = self.__process.waitForStarted(10000)
80 if ok:
81 ok = self.__process.waitForFinished(10000)
82 if ok:
83 out = str(self.__process.readAllStandardOutput(),
84 "utf-8")
85 self.__processOutput(out)
86 self.__selectItem(self.__currentRevision)
87 else:
88 with EricOverridenCursor():
89 EricMessageBox.critical(
90 None,
91 self.tr("Migrations Summary"),
92 self.tr("""The 'alembic' process did not finish"""
93 """ within 10 seconds."""))
94 else:
95 with EricOverridenCursor():
96 EricMessageBox.critical(
97 None,
98 self.tr("Migrations Summary"),
99 self.tr("""The 'alembic' process could not be"""
100 """ started."""))
101 for column in range(self.summaryWidget.columnCount()):
102 self.summaryWidget.resizeColumnToContents(column)
103
104 self.buttonBox.button(
105 QDialogButtonBox.StandardButton.Cancel).setEnabled(False)
106 self.buttonBox.button(
107 QDialogButtonBox.StandardButton.Close).setEnabled(True)
108 self.buttonBox.button(
109 QDialogButtonBox.StandardButton.Close).setDefault(True)
110 self.buttonBox.button(
111 QDialogButtonBox.StandardButton.Close).setFocus(
112 Qt.FocusReason.OtherFocusReason)
113
114 def __processOutput(self, output):
115 """
116 Private method to process the flask output and populate the summary
117 list.
118
119 @param output output of the flask process
120 @type str
121 """
122 self.summaryWidget.clear()
123 self.upDownButton.setEnabled(False)
124 self.__currentItemIndex = 1000000
125 self.__currentRevision = ""
126
127 lines = output.splitlines()
128 for line in lines:
129 isCurrent = False
130 oldRev, rest = line.split("->")
131 rest, message = rest.split(",", 1)
132 newRev, *labels = rest.split()
133 if labels:
134 labelList = [
135 label.replace("(", "").replace(")", "")
136 for label in labels
137 ]
138 labelsStr = ", ".join(labelList)
139 if "current" in labelList:
140 isCurrent = True
141 else:
142 labelsStr = ""
143
144 itm = QTreeWidgetItem(self.summaryWidget, [
145 oldRev.strip(),
146 newRev.strip(),
147 message.strip(),
148 labelsStr,
149 ])
150 if isCurrent:
151 font = itm.font(0)
152 font.setBold(True)
153 for column in range(self.summaryWidget.columnCount()):
154 itm.setFont(column, font)
155
156 self.__currentItemIndex = (
157 self.summaryWidget.indexOfTopLevelItem(itm)
158 )
159 self.__currentRevision = newRev.strip()
160
161 @pyqtSlot()
162 def on_summaryWidget_itemSelectionChanged(self):
163 """
164 Private slot to handle the selection of an entry.
165 """
166 items = self.summaryWidget.selectedItems()
167 if items:
168 index = self.summaryWidget.indexOfTopLevelItem(items[0])
169 if index < self.__currentItemIndex:
170 self.upDownButton.setText(self.tr("Upgrade"))
171 elif index > self.__currentItemIndex:
172 self.upDownButton.setText(self.tr("Downgrade"))
173 self.upDownButton.setEnabled(index != self.__currentItemIndex)
174 else:
175 self.upDownButton.setEnabled(False)
176
177 @pyqtSlot()
178 def on_upDownButton_clicked(self):
179 """
180 Private slot to upgrade/downgrade to the selected revision.
181 """
182 itm = self.summaryWidget.selectedItems()[0]
183 rev = itm.text(1)
184 if self.upDownButton.text() == self.tr("Upgrade"):
185 self.__project.upgradeDatabase(revision=rev)
186 else:
187 self.__project.downgradeDatabase(revision=rev)
188 self.showSummary()
189
190 @pyqtSlot(QAbstractButton)
191 def on_buttonBox_clicked(self, button):
192 """
193 Private slot handling a button press of the button box.
194
195 @param button reference to the pressed button
196 @type QAbstractButton
197 """
198 if button is self.buttonBox.button(
199 QDialogButtonBox.StandardButton.Cancel
200 ):
201 self.__cancelProcess()
202
203 @pyqtSlot()
204 def __cancelProcess(self):
205 """
206 Private slot to terminate the current process.
207 """
208 if (
209 self.__process is not None and
210 self.__process.state() != QProcess.ProcessState.NotRunning
211 ):
212 self.__process.terminate()
213 QTimer.singleShot(2000, self.__process.kill)
214 self.__process.waitForFinished(3000)
215
216 self.__process = None
217
218 def __selectItem(self, revision):
219 """
220 Private method to select an item given its revision.
221
222 @param revision revision of the item to select
223 @type str
224 """
225 if revision:
226 items = self.summaryWidget.findItems(
227 revision, Qt.MatchFlag.MatchExactly, 1)
228 if items:
229 # select the first item
230 items[0].setSelected(True)
231 self.summaryWidget.scrollToItem(
232 items[0], QAbstractItemView.ScrollHint.PositionAtCenter)

eric ide

mercurial