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  {
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  }
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);
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  percent = std::clamp(percent, 0, 100);
167  ui->pb_StashingProgress->setValue(percent);
168  ui->le_Stashed->setText(QString::number(m_noStashed));
169  ui->le_NoData->setText(QString::number(m_noData));
170  ui->le_ValidationFailed->setText(QString::number(m_noValidationFailed));
171  }
172 
173  int CDbAutoStashingComponent::getSelectedOrAllCount() const
174  {
175  if (!this->currentModelView()) { return 0; }
176  if (ui->rb_Selected->isChecked()) { return this->currentModelView()->selectedRowCount(); }
177  else { return this->currentModelView()->rowCount(); }
178  }
179 
180  CAircraftModelView *CDbAutoStashingComponent::currentModelView() const
181  {
182  return this->getMappingComponent()->currentModelView();
183  }
184 
185  void CDbAutoStashingComponent::addStatusMessage(const CStatusMessage &msg)
186  {
187  if (msg.isEmpty()) { return; }
188  ui->tvp_StatusMessages->insert(msg);
189  }
190 
191  void CDbAutoStashingComponent::addStatusMessage(const CStatusMessage &msg, const CAircraftModel &model)
192  {
193  if (msg.isEmpty()) { return; }
194  if (model.hasModelString())
195  {
196  CStatusMessage prefixMessage(msg);
197  prefixMessage.prependMessage(QString(model.getModelString() + ", " + model.getMembersDbStatus() + ": "));
198  ui->tvp_StatusMessages->insert(prefixMessage);
199  }
200  else { ui->tvp_StatusMessages->insert(msg); }
201  }
202 
203  void CDbAutoStashingComponent::tryToStashModels()
204  {
205  Q_ASSERT_X(this->currentModelView(), Q_FUNC_INFO, "No view");
206  const CAircraftModelList models(ui->rb_Selected->isChecked() ?
207  this->currentModelView()->selectedObjects() :
208  this->currentModelView()->containerOrFilteredContainer());
209  if (models.isEmpty()) { return; }
210  if (!sGui || sGui->isShuttingDown()) { return; }
211 
212  // we have data and are good to go
213  m_state = Running;
214  const int all = models.size();
215 
216  // maximum
218  const QString maxStr(ui->le_MaxModelsStashed->text());
219  bool okMaxStr = true;
220  if (!maxStr.isEmpty()) { max = maxStr.toInt(&okMaxStr); }
221  if (!okMaxStr || max > all) { max = all; }
222 
223  // override description
224  const QString description(ui->le_Description->text().trimmed());
225 
226  // temp livery if applicable
227  const CLivery tempLivery(ui->cb_UseTempLivery->isChecked() ? getTempLivery() : CLivery());
228 
229  int c = 0;
230  CAircraftModelList autoStashed;
231  for (const CAircraftModel &model : models)
232  {
233  CAircraftModel stashModel(model);
234  const bool stashed = this->tryToStashModel(stashModel, tempLivery);
235  if (stashed)
236  {
237  if (!description.isEmpty()) { this->setModelDescription(stashModel, description); }
238  autoStashed.push_back(stashModel);
239  }
240 
241  c++;
242  if (c % 25 == 0)
243  {
244  Q_ASSERT_X(c <= all, Q_FUNC_INFO, "illegal numbers");
246 
247  int percent = c * 100 / all;
248  if (max < all)
249  {
250  int maxPercent = autoStashed.size() * 100 / max;
251  percent = std::max(maxPercent, percent);
252  }
253  this->updateProgressIndicator(percent);
254  }
255  if (autoStashed.size() >= max) { break; }
256  }
257 
258  this->updateProgressIndicator(100);
260 
261  const CStatusMessage stashedMsg(this, CStatusMessage::SeverityInfo,
262  QStringLiteral("Ready to auto stash %1 models").arg(autoStashed.size()));
263  this->addStatusMessage(stashedMsg);
264  m_modelsToStash = autoStashed;
265  m_state = Completed;
266  }
267 
268  bool CDbAutoStashingComponent::tryToStashModel(CAircraftModel &model, const CLivery &tempLivery)
269  {
270  const bool useTempLivery = tempLivery.isLoadedFromDb();
271 
272  // no airline and no livery, here replaced by temp livery
273  if (useTempLivery && !model.hasAirlineDesignator() && !model.getLivery().hasValidDbKey())
274  {
275  model.setLivery(tempLivery);
276  }
277 
279  if (!model.hasModelString())
280  {
281  this->addStatusMessage(CStatusMessage(this, CStatusMessage::SeverityError, u"No model string"));
282  m_noData++;
283  return false;
284  }
285 
286  if (!model.hasAircraftDesignator())
287  {
288  this->addStatusMessage(CStatusMessage(this, CStatusMessage::SeverityError, u"No aircraft designator"),
289  model);
290  m_noData++;
291  return false;
292  }
293 
294  if (!model.hasAirlineDesignator() && !model.getLivery().hasValidDbKey())
295  {
296  // no valid airline and NO DB livery
297  // we try one fallback
298  const QString liveryCombinedCode = model.getLivery().getCombinedCode();
299  bool fallback = false;
300  if (liveryCombinedCode.length() == 3 || liveryCombinedCode.length() == 4)
301  {
302  // could we use the combined code as airline
303  if (CAirlineIcaoCode::isValidAirlineDesignator(liveryCombinedCode))
304  {
305  model.setAirlineIcaoDesignator(liveryCombinedCode);
306  fallback = true;
307  }
308  }
309 
310  // if there is no livery (normal) we need an airline
311  if (!fallback)
312  {
313  this->addStatusMessage(CStatusMessage(this, CStatusMessage::SeverityError, u"No airline designator"),
314  model);
315  m_noData++;
316  return false;
317  }
318  }
319 
320  // stash here consolidates with DB data and validates
321  bool stashed = false;
322  const CAircraftModel stashModel(this->getMappingComponent()->consolidateModel(model));
323  CStatusMessageList validationMsgs(stashModel.validate(true));
324  validationMsgs.removeWarningsAndBelow();
325  CStatusMessage msg = validationMsgs.toSingleMessage();
326  if (msg.getSeverity() == CStatusMessage::SeverityError) { m_noValidationFailed++; }
327  else
328  {
329  msg = CStatusMessage(this, CStatusMessage::SeverityInfo, u"Stashed succesfully");
330  stashed = true;
331  m_noStashed++;
332  model = stashModel;
333  }
334  this->addStatusMessage(msg, stashModel);
335  return stashed;
336  }
337 
338  void CDbAutoStashingComponent::setModelDescription(CAircraftModel &model, const QString &description) const
339  {
340  if (description.isEmpty()) { return; }
341  if (ui->rb_All->isChecked()) { model.setDescription(description); }
342  else
343  {
344  // only for "empty" ones
345  if (model.hasDescription(true)) { return; }
346  model.setDescription(description);
347  }
348  }
349 
350  swift::misc::aviation::CLivery CDbAutoStashingComponent::getTempLivery()
351  {
352  if (!sGui || !sGui->hasWebDataServices()) { return {}; }
354  }
355 } // 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.
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.
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:14
Views, mainly QTableView.
Free functions in swift::misc.
void clicked(bool checked)
virtual void accept()
virtual int exec()
virtual void setVisible(bool visible) override
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool isEmpty() const const
qsizetype length() const const
QString number(double n, char format, int precision)