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

Open-MSS / MSS / 11571608056

29 Oct 2024 10:07AM UTC coverage: 70.856%. First build
11571608056

Pull #2563

github

web-flow
Merge 28b053c55 into d0b1badcc
Pull Request #2563: Autoplot - Enable mssautoplot for side and linearviews

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

14279 of 20152 relevant lines covered (70.86%)

0.71 hits per line

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

19.9
/mslib/utils/mssautoplot.py
1
"""
2

3
    mslib.utils.mssautoplot
4
    ~~~~~~~~~~~~~~~~~~~~~~~
5

6
    A CLI tool to create for instance a number of the same plots
7
    for several flights or several forecast steps
8

9
    This file is part of MSS.
10

11
    :copyright: Copyright 2022 Sreelakshmi Jayarajan
12
    :copyright: Copyright 2022-2024 by the MSS team, see AUTHORS.
13
    :license: APACHE-2.0, see LICENSE for details.
14

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

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

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

28
import os
1✔
29
import io
1✔
30
import re
1✔
31
import json
1✔
32
import logging
1✔
33
from datetime import datetime, timedelta
1✔
34
from urllib.parse import urljoin
1✔
35

36
import requests
1✔
37
from PyQt5.QtCore import Qt
1✔
38
from PyQt5.QtWidgets import QMessageBox, QProgressDialog
1✔
39
import click
1✔
40
import defusedxml.ElementTree as etree
1✔
41
import PIL.Image
1✔
42
import matplotlib
1✔
43
from fs import open_fs
1✔
44

45
import mslib
1✔
46
import mslib.utils
1✔
47
import mslib.msui
1✔
48
import mslib.msui.mpl_map
1✔
49
import mslib.utils.auth
1✔
50
import mslib.utils.qt
1✔
51
import mslib.utils.thermolib
1✔
52
from mslib.utils.config import config_loader, read_config_file
1✔
53
from mslib.utils.units import units
1✔
54
from mslib.msui.wms_control import MSUIWebMapService
1✔
55
from mslib.msui import constants
1✔
56
from mslib.msui import mpl_qtwidget as qt
1✔
57
from mslib.msui import mpl_pathinteractor as mpath
1✔
58
from mslib.msui import flighttrack as ft
1✔
59
from mslib.utils import config as conf
1✔
60
from mslib.utils.auth import get_auth_from_url_and_name
1✔
61
from mslib.utils.loggerdef import configure_mpl_logger
1✔
62
from mslib.utils.verify_user_token import verify_user_token
1✔
63

64

65
TEXT_CONFIG = {
1✔
66
    "bbox": dict(boxstyle="round", facecolor="white", alpha=0.5, edgecolor="none"), "fontweight": "bold",
67
    "zorder": 4, "fontsize": 6, "clip_on": True}
68

69
mpl_logger = configure_mpl_logger()
1✔
70

71

72
def load_from_ftml(filename):
1✔
73
    """Load a flight track from an XML file at <filename>.
74
    """
75
    _dirname, _name = os.path.split(filename)
1✔
76
    _fs = open_fs(_dirname)
1✔
77
    datasource = _fs.readtext(_name)
1✔
78
    wp_list = ft.load_from_xml_data(datasource)
1✔
79
    now = datetime.now()
1✔
80
    for wp in wp_list:
1✔
81
        wp.utc_time = now
1✔
82
    data_list = [
1✔
83
        (wp.lat, wp.lon, wp.flightlevel, wp.location, wp.comments) for wp in wp_list]
84
    return data_list, wp_list
1✔
85

86

87
def load_from_operation(op_name, msc_url, msc_auth_password, username, password):
1✔
88
    """
89
    Method to load data from an operation in MSColab.
90

91
    Parameters:
92
    :op_name: Name of the operation to load data from
93
    :msc_url: URL of the MS Colab server
94
    :msc_auth_password: Password for MS Colab authentication
95
    :username: Username for authentication
96
    :password: Password for authentication
97

98
    Returns:
99
    Tuple containing a list of data points and a list of Waypoints if successful, None otherwise
