summaryrefslogtreecommitdiff
path: root/src/atomchannel.cpp
diff options
context:
space:
mode:
authorNikita Kostovsky <nikita@kostovsky.me>2025-06-22 16:54:02 +0200
committerNikita Kostovsky <nikita@kostovsky.me>2025-06-22 16:54:02 +0200
commitf674e179d602d3ccb9818d28fe06f371059449dc (patch)
tree996fb624986512de91581a18332f004d34220ba2 /src/atomchannel.cpp
parse and insert feeds and items
Diffstat (limited to 'src/atomchannel.cpp')
-rw-r--r--src/atomchannel.cpp197
1 files changed, 197 insertions, 0 deletions
diff --git a/src/atomchannel.cpp b/src/atomchannel.cpp
new file mode 100644
index 0000000..6982e8b
--- /dev/null
+++ b/src/atomchannel.cpp
@@ -0,0 +1,197 @@
+#include "atomchannel.h"
+
+#include <QSqlError>
+#include <QSqlQuery>
+#include <QXmlStreamReader>
+
+#include "constants.h"
+#include "macros.h"
+#include "rsshit_db.h"
+
+AtomChannel::AtomChannel(QXmlStreamReader *xmlReader)
+{
+ Q_ASSERT(xmlReader != nullptr);
+
+ const QString titleTag{"title"};
+ const QString linkTag{"link"};
+ const QString descriptionTag{"description"};
+ const QString lastBuildDateTag{"lastBuildDate"};
+ const QString languageTag{"language"};
+
+ while (!xmlReader->atEnd() && !xmlReader->hasError()) {
+ const auto itemNext = xmlReader->readNext();
+
+ switch (itemNext) {
+ case QXmlStreamReader::TokenType::StartElement: {
+ const auto name = xmlReader->name();
+ // qDebug() << __func__ << ": StartElement" << name;
+ // qDebug() << __func__ << "namespaceUri:" << xmlReader->namespaceUri();
+ // qDebug() << __func__ << "prefix:" << xmlReader->prefix();
+ // qDebug() << __func__ << "qualifiedName:" << xmlReader->qualifiedName();
+ // const auto elementText = xmlReader->readElementText();
+
+ if (name == titleTag)
+ title = xmlReader->readElementText();
+ else if (name == linkTag)
+ link = xmlReader->readElementText();
+ else if (name == descriptionTag)
+ description = xmlReader->readElementText();
+ else if (name == lastBuildDateTag)
+ lastBuildDate = QDateTime::fromString(xmlReader->readElementText(),
+ Qt::DateFormat::RFC2822Date);
+ else if (name == languageTag)
+ language = QLocale{xmlReader->readElementText()}.language();
+ else if (name == AtomChannelImage::tag) {
+ // qDebug() << "got image tag";
+ image = AtomChannelImage{xmlReader};
+ // qDebug() << image;
+ } else if (name == AtomItem::tag) {
+ items << AtomItem{xmlReader};
+ // qDebug() << items.constLast();
+ // qDebug() << __func__ << "got feed item";
+ } else {
+ // qDebug() << "exit" << __func__;
+ // qDebug() << __func__ << "unknown tag:" << name;
+ continue;
+ }
+
+ break;
+ }
+ case QXmlStreamReader::TokenType::EndElement: {
+ // qDebug() << "EndElement: " << xmlReader->name();
+
+ if (xmlReader->name() == AtomChannel::xmlTag)
+ return;
+ }
+ case QXmlStreamReader::TokenType::Characters: {
+ const auto characters = xmlReader->text().toString().simplified();
+
+ if (characters.isEmpty())
+ break;
+
+ qDebug() << "channel: characters: " << characters;
+ break;
+ }
+ }
+ }
+
+ qDebug() << "exit " << __func__;
+}
+
+int AtomChannel::getDbId()
+{
+ if (dbId != rsshit::db::IdNotFound)
+ return dbId;
+
+ const auto db = rsshit::db::open();
+
+ if (!db)
+ return rsshit::db::IdNotFound;
+
+ QSqlQuery selectQ{"select id from feeds where link=?"};
+ selectQ.addBindValue(link);
+
+ if (!selectQ.exec()) {
+ qWarning() << "cannot exec query" << selectQ.lastQuery() << ":"
+ << selectQ.lastError().text() << ":" << selectQ.executedQuery();
+
+ return rsshit::db::IdNotFound;
+ }
+
+ if (!selectQ.next())
+ return rsshit::db::IdNotFound;
+
+ const auto idVariant = selectQ.value(rsshit::db::idTag);
+
+ if (!idVariant.isValid() || !idVariant.canConvert<int>())
+ return rsshit::db::IdNotFound;
+
+ bool ok{false};
+ const auto result = idVariant.toInt(&ok);
+
+ if (!ok) {
+ qWarning() << "got invalid id from db:" << idVariant;
+
+ return rsshit::db::IdNotFound;
+ }
+
+ return result;
+}
+
+int AtomChannel::createInDb()
+{
+ if (dbId != rsshit::db::IdNotFound)
+ return dbId;
+
+ const auto db = rsshit::db::open();
+
+ if (!db)
+ return rsshit::db::IdNotFound;
+
+ QSqlQuery insertQ{"insert into feeds(link, title, image_url) values(?, ?, ?)"};
+ insertQ.addBindValue(link);
+ insertQ.addBindValue(title);
+ insertQ.addBindValue(image.url);
+
+ if (!insertQ.exec()) {
+ qWarning() << "cannot exec query" << insertQ.lastQuery() << ":"
+ << insertQ.lastError().text() << ":" << insertQ.executedQuery();
+
+ return rsshit::db::IdNotFound;
+ }
+
+ return insertQ.lastInsertId().toInt();
+}
+
+// TODO: can be moved to IDbObject
+int AtomChannel::getOrInsertDbId()
+{
+ const auto id = getDbId();
+
+ if (id != rsshit::db::IdNotFound)
+ return id;
+
+ return createInDb();
+}
+
+QList<int> AtomChannel::syncDbItems()
+{
+ if (dbId == rsshit::db::IdNotFound)
+ dbId = getOrInsertDbId();
+
+ if (dbId == rsshit::db::IdNotFound)
+ return {};
+
+ QList<int> result;
+
+ for (auto &item : items) {
+ auto id = item.getOrInsertDbId(this->dbId);
+
+ if (id != rsshit::db::IdNotFound)
+ result << id;
+ }
+
+ return result;
+}
+
+QDebug operator<<(QDebug debug, const AtomChannel &channel)
+{
+ QDebugStateSaver saver{debug};
+
+ debug.nospace() << typeid(AtomChannel).name() << " {" << Qt::endl;
+
+ PRINT_ATOM_FIELD(channel, title);
+ PRINT_ATOM_FIELD(channel, link);
+ PRINT_ATOM_FIELD(channel, description);
+ PRINT_ATOM_FIELD(channel, lastBuildDate);
+ PRINT_ATOM_FIELD(channel, language);
+ debug << "\timage:\n" << channel.image << Qt::endl;
+ debug << "\titems count:" << channel.items.size() << Qt::endl;
+
+ for (const auto &item : channel.items)
+ debug << item << Qt::endl;
+
+ debug.nospace() << "}";
+
+ return debug;
+}