swift
dbautostashingcomponent.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2016 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QCheckBox>
7 #include <QDialogButtonBox>
8 #include <QFlags>
9 #include <QIntValidator>
10 #include <QLineEdit>
11 #include <QProgressBar>
12 #include <QRadioButton>
13 #include <QString>
14 #include <QStringBuilder>
15 #include <QWidget>
16 #include <Qt>
17 #include <QtGlobal>
18 
19 #include "ui_dbautostashingcomponent.h"
20 
21 #include "core/webdataservices.h"
24 #include "gui/guiapplication.h"
27 #include "gui/views/viewbase.h"
28 #include "misc/aviation/livery.h"
29 #include "misc/logcategories.h"
30 #include "misc/sequence.h"
33 #include "misc/statusmessagelist.h"
34 
35 using namespace swift::core;
36 using namespace swift::misc;
37 using namespace swift::misc::aviation;
38 using namespace swift::misc::network;
39 using namespace swift::misc::simulation;
40 using namespace swift::gui::views;
41 
42 namespace swift::gui::components
43 {
44  const QStringList &CDbAutoStashingComponent::getLogCategories()
45  {
46  static const QStringList cats { CLogCategories::mapping(), CLogCategories::guiComponent() };
47  return cats;
48  }
49 
50  CDbAutoStashingComponent::CDbAutoStashingComponent(QWidget *parent)
51  : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint),
52  CDbMappingComponentAware(qobject_cast<CDbMappingComponent *>(parent)), ui(new Ui::CDbAutoStashingComponent)
53  {
54  ui->setupUi(this);
55  ui->tvp_StatusMessages->setResizeMode(CAircraftModelView::ResizingAuto);
56  ui->tvp_StatusMessages->menuAddItems(CAircraftModelView::MenuSave);
57  ui->le_MaxModelsStashed->setValidator(new QIntValidator(10, CDbStashComponent::MaxModelPublished, this));
58  Q_ASSERT_X(this->getMappingComponent(), Q_FUNC_INFO, "Expect mapping componet");
59 
60  connect(ui->tb_ResetDescription, &QToolButton::clicked, this, &CDbAutoStashingComponent::resetDescription);
61 
62  this->resetDescription();
63  }
64 
66 
68  {
69  switch (m_state)
70  {
71  case Running: return;
72  case Completed:
73  {
74  if (!m_modelsToStash.isEmpty())
75  {
76  // this removes previously stashed models
77  this->getMappingComponent()->replaceStashedModelsUnvalidated(m_modelsToStash);
78  if (ui->cb_RemovedChecked->isChecked())
79  {
80  this->currentModelView()->removeModelsWithModelString(m_modelsToStash);
81  }
82  const CStatusMessage stashedMsg(this, CStatusMessage::SeverityInfo,
83  QStringLiteral("Auto stashed %1 models").arg(m_modelsToStash.size()));
84  this->addStatusMessage(stashedMsg);
85  m_modelsToStash.clear();
86  }
87  QDialog::accept();
88  break;
89  }
90  default:
91  {
92  if (this->getSelectedOrAllCount() < 1)
93  {
94  const CStatusMessage m(this, CStatusMessage::SeverityError, u"No data, nothing to do");
95  this->addStatusMessage(m);
96  QDialog::accept();
97  }
98  this->tryToStashModels();
99  }
100  }
101  }
102 
104  {
105  this->initGui();
106  return QDialog::exec();
107  }
108 
110  {
111  ui->bb_AutoStashing->setStandardButtons(QDialogButtonBox::Close);
112  this->setVisible(true);
113  }
114 
115  void CDbAutoStashingComponent::resetDescription()
116  {
117  ui->rb_DescriptionEmptyOnly->setChecked(true);
118  ui->le_Description->setText(CAircraftModel::autoGenerated());
119  }
120 
121  void CDbAutoStashingComponent::initGui()
122  {
123  m_state = Idle;
124  ui->bb_AutoStashing->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
125  ui->tvp_StatusMessages->clear();
126  m_noData = 0;
127  m_noValidationFailed = 0;
128  m_noStashed = 0;
129  this->updateProgressIndicator(0);
130 
131  if (!this->currentModelView())
132  {
133  const CStatusMessage m(this, CStatusMessage::SeverityError, u"No data for auto stashing");
134  this->addStatusMessage(m);
135  }
136  else
137  {
138  int selected = this->currentModelView()->selectedRowCount();
139  int all = this->currentModelView()->rowCount();
140  ui->le_Selected->setText(QString::number(selected));
141  QString allStr(QString::number(all));
143  {
144  allStr += u" (Max." % QString::number(CDbStashComponent::MaxModelPublished) % u")";
145  }
146  ui->le_All->setText(allStr);
147  if (ui->le_MaxModelsStashed->text().isEmpty())
148  {
149  ui->le_MaxModelsStashed->setText(all > 100 ? QStringLiteral("100") : QString());
150  }
151  if (selected > 0)
152  {
153  ui->rb_Selected->setChecked(true);
154  ui->rb_Selected->setEnabled(true);
155  }
156  else
157  {
158  ui->rb_All->setChecked(true);
159  ui->rb_Selected->setEnabled(false);
160  }
161  }
162  }
163 
165  {
166  if (percent > 100) { percent = 100; }
167  if (percent < 0) { percent = 0; }
168  ui->pb_StashingProgress->setValue(percent);
169  ui->le_Stashed->setText(QString::number(m_noStashed));
170  ui->le_NoData->setText(QString::number(m_noData));
171  ui->le_ValidationFailed->setText(QString::number(m_noValidationFailed));
172  }
173 
174  int CDbAutoStashingComponent::getSelectedOrAllCount() const
175  {
176  if (!this->currentModelView()) { return 0; }
177  if (ui->rb_Selected->isChecked()) { return this->currentModelView()->selectedRowCount(); }
178  else { return this->currentModelView()->rowCount(); }
179  }
180 
181  CAircraftModelView *CDbAutoStashingComponent::currentModelView() const
182  {
183  return this->getMappingComponent()->currentModelView();
184  }
185 
186  void CDbAutoStashingComponent::addStatusMessage(const CStatusMessage &msg)
187  {
188  if (msg.isEmpty()) { return; }
189  ui->tvp_StatusMessages->insert(msg);
190  }
191 
192  void CDbAutoStashingComponent::addStatusMessage(const CStatusMessage &msg, const CAircraftModel &model)
193  {
194  if (msg.isEmpty()) { return; }
195  if (model.hasModelString())
196  {
197  CStatusMessage prefixMessage(msg);
198  prefixMessage.prependMessage(QString(model.getModelString() + ", " + model.getMembersDbStatus() + ": "));
199  ui->tvp_StatusMessages->insert(prefixMessage);
200  }
201  else { ui->tvp_StatusMessages->insert(msg); }
202  }
203 
204  void CDbAutoStashingComponent::tryToStashModels()
205  {
206  Q_ASSERT_X(this->currentModelView(), Q_FUNC_INFO, "No view");
207  const CAircraftModelList models(ui->rb_Selected->isChecked() ?
208  this->currentModelView()->selectedObjects() :
209  this->currentModelView()->containerOrFilteredContainer());
210  if (models.isEmpty()) { return; }
211  if (!sGui || sGui->isShuttingDown()) { return; }
212 
213  // we have data and are good to go
214  m_state = Running;
215  const int all = models.size();
216 
217  // maximum
219  const QString maxStr(ui->le_MaxModelsStashed->text());
220  bool okMaxStr = true;
221  if (!maxStr.isEmpty()) { max = maxStr.toInt(&okMaxStr); }
222  if (!okMaxStr || max > all) { max = all; }
223 
224  // override description
225  const QString description(ui->le_Description->text().trimmed());
226 
227  // temp livery if applicable
228  const CLivery tempLivery(ui->cb_UseTempLivery->isChecked() ? getTempLivery() : CLivery());
229 
230  int c = 0;
231  CAircraftModelList autoStashed;
232  for (const CAircraftModel &model : models)
233  {
234  CAircraftModel stashModel(model);
235  const bool stashed = this->tryToStashModel(stashModel, tempLivery);
236  if (stashed)
237  {
238  if (!description.isEmpty()) { this->setModelDescription(stashModel, description); }
239  autoStashed.push_back(stashModel);
240  }
241 
242  c++;
243  if (c % 25 == 0)
244  {
245  Q_ASSERT_X(c <= all, Q_FUNC_INFO, "illegal numbers");
247 
248  int percent = c * 100 / all;
249  if (max < all)
250  {
251  int maxPercent = autoStashed.size() * 100 / max;
252  if (maxPercent > percent) { percent = maxPercent; }
253  }
254  this->updateProgressIndicator(percent);
255  }
256  if (autoStashed.size() >= max) { break; }
257  }
258 
259  this->updateProgressIndicator(100);
261 
262  const CStatusMessage stashedMsg(this, CStatusMessage::SeverityInfo,
263  QStringLiteral("Ready to auto stash %1 models").arg(autoStashed.size()));
264  this->addStatusMessage(stashedMsg);
265  m_modelsToStash = autoStashed;
266  m_state = Completed;
267  }
268 
269  bool CDbAutoStashingComponent::tryToStashModel(CAircraftModel &model, const CLivery &tempLivery)
270  {
271  const bool useTempLivery = tempLivery.isLoadedFromDb();
272 
273  // no airline and no livery, here replaced by temp livery
274  if (useTempLivery && !model.hasAirlineDesignator() && !model.getLivery().hasValidDbKey())
275  {
276  model.setLivery(tempLivery);
277  }
278 
280  if (!model.hasModelString())
281  {
282  this->addStatusMessage(CStatusMessage(this, CStatusMessage::SeverityError, u"No model string"));
283  m_noData++;
284  return false;
285  }
286 
287  if (!model.hasAircraftDesignator())
288  {
289  this->addStatusMessage(CStatusMessage(this, CStatusMessage::SeverityError, u"No aircraft designator"),
290  model);
291  m_noData++;
292  return false;
293  }
294 
295  if (!model.hasAirlineDesignator() && !model.getLivery().hasValidDbKey())
296  {
297  // no valid airline and NO DB livery
298  // we try one fallback
299  const QString liveryCombinedCode = model.getLivery().getCombinedCode();
300  bool fallback = false;
301  if (liveryCombinedCode.length() == 3 || liveryCombinedCode.length() == 4)
302  {
303  // could we use the combined code as airline
304  if (CAirlineIcaoCode::isValidAirlineDesignator(liveryCombinedCode))
305  {
306  model.setAirlineIcaoDesignator(liveryCombinedCode);
307  fallback = true;
308  }
309  }
310 
311  // if there is no livery (normal) we need an airline
312  if (!fallback)
313  {
314  this->addStatusMessage(CStatusMessage(this, CStatusMessage::SeverityError, u"No airline designator"),
315  model);
316  m_noData++;
317  return false;
318  }
319  }
320 
321  // stash here consolidates with DB data and validates
322  bool stashed = false;
323  const CAircraftModel stashModel(this->getMappingComponent()->consolidateModel(model));
324  CStatusMessageList validationMsgs(stashModel.validate(true));
325  validationMsgs.removeWarningsAndBelow();
326  CStatusMessage msg = validationMsgs.toSingleMessage();
327  if (msg.getSeverity() == CStatusMessage::SeverityError) { m_noValidationFailed++; }
328  else
329  {
330  msg = CStatusMessage(this, CStatusMessage::SeverityInfo, u"Stashed succesfully");
331  stashed = true;
332  m_noStashed++;
333  model = stashModel;
334  }
335  this->addStatusMessage(msg, stashModel);
336  return stashed;
337  }
338 
339  void CDbAutoStashingComponent::setModelDescription(CAircraftModel &model, const QString &description) const
340  {
341  if (description.isEmpty()) { return; }
342  if (ui->rb_All->isChecked()) { model.setDescription(description); }
343  else
344  {
345  // only for "empty" ones
346  if (model.hasDescription(true)) { return; }
347  model.setDescription(description);
348  }
349  }
350 
351  swift::misc::aviation::CLivery CDbAutoStashingComponent::getTempLivery()
352  {
353  if (!sGui || !sGui->hasWebDataServices()) { return CLivery(); }
355  }
356 } // namespace swift::gui::components
bool hasWebDataServices() const
Web data services available?
bool isShuttingDown() const
Is application shutting down?
CWebDataServices * getWebDataServices() const
Get the web data services.
swift::misc::aviation::CLivery getTempLiveryOrDefault() const
The temp. livery if available.
void processEventsToRefreshGui() const
Allow the GUI to refresh by processing events, call the event loop.
virtual void updateProgressIndicator(int percent)
Update the progress indicator 0..100.
Allows subcomponents to gain access to model component.
CDbMappingComponent * getMappingComponent() const
Get the mapping component.
void replaceStashedModelsUnvalidated(const swift::misc::simulation::CAircraftModelList &models) const
Replace models, no validation.
views::CAircraftModelView * currentModelView() const
Current model view.
static constexpr int MaxModelPublished
Number of models which can be published at once.
int removeModelsWithModelString(const QStringList &modelStrings, Qt::CaseSensitivity sensitivity=Qt::CaseInsensitive)
Remove models with model strings.
virtual int rowCount() const
Elements in container.
Definition: viewbase.cpp:386
int selectedRowCount() const
Number of selected rows.
static const QString & guiComponent()
GUI components.
Definition: logcategories.h:94
static const QString & mapping()
Mapping.
bool isEmpty() const
Message empty.
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
void clear()
Removes all elements in the sequence.
Definition: sequence.h:288
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
Streamable status message, e.g.
StatusSeverity getSeverity() const
Message severity.
Status messages, e.g. from Core -> GUI.
Value object encapsulating information about an airpot.
Definition: livery.h:29
const QString & getCombinedCode() const
Combined code.
Definition: livery.h:71
bool isLoadedFromDb() const
Loaded from DB.
Definition: datastore.cpp:49
bool hasValidDbKey() const
Has valid DB key.
Definition: datastore.h:102
Aircraft model (used by another pilot, my models on disk)
Definition: aircraftmodel.h:71
bool hasAirlineDesignator() const
Airline designator?
const aviation::CLivery & getLivery() const
Get livery.
QString getMembersDbStatus() const
Info, which members (Livery, Aircraft ICAO, ...) are already based on DB data.
const QString & getModelString() const
Model key, either queried or loaded from simulator model.
bool hasAircraftDesignator() const
Has aircraft designator?
void setDescription(const QString &description)
Descriptive text.
bool hasDescription(bool ignoreAutoGenerated=false) const
Description.
bool setAirlineIcaoDesignator(const QString &designator)
Set airline ICAO code designator.
bool hasModelString() const
Non empty model string?
void setLivery(const aviation::CLivery &livery)
Livery.
Value object encapsulating a list of aircraft models.
SWIFT_GUI_EXPORT swift::gui::CGuiApplication * sGui
Single instance of GUI application object.
Backend services of the swift project, like dealing with the network or the simulators.
Definition: actionbind.cpp:7
High level reusable GUI components.
Definition: aboutdialog.cpp:13
Views, mainly QTableView.
Free functions in swift::misc.