From f674e179d602d3ccb9818d28fe06f371059449dc Mon Sep 17 00:00:00 2001 From: Nikita Kostovsky Date: Sun, 22 Jun 2025 16:54:02 +0200 Subject: parse and insert feeds and items --- src/atomchannel.cpp | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 src/atomchannel.cpp (limited to 'src/atomchannel.cpp') 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 +#include +#include + +#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()) + 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 AtomChannel::syncDbItems() +{ + if (dbId == rsshit::db::IdNotFound) + dbId = getOrInsertDbId(); + + if (dbId == rsshit::db::IdNotFound) + return {}; + + QList 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; +} -- cgit v1.2.3-70-g09d2