File indexing completed on 2024-04-28 16:13:25

0001 /*
0002     SPDX-FileCopyrightText: 2018 Ralf Habacker ralf.habacker @freenet.de
0003 
0004     This file is part of libalkimia.
0005 
0006     SPDX-License-Identifier: LGPL-2.1-or-later
0007 */
0008 
0009 #include "alkonlinequotesource.h"
0010 
0011 #include "alkonlinequotesprofile.h"
0012 
0013 #include <QFile>
0014 #include <QFileInfo>
0015 #include <QtDebug>
0016 
0017 #include <KConfig>
0018 #include <KConfigGroup>
0019 
0020 /**
0021  * Key to identifying "Finance::Quote" sources
0022  */
0023 static const char *fqName = "Finance::Quote";
0024 
0025 class AlkOnlineQuoteSource::Private
0026 {
0027 public:
0028     Private()
0029         : m_skipStripping(false)
0030         , m_profile(nullptr)
0031         , m_isGHNSSource(false)
0032         , m_storageChanged(false)
0033         , m_readOnly(true)
0034     {
0035     }
0036 
0037     Private(const Private *other)
0038         : m_name(other->m_name)
0039         , m_url(other->m_url)
0040         , m_sym(other->m_sym)
0041         , m_price(other->m_price)
0042         , m_date(other->m_date)
0043         , m_dateformat(other->m_dateformat)
0044         , m_skipStripping(other->m_skipStripping)
0045         , m_profile(other->m_profile)
0046         , m_isGHNSSource(other->m_isGHNSSource)
0047         , m_storageChanged(other->m_storageChanged)
0048         , m_readOnly(other->m_readOnly)
0049     {
0050     }
0051 
0052     bool read()
0053     {
0054         KConfig *kconfig = m_profile->kConfig();
0055         if (!kconfig)
0056             return false;
0057         const QString &group = QString("Online-Quote-Source-%1").arg(m_name);
0058         if (!kconfig->hasGroup(group)) {
0059             return false;
0060         }
0061         KConfigGroup grp = kconfig->group(group);
0062         m_sym = grp.readEntry("SymbolRegex");
0063         m_date = grp.readEntry("DateRegex");
0064         m_dateformat = grp.readEntry("DateFormatRegex", "%m %d %y");
0065         m_price = grp.readEntry("PriceRegex");
0066         m_url = grp.readEntry("URL");
0067         m_skipStripping = grp.readEntry("SkipStripping", false);
0068         m_isGHNSSource = false;
0069         m_readOnly = false;
0070         return true;
0071     }
0072 
0073     bool write()
0074     {
0075         KConfig *kconfig = m_profile->kConfig();
0076         if (!kconfig)
0077             return false;
0078         KConfigGroup grp = kconfig->group(QString("Online-Quote-Source-%1").arg(m_name));
0079         grp.writeEntry("URL", m_url);
0080         grp.writeEntry("PriceRegex", m_price);
0081         grp.writeEntry("DateRegex", m_date);
0082         grp.writeEntry("DateFormatRegex", m_dateformat);
0083         grp.writeEntry("SymbolRegex", m_sym);
0084         if (m_skipStripping) {
0085             grp.writeEntry("SkipStripping", m_skipStripping);
0086         } else {
0087             grp.deleteEntry("SkipStripping");
0088         }
0089         kconfig->sync();
0090         return true;
0091     }
0092 
0093     bool remove()
0094     {
0095         KConfig *kconfig = m_profile->kConfig();
0096         if (!kconfig)
0097             return false;
0098         kconfig->deleteGroup(QString("Online-Quote-Source-%1").arg(m_name));
0099         kconfig->sync();
0100         return true;
0101     }
0102 
0103     QString ghnsReadFilePath()
0104     {
0105         return m_profile->hotNewStuffReadFilePath(m_name + QLatin1String(".txt"));
0106     }
0107 
0108     QString ghnsWriteFilePath()
0109     {
0110         return m_profile->hotNewStuffWriteFilePath(m_name + QLatin1String(".txt"));
0111     }
0112 
0113     // This is currently in skrooge format
0114     bool readFromGHNSFile()
0115     {
0116         QFileInfo f(ghnsReadFilePath());
0117         if (!f.exists())
0118             f.setFile(ghnsWriteFilePath());
0119         m_readOnly = !f.isWritable();
0120 
0121         QFile file(f.absoluteFilePath());
0122         if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
0123             return false;
0124 
0125         QTextStream in(&file);
0126         while (!in.atEnd()) {
0127             QString line = in.readLine();
0128             int index = line.indexOf("=");
0129             if (index == -1)
0130                 return false;
0131             QString key = line.left(index);
0132             QString value = line.mid(index+1);
0133             if (key == "url")
0134                 m_url = value;
0135             else if (key == "price") {
0136                 m_price = value;
0137                 m_price.replace("\\\\", "\\");
0138             } else if (key == "date") {
0139                 m_date = value;
0140                 m_date.replace("\\\\", "\\");
0141             } else if (key == "dateformat")
0142                 m_dateformat = value;
0143         }
0144 
0145         m_skipStripping = true;
0146         m_isGHNSSource = true;
0147         return true;
0148     }
0149 
0150     // This is currently in skrooge format
0151     bool writeToGHNSFile()
0152     {
0153         QFile file(ghnsWriteFilePath());
0154         if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
0155             return false;
0156 
0157         QTextStream out(&file);
0158         out << "date=" << m_date << "\n";
0159         out << "dateformat=" << m_dateformat << "\n";
0160         out << "mode=HTML\n";
0161         out << "price=" << m_price << "\n";
0162         out << "url=" << m_url << "\n";
0163         return true;
0164     }
0165 
0166     bool removeGHNSFile()
0167     {
0168         qDebug() << "delete" << ghnsWriteFilePath();
0169         return true;
0170     }
0171 
0172     QString m_name;
0173     QString m_url;
0174     QString m_sym;
0175     QString m_price;
0176     QString m_date;
0177     QString m_dateformat;
0178     bool m_skipStripping;
0179     AlkOnlineQuotesProfile *m_profile;
0180     bool m_isGHNSSource;
0181     bool m_storageChanged;
0182     bool m_readOnly;
0183 };
0184 
0185 AlkOnlineQuoteSource::AlkOnlineQuoteSource()
0186     : d(new Private)
0187 {
0188 }
0189 
0190 AlkOnlineQuoteSource::AlkOnlineQuoteSource(const AlkOnlineQuoteSource &other)
0191     : d(new Private(other.d))
0192 {
0193 }
0194 
0195 AlkOnlineQuoteSource &AlkOnlineQuoteSource::operator=(AlkOnlineQuoteSource other)
0196 {
0197     swap(*this, other);
0198     return *this;
0199 }
0200 
0201 AlkOnlineQuoteSource::AlkOnlineQuoteSource(const QString &name, const QString &url,
0202                                            const QString &sym, const QString &price,
0203                                            const QString &date, const QString &dateformat,
0204                                            bool skipStripping)
0205     : d(new Private)
0206 {
0207     d->m_name = name;
0208     d->m_url = url;
0209     d->m_sym = sym;
0210     d->m_price = price;
0211     d->m_date = date;
0212     d->m_dateformat = dateformat;
0213     d->m_skipStripping = skipStripping;
0214     d->m_isGHNSSource = false;
0215 }
0216 
0217 AlkOnlineQuoteSource::AlkOnlineQuoteSource(const QString &name, AlkOnlineQuotesProfile *profile)
0218     : d(new Private)
0219 {
0220     if ((profile != nullptr) && (profile->type() == AlkOnlineQuotesProfile::Type::None) && (profile->defaultQuoteSources().contains(name))) {
0221         *this = profile->defaultQuoteSources()[name];
0222     } else {
0223         d->m_profile = profile;
0224         d->m_name = name;
0225         read();
0226     }
0227 }
0228 
0229 AlkOnlineQuoteSource::~AlkOnlineQuoteSource()
0230 {
0231     delete d;
0232 }
0233 
0234 bool AlkOnlineQuoteSource::isEmpty()
0235 {
0236     return !isValid()  && !d->m_url.isEmpty();
0237 }
0238 
0239 bool AlkOnlineQuoteSource::isValid()
0240 {
0241     return !d->m_name.isEmpty();
0242 }
0243 
0244 QString AlkOnlineQuoteSource::name() const
0245 {
0246     return d->m_name;
0247 }
0248 
0249 QString AlkOnlineQuoteSource::url() const
0250 {
0251     return d->m_url;
0252 }
0253 
0254 QString AlkOnlineQuoteSource::sym() const
0255 {
0256     return d->m_sym;
0257 }
0258 
0259 QString AlkOnlineQuoteSource::price() const
0260 {
0261     return d->m_price;
0262 }
0263 
0264 QString AlkOnlineQuoteSource::date() const
0265 {
0266     return d->m_date;
0267 }
0268 
0269 QString AlkOnlineQuoteSource::dateformat() const
0270 {
0271     return d->m_dateformat;
0272 }
0273 
0274 /**
0275  * Returns the name of the "Finance::Quote" source.
0276  * This function only makes sense if the current source
0277  * is of the specified type.
0278  *
0279  * @return "Finance::Quote" source name
0280  */
0281 QString AlkOnlineQuoteSource::financeQuoteName() const
0282 {
0283     return d->m_name.section(' ', 1);
0284 }
0285 
0286 bool AlkOnlineQuoteSource::skipStripping() const
0287 {
0288     return d->m_skipStripping;
0289 }
0290 
0291 void AlkOnlineQuoteSource::setName(const QString &name)
0292 {
0293     d->m_name = name;
0294 }
0295 
0296 void AlkOnlineQuoteSource::setUrl(const QString &url)
0297 {
0298     d->m_url = url;
0299 }
0300 
0301 void AlkOnlineQuoteSource::setSym(const QString &symbol)
0302 {
0303     d->m_sym = symbol;
0304 }
0305 
0306 void AlkOnlineQuoteSource::setPrice(const QString &price)
0307 {
0308     d->m_price = price;
0309 }
0310 
0311 /**
0312  * Set regular expression for parsing dates
0313  *
0314  * An empty string as expression disables the extraction
0315  * of the date, which is sometimes necessary, for example
0316  * if the service does not provide a complete date.
0317  *
0318  * @param date regular expression
0319  */
0320 void AlkOnlineQuoteSource::setDate(const QString &date)
0321 {
0322     d->m_date = date;
0323 }
0324 
0325 void AlkOnlineQuoteSource::setDateformat(const QString &dateformat)
0326 {
0327     d->m_dateformat = dateformat;
0328 }
0329 
0330 void AlkOnlineQuoteSource::setSkipStripping(bool state)
0331 {
0332     d->m_skipStripping = state;
0333 }
0334 
0335 void AlkOnlineQuoteSource::setGHNS(bool state)
0336 {
0337     d->m_storageChanged = d->m_isGHNSSource != state;
0338     d->m_isGHNSSource = state;
0339 }
0340 
0341 bool AlkOnlineQuoteSource::isGHNS()
0342 {
0343     return d->m_isGHNSSource;
0344 }
0345 
0346 bool AlkOnlineQuoteSource::isReadOnly()
0347 {
0348     return d->m_readOnly;
0349 }
0350 
0351 /**
0352  * Checks whether the current source is of type "Finance::Quote"
0353  *
0354  * @return state
0355  */
0356 bool AlkOnlineQuoteSource::isFinanceQuote() const
0357 {
0358     return d->m_name.contains(fqName);
0359 }
0360 
0361 /**
0362  * Checks whether the specified source name is of type "Finance::Quote"
0363  *
0364  * @return state
0365  */
0366 bool AlkOnlineQuoteSource::isFinanceQuote(const QString &name)
0367 {
0368     return name.contains(fqName);
0369 }
0370 
0371 QString AlkOnlineQuoteSource::ghnsWriteFileName()
0372 {
0373     return d->ghnsWriteFilePath();
0374 }
0375 
0376 void AlkOnlineQuoteSource::setProfile(AlkOnlineQuotesProfile *profile)
0377 {
0378     d->m_profile = profile;
0379     qDebug() << "using profile" << profile->name();
0380 }
0381 
0382 AlkOnlineQuotesProfile *AlkOnlineQuoteSource::profile()
0383 {
0384     return d->m_profile;
0385 }
0386 
0387 bool AlkOnlineQuoteSource::read()
0388 {
0389     bool result = false;
0390     if (d->m_profile->hasGHNSSupport()) {
0391         result = d->readFromGHNSFile();
0392         if (result)
0393             return true;
0394     }
0395     return d->read();
0396 }
0397 
0398 bool AlkOnlineQuoteSource::write()
0399 {
0400     bool result = false;
0401     // check if type has been changedd->isGHNS
0402     if (d->m_profile->hasGHNSSupport() && d->m_isGHNSSource) {
0403         result = d->writeToGHNSFile();
0404         if (d->m_storageChanged)
0405             d->remove();
0406         return result;
0407     } else {
0408         if (d->m_storageChanged && d->m_profile->quoteSources().contains(d->m_name))
0409             d->m_name.append(".local");
0410         result = d->write();
0411         if (d->m_profile->hasGHNSSupport() && d->m_storageChanged) {
0412             d->removeGHNSFile();
0413         }
0414     }
0415     d->m_storageChanged = false;
0416     return result;
0417 }
0418 
0419 void AlkOnlineQuoteSource::rename(const QString &name)
0420 {
0421     if (d->m_profile->type() != AlkOnlineQuotesProfile::Type::None) {
0422         remove();
0423         d->m_name = name;
0424         write();
0425     } else
0426         d->m_name = name;
0427 }
0428 
0429 void AlkOnlineQuoteSource::remove()
0430 {
0431     if (d->m_profile->hasGHNSSupport() && d->m_isGHNSSource) {
0432         d->removeGHNSFile();
0433     } else if (d->m_profile->type() != AlkOnlineQuotesProfile::Type::None) {
0434         d->remove();
0435     }
0436 }