• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Open-MSS / MSS / 12771370806

14 Jan 2025 03:51PM UTC coverage: 70.949% (+0.1%) from 70.852%
12771370806

push

github

web-flow
mscolab version history changed default to "All Changes" and is always uptodate  (#2585)

3 of 3 new or added lines in 1 file covered. (100.0%)

4 existing lines in 1 file now uncovered.

14629 of 20619 relevant lines covered (70.95%)

0.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.78
/mslib/msui/mscolab_version_history.py
1
# -*- coding: utf-8 -*-
2
"""
3

4
    mslib.msui.mscolab_change_history
5
    ~~~~~~~~~~~~~~~~~~~~~
6

7
    Mscolab change history window to display the change history of the flight path so that users
8
    can revert back to a previous version
9

10
    This file is part of MSS.
11

12
    :copyright: 2020 Tanish Grover
13
    :copyright: Copyright 2020-2024 by the MSS team, see AUTHORS.
14
    :license: APACHE-2.0, see LICENSE for details.
15

16
    Licensed under the Apache License, Version 2.0 (the "License");
17
    you may not use this file except in compliance with the License.
18
    You may obtain a copy of the License at
19

20
       http://www.apache.org/licenses/LICENSE-2.0
21

22
    Unless required by applicable law or agreed to in writing, software
23
    distributed under the License is distributed on an "AS IS" BASIS,
24
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25
    See the License for the specific language governing permissions and
26
    limitations under the License.
27
"""
28
from datetime import datetime
1✔
29
import json
1✔
30

31
import requests
1✔
32
from urllib.parse import urljoin, urlencode
1✔
33
from PyQt5 import QtCore, QtWidgets, QtGui
1✔
34

35
from mslib.utils.verify_user_token import verify_user_token
1✔
36
from mslib.msui.flighttrack import WaypointsTableModel
1✔
37
from mslib.msui.qt5 import ui_mscolab_version_history as ui
1✔
38
from mslib.utils.qt import show_popup
1✔
39
from mslib.utils.config import config_loader
1✔
40
from mslib.utils.time import utc_to_local_datetime
1✔
41

42

43
class MSColabVersionHistory(QtWidgets.QMainWindow, ui.Ui_MscolabVersionHistory):
1✔
44
    """Derives QMainWindow to provide some common functionality to all
45
       MSUI view windows.
46
    """
47
    name = "MSColab Version History Window"
1✔
48
    identifier = None
1✔
49
    viewCloses = QtCore.pyqtSignal(name="viewCloses")
1✔
50
    reloadWindows = QtCore.pyqtSignal(name="reloadWindows")
1✔
51

52
    def __init__(self, token, op_id, user, operation_name, conn, parent=None,
1✔
53
                 mscolab_server_url=config_loader(dataset="default_MSCOLAB")):
54
        """
55
        token: access_token
56
        op_id: operation id
57
        user: logged in user
58
        operation_name: name of operation,
59
        conn: socket connection
60
        parent: parent of widget
61
        mscolab_server_url: server url of mscolab
62
        """
63
        super().__init__(parent)
1✔
64
        self.setupUi(self)
1✔
65
        # Initialise Variables
66
        self.token = token
1✔
67
        self.op_id = op_id
1✔
68
        self.user = user
1✔
69
        self.operation_name = operation_name
1✔
70
        self.conn = conn
1✔
71
        self.mscolab_server_url = mscolab_server_url
1✔
72

73
        # Event handlers
74
        self.conn.signal_reload.connect(self.handle_refresh)
1✔
75
        self.checkoutBtn.clicked.connect(self.handle_undo)
1✔
76
        self.nameVersionBtn.clicked.connect(self.handle_named_version)
1✔
77
        self.deleteVersionNameBtn.clicked.connect(self.handle_delete_version_name)
1✔
78
        self.versionFilterCB.currentIndexChanged.connect(lambda: self.load_all_changes())
1✔
79
        self.changes.currentItemChanged.connect(self.preview_change)
1✔
80
        # Setup UI
81
        self.deleteVersionNameBtn.setVisible(False)
1✔
82
        self.set_label_text()
1✔
83
        self.set_change_list_style()
1✔
84
        self.toggle_version_buttons(False)
1✔
85
        self.load_current_waypoints()
1✔
86
        self.load_all_changes()
1✔
87

88
    def set_label_text(self):
1✔
89
        self.usernameLabel.setText(f"Logged in: {self.user['username']}")
1✔
90
        self.operationNameLabel.setText(f"Operation: {self.operation_name}")
1✔
91

92
    def set_change_list_style(self):
1✔
93
        palette = self.changes.palette()
1✔
94
        self.changes.setStyleSheet(f"""
1✔
95
            QListWidget::item {{
96
                border-bottom: 1px solid #222;
97
            }}
98
            QListWidget::item:selected {{
99
                background-color: {palette.highlight().color().name()};
100
                color: {palette.highlightedText().color().name()};
101
            }}
102
        """)
103

104
    def toggle_version_buttons(self, state):
1✔
105
        self.checkoutBtn.setEnabled(state)
1✔
106
        self.nameVersionBtn.setEnabled(state)
1✔
107

108
    def load_current_waypoints(self):
1✔
109
        if verify_user_token(self.mscolab_server_url, self.token):
1✔
110
            data = {
1✔
111
                "token": self.token,
112
                "op_id": self.op_id
113
            }
114
            url = urljoin(self.mscolab_server_url, 'get_operation_by_id')
1✔
115
            res = requests.get(url, data=data, timeout=tuple(config_loader(dataset="MSCOLAB_timeout")))
1✔
116
            if res.text != "False":
1✔
117
                xml_content = json.loads(res.text)["content"]
1✔
118
                waypoint_model = WaypointsTableModel(name="Current Waypoints", xml_content=xml_content)
1✔
119
                self.currentWaypointsTable.setModel(waypoint_model)
1✔
120
            else:
121
                # this triggers disconnect
122
                self.conn.signal_reload.emit(self.op_id)
×
123
        else:
124
            # this triggers disconnect
125
            self.conn.signal_reload.emit(self.op_id)
×
126

127
    def load_all_changes(self):
1✔
128
        """
129
        get changes from api, clear listwidget, render them to ui
130
        """
131
        if verify_user_token(self.mscolab_server_url, self.token):
1✔
132
            data = {
1✔
133
                "token": self.token,
134
                "op_id": self.op_id
135
            }
136
            named_version_only = False
1✔
137
            if self.versionFilterCB.currentIndex() == 1:
1✔
UNCOV
138
                named_version_only = True
×
139
            query_string = urlencode({"named_version": named_version_only})
1✔
140
            url_path = f'get_all_changes?{query_string}'
1✔
141
            url = urljoin(self.mscolab_server_url, url_path)
1✔
142
            r = requests.get(url, data=data, timeout=tuple(config_loader(dataset="MSCOLAB_timeout")))
1✔
143
            if r.text != "False":
1✔
144
                changes = json.loads(r.text)["changes"]
1✔
145
                self.changes.clear()
1✔
146
                for change in changes:
1✔
147
                    created_at = datetime.fromisoformat(change["created_at"])
1✔
148
                    local_time = utc_to_local_datetime(created_at)
1✔
149
                    date = local_time.strftime('%d/%m/%Y')
1✔
150
                    time = local_time.strftime('%I:%M %p')
1✔
151
                    item_text = f'{change["username"]} made change on {date} at {time}'
1✔
152
                    if change["version_name"] is not None:
1✔
153
                        item_text = f'{change["version_name"]}\n{item_text}'
1✔
154
                    item = QtWidgets.QListWidgetItem(item_text, parent=self.changes)
1✔
155
                    item.id = change["id"]
1✔
156
                    item.version_name = change["version_name"]
1✔
157
                    self.changes.addItem(item)
1✔
158
            else:
159
                # this triggers disconnect
160
                self.conn.signal_reload.emit(self.op_id)
×
161
        else:
162
            # this triggers disconnect
163
            self.conn.signal_reload.emit(self.op_id)
×
164

165
    def preview_change(self, current_item, previous_item):
1✔
166
        if verify_user_token(self.mscolab_server_url, self.token):
1✔
167
            font = QtGui.QFont()
1✔
168
            if previous_item is not None:
1✔
169
                previous_item.setFont(font)
1✔
170

171
            if current_item is None:
1✔
172
                self.changePreviewTable.setModel(None)
1✔
173
                self.deleteVersionNameBtn.setVisible(False)
1✔
174
                self.toggle_version_buttons(False)
1✔
175
                return
1✔
176

177
            font.setBold(True)
1✔
178
            current_item.setFont(font)
1✔
179
            data = {
1✔
180
                "token": self.token,
181
                "ch_id": current_item.id
182
            }
183
            url = urljoin(self.mscolab_server_url, 'get_change_content')
1✔
184
            res = requests.get(url, data=data, timeout=tuple(config_loader(dataset="MSCOLAB_timeout")))
1✔
185
            if res.text != "False":
1✔
186
                res = res.json()
1✔
187
                waypoint_model = WaypointsTableModel(xml_content=res["content"])
1✔
188
                self.changePreviewTable.setModel(waypoint_model)
1✔
189
                if current_item.version_name is not None:
1✔
190
                    self.deleteVersionNameBtn.setVisible(True)
×
191
                else:
192
                    self.deleteVersionNameBtn.setVisible(False)
1✔
193
                self.toggle_version_buttons(True)
1✔
194
            else:
195
                # this triggers disconnect
196
                self.conn.signal_reload.emit(self.op_id)
×
197
        else:
198
            # this triggers disconnect
199
            self.conn.signal_reload.emit(self.op_id)
×
200

201
    def request_set_version_name(self, version_name, ch_id):
1✔
202
        if verify_user_token(self.mscolab_server_url, self.token):
1✔
203
            data = {
1✔
204
                "token": self.token,
205
                "version_name": version_name,
206
                "ch_id": ch_id,
207
                "op_id": self.op_id
208
            }
209
            url = urljoin(self.mscolab_server_url, 'set_version_name')
1✔
210
            res = requests.post(url, data=data, timeout=tuple(config_loader(dataset="MSCOLAB_timeout")))
1✔
211
            return res
1✔
212
        else:
213
            # this triggers disconnect
214
            self.conn.signal_reload.emit(self.op_id)
×
215

216
    def handle_named_version(self):
1✔
217
        if verify_user_token(self.mscolab_server_url, self.token):
1✔
218
            version_name, completed = QtWidgets.QInputDialog.getText(self, 'Version Name Dialog', 'Enter version name:')
1✔
219
            if completed is True:
1✔
220
                if len(version_name) > 255 or len(version_name) == 0:
1✔
221
                    show_popup(self, "Error", "Version name length has to be between 1 and 255")
×
222
                    return
×
223
                selected_item = self.changes.currentItem()
1✔
224
                res = self.request_set_version_name(version_name, selected_item.id)
1✔
225
                if res.text != "False":
1✔
226
                    res = res.json()
1✔
227
                    if res["success"] is True:
1✔
228
                        item_text = selected_item.text().split('\n')[-1]
1✔
229
                        new_text = f"{version_name}\n{item_text}"
1✔
230
                        selected_item.setText(new_text)
1✔
231
                        selected_item.version_name = version_name
1✔
232
                        self.deleteVersionNameBtn.setVisible(True)
1✔
233
                    else:
234
                        show_popup(self, "Error", res["message"])
×
235
                else:
236
                    # this triggers disconnect
237
                    self.conn.signal_reload.emit(self.op_id)
×
238
        else:
239
            # this triggers disconnect
240
            self.conn.signal_reload.emit(self.op_id)
×
241

242
    def handle_delete_version_name(self):
1✔
243
        if verify_user_token(self.mscolab_server_url, self.token):
1✔
244
            selected_item = self.changes.currentItem()
1✔
245
            res = self.request_set_version_name(None, selected_item.id)
1✔
246
            if res.text != "False":
1✔
247
                res = res.json()
1✔
248
                if res["success"] is True:
1✔
249
                    # Remove item if the filter is set to Named version
250
                    if self.versionFilterCB.currentIndex() == 0:
1✔
251
                        self.changes.takeItem(self.changes.currentRow())
1✔
252
                    # Remove name from item
253
                    else:
UNCOV
254
                        item_text = selected_item.text().split('\n')[-1]
×
UNCOV
255
                        selected_item.setText(item_text)
×
UNCOV
256
                        selected_item.version_name = None
×
257
                    self.deleteVersionNameBtn.setVisible(False)
1✔
258
                else:
259
                    show_popup(self, "Error", res["message"])
×
260
            else:
261
                # this triggers disconnect
262
                self.conn.signal_reload.emit(self.op_id)
×
263
        else:
264
            # this triggers disconnect
265
            self.conn.signal_reload.emit(self.op_id)
×
266

267
    def handle_undo(self):
1✔
268
        if verify_user_token(self.mscolab_server_url, self.token):
1✔
269
            qm = QtWidgets.QMessageBox
1✔
270
            ret = qm.question(self, self.tr("Undo"), "Do you want to checkout to this change?", qm.Yes, qm.No)
1✔
271
            if ret == qm.Yes:
1✔
272
                data = {
1✔
273
                    "token": self.token,
274
                    "ch_id": self.changes.currentItem().id
275
                }
276
                url = urljoin(self.mscolab_server_url, 'undo_changes')
1✔
277
                r = requests.post(url, data=data, timeout=tuple(config_loader(dataset="MSCOLAB_timeout")))
1✔
278
                if r.text != "False":
1✔
279
                    # reload windows
280
                    self.reloadWindows.emit()
1✔
281
                    self.load_current_waypoints()
1✔
282
                    self.load_all_changes()
1✔
283
                else:
284
                    # this triggers disconnect
285
                    self.conn.signal_reload.emit(self.op_id)
×
286
        else:
287
            # this triggers disconnect
288
            self.conn.signal_reload.emit(self.op_id)
×
289

290
    def handle_refresh(self):
1✔
291
        if verify_user_token(self.mscolab_server_url, self.token):
1✔
292
            self.load_current_waypoints()
1✔
293
            self.load_all_changes()
1✔
294
        else:
295
            # this triggers disconnect
296
            self.conn.signal_reload.emit(self.op_id)
×
297

298
    def closeEvent(self, event):
1✔
299
        self.viewCloses.emit()
1✔
300
        event.accept()
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc