1 /***************************************************************************
2  * SPDX-FileCopyrightText: 2021 S. MANKOWSKI stephane@mankowski.fr
3  * SPDX-FileCopyrightText: 2021 G. DE BURE support@mankowski.fr
4  * SPDX-License-Identifier: GPL-3.0-or-later
5  ***************************************************************************/
6 /** @file
7  * A report class for document
8  *
9  * @author Stephane MANKOWSKI
10  */
11 #include "skgreport.h"
12 
13 #include <grantlee/engine.h>
14 #include <grantlee/metatype.h>
15 #include <grantlee/qtlocalizer.h>
16 #include <grantlee/templateloader.h>
17 
18 #include <kaboutdata.h>
19 #include <kcolorscheme.h>
20 
21 #include <qdir.h>
22 #include <qfile.h>
23 #include <qfont.h>
24 #include <qfontdatabase.h>
25 #include <qstandardpaths.h>
26 #include <qurl.h>
27 #include "qregularexpression.h"
28 #include <QRandomGenerator>
29 
30 #include "skgdocument.h"
31 #include "skgobjectbase.h"
32 #include "skgtraces.h"
33 
34 GRANTLEE_BEGIN_LOOKUP(SKGObjectBase)
Q_UNUSED(object)35 Q_UNUSED(object)
36 Q_UNUSED(property)
37 GRANTLEE_END_LOOKUP
38 
39 SKGReport::SKGReport(SKGDocument* iDocument)
40     :  m_document(iDocument), m_previous(nullptr), m_pointSize(10)
41 {
42     SKGTRACEINFUNC(1)
43 
44     // Grantlee initialization
45     Grantlee::registerMetaType<SKGObjectBase>();
46 }
47 
~SKGReport()48 SKGReport::~SKGReport()
49 {
50     SKGTRACEINFUNC(1)
51     if (m_previous != nullptr) {
52         delete m_previous;
53         m_previous = nullptr;
54     }
55 }
56 
getDocument() const57 SKGDocument* SKGReport::getDocument() const
58 {
59     return m_document;
60 }
61 
setPeriod(const QString & iPeriod)62 void SKGReport::setPeriod(const QString& iPeriod)
63 {
64     if (iPeriod != m_cache[QStringLiteral("period")]) {
65         cleanCache(false);
66         if (m_previous != nullptr) {
67             delete m_previous;
68             m_previous = nullptr;
69         }
70         m_cache[QStringLiteral("period")] = iPeriod;
71         emit changed();
72     }
73 }
74 
getPeriod()75 QString SKGReport::getPeriod()
76 {
77     QString month = m_cache.value(QStringLiteral("period")).toString();
78     if (month.isEmpty()) {
79         month = QDate::currentDate().toString(QStringLiteral("yyyy-MM"));
80         m_cache[QStringLiteral("period")] = month;
81     }
82     return month;
83 }
84 
setSqlFilter(const QString & iFilter)85 void SKGReport::setSqlFilter(const QString& iFilter)
86 {
87     if (iFilter != m_cache[QStringLiteral("filter")]) {
88         cleanCache(false);
89         m_cache[QStringLiteral("filter")] = iFilter;
90         emit changed();
91     }
92 }
93 
getSqlFilter()94 QString SKGReport::getSqlFilter()
95 {
96     return m_cache.value(QStringLiteral("filter")).toString();;
97 }
98 
getPreviousPeriod()99 QString SKGReport::getPreviousPeriod()
100 {
101     QString previousmonth = m_cache.value(QStringLiteral("previousperiod")).toString();
102     if (previousmonth.isEmpty()) {
103         QString period = getPeriod();
104         if (!period.isEmpty()) {
105             previousmonth = SKGServices::getNeighboringPeriod(period);
106         }
107         m_cache[QStringLiteral("previousperiod")] = previousmonth;
108     }
109     return previousmonth;
110 }
111 
getPrevious()112 SKGReport* SKGReport::getPrevious()
113 {
114     if (m_previous == nullptr) {
115         m_previous = m_document->getReport();
116         m_previous->setPeriod(getPreviousPeriod());
117     }
118     return m_previous;
119 }
120 
setTipsOfDay(const QStringList & iTipsOfDays)121 void SKGReport::setTipsOfDay(const QStringList& iTipsOfDays)
122 {
123     m_tipsOfTheDay = iTipsOfDays;
124 
125     emit changed();
126 }
127 
getTipOfDay() const128 QString SKGReport::getTipOfDay() const
129 {
130     auto tips = getTipsOfDay();
131     auto tip = tips.count() > 0 ? SKGServices::htmlToString(tips.at(QRandomGenerator::global()->bounded(tips.size()))) : QString();
132     return tip;
133 }
134 
getTipsOfDay() const135 QStringList SKGReport::getTipsOfDay() const
136 {
137     return m_tipsOfTheDay;
138 }
139 
getContextProperty()140 QVariantHash SKGReport::getContextProperty()
141 {
142     QVariantHash mapping;
143     addItemsInMapping(mapping);
144 
145     if (m_document != nullptr) {
146         mapping.insert(QStringLiteral("document"), QVariant::fromValue<QObject*>(m_document));
147     }
148     return mapping;
149 }
150 
addItemsInMapping(QVariantHash & iMapping)151 void SKGReport::addItemsInMapping(QVariantHash& iMapping)
152 {
153     iMapping.insert(QStringLiteral("report"), QVariant::fromValue<QObject*>(this));
154     iMapping.insert(QStringLiteral("current_date"), QDate::currentDate());
155     KColorScheme scheme(QPalette::Normal, KColorScheme::Window);
156     iMapping.insert(QStringLiteral("color_negativetext"), scheme.foreground(KColorScheme::NegativeText).color().name().right(6));
157     iMapping.insert(QStringLiteral("color_positivetext"), scheme.foreground(KColorScheme::PositiveText).color().name().right(6));
158     iMapping.insert(QStringLiteral("color_neutraltext"), scheme.foreground(KColorScheme::NeutralText).color().name().right(6));
159     iMapping.insert(QStringLiteral("color_normaltext"), scheme.foreground(KColorScheme::NormalText).color().name().right(6));
160     iMapping.insert(QStringLiteral("color_inactivetext"), scheme.foreground(KColorScheme::InactiveText).color().name().right(6));
161     iMapping.insert(QStringLiteral("color_activetext"), scheme.foreground(KColorScheme::ActiveText).color().name().right(6));
162     iMapping.insert(QStringLiteral("color_linktext"), scheme.foreground(KColorScheme::LinkText).color().name().right(6));
163     iMapping.insert(QStringLiteral("color_visitedtext"), scheme.foreground(KColorScheme::VisitedText).color().name().right(6));
164     iMapping.insert(QStringLiteral("color_normalbackground"), scheme.background(KColorScheme::NormalBackground).color().name().right(6));
165     iMapping.insert(QStringLiteral("color_activebackground"), scheme.background(KColorScheme::ActiveBackground).color().name().right(6));
166 
167     QFont generalFont = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
168     iMapping.insert(QStringLiteral("font_family"), generalFont.family());
169 
170     QString dir = "file://" % QFileInfo(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/infopage/kde_infopage.css"))).dir().absolutePath() % '/';
171     {
172         QFile file(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/infopage/kde_infopage.css")));
173         if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
174             iMapping.insert(QStringLiteral("kde_infopage_css"), QString(file.readAll()).replace(QStringLiteral("url("), "url(" % dir));
175         }
176     }
177 
178     KAboutData about = KAboutData::applicationData();
179     iMapping.insert(QStringLiteral("about_welcome"), i18nc("Welcome message", "Welcome to %1", about.displayName()));
180     iMapping.insert(QStringLiteral("about_programname"), about.displayName());
181     iMapping.insert(QStringLiteral("about_version"), about.version());
182     iMapping.insert(QStringLiteral("about_bugaddress"), about.bugAddress());
183     iMapping.insert(QStringLiteral("about_copyrightstatement"), about.copyrightStatement());
184     iMapping.insert(QStringLiteral("about_homepage"), about.homepage());
185     iMapping.insert(QStringLiteral("about_shortdescription"), about.shortDescription());
186     iMapping.insert(QStringLiteral("about_othertext"), about.otherText());
187     iMapping.insert(QStringLiteral("about_did_you_know"), i18nc("Title for tips of the day", "Did you know ...?"));
188 }
189 
getReportFromTemplate(SKGReport * iReport,const QString & iFile,QString & oHtml)190 SKGError SKGReport::getReportFromTemplate(SKGReport* iReport, const QString& iFile, QString& oHtml)
191 {
192     SKGError err;
193     SKGTRACEINFUNCRC(10, err)
194 
195     // Prepare grantlee engine
196     Grantlee::Engine gEngine;
197     gEngine.addDefaultLibrary(QStringLiteral("grantlee_skgfilters"));
198 
199     QSharedPointer<Grantlee::FileSystemTemplateLoader> gLoader = QSharedPointer<Grantlee::FileSystemTemplateLoader>(new Grantlee::FileSystemTemplateLoader());
200     gLoader->setTemplateDirs(QStringList(QFileInfo(iFile).dir().absolutePath()));
201     gEngine.addTemplateLoader(gLoader);
202 
203     Grantlee::Template gTemplate = gEngine.loadByName(QFileInfo(iFile).fileName());
204     if (gTemplate->error() != 0u) {
205         err = SKGError(gTemplate->error(), gTemplate->errorString());
206     } else {
207         QVariantHash mapping;
208         if (iReport != nullptr) {
209             mapping = iReport->getContextProperty();
210         }
211         Grantlee::Context gContext(mapping);
212 
213         // Generation
214         {
215             SKGTRACEINFUNCRC(10, err)
216             oHtml = gTemplate->render(&gContext);
217             QRegularExpression rx(QStringLiteral("\\n\\s*\\n"));
218             oHtml = oHtml.replace(rx, QStringLiteral("\n"));
219             if (gTemplate->error() != 0u) {
220                 err = SKGError(gTemplate->error(), gTemplate->errorString());
221             }
222         }
223     }
224     return err;
225 }
226 
cleanCache(bool iEmitSignal)227 void SKGReport::cleanCache(bool iEmitSignal)
228 {
229     QString month = m_cache.value(QStringLiteral("period")).toString();
230     QString filter = m_cache.value(QStringLiteral("filter")).toString();
231     m_cache.clear();
232     if (!month.isEmpty()) {
233         m_cache[QStringLiteral("period")] = month;
234     }
235     if (!filter.isEmpty()) {
236         m_cache[QStringLiteral("filter")] = filter;
237     }
238     if (iEmitSignal) {
239         emit changed();
240     }
241 }
242 
addParameter(const QString & iName,const QVariant & ivalue)243 void SKGReport::addParameter(const QString& iName, const QVariant& ivalue)
244 {
245     m_parameters[iName] = ivalue;
246 }
247 
setPointSize(int iPointSize)248 void SKGReport::setPointSize(int iPointSize)
249 {
250     m_pointSize = iPointSize;
251     emit changed();
252 }
253 
getPointSize() const254 int SKGReport::getPointSize() const
255 {
256     return m_pointSize;
257 }
258