Alkimia API

alkonlinequotesource.cpp
1/*
2 * SPDX-FileCopyrightText: 2018,2024 Ralf Habacker ralf.habacker@freenet.de
3 * SPDX-FileCopyrightText: 2023 Thomas Baumgart <tbaumgart@kde.org>
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5 *
6 * This file is part of libalkimia.
7 */
8
9#include "alkonlinequotesource.h"
10
11#include "alkonlinequotesource_p.h"
12#include "alkonlinequotesprofile.h"
13#include "alktestdefs.h"
14
15#include <klocalizedstring.h>
16
17/**
18 * Key to identifying "Finance::Quote" sources
19 */
20static const char *fqName = "Finance::Quote";
21
22AlkOnlineQuoteSource::AlkOnlineQuoteSource()
23 : d(new Private)
24{
25}
26
27AlkOnlineQuoteSource::AlkOnlineQuoteSource(const AlkOnlineQuoteSource &other)
28 : d(new Private(other.d))
29{
30}
31
32AlkOnlineQuoteSource &AlkOnlineQuoteSource::operator=(AlkOnlineQuoteSource other)
33{
34 swap(*this, other);
35 return *this;
36}
37
38AlkOnlineQuoteSource::AlkOnlineQuoteSource(const QString& name,
39 const QString& url,
40 const QString& idRegex,
41 const IdSelector idBy,
42 const QString& priceRegex,
43 const QString& dateRegex,
44 const QString& dateFormat,
45 DataFormat dataFormat,
46 DecimalSeparator priceDecimalSeparator,
47 DownloadType downloadType)
48 : d(new Private)
49{
50 d->m_name = name;
51 d->m_url = url;
52 d->m_idRegex = idRegex;
53 d->m_idSelector = idBy;
54 d->m_priceDecimalSeparator = priceDecimalSeparator;
55 d->m_priceRegex = priceRegex;
56 d->m_dataFormat = dataFormat;
57 d->m_dateRegex = dateRegex;
58 d->m_dateFormat = dateFormat;
59 d->m_downloadType = downloadType;
60 d->m_isGHNSSource = false;
61}
62
63AlkOnlineQuoteSource::AlkOnlineQuoteSource(const QString &name, AlkOnlineQuotesProfile *profile)
64 : d(new Private)
65{
66 if ((profile != nullptr) && (profile->type() == AlkOnlineQuotesProfile::Type::None) && (profile->defaultQuoteSources().contains(name))) {
67 *this = profile->defaultQuoteSources()[name];
68 } else {
69 d->m_profile = profile;
70 d->m_name = name;
71 read();
72 }
73}
74
75AlkOnlineQuoteSource AlkOnlineQuoteSource::defaultCurrencyQuoteSource(const QString& name)
76{
77 return AlkOnlineQuoteSource(name,
78 "https://fx-rate.net/%1/%2",
79 QString(), // idregexp
80 AlkOnlineQuoteSource::Symbol,
81 "Today\\s+=\\s+([^<]+)",
82 ",\\s*(\\d+\\s*[a-zA-Z]{3}\\s*\\d{4})",
83 "%d %m %y",
84 HTML
85 );
86}
87AlkOnlineQuoteSource AlkOnlineQuoteSource::testQuoteSource(const QString& name, bool twoSymbols, DownloadType downloadType, DataFormat format)
88{
89 QString urlString = TEST_LAUNCH_URL;
91 QString priceRegexString;
92 QString dateRegexString;
93 QString dateFormatRegex(QLatin1String("%d/%m/%y"));
94
95 urlString.append(QLatin1String("a=%1"));
96 if (twoSymbols) {
97 urlString.append("&b=%2");
98 }
99
100 if (format == HTML) {
101 if (twoSymbols) {
102 priceRegexString = QLatin1String("</span><br\\s*/>\\s+([\\d.]+)\\s+\\w");
103 dateRegexString = QLatin1String("([\\d]+/[\\d]+/[\\d]+)");
104 } else {
105 priceRegexString = QLatin1String("<body>([\\d.]+) ");
106 dateRegexString = QLatin1String("([\\d]+/[\\d]+/[\\d]+)");
107 }
108 } else if (format == CSV) {
109 type = QLatin1String("&type=csv");
110 priceRegexString = QLatin1String("value");
111 dateRegexString = QLatin1String("date");
112 } else if (format == JSON) {
113 type = QLatin1String("&type=json");
114 priceRegexString = QLatin1String("chart:result:indicators:quote:open");
115 dateRegexString = QLatin1String("chart:result:timestamp");
116 dateFormatRegex = QLatin1String("%u");
117 } else {
118 return AlkOnlineQuoteSource();
119 }
120 urlString.append(type);
121
122 if (downloadType == Javascript) {
123 urlString.append(QLatin1String("&dtype=javascript"));
124 priceRegexString = QLatin1String("<span>\\s*([\\d.]+)\\s+\\w");
125 dateRegexString = QLatin1String("([\\d]+/[\\d]+/[\\d]+)");
126 }
127
128 return AlkOnlineQuoteSource(name,
129 urlString,
130 QString(), // idregexp
131 AlkOnlineQuoteSource::Symbol,
132 priceRegexString,
133 dateRegexString,
134 dateFormatRegex,
135 format,
136 AlkOnlineQuoteSource::Period,
137 downloadType
138 );
139}
140
141AlkOnlineQuoteSource::~AlkOnlineQuoteSource()
142{
143 delete d;
144}
145
150
152{
153 return !d->m_referenceId.isEmpty();
154}
155
156bool AlkOnlineQuoteSource::isEmpty()
157{
158 return !isValid() && !d->m_url.isEmpty();
159}
160
161bool AlkOnlineQuoteSource::isValid()
162{
163 return !d->m_name.isEmpty();
164}
165
167{
168 return d->m_profile->GHNSName(d->m_referenceId);
169}
170
171QString AlkOnlineQuoteSource::name() const
172{
173 return d->m_name;
174}
175
176QString AlkOnlineQuoteSource::url() const
177{
178 return d->m_url;
179}
180
181QString AlkOnlineQuoteSource::idRegex() const
182{
183 return d->m_idRegex;
184}
185
186AlkOnlineQuoteSource::IdSelector AlkOnlineQuoteSource::idSelector() const
187{
188 return d->m_idSelector;
189}
190
191QString AlkOnlineQuoteSource::priceRegex() const
192{
193 return d->m_priceRegex;
194}
195
196AlkOnlineQuoteSource::DecimalSeparator AlkOnlineQuoteSource::priceDecimalSeparator() const
197{
198 return d->m_priceDecimalSeparator;
199}
200
201QString AlkOnlineQuoteSource::dateRegex() const
202{
203 return d->m_dateRegex;
204}
205
207{
208 if (isReference())
209 return asReference().dataFormat();
210
211 return d->m_dataFormat;
212}
213
214QString AlkOnlineQuoteSource::dateFormat() const
215{
216 return d->m_dateFormat;
217}
218
219AlkOnlineQuoteSource::DownloadType AlkOnlineQuoteSource::downloadType() const
220{
221 return d->m_downloadType;
222}
223
224/**
225 * Returns the name of the "Finance::Quote" source.
226 * This function only makes sense if the current source
227 * is of the specified type.
228 *
229 * @return "Finance::Quote" source name
230 */
232{
233 return d->m_name.section(' ', 1);
234}
235
237{
238 d->m_referenceId = d->m_profile->GHNSId(name);
239}
240
241void AlkOnlineQuoteSource::setName(const QString &name)
242{
243 d->m_name = name;
244}
245
246void AlkOnlineQuoteSource::setUrl(const QString &url)
247{
248 d->m_url = url;
249}
250
251void AlkOnlineQuoteSource::setPriceRegex(const QString &priceRegex)
252{
253 d->m_priceRegex = priceRegex;
254}
255
256void AlkOnlineQuoteSource::setPriceDecimalSeparator(DecimalSeparator separator)
257{
258 d->m_priceDecimalSeparator = separator;
259}
260
261void AlkOnlineQuoteSource::setIdRegex(const QString &idRegex)
262{
263 d->m_idRegex = idRegex;
264}
265
266void AlkOnlineQuoteSource::setIdSelector(AlkOnlineQuoteSource::IdSelector idSelector)
267{
268 d->m_idSelector = idSelector;
269}
270
271/**
272 * Set regular expression for parsing dates
273 *
274 * An empty string as expression disables the extraction
275 * of the date, which is sometimes necessary, for example
276 * if the service does not provide a complete date.
277 *
278 * @param date regular expression
279 */
281{
282 d->m_dateRegex = dateRegex;
283}
284
286{
287 d->m_dataFormat = dataFormat;
288}
289
290void AlkOnlineQuoteSource::setDateFormat(const QString &dateFormat)
291{
292 d->m_dateFormat = dateFormat;
293}
294
295void AlkOnlineQuoteSource::setDownloadType(DownloadType downloadType)
296{
297 d->m_downloadType = downloadType;
298}
299
300void AlkOnlineQuoteSource::setGHNS(bool state)
301{
302 d->m_storageChanged = d->m_isGHNSSource != state;
303 d->m_isGHNSSource = state;
304}
305
306bool AlkOnlineQuoteSource::isGHNS() const
307{
308 return d->m_isGHNSSource;
309}
310
311bool AlkOnlineQuoteSource::isReadOnly() const
312{
313 return d->m_readOnly;
314}
315
316/**
317 * Checks whether the current source is of type "Finance::Quote"
318 *
319 * @return state
320 */
322{
323 return d->m_name.contains(fqName);
324}
325
326/**
327 * Checks whether the specified source name is of type "Finance::Quote"
328 *
329 * @return state
330 */
332{
333 return name.contains(fqName);
334}
335
336QString AlkOnlineQuoteSource::ghnsWriteFileName() const
337{
338 return d->ghnsWriteFilePath();
339}
340
341void AlkOnlineQuoteSource::setProfile(AlkOnlineQuotesProfile *profile)
342{
343 d->m_profile = profile;
344 alkDebug() << "using profile" << profile->name();
345}
346
347AlkOnlineQuotesProfile *AlkOnlineQuoteSource::profile()
348{
349 return d->m_profile;
350}
351
352AlkOnlineQuotesProfile *AlkOnlineQuoteSource::profile() const
353{
354 return d->m_profile;
355}
356
357bool AlkOnlineQuoteSource::read()
358{
359 if (d->m_profile->hasGHNSSupport()) {
360 if (d->readFromGHNSFile()) {
361 return true;
362 }
363 }
364 return d->read();
365}
366
367bool AlkOnlineQuoteSource::write()
368{
369 bool result = false;
370 if (d->m_profile) {
371 // check if type has been changedd->isGHNS
372 if (d->m_profile->hasGHNSSupport() && d->m_isGHNSSource) {
373 result = d->writeToGHNSFile();
374 if (d->m_storageChanged)
375 d->remove();
376 return result;
377 } else {
378 if (d->m_storageChanged && d->m_profile->quoteSources().contains(d->m_name))
379 d->m_name.append(".local");
380 result = d->write();
381 if (d->m_profile->hasGHNSSupport() && d->m_storageChanged) {
382 d->removeGHNSFile();
383 }
384 }
385 d->m_storageChanged = false;
386 }
387 return result;
388}
389
390void AlkOnlineQuoteSource::rename(const QString &name)
391{
392 if (d->m_profile->type() != AlkOnlineQuotesProfile::Type::None) {
393 remove();
394 d->m_name = name;
395 write();
396 } else
397 d->m_name = name;
398}
399
400void AlkOnlineQuoteSource::remove()
401{
402 if (d->m_profile->hasGHNSSupport() && d->m_isGHNSSource) {
403 d->removeGHNSFile();
404 } else if (d->m_profile->type() != AlkOnlineQuotesProfile::Type::None) {
405 d->remove();
406 }
407}
408
410{
411 return d->m_defaultId;
412}
413
415{
416 d->m_defaultId = defaultId;
417}
418
420{
421 if (isReference())
422 return asReference().url().contains("%2");
423
424 return url().contains("%2");
425}
426
428{
429 return d->m_dataFormat == CSV || d->m_dataFormat == JSON;
430}
431
432AlkOnlineQuoteSource::Private::Private(const Private *other)
433 : m_referenceId(other->m_referenceId)
434 , m_name(other->m_name)
435 , m_url(other->m_url)
436 , m_priceDecimalSeparator(other->m_priceDecimalSeparator)
437 , m_priceRegex(other->m_priceRegex)
438 , m_dataFormat(other->m_dataFormat)
439 , m_dateRegex(other->m_dateRegex)
440 , m_dateFormat(other->m_dateFormat)
441 , m_defaultId(other->m_defaultId)
442 , m_downloadType(other->m_downloadType)
443 , m_idRegex(other->m_idRegex)
444 , m_idSelector(other->m_idSelector)
445 , m_profile(other->m_profile)
446 , m_isGHNSSource(other->m_isGHNSSource)
447 , m_storageChanged(other->m_storageChanged)
448 , m_readOnly(other->m_readOnly)
449{
450}
451
453{
454 switch(format) {
455 case AlkOnlineQuoteSource::StrippedHTML:
456 return i18nc("@item:inlistbox Stock", "Stripped HTML");
457 case AlkOnlineQuoteSource::DataFormat::HTML:
458 return i18nc("@item:inlistbox Stock", "HTML");
459 case AlkOnlineQuoteSource::DataFormat::CSV:
460 return i18nc("@item:inlistbox Stock", "CSV");
461 case AlkOnlineQuoteSource::DataFormat::CSS:
462 return i18nc("@item:inlistbox Stock", "CSS");
463 case AlkOnlineQuoteSource::DataFormat::JSON:
464 return i18nc("@item:inlistbox Stock", "JSON");
465 default:
466 return QString();
467 }
468}
void setReferenceName(const QString &name)
Make this source a reference.
const QString & defaultId() const
Return the default identifier known to work.
void setDefaultId(const QString &defaultId)
Set the default identifier, which is known to work.
bool requiresTwoIdentifier() const
Return state if this source requires two identifier.
DataFormat
Supported formats of downloaded data.
bool isReference() const
Return state if this source is a reference.
DecimalSeparator
Type of decimal separator.
AlkOnlineQuoteSource asReference() const
Return referenced quote source.
QString financeQuoteName() const
Returns the name of the "Finance::Quote" source.
DataFormat dataFormat() const
Return the format of the downloaded data.
bool isFinanceQuote() const
Checks whether the current source is of type "Finance::Quote".
void setDataFormat(DataFormat dataFormat)
Set the format of the downloaded data.
QString referenceName() const
Return name of the referenced quote source.
void setDateRegex(const QString &dateRegex)
Set regular expression for parsing dates.
bool supportsDateRange() const
Return state if this source supports a date range.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
char * toString(const EngineQuery &query)
QString name(StandardAction id)
bool contains(const Key &key) const const
QString & append(QChar ch)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:01:13 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.