File indexing completed on 2024-05-26 05:10:26

0001 /***************************************************************************
0002  * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr
0003  * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  ***************************************************************************/
0006 /** @file
0007  * A plugin for monthly report.
0008  *
0009  * @author Stephane MANKOWSKI
0010  */
0011 #include "skgmonthlypluginwidget.h"
0012 
0013 #include <kaboutdata.h>
0014 #include <kzip.h>
0015 
0016 #include <kns3/qtquickdialogwrapper.h>
0017 #include <qdesktopservices.h>
0018 #include <qdir.h>
0019 #include <qdom.h>
0020 #include <qfile.h>
0021 #include <qmenu.h>
0022 #include <qtextstream.h>
0023 #ifdef SKG_WEBENGINE
0024 #include <qwebenginepage.h>
0025 #endif
0026 #ifdef SKG_WEBKIT
0027 #include <qwebframe.h>
0028 #endif
0029 #include <qdiriterator.h>
0030 #include <qstandardpaths.h>
0031 #include <qvalidator.h>
0032 
0033 #include "skgmainpanel.h"
0034 #include "skgreport.h"
0035 #include "skgtraces.h"
0036 #include "skgtransactionmng.h"
0037 
0038 SKGMonthlyPluginWidget::SKGMonthlyPluginWidget(QWidget* iParent, SKGDocument* iDocument)
0039     : SKGTabPage(iParent, iDocument), m_upload(nullptr)
0040 {
0041     SKGTRACEINFUNC(1)
0042     if (iDocument == nullptr) {
0043         return;
0044     }
0045 
0046     ui.setupUi(this);
0047     ui.kMonth->setMode(SKGSimplePeriodEdit::PREVIOUS_PERIODS | SKGSimplePeriodEdit::PREVIOUS_MONTHS);
0048     ui.kDeleteTemplate->hide();
0049 
0050     ui.kRefresh->setIcon(SKGServices::fromTheme(QStringLiteral("view-refresh")));
0051     ui.kGetNewHotStuff->setIcon(SKGServices::fromTheme(QStringLiteral("get-hot-new-stuff")));
0052     ui.kDeleteTemplate->setIcon(SKGServices::fromTheme(QStringLiteral("edit-delete")));
0053 
0054     auto newValidator = new QRegularExpressionValidator(QRegularExpression(QStringLiteral("^[\\w\\s]+$")), this);
0055     ui.kTemplate->setValidator(newValidator);
0056 
0057     connect(getDocument(), &SKGDocument::tableModified, this, &SKGMonthlyPluginWidget::dataModified, Qt::QueuedConnection);
0058     connect(ui.kMonth, static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::currentTextChanged), this, &SKGMonthlyPluginWidget::onPeriodChanged, Qt::QueuedConnection);
0059 
0060     QStringList overlays;
0061     overlays.push_back(QStringLiteral("list-add"));
0062     m_upload = new QAction(SKGServices::fromTheme(QStringLiteral("get-hot-new-stuff"), overlays), i18n("Upload"), this);
0063     connect(m_upload, &QAction::triggered, this, &SKGMonthlyPluginWidget::onPutNewHotStuff);
0064 
0065     auto menu = new QMenu(this);
0066     menu->addAction(m_upload);
0067     ui.kGetNewHotStuff->setMenu(menu);
0068 
0069     connect(ui.kDeleteTemplate, &QToolButton::clicked, this, &SKGMonthlyPluginWidget::onDeleteTemplate);
0070     connect(ui.kTemplate, static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::returnPressed), this, &SKGMonthlyPluginWidget::onAddTemplate);
0071     connect(ui.kTemplate, &SKGComboBox::editTextChanged, this, &SKGMonthlyPluginWidget::onTemplateChanged);
0072     connect(ui.kGetNewHotStuff, &QToolButton::clicked, this, &SKGMonthlyPluginWidget::onGetNewHotStuff);
0073     connect(ui.kRefresh, &QToolButton::clicked, this, &SKGMonthlyPluginWidget::onPeriodChanged);
0074 
0075     // Refresh
0076     fillTemplateList();
0077     dataModified(QLatin1String(""), 0);
0078 }
0079 
0080 SKGMonthlyPluginWidget::~SKGMonthlyPluginWidget()
0081 {
0082     SKGTRACEINFUNC(1)
0083 }
0084 
0085 QString SKGMonthlyPluginWidget::getState()
0086 {
0087     SKGTRACEINFUNC(10)
0088     QDomDocument doc(QStringLiteral("SKGML"));
0089     QDomElement root = doc.createElement(QStringLiteral("parameters"));
0090     doc.appendChild(root);
0091 
0092     // Get state
0093     root.setAttribute(QStringLiteral("month"), ui.kMonth->text());
0094     root.setAttribute(QStringLiteral("template"), ui.kTemplate->text());
0095     root.setAttribute(QStringLiteral("web"), ui.kWebView->getState());
0096 
0097     return doc.toString();
0098 }
0099 
0100 void SKGMonthlyPluginWidget::setState(const QString& iState)
0101 {
0102     SKGTRACEINFUNC(10)
0103     QDomDocument doc(QStringLiteral("SKGML"));
0104     doc.setContent(iState);
0105     QDomElement root = doc.documentElement();
0106 
0107     // Set state
0108     QString webS = root.attribute(QStringLiteral("web"));
0109     if (!webS.isEmpty()) {
0110         ui.kWebView->setState(webS);
0111     }
0112 
0113     QString templat = root.attribute(QStringLiteral("template"));
0114     if (!templat.isEmpty()) {
0115         bool p = ui.kTemplate->blockSignals(true);
0116         ui.kTemplate->setText(templat);
0117         ui.kTemplate->blockSignals(p);
0118         onTemplateChanged();
0119     }
0120 
0121     QString month = root.attribute(QStringLiteral("month"));
0122     if (!month.isEmpty()) {
0123         ui.kMonth->setText(month);
0124     }
0125     onPeriodChanged();
0126 }
0127 
0128 QString SKGMonthlyPluginWidget::getDefaultStateAttribute()
0129 {
0130     return QStringLiteral("SKGMONTHLY_DEFAULT_PARAMETERS");
0131 }
0132 
0133 QWidget* SKGMonthlyPluginWidget::mainWidget()
0134 {
0135     return ui.kWebView;
0136 }
0137 
0138 void SKGMonthlyPluginWidget::fillTemplateList()
0139 {
0140     disconnect(ui.kTemplate, static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::currentTextChanged), this, &SKGMonthlyPluginWidget::onPeriodChanged);
0141     // Get previous selected item
0142     QString current = ui.kTemplate->text();
0143 
0144     // Fill
0145     ui.kTemplate->clear();
0146     const auto dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, KAboutData::applicationData().componentName() % "/html", QStandardPaths::LocateDirectory);
0147     for (const auto& dir : dirs) {
0148         QDirIterator it(dir, QStringList() << QStringLiteral("*.txt"));
0149         while (it.hasNext()) {
0150             QString file = it.next();
0151 
0152             QFileInfo f(file);
0153             QString file2 = f.completeBaseName();
0154             if (!ui.kTemplate->contains(file2) && file2 != QStringLiteral("main")) {
0155                 ui.kTemplate->addItem(file2, file);
0156             }
0157         }
0158     }
0159 
0160     // Set previous selected itemData
0161     if (!current.isEmpty() && ui.kTemplate->contains(current)) {
0162         ui.kTemplate->setCurrentItem(current);
0163     }
0164     connect(ui.kTemplate, static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::currentTextChanged), this, &SKGMonthlyPluginWidget::onPeriodChanged, Qt::QueuedConnection);
0165 }
0166 
0167 void SKGMonthlyPluginWidget::onAddTemplate()
0168 {
0169     QString templat = ui.kTemplate->text().trimmed();
0170     QString templateDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + KAboutData::applicationData().componentName();
0171     QString templatFileName = templateDir % "/html/" % templat % ".txt";
0172     QStringList templates;
0173     const auto dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, KAboutData::applicationData().componentName() % "/html", QStandardPaths::LocateDirectory);
0174     for (const auto& dir : dirs) {
0175         QDirIterator it(dir, QStringList() << QStringLiteral("*.txt"));
0176         while (it.hasNext()) {
0177             templates.append(it.next());
0178         }
0179     }
0180 
0181     if (!templat.isEmpty() && (!templates.contains(templatFileName) || QFileInfo(templatFileName).isWritable())) {
0182         SKGError err;
0183         if (!templates.contains(templatFileName)) {
0184             // Create the new template
0185             QString source = QStandardPaths::locate(QStandardPaths::GenericDataLocation, KAboutData::applicationData().componentName() % "/html/tutorial.txt");
0186             QDir(templateDir).mkpath(QStringLiteral("html"));
0187             if (SKGServices::upload(QUrl::fromLocalFile(source), QUrl::fromLocalFile(templatFileName))) {
0188                 err.setReturnCode(ERR_FAIL).setMessage(i18nc("An error message", "Impossible to copy file from '%1' to '%2'", source, templatFileName));
0189             } else {
0190                 fillTemplateList();
0191             }
0192         }
0193 
0194         // Open the created or already existing file
0195         QDesktopServices::openUrl(QUrl::fromLocalFile(templatFileName));
0196 
0197         onTemplateChanged();
0198 
0199         // Display error
0200         SKGMainPanel::displayErrorMessage(err);
0201     }
0202 }
0203 
0204 void SKGMonthlyPluginWidget::onDeleteTemplate()
0205 {
0206     QString templat = ui.kTemplate->text().trimmed();
0207     QString templatFileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + KAboutData::applicationData().componentName() % "/html/" % templat % ".txt";
0208     if (!templat.isEmpty()) {
0209         // This is a new source
0210         SKGError err;
0211 
0212         // Delete the file
0213         QFile file(templatFileName);
0214         if (!file.remove()) {
0215             err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message",  "Deletion of '%1' failed", templatFileName));
0216         }
0217 
0218         IFOK(err) ui.kTemplate->removeItem(ui.kTemplate->findText(templat));
0219 
0220         // Display error
0221         SKGMainPanel::displayErrorMessage(err);
0222     }
0223 }
0224 
0225 void SKGMonthlyPluginWidget::onTemplateChanged()
0226 {
0227     QString templat = ui.kTemplate->text().trimmed();
0228     QString templatFileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + KAboutData::applicationData().componentName() % "/html/" % templat % ".txt";
0229     bool local = !templat.isEmpty() && QFileInfo(templatFileName).isWritable();
0230     ui.kDeleteTemplate->setVisible(local);
0231     m_upload->setEnabled(local);
0232 }
0233 
0234 void SKGMonthlyPluginWidget::dataModified(const QString& iTableName, int iIdTransaction)
0235 {
0236     SKGTRACEINFUNC(1)
0237     Q_UNUSED(iIdTransaction)
0238 
0239     QSqlDatabase* db = getDocument()->getMainDatabase();
0240     setEnabled(db != nullptr);
0241     // TODO(Stephane MANKOWSKI): v_operation_display must be generic
0242     if (db != nullptr && (iTableName == QStringLiteral("v_operation_display") || iTableName.isEmpty())) {
0243         // Fill combo
0244 
0245         QDate date = QDate::currentDate();
0246         QStringList list;
0247         // TODO(Stephane MANKOWSKI): v_operation_display must be generic
0248         getDocument()->getDistinctValues(QStringLiteral("v_operation_display"), QStringLiteral("MIN(d_DATEMONTH)"), QStringLiteral("d_date<=CURRENT_DATE"), list);
0249         if (!list.isEmpty()) {
0250             if (!list[0].isEmpty()) {
0251                 date = SKGServices::periodToDate(list[0]);
0252             }
0253         }
0254 
0255         ui.kMonth->setFirstDate(date);
0256         ui.kRefresh->setEnabled(!list.isEmpty());
0257     }
0258 }
0259 
0260 QString SKGMonthlyPluginWidget::getPeriod()
0261 {
0262     return ui.kMonth->period();
0263 }
0264 
0265 void SKGMonthlyPluginWidget::onPeriodChanged()
0266 {
0267     SKGTRACEINFUNC(1)
0268     QString month = getPeriod();
0269     if (!month.isEmpty()) {
0270         // Display report
0271         QString htmlReport = getDocument()->getParameter("SKG_MONTHLY_REPORT_" % month);
0272         if (htmlReport.isEmpty() || sender() == ui.kRefresh || sender() == ui.kTemplate) {
0273             SKGError err;
0274             SKGBEGINLIGHTTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Compute monthly report for '%1'", month), err)
0275             htmlReport = getReport();
0276             err = getDocument()->setParameter("SKG_MONTHLY_REPORT_" % month, htmlReport);
0277         }
0278 
0279         // Display html report
0280 #ifdef SKG_WEBENGINE
0281         ui.kWebView->page()->setHtml(htmlReport, QUrl("file://"));
0282 #endif
0283 #ifdef SKG_WEBKIT
0284         ui.kWebView->setHtml(htmlReport);
0285         ui.kWebView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
0286 #endif
0287 #if !defined(SKG_WEBENGINE) && !defined(SKG_WEBKIT)
0288         ui.kWebView->setHtml(htmlReport);
0289 #endif
0290     }
0291 }
0292 
0293 void SKGMonthlyPluginWidget::onGetNewHotStuff()
0294 {
0295     QPointer<KNS3::QtQuickDialogWrapper> dialog = new KNS3::QtQuickDialogWrapper(KAboutData::applicationData().componentName() % "_monthly.knsrc", this);
0296     dialog->exec();
0297 
0298     fillTemplateList();
0299 }
0300 
0301 void SKGMonthlyPluginWidget::onPutNewHotStuff()
0302 {
0303     QString templat = ui.kTemplate->text().trimmed();
0304 
0305     // Create zip file
0306     QString templatFileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + KAboutData::applicationData().componentName() % "/html/" % templat % ".txt";
0307     QString templatHFileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + KAboutData::applicationData().componentName() % "/html/" % templat % ".html";
0308     QString zipFileName = QDir::tempPath() % "/" % templat % ".zip";
0309     KZip zip(zipFileName);
0310     if (zip.open(QIODevice::WriteOnly)) {
0311         zip.addLocalFile(templatFileName, templat % ".txt");
0312         if (QFile(templatHFileName).exists()) {
0313             zip.addLocalFile(templatHFileName, templat % ".html");
0314         }
0315         zip.close();
0316 
0317         // Create screen shots
0318         QString preview1 = QDir::tempPath() % "/" % templat % "_preview1.png";
0319         QString preview2 = QDir::tempPath() % "/" % templat % "_preview2.png";
0320         QString preview3 = QDir::tempPath() % "/" % templat % "_preview3.png";
0321 #ifdef SKG_WEBENGINE
0322         // TODO(SMI): QWebEngine
0323 #endif
0324 #ifdef SKG_WEBKIT
0325         bool previous = ui.kWebView->blockSignals(true);
0326         QWebFrame* frame = ui.kWebView->page()->mainFrame();
0327         frame->setScrollBarValue(Qt::Vertical, frame->scrollBarMaximum(Qt::Vertical));
0328         ui.kWebView->exportInFile(preview2);
0329 
0330         frame->setScrollBarValue(Qt::Vertical, frame->scrollBarMaximum(Qt::Vertical) / 2);
0331         ui.kWebView->exportInFile(preview3);
0332 
0333         frame->setScrollBarValue(Qt::Vertical, 0);
0334         ui.kWebView->exportInFile(preview1);
0335         ui.kWebView->blockSignals(previous);
0336 #endif
0337 #if !defined(SKG_WEBENGINE) && !defined(SKG_WEBKIT)
0338         // TODO(SMI): QTextBrowser
0339 #endif
0340         // Open dialog
0341         SKGMainPanel::getMainPanel()->displayMessage(i18nc("Upload message",
0342                 "The package is ready. You can find it here %1 and 3 screen capture there %2, %3 , %4. You can now upload it manually.",
0343                 zipFileName, preview1, preview2, preview3
0344                                                           )
0345                                                     );
0346     }
0347 }
0348 
0349 QString SKGMonthlyPluginWidget::getReport()
0350 {
0351     QString html;
0352     SKGError err;
0353     SKGTRACEINFUNCRC(10, err)
0354     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0355 
0356     // Get period
0357     if (!getPeriod().isEmpty()) {
0358         SKGReport* rep = getDocument()->getReport();
0359         if (rep != nullptr) {
0360             rep->setPeriod(getPeriod());
0361 
0362             // Enrich with tips of the day
0363             rep->setTipsOfDay(SKGMainPanel::getMainPanel()->getTipsOfDay());
0364 
0365             err = SKGReport::getReportFromTemplate(rep, ui.kTemplate->itemData(ui.kTemplate->currentIndex()).toString(), html);
0366 
0367             delete rep;
0368         }
0369     }
0370     QApplication::restoreOverrideCursor();
0371 
0372     // status bar
0373     IFKO(err) html += err.getFullMessageWithHistorical();
0374     return html;
0375 }
0376 
0377