File indexing completed on 2025-02-02 04:56:55

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 to generate statistic
0008  *
0009  * @author Stephane MANKOWSKI
0010  */
0011 #include "skgstatisticplugin.h"
0012 
0013 #ifdef HAVE_UNAME
0014 # include <sys/utsname.h>
0015 #endif
0016 
0017 #include <kaboutdata.h>
0018 #include <kactioncollection.h>
0019 #include <kpluginfactory.h>
0020 #include <kstandardaction.h>
0021 
0022 #include <qapplication.h>
0023 #include <qcryptographichash.h>
0024 #include <qdesktopwidget.h>
0025 #include <qdir.h>
0026 #include <qjsondocument.h>
0027 #include <qscreen.h>
0028 
0029 #include "skgmainpanel.h"
0030 #include "skgtraces.h"
0031 
0032 /**
0033  * This plugin factory.
0034  */
0035 K_PLUGIN_CLASS_WITH_JSON(SKGStatisticPlugin, "metadata.json")
0036 
0037 SKGStatisticPlugin::SKGStatisticPlugin(QWidget* iWidget, QObject* iParent, const QVariantList& /*iArg*/) :
0038     SKGInterfacePlugin(iParent), m_currentDocument(nullptr)
0039 {
0040     Q_UNUSED(iWidget)
0041     m_timeInit = QDateTime::currentDateTime();
0042     SKGTRACEINFUNC(10)
0043 
0044     connect(SKGMainPanel::getMainPanel(), &SKGMainPanel::currentPageChanged, this, &SKGStatisticPlugin::pageChanged);
0045     connect(SKGMainPanel::getMainPanel(), &SKGMainPanel::pageOpened, this, &SKGStatisticPlugin::pageOpened);
0046 }
0047 
0048 SKGStatisticPlugin::~SKGStatisticPlugin()
0049 {
0050     SKGTRACEINFUNC(10)
0051     // Set duration
0052     m_stats[QStringLiteral("avg.exec_time_sec")] = (m_stats.value(QStringLiteral("avg.exec_time_sec")).toDouble() * (m_stats.value(QStringLiteral("nb_launch")).toInt() - 1) + m_timeInit.secsTo(QDateTime::currentDateTime())) / m_stats.value(QStringLiteral("nb_launch")).toInt();
0053 
0054     // Write stat file
0055     writeStats();
0056 
0057     m_currentDocument = nullptr;
0058 }
0059 
0060 bool SKGStatisticPlugin::setupActions(SKGDocument* iDocument)
0061 {
0062     SKGTRACEINFUNC(10)
0063 
0064     m_currentDocument = iDocument;
0065 
0066     setComponentName(QStringLiteral("skg_statistic"), title());
0067     setXMLFile(QStringLiteral("skg_statistic.rc"));
0068     return true;
0069 }
0070 
0071 void SKGStatisticPlugin::refresh()
0072 {
0073     SKGTRACEINFUNC(10)
0074     if (m_currentDocument != nullptr) {
0075         if (m_currentDocument->getMainDatabase() != nullptr) {
0076             static bool initialisationDone = false;
0077             if (!initialisationDone) {
0078                 // Connect actions
0079                 QMap<QString, QPointer<QAction> > actions = SKGMainPanel::getMainPanel()->getGlobalActions();
0080                 QStringList keys = actions.keys();
0081                 for (const auto& k : qAsConst(keys)) {
0082                     QPointer<QAction> act = actions[k];
0083                     connect(act.data(), &QAction::triggered, this, &SKGStatisticPlugin::triggerAction);
0084                 }
0085                 initialisationDone = true;
0086             }
0087 
0088             QString doc_id = m_currentDocument->getUniqueIdentifier();
0089             if (m_docUniqueIdentifier != doc_id) {
0090                 m_docUniqueIdentifier = doc_id;
0091 
0092                 // Initialize
0093                 QString appname = KAboutData::applicationData().componentName();
0094                 auto dir = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) ;
0095                 QDir(dir).mkdir("statistics");
0096                 QString docUUID = QString(QCryptographicHash::hash(m_currentDocument->getCurrentFileName().toLatin1(), QCryptographicHash::Md5).toHex());
0097                 m_file =  dir % "/statistics/" % docUUID % ".stats.txt";
0098 
0099                 // Read previous stat file
0100                 readStats();
0101 
0102                 // Initial values
0103                 if (!m_stats.contains(QStringLiteral("init.date"))) {
0104                     m_stats[QStringLiteral("init.date")] = QDate::currentDate();
0105                     m_stats[QStringLiteral("init.qt_version")] = qVersion();
0106                     m_stats[QStringLiteral("init.app_name")] = appname;
0107                     m_stats[QStringLiteral("init.app_version")] = KAboutData::applicationData().version();
0108                 }
0109                 m_stats[QStringLiteral("nb_launch")] = m_stats.value(QStringLiteral("nb_launch")).toInt() + 1;
0110 
0111                 // Current values
0112                 m_stats[QStringLiteral("current.date")] = QDate::currentDate();
0113                 m_stats[QStringLiteral("current.qt_version")] = qVersion();
0114                 m_stats[QStringLiteral("current.app_version")] = KAboutData::applicationData().version();
0115                 m_stats[QStringLiteral("current.language")] = QLocale::languageToString(QLocale().language());
0116                 m_stats[QStringLiteral("current.country")] = QLocale::countryToString(QLocale().country());
0117                 m_stats[QStringLiteral("current.country.code")] = QLocale().name().split(QStringLiteral("_")).at(0);
0118                 m_stats[QStringLiteral("current.locale")] = QLocale().name();
0119 
0120                 // OS
0121 #ifdef Q_OS_WIN32
0122                 QString os = QStringLiteral("Windows");
0123 #elif defined(Q_OS_FREEBSD)
0124                 QString os = QStringLiteral("FreeBSD");
0125 #elif defined(Q_OS_NETBSD)
0126                 QString os = QStringLiteral("NetBSD");
0127 #elif defined(Q_OS_OPENBSD)
0128                 QString os = QStringLiteral("OpenBSD");
0129 #elif defined(Q_OS_LINUX)
0130                 QString os = QStringLiteral("Linux");
0131 #elif defined(Q_OS_MAC)
0132                 QString os = QStringLiteral("Mac OS");
0133 #else
0134                 QString os = QStringLiteral("Unknown");
0135 #endif
0136                 m_stats[QStringLiteral("current.os")] = os;
0137                 QRect scr = QGuiApplication::primaryScreen()->geometry();
0138                 m_stats[QStringLiteral("current.screen")] = QString(SKGServices::intToString(scr.width()) % 'x' % SKGServices::intToString(scr.height()));
0139 
0140 #ifdef HAVE_UNAME
0141                 struct utsname buf {};
0142                 if (uname(&buf) != -1) {
0143                     m_stats[QStringLiteral("current.os.machine")] = QString::fromLocal8Bit(buf.machine);
0144                     m_stats[QStringLiteral("current.os.version")] = QString::fromLocal8Bit(buf.version);
0145                 }
0146 #endif
0147 
0148                 // Nb calls
0149                 QMap<QString, QPointer<QAction> > actions = SKGMainPanel::getMainPanel()->getGlobalActions();
0150                 QStringList keys = actions.keys();
0151                 for (const auto& k : qAsConst(keys)) {
0152                     QPointer<QAction> act = actions[k];
0153                     if (act != nullptr) {
0154                         QString id = "nb_call." % act->objectName();
0155                         if (!m_stats.contains(id)) {
0156                             m_stats[id] = 0;
0157                         }
0158                     }
0159                 }
0160                 m_stats[QStringLiteral("document.uuid")] = docUUID;
0161 
0162                 // Set tables sizes
0163                 QStringList tables;
0164                 m_currentDocument->getTablesList(tables);
0165                 for (const auto& t : qAsConst(tables)) {
0166                     QString r;
0167                     m_currentDocument->executeSingleSelectSqliteOrder("SELECT COUNT(1) FROM " % t, r);
0168                     m_stats["count." % t] = SKGServices::stringToInt(r);
0169                 }
0170             }
0171         }
0172     }
0173 }
0174 
0175 void SKGStatisticPlugin::readStats()
0176 {
0177     m_stats.clear();
0178 
0179     // Read file
0180     QFile data(m_file);
0181     if (data.open(QFile::ReadOnly)) {
0182         // Parse json
0183         m_stats = QJsonDocument::fromJson(data.readAll()).toVariant().toMap();
0184         data.close();
0185     }
0186 }
0187 
0188 void SKGStatisticPlugin::writeStats()
0189 {
0190     // Write it in file
0191     QFile data(m_file);
0192     if (data.open(QFile::WriteOnly | QFile::Truncate)) {
0193         // serialize json
0194         QJsonDocument serializer = QJsonDocument::fromVariant(m_stats);
0195         QByteArray doc = serializer.toJson(QJsonDocument::Indented);
0196 
0197         data.write(doc);
0198         data.close();
0199     } else {
0200         SKGTRACE << "ERROR: Impossible to write " << m_file << SKGENDL;
0201     }
0202 }
0203 
0204 void SKGStatisticPlugin::triggerAction()
0205 {
0206     SKGTRACEINFUNC(10)
0207     auto* act = qobject_cast< QAction* >(sender());
0208     if (act != nullptr) {
0209         QString id = "nb_call." % act->objectName();
0210         SKGTRACEL(10) << "SKGStatisticPlugin::triggerAction " << id << "++" << SKGENDL;
0211         m_stats[id] = m_stats[id].toInt() + 1;
0212     }
0213 }
0214 
0215 void SKGStatisticPlugin::pageChanged()
0216 {
0217     SKGTabPage::SKGPageHistoryItem currentPage = SKGMainPanel::getMainPanel()->currentPageHistoryItem();
0218     if (!currentPage.plugin.isEmpty()) {
0219         QString id = "nb_activated_" % QString(currentPage.bookmarkID.isEmpty() ? QStringLiteral("page") : QStringLiteral("bookmark")) % "." % currentPage.plugin;
0220         m_stats[id] = m_stats[id].toInt() + 1;
0221     }
0222 }
0223 
0224 void SKGStatisticPlugin::pageOpened()
0225 {
0226     SKGTabPage::SKGPageHistoryItem currentPage = SKGMainPanel::getMainPanel()->currentPageHistoryItem();
0227     if (!currentPage.plugin.isEmpty()) {
0228         QString id = "nb_opened_" % QString(currentPage.bookmarkID.isEmpty() ? QStringLiteral("page") : QStringLiteral("bookmark")) % "." % currentPage.plugin;
0229         m_stats[id] = m_stats[id].toInt() + 1;
0230     }
0231 }
0232 
0233 QString SKGStatisticPlugin::title() const
0234 {
0235     return i18nc("The title", "Statistic");
0236 }
0237 
0238 QString SKGStatisticPlugin::icon() const
0239 {
0240     return QStringLiteral("dialog-information");
0241 }
0242 
0243 QString SKGStatisticPlugin::toolTip() const
0244 {
0245     return title();
0246 }
0247 
0248 int SKGStatisticPlugin::getOrder() const
0249 {
0250     return 9999;
0251 }
0252 
0253 bool SKGStatisticPlugin::isInPagesChooser() const
0254 {
0255     return false;
0256 }
0257 
0258 #include <skgstatisticplugin.moc>