LCOV - code coverage report
Current view: top level - src/operations - LoadNewsOperation.cpp (source / functions) Coverage Total Hit
Test: coverage.info.cleaned Lines: 89.6 % 135 121
Test Date: 2026-04-19 00:35:54 Functions: 92.9 % 14 13

            Line data    Source code
       1              : #include "LoadNewsOperation.h"
       2              : #include "../models/NewsList.h"
       3              : #include "../utilities/ErrorHandling.h"
       4              : #include "../utilities/FangLogging.h"
       5              : 
       6           47 : LoadNewsOperation::LoadNewsOperation(OperationManager *parent, FeedItem* feedItem, LoadMode mode, int loadLimit) :
       7              :     DBOperation(parent),
       8           47 :     feedItem(feedItem),
       9           47 :     listAppend(),
      10           47 :     listPrepend(),
      11           47 :     mode(mode),
      12           47 :     loadLimit(loadLimit)
      13              : {
      14           47 : }
      15              : 
      16           47 : LoadNewsOperation::~LoadNewsOperation()
      17              : {
      18              : 
      19           47 : }
      20              : 
      21            3 : QString LoadNewsOperation::modeToString(LoadMode mode)
      22              : {
      23            3 :     switch(mode) {
      24            1 :     case Initial:
      25            1 :         return "initial";
      26            1 :     case Prepend:
      27            1 :         return "prepend";
      28            1 :     case Append:
      29            1 :         return "append";
      30            0 :     default:
      31            0 :         FANG_UNREACHABLE("Invalid LoadMode enum value");
      32              :         return "[ERROR]";
      33              :     }
      34              : }
      35              : 
      36            3 : LoadNewsOperation::LoadMode LoadNewsOperation::stringToMode(QString modeString)
      37              : {
      38            3 :     if (modeString == "initial") {
      39            1 :         return LoadMode::Initial;
      40            2 :     } else if (modeString == "prepend") {
      41            1 :         return LoadMode::Prepend;
      42            1 :     } else if (modeString == "append") {
      43            1 :         return LoadMode::Append;
      44              :     } else {
      45            0 :         qCWarning(logOperation) << "Invalid string load mode: " << modeString;
      46            0 :         FANG_UNREACHABLE("Invalid LoadMode string");
      47              :         return LoadMode::Error;
      48              :     }
      49              : }
      50              : 
      51           56 : void LoadNewsOperation::queryToNewsList(QSqlQuery& query, QList<NewsItem*>* list)
      52              : {
      53          175 :     while (query.next()) {
      54              :         // Load from DB query result.
      55              :         NewsItem* newsItem = new NewsItem(
      56              :                     feedItem,
      57          119 :                     query.value("id").toULongLong(),
      58          238 :                     query.value("feed_id").toULongLong(),
      59          238 :                     query.value("title").toString(),
      60          238 :                     query.value("author").toString(),
      61          238 :                     query.value("summary").toString(),
      62          238 :                     query.value("content").toString(),
      63          238 :                     QDateTime::fromMSecsSinceEpoch(query.value("timestamp").toLongLong()),
      64          238 :                     query.value("url").toString(),
      65          119 :                     (bool) query.value("pinned").toInt(),
      66          238 :                     query.value("media_image_url").toString()
      67          833 :                     );
      68              :         
      69              :         // Add to our list.
      70          119 :         list->append(newsItem);
      71              :     }
      72           56 : }
      73              : 
      74           11 : qint64 LoadNewsOperation::getBookmarkID()
      75              : {
      76           11 :     QSqlQuery query(db());
      77           11 :     query.prepare("SELECT bookmark_id FROM FeedItemTable WHERE id = :id");
      78           11 :     query.bindValue(":id", feedItem->getDbID());
      79              :     
      80           11 :     if (!query.exec() || !query.next()) {
      81            0 :        qCDebug(logOperation) << "Could not update bookmark for feed id: " << feedItem->getDbID();
      82            0 :        qCDebug(logOperation) << query.lastError();
      83              :        
      84            0 :        return -1;
      85              :     }
      86              :     
      87           11 :     return query.value("bookmark_id").toULongLong();
      88           11 : }
      89              : 
      90           11 : qint64 LoadNewsOperation::getFirstNewsID()
      91              : {
      92              :     const QString queryString = "SELECT id FROM NewsItemTable WHERE feed_id = :feed_id "
      93           11 :             "ORDER BY timestamp ASC, id ASC LIMIT 1";
      94              :     
      95           11 :     QSqlQuery query(db());
      96           11 :     query.prepare(queryString);
      97              :     
      98           11 :     query.bindValue(":feed_id", feedItem->getDbID());
      99              :     
     100           11 :     if (!query.exec() || !query.next()) {
     101              :         // No news yet!
     102            1 :         return -1;
     103              :     }
     104              :     
     105           10 :     return query.value("id").toULongLong();
     106           11 : }
     107              : 
     108            9 : bool LoadNewsOperation::doAppend(qint64 startId)
     109              : {
     110           18 :     qCDebug(logOperation) << "LoadNewsOperation::doAppend " << startId;
     111              :     // Extract the query into our news list.
     112            9 :     return executeLoadQuery(startId, true);
     113              : }
     114              : 
     115            5 : bool LoadNewsOperation::doPrepend(qint64 startId)
     116              : {
     117           10 :     qCDebug(logOperation) << "LoadNewsOperation::doPrepend " << startId;
     118              :     // Extract the query into our news list.
     119            5 :     return executeLoadQuery(startId, false);
     120              : }
     121              : 
     122           14 : bool LoadNewsOperation::executeLoadQuery(qint64 startId, bool append)
     123              : {
     124           14 :     if (append) {
     125            9 :         listAppend.clear();
     126              :     } else {
     127            5 :         listPrepend.clear();
     128              :     }
     129              : 
     130           14 :     QString direction = append ? ">=" : "<";
     131           14 :     QString sortOrder = append ? "ASC" : "DESC";
     132              :     QString queryString = "SELECT * FROM NewsItemTable WHERE feed_id = :feed_id AND id " 
     133           28 :             + direction + " :start_id ORDER BY timestamp " + sortOrder + ", id " + sortOrder
     134           14 :             + " LIMIT :load_limit";
     135              :     
     136           28 :     qCDebug(logOperation) << "executeLoadQuery: Query string: " << queryString;
     137           14 :     QSqlQuery query(db());
     138           14 :     query.prepare(queryString);
     139              :     
     140           14 :     query.bindValue(":feed_id", feedItem->getDbID());
     141           14 :     query.bindValue(":start_id", startId);
     142           14 :     query.bindValue(":load_limit", loadLimit);
     143              :     
     144           14 :     if (!query.exec()) {
     145            0 :        qCDebug(logOperation) << "Could not load news for feed id: " << feedItem->getDbID();
     146            0 :        qCDebug(logOperation) << query.lastError();
     147              :        
     148            0 :        return false;
     149              :     }
     150              :     
     151              :     // Extract the query into our news list.
     152           14 :     queryToNewsList(query, append ? &listAppend : &listPrepend);
     153              :     
     154           14 :     return true;
     155           14 : }
     156              : 
     157            2 : qint64 LoadNewsOperation::getStartIDForAppend()
     158              : {
     159            2 :     qint64 startId = -1;
     160            2 :     if (feedItem->getNewsList() != nullptr && feedItem->getNewsList()->size() > 0) {
     161            1 :         startId = feedItem->getNewsList()->last()->getDbID() + 1; // Advance to next item
     162              :     }
     163              : 
     164            2 :     return startId;
     165              : }
     166              : 
     167            2 : qint64 LoadNewsOperation::getStartIDForPrepend()
     168              : {
     169            2 :     qint64 startId = -1;
     170            2 :     if (feedItem->getNewsList() != nullptr && feedItem->getNewsList()->size() > 0) {
     171            1 :         startId = feedItem->getNewsList()->first()->getDbID();
     172              :     }
     173              : 
     174            2 :     return startId;
     175              : }
     176              : 
     177           12 : void LoadNewsOperation::execute()
     178              : {
     179           12 :     if (feedItem->isSpecialFeed()) {
     180              :         // Wrong type of load operation for special feed.
     181            2 :         qCCritical(logOperation) << "LoadNewsOperation: Cannot use base LoadNewsOperation for special feeds";
     182            1 :         return;
     183              :     }
     184              :     
     185              :     // DB query/ies.
     186           11 :     bool dbResult = true;
     187           11 :     qint64 bookmarkID = getBookmarkID(); // Default: unbookmarked = -1
     188           11 :     qint64 firstNewsID = getFirstNewsID();
     189           11 :     switch (mode) {
     190            7 :     case Initial:
     191              :     {
     192              :         // For an initial load, make sure the feed isn't populated yet.
     193            7 :         FANG_CHECK(feedItem->getNewsList() == nullptr || feedItem->getNewsList()->isEmpty(),
     194              :                    "LoadNewsOperation: Initial load on already-populated feed");
     195              : 
     196            7 :         qint64 startId = bookmarkID;
     197            7 :         if (startId > 0) {
     198              :             // We has a bookmark, yo!
     199              :             // Try to load previous items.
     200            3 :             dbResult &= doPrepend(startId);
     201              :             
     202            6 :             qCDebug(logOperation) << "Start id: " << startId;
     203              :         }
     204              :         
     205              :         // Load next items, if available.
     206            7 :         dbResult &= doAppend(startId);
     207              :         
     208            7 :         break;
     209              :     }
     210              :     
     211            2 :     case Append:
     212              :     {
     213            2 :         dbResult &= doAppend(getStartIDForAppend());
     214              :         
     215            2 :         break;
     216              :     }
     217              :         
     218            2 :     case Prepend:
     219              :     {
     220            2 :         dbResult &= doPrepend(getStartIDForPrepend());
     221              : 
     222            2 :         break;
     223              :     }
     224              : 
     225            0 :     default:
     226            0 :         FANG_UNREACHABLE("LoadNewsOperation::executeSynchronous: Invalid LoadMode");
     227              :         break;
     228              :     }
     229              :     
     230              :     // Check if we done goofed.
     231           11 :     if (!dbResult) {
     232            0 :         reportError("Got no DB result.");
     233              :         
     234            0 :         return;
     235              :     }
     236              :     
     237              :     // Append/prepend items from our lists.
     238           11 :     if (!listAppend.isEmpty()) {
     239           35 :         for (NewsItem* newsItem: listAppend) {
     240           27 :             feedItem->getNewsList()->append(newsItem);
     241              :         }
     242              :     }
     243              :     
     244           11 :     if (!listPrepend.isEmpty()) {
     245           15 :         for (NewsItem* newsItem: listPrepend) {
     246           11 :             feedItem->getNewsList()->prepend(newsItem);
     247              :         }
     248              :     }
     249              :     
     250              :     // Set our bookmark.
     251           11 :     if (bookmarkID >= 0) {
     252            4 :         feedItem->setBookmark(bookmarkID);
     253              :     }
     254              :     
     255              :     // Set the first known ID.
     256           11 :     feedItem->setFirstNewsID(firstNewsID);
     257              : }
        

Generated by: LCOV version 2.0-1