100
    """
101
    data = {
×
102
        "email": username,
103
        "password": password
104
    }
105
    session = requests.Session()
×
106
    msc_auth = ("mscolab", msc_auth_password)
×
107
    session.auth = msc_auth
×
108
    session.headers.update({'x-test': 'true'})
×
109
    # ToDp fix config_loader it gets a list of two times the entry
110
    response = session.get(urljoin(msc_url, 'status'), timeout=tuple(config_loader(dataset="MSCOLAB_timeout")[0]))
×
111
    session.close()
×
112
    if response.status_code == 401:
×
113
        logging.error("Error", 'Server authentication data were incorrect.')
×
114
    elif response.status_code == 200:
×
115
        session = requests.Session()
×
116
        session.auth = msc_auth
×
117
        session.headers.update({'x-test': 'true'})
×
118
        url = urljoin(msc_url, "token")
×
119
        try:
×
120
            # ToDp fix config_loader it gets a list of two times the entry
121
            response = session.post(url, data=data, timeout=tuple(config_loader(dataset="MSCOLAB_timeout")[0]))
×
122
            response.raise_for_status()
×
123
        except requests.exceptions.RequestException as ex:
×
124
            logging.error("unexpected error: %s %s %s", type(ex), url, ex)
×
125
            return
×
126
        if response.text != "False":
×
127
            _json = json.loads(response.text)
×
128
            token = _json["token"]
×
129
            msc_url = url
×
130
            op_id = get_op_id(msc_url=msc_url, token=token, op_name=op_name)
×
131
            xml_data = get_xml_data(msc_url=msc_url, token=token, op_id=op_id)
×
132
            wp_list = ft.load_from_xml_data(xml_data)
×
133
            now = datetime.now()
×
134
            for wp in wp_list:
×
135
                wp.utc_time = now
×
136
            data_list = [
×
137
                (wp.lat, wp.lon, wp.flightlevel, wp.location, wp.comments) for wp in wp_list]
138
            return data_list, wp_list
×
139

140

141
def get_xml_data(msc_url, token, op_id):
1✔
142
    """
143

144
    Parameters:
145
        :msc_url: The URL of the MSColab Server
146
        :token: The user's token for authentication
147
        :op_id: The id of the operation to retrieve
148

149
    Returns:
150
        str: The content of the XML data retrieved from the server
151

152
    """
153
    if verify_user_token(msc_url, token):
×
154
        data = {
×
155
            "token": token,
156
            "op_id": op_id
157
        }
158
        url = urljoin(msc_url, "get_operation_by_id")
×
159
        r = requests.get(url, data=data)
×
160
        if r.text != "False":
×
161
            xml_content = json.loads(r.text)["content"]
×
162
            return xml_content
×
163

164

165
def get_op_id(msc_url, token, op_name):
1✔
166
    """
167
    gets the operation id of the given operation name
168

169
    Parameters:
170
        :msc_url: The URL of the MSColab server
171
        :token: The user token for authentication
172
        :op_name:: The name of the operation to retrieve op_id for
173

174
    Returns:
175
        :op_id: The op_id of the operation with the specified name
176
    """
177
    logging.debug('get_recent_op_id')
×
178
    if verify_user_token(msc_url, token):
×
179
        """
×
180
        get most recent operation's op_id
181
        """
182
        skip_archived = config_loader(dataset="MSCOLAB_skip_archived_operations")
×
183
        data = {
×
184
            "token": token,
185
            "skip_archived": skip_archived
186
        }
187
        url = urljoin(msc_url, "operations")
×
188
        r = requests.get(url, data=data)
×
189
        if r.text != "False":
×
190
            _json = json.loads(r.text)
×
191
            operations = _json["operations"]
×
192
            for op in operations:
×
193
                if op["path"] == op_name:
×
194
                    return op["op_id"]
×
195

196

197
class Plotting:
1✔
198
    def __init__(self, cpath, msc_url=None, msc_auth_password=None, username=None, password=None, pdlg=None):
1✔
199
        """
200
        Initialize the Plotting object with the provided parameters.
201

202
        Parameters:
203
        :cpath: Path to the configuration file
204
        :msc_url: URL for MSColab service
205
        :msc_auth_password: Authentication password for MSColab service
206
        :username: User's username
207
        :password: User's password
208
        """
209
        read_config_file(cpath)
×
210
        self.pdlg = pdlg
×
211
        self.config = config_loader()
×
212
        self.num_interpolation_points = self.config["num_interpolation_points"]
×
213
        self.num_labels = self.config["num_labels"]
×
214
        self.tick_index_step = self.num_interpolation_points // self.num_labels
×
215
        self.bbox = None
×
216
        flight = self.config["automated_plotting_flights"][0][0]
×
217
        section = self.config["automated_plotting_flights"][0][1]
×
218
        filename = self.config["automated_plotting_flights"][0][3]
×
219
        if self.__class__.__name__ == "TopViewPlotting":
×
220
            try:
×
221
                self.params = mslib.utils.coordinate.get_projection_params(
×
222
                    self.config["predefined_map_sections"][section]["CRS"].lower())
223
            except KeyError as e:
×
224
                print(e)
×
225
                raise SystemExit("Invalid SECTION and/or CRS")
×
226
            self.params["basemap"].update(self.config["predefined_map_sections"][section]["map"])
×
227
            self.bbox_units = self.params["bbox"]
×
228
        if filename != "" and filename == flight:
×
229
            self.read_operation(flight, msc_url, msc_auth_password, username, password)
×
230
        elif filename != "":
×
231
            # Todo add the dir to the file in the mssautoplot.json
232
            dirpath = "./"
×
233
            file_path = os.path.join(dirpath, filename)
×
234
            exists = os.path.exists(file_path)
×
235
            if not exists:
×
236
                print("Filename {} doesn't exist".format(filename))
×
237
                self.pdlg.close()
×
238
                raise SystemExit("Filename {} doesn't exist".format(filename))
×
239
            self.read_ftml(filename)
×
240

241
    def read_ftml(self, filename):
1✔
242
        self.wps, self.wp_model_data = load_from_ftml(filename)
×
243
        self.wp_lats, self.wp_lons, self.wp_locs = [[x[i] for x in self.wps] for i in [0, 1, 3]]
×
244
        self.wp_press = [mslib.utils.thermolib.flightlevel2pressure(wp[2] * units.hft).to("Pa").m for wp in self.wps]
×
245
        self.path = [(wp[0], wp[1], datetime.now()) for wp in self.wps]
×
246
        self.vertices = [list(a) for a in (zip(self.wp_lons, self.wp_lats))]
×
247
        self.lats, self.lons = mslib.utils.coordinate.path_points([_x[0] for _x in self.path],
×
248
                                                                  [_x[1] for _x in self.path],
249
                                                                  numpoints=self.num_interpolation_points + 1,
250
                                                                  connection="greatcircle")
251

252
    def read_operation(self, op_name, msc_url, msc_auth_password, username, password):
1✔
253
        self.wps, self.wp_model_data = load_from_operation(op_name, msc_url=msc_url,
×
254
                                                           msc_auth_password=msc_auth_password, username=username,
255
                                                           password=password)
256
        self.wp_lats, self.wp_lons, self.wp_locs = [[x[i] for x in self.wps] for i in [0, 1, 3]]
×
257
        self.wp_press = [mslib.utils.thermolib.flightlevel2pressure(wp[2] * units.hft).to("Pa").m for wp in self.wps]
×
258
        self.path = [(wp[0], wp[1], datetime.now()) for wp in self.wps]
×
259
        self.vertices = [list(a) for a in (zip(self.wp_lons, self.wp_lats))]
×
260
        self.lats, self.lons = mslib.utils.coordinate.path_points([_x[0] for _x in self.path],
×
261
                                                                  [_x[1] for _x in self.path],
262
                                                                  numpoints=self.num_interpolation_points + 1,
263
                                                                  connection="greatcircle")
264

265

266
class TopViewPlotting(Plotting):
1✔
267
    def __init__(self, cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg):
1✔
268
        super(TopViewPlotting, self).__init__(cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg)
×
269
        self.pdlg = pdlg
×
270
        self.myfig = qt.TopViewPlotter()
×
271
        self.myfig.fig.canvas.draw()
×
272
        self.fig, self.ax = self.myfig.fig, self.myfig.ax
×
273
        matplotlib.backends.backend_agg.FigureCanvasAgg(self.fig)
×
274
        self.myfig.init_map(**(self.params["basemap"]))
×
275
        self.plotter = mpath.PathH_Plotter(self.myfig.map)
×
276
        self.username = msc_username
×
277
        self.password = msc_password
×
278
        self.msc_auth = msc_auth_password
×
279
        self.url = msc_url
×
280

281
    def update_path(self, filename=None):
1✔
282
        """
283
        Update the path by reading the FTML data from the given filename
284
         and redrawing the path based on the updated waypoints model data.
285

286
        Parameters:
287
        :filename: The name of the file to read FTML data from.
288

289
        Returns:
290
        None
291
        """
292
        # plot path and label
293
        if filename != "":
×
294
            self.read_ftml(filename)
×
295
        self.fig.canvas.draw()
×
296
        self.plotter.update_from_waypoints(self.wp_model_data)
×
297
        self.plotter.redraw_path(waypoints_model_data=self.wp_model_data)
×
298

299
    def update_path_ops(self, filename=None):
1✔
300
        # plot path and label
301
        if filename != "":
×
302
            self.read_operation(filename, self.url, self.msc_auth, self.username, self.password)
×
303
        self.fig.canvas.draw()
×
304
        self.plotter.update_from_waypoints(self.wp_model_data)
×
305
        self.plotter.redraw_path(waypoints_model_data=self.wp_model_data)
×
306

307
    def draw(self, flight, section, vertical, filename, init_time, time, url, layer, style, elevation, no_of_plots):
1✔
308
        if filename != "" and filename == flight:
×
309
            self.update_path_ops(filename)
×
310
        elif filename != "":
×
311
            self.update_path(filename)
×
312

313
        width, height = self.myfig.get_plot_size_in_px()
×
314
        self.bbox = self.params['basemap']
×
315
        if not init_time:
×
316
            init_time = None
×
317

318
        kwargs = {"layers": [layer],
×
319
                  "styles": [style],
320
                  "time": time,
321
                  "init_time": init_time,
322
                  "exceptions": 'application/vnd.ogc.se_xml',
323
                  "level": elevation,
324
                  "srs": self.config["predefined_map_sections"][section]["CRS"],
325
                  "bbox": (self.bbox['llcrnrlon'], self.bbox['llcrnrlat'],
326
                           self.bbox['urcrnrlon'], self.bbox['urcrnrlat']
327
                          ),
328
                  "format": "image/png",
329
                  "size": (width, height)
330
                }
331

332
        auth_username, auth_password = get_auth_from_url_and_name(url, self.config["MSS_auth"])
×
333
        wms = MSUIWebMapService(url,
×
334
                                username=auth_username,
335
                                password=auth_password,
336
                                version='1.3.0')
337

338
        img = wms.getmap(**kwargs)
×
339
        image_io = io.BytesIO(img.read())
×
340
        img = PIL.Image.open(image_io)
×
341
        self.myfig.draw_image(img)
×
342
        t = str(time)
×
343
        date_time = re.sub(r'\W+', '', t)
×
344
        self.myfig.fig.savefig(f"{flight}_{layer}_{section}_{date_time}_{no_of_plots}_{elevation}.png")
×
345

346

347
class SideViewPlotting(Plotting):
1✔
348
    def __init__(self, cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg):
1✔
349
        # ToDo Implement access to MSColab
NEW
350
        super(SideViewPlotting, self).__init__(cpath, msc_url, msc_auth_password, msc_username, msc_password)
×
351
        self.pdlg = pdlg
×
352
        self.myfig = qt.SideViewPlotter()
×
353
        self.ax = self.myfig.ax
×
354
        self.fig = self.myfig.fig
×
355
        self.tick_index_step = self.num_interpolation_points // self.num_labels
×
356
        self.fig.canvas.draw()
×
357
        matplotlib.backends.backend_agg.FigureCanvasAgg(self.myfig.fig)
×
358
        self.plotter = mpath.PathV_Plotter(self.myfig.ax)
×
NEW
359
        self.username = msc_username
×
NEW
360
        self.password = msc_password
×
NEW
361
        self.msc_auth = msc_auth_password
×
NEW
362
        self.url = msc_url
×
363

364
    def setup(self):
1✔
365
        self.intermediate_indexes = []
×
366
        ipoint = 0
×
367
        for i, (lat, lon) in enumerate(zip(self.lats, self.lons)):
×
368
            if abs(lat - self.wps[ipoint][0]) < 1E-10 and abs(lon - self.wps[ipoint][1]) < 1E-10:
×
369
                self.intermediate_indexes.append(i)
×
370
                ipoint += 1
×
371
            if ipoint >= len(self.wps):
×
372
                break
×
373
        self.myfig.setup_side_view()
×
374
        times = None
×
375
        times_visible = False
×
376
        self.myfig.redraw_xaxis(self.lats, self.lons, times, times_visible)
×
377

378
    def update_path(self, filename=None):
1✔
379
        self.setup()
×
380
        if filename is not None:
×
381
            self.read_ftml(filename)
×
382
        self.fig.canvas.draw()
×
383
        self.plotter.update_from_waypoints(self.wp_model_data)
×
384
        indices = list(zip(self.intermediate_indexes, self.wp_press))
×
385
        self.plotter.redraw_path(vertices=indices,
×
386
                                 waypoints_model_data=self.wp_model_data)
387
        highlight = [[wp[0], wp[1]] for wp in self.wps]
×
388
        self.myfig.draw_vertical_lines(highlight, self.lats, self.lons)
×
389

390
    def update_path_ops(self, filename=None):
1✔
391
        # plot path and label
NEW
392
        if filename != "":
×
NEW
393
            self.read_operation(filename, self.url, self.msc_auth, self.username, self.password)
×
NEW
394
        self.fig.canvas.draw()
×
NEW
395
        self.plotter.update_from_waypoints(self.wp_model_data)
×
NEW
396
        self.plotter.redraw_path(waypoints_model_data=self.wp_model_data)
×
397

398
    def draw(self, flight, section, vertical, filename, init_time, time, url, layer, style, elevation, no_of_plots):
1✔
399
        try:
×
400
            self.update_path(filename)
×
401
        except AttributeError as e:
×
402
            logging.debug(e)
×
403
            raise SystemExit("No FLIGHT Selected")
×
404
        width, height = self.myfig.get_plot_size_in_px()
×
405
        p_bot, p_top = [float(x) * 100 for x in vertical.split(",")]
×
406
        self.bbox = tuple([x for x in (self.num_interpolation_points,
×
407
                          p_bot / 100, self.num_labels, p_top / 100)]
408
                         )
409

410
        if not init_time:
×
411
            init_time = None
×
412

413
        kwargs = {"layers": [layer],
×
414
                  "styles": [style],
415
                  "time": time,
416
                  "init_time": init_time,
417
                  "exceptions": 'application/vnd.ogc.se_xml',
418
                  "srs": "VERT:LOGP",
419
                  "path_str": ",".join(f"{wp[0]:.2f},{wp[1]:.2f}" for wp in self.wps),
420
                  "bbox": self.bbox,
421
                  "format": "image/png",
422
                  "size": (width, height)
423
                }
424
        auth_username, auth_password = get_auth_from_url_and_name(url, self.config["MSS_auth"])
×
425
        wms = MSUIWebMapService(url,
×
426
                                username=auth_username,
427
                                password=auth_password,
428
                                version='1.3.0')
429

430
        img = wms.getmap(**kwargs)
×
431

432
        image_io = io.BytesIO(img.read())
×
433
        img = PIL.Image.open(image_io)
×
434
        self.myfig.draw_image(img)
×
435
        self.myfig.fig.savefig(f"{flight}_{layer}_{no_of_plots}.png", bbox_inches='tight')
×
436

437

438
class LinearViewPlotting(Plotting):
1✔
439
    # ToDo Implement access of MSColab
440
    def __init__(self, cpath, msc_url, msc_auth_password, msc_username, msc_password):
1✔
NEW
441
        super(LinearViewPlotting, self).__init__(cpath, msc_url, msc_auth_password, msc_username, msc_password)
×
442
        self.myfig = qt.LinearViewPlotter()
×
443
        self.ax = self.myfig.ax
×
444
        matplotlib.backends.backend_agg.FigureCanvasAgg(self.myfig.fig)
×
445
        self.fig = self.myfig.fig
×
NEW
446
        self.username = msc_username
×
NEW
447
        self.password = msc_password
×
NEW
448
        self.msc_auth = msc_auth_password
×
NEW
449
        self.url = msc_url
×
450

451
    def setup(self):
1✔
452
        self.bbox = (self.num_interpolation_points,)
×
453
        linearview_size_settings = config_loader(dataset="linearview")
×
454
        settings_dict = {"plot_title_size": linearview_size_settings["plot_title_size"],
×
455
                         "axes_label_size": linearview_size_settings["axes_label_size"]}
456
        self.myfig.set_settings(settings_dict)
×
457
        self.myfig.setup_linear_view()
×
458

459
    def update_path_ops(self, filename=None):
1✔
460
        # plot path and label
NEW
461
        if filename != "":
×
NEW
462
            self.read_operation(filename, self.url, self.msc_auth, self.username, self.password)
×
NEW
463
        self.fig.canvas.draw()
×
NEW
464
        self.plotter.update_from_waypoints(self.wp_model_data)
×
NEW
465
        self.plotter.redraw_path(waypoints_model_data=self.wp_model_data)
×
466

467
    def draw(self):
1✔
468
        for flight, section, vertical, filename, init_time, time in self.config["automated_plotting_flights"]:
×
469
            for url, layer, style in self.config["automated_plotting_lsecs"]:
×
470
                width, height = self.myfig.get_plot_size_in_px()
×
471

472
                if not init_time:
×
473
                    init_time = None
×
474

475
                auth_username, auth_password = get_auth_from_url_and_name(url, self.config["MSS_auth"])
×
476
                wms = MSUIWebMapService(url,
×
477
                                        username=auth_username,
478
                                        password=auth_password,
479
                                        version='1.3.0')
480

481
                path_string = ""
×
482
                for i, wp in enumerate(self.wps):
×
483
                    path_string += f"{wp[0]:.2f},{wp[1]:.2f},{self.wp_press[i]},"
×
484
                path_string = path_string[:-1]
×
485

486
                # retrieve and draw image
487
                kwargs = {"layers": [layer],
×
488
                          "styles": [style],
489
                          "time": time,
490
                          "init_time": init_time,
491
                          "exceptions": 'application/vnd.ogc.se_xml',
492
                          "srs": "LINE:1",
493
                          "path_str": path_string,
494
                          "bbox": self.bbox,
495
                          "format": "text/xml",
496
                          "size": (width, height)
497
                         }
498

499
                xmls = wms.getmap(**kwargs)
×
500

501
                if not isinstance(xmls, list):
×
502
                    xmls = [xmls]
×
503

504
                xml_objects = []
×
505
                for xml_ in xmls:
×
506
                    xml_data = etree.fromstring(xml_.read())
×
507
                    xml_objects.append(xml_data)
×
508

509
                self.myfig.draw_image(xml_objects, colors=None, scales=None)
×
510
                self.myfig.redraw_xaxis(self.lats, self.lons)
×
511
                highlight = [[wp[0], wp[1]] for wp in self.wps]
×
512
                self.myfig.draw_vertical_lines(highlight, self.lats, self.lons)
×
513
                self.myfig.fig.savefig(f"{flight}_{layer}.png", bbox_inches='tight')
×
514

515

516
@click.command()
1✔
517
@click.option('--cpath', default=constants.MSS_AUTOPLOT, help='Path of the configuration file.')
1✔
518
@click.option('--view', default="top", help='View of the plot (top/side/linear).')
1✔
519
@click.option('--ftrack', default="", help='Flight track.')
1✔
520
@click.option('--itime', default="", help='Initial time.')
1✔
521
@click.option('--vtime', default="", help='Valid time.')
1✔
522
@click.option('--intv', default=0, help='Time interval.')
1✔
523
@click.option('--stime', default="", help='Starting time for downloading multiple plots with a fixed interval.')
1✔
524
@click.option('--etime', default="", help='Ending time for downloading multiple plots with a fixed interval.')
1✔
525
@click.pass_context
1✔
526
def main(ctx, cpath, view, ftrack, itime, vtime, intv, stime, etime):
1✔
527
    def close_process_dialog(pdlg):
×
528
        pdlg.close()
×
529

530
    if ctx.obj is not None:
×
531
        # ToDo find a simpler solution, on a split of the package, QT is expensive for such a progressbar
532
        pdlg = QProgressDialog("Downloading images", "Cancel", 0, 10, parent=ctx.obj)
×
533
        pdlg.setMinimumDuration(0)
×
534
        pdlg.repaint()
×
535
        pdlg.canceled.connect(lambda: close_process_dialog(pdlg))
×
536
        pdlg.setWindowModality(Qt.WindowModal)
×
537
        pdlg.setAutoReset(True)     # Close dialog automatically when reaching max value
×
538
        pdlg.setAutoClose(True)     # Automatically close when value reaches maximum
×
539
        pdlg.setValue(0)            # Initial progress value
×
540

541
        # Set window flags to ensure visibility and modality
542
        pdlg.setWindowFlags(pdlg.windowFlags() | Qt.CustomizeWindowHint | Qt.WindowTitleHint)
×
543

544
        pdlg.setValue(0)
×
545

546
    conf.read_config_file(path=cpath)
×
547
    config = conf.config_loader()
×
548

549
    # flight_name = config["automated_plotting_flights"][0][0]
550
    # file = config["automated_plotting_flights"][0][3]
551
    if ctx.obj is not None:
×
552
        pdlg.setValue(1)
×
553

554
    msc_url = config["mscolab_server_url"]
×
555
    msc_auth_password = mslib.utils.auth.get_password_from_keyring(service_name=f"MSCOLAB_AUTH_{msc_url}",
×
556
                                                                   username="mscolab")
557
    msc_username = config["MSS_auth"][msc_url]
×
558
    msc_password = mslib.utils.auth.get_password_from_keyring(service_name=msc_url, username=msc_username)
×
559

560
    # Choose view (top or side)
561
    if view == "top":
×
562
        top_view = TopViewPlotting(cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg)
×
563
        sec = "automated_plotting_hsecs"
×
564
    else:
565
        side_view = SideViewPlotting(cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg)
×
566
        sec = "automated_plotting_vsecs"
×
567
    if ctx.obj is not None:
×
568
        pdlg.setValue(2)
×
569

570
    def draw(no_of_plots):
×
571
        try:
×
572
            if view == "top":
×
573
                top_view.draw(flight, section, vertical, filename, init_time, time,
×
574
                              url, layer, style, elevation, no_of_plots)
575
            elif view == "side":
×
576
                side_view.draw(flight, section, vertical, filename, init_time, time,
×
577
                               url, layer, style, elevation, no_of_plots=no_of_plots)
578
        except Exception as e:
×
579
            if "times" in str(e):
×
580
                print("Invalid times and/or levels requested")
×
581
            elif "LAYER" in str(e):
×
582
                print(f"Invalid LAYER '{layer}' requested")
×
583
            elif "404 Client Error" in str(e) or "NOT FOUND for url" in str(e):
×
584
                print("Invalid STYLE and/or URL requested")
×
585
            else:
586
                print(str(e))
×
587
        else:
588
            print("Plot downloaded!")
×
589
            return True
×
590
        return False
×
591
    if ctx.obj is not None:
×
592
        pdlg.setValue(4)
×
593
    flag = False
×
594
    for flight, section, vertical, filename, init_time, time in config["automated_plotting_flights"]:
×
595
        if ctx.obj is not None:
×
596
            pdlg.setValue(8)
×
597
        for url, layer, style, elevation in config[sec]:
×
598
            if vtime == "" and stime == "":
×
599
                no_of_plots = 1
×
600
                flag = draw(no_of_plots)
×
601
            elif intv == 0:
×
602
                if itime != "":
×
603
                    init_time = datetime.strptime(itime, "%Y-%m-%dT%H:%M:%S")
×
604
                time = datetime.strptime(vtime, "%Y-%m-%dT%H:%M:%S")
×
605
                if ftrack != "":
×
606
                    flight = ftrack
×
607
                no_of_plots = 1
×
608
                flag = draw(no_of_plots)
×
609
            elif intv > 0:
×
610
                if itime != "":
×
611
                    init_time = datetime.strptime(itime, "%Y-%m-%dT%H:%M:%S")
×
612
                starttime = datetime.strptime(stime, "%Y-%m-%dT%H:%M:%S")
×
613
                endtime = datetime.strptime(etime, "%Y-%m-%dT%H:%M:%S")
×
614
                i = 1
×
615
                time = starttime
×
616
                while time <= endtime:
×
617
                    logging.debug(time)
×
618
                    if ftrack != "":
×
619
                        flight = ftrack
×
620
                    no_of_plots = i
×
621
                    flag = draw(no_of_plots)
×
622
                    time = time + timedelta(hours=intv)
×
623
                    i += 1
×
624
            else:
625
                raise Exception("Invalid interval")
×
626
    if ctx.obj is not None:
×
627
        pdlg.setValue(10)
×
628
        pdlg.close()
×
629
        if flag:
×
630
            QMessageBox.information(
×
631
                ctx.obj,  # The parent widget (use `None` if no parent)
632
                "SUCCESS",  # Title of the message box
633
                "Plots downloaded successfully."  # Message text
634
            )
635
        else:
636
            QMessageBox.information(
×
637
                ctx.obj,  # The parent widget (use `None` if no parent)
638
                "FAILURE",  # Title of the message box
639
                "Plots couldnot be downloaded."  # Message text
640
            )
641

642

643
if __name__ == '__main__':
644
    main()
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