File indexing completed on 2025-03-16 13:29:32
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 }