File indexing completed on 2025-01-19 03:53:31

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2015-06-01
0007  * Description : DB Jobs for listing and scanning
0008  *
0009  * SPDX-FileCopyrightText: 2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "dbjob.h"
0016 
0017 // Local includes
0018 
0019 #include "coredbaccess.h"
0020 #include "dbengineparameters.h"
0021 #include "coredb.h"
0022 #include "facetags.h"
0023 #include "itemlister.h"
0024 #include "digikam_debug.h"
0025 #include "dbjobsthread.h"
0026 
0027 namespace Digikam
0028 {
0029 
0030 DBJob::DBJob()
0031     : ActionJob()
0032 {
0033 }
0034 
0035 DBJob::~DBJob()
0036 {
0037 }
0038 
0039 // ----------------------------------------------
0040 
0041 AlbumsJob::AlbumsJob(const AlbumsDBJobInfo& jobInfo)
0042     : DBJob    (),
0043       m_jobInfo(jobInfo)
0044 {
0045 }
0046 
0047 AlbumsJob::~AlbumsJob()
0048 {
0049 }
0050 
0051 void AlbumsJob::run()
0052 {
0053     if (m_jobInfo.isFoldersJob())
0054     {
0055         const QHash<int, int>& albumNumberHash = CoreDbAccess().db()->getNumberOfImagesInAlbums();
0056 
0057         Q_EMIT foldersData(albumNumberHash);
0058     }
0059     else
0060     {
0061         ItemLister lister;
0062         lister.setRecursive(m_jobInfo.isRecursive());
0063         lister.setListOnlyAvailable(m_jobInfo.isListAvailableImagesOnly());
0064 
0065         // Send data every 200 images to be more responsive
0066 
0067         ItemListerJobGrowingPartsSendingReceiver receiver(this, 200, 2000, 100);
0068         lister.listPAlbum(&receiver, m_jobInfo.albumRootId(), m_jobInfo.album());
0069         receiver.sendData();
0070     }
0071 
0072     Q_EMIT signalDone();
0073 }
0074 
0075 // ----------------------------------------------
0076 
0077 DatesJob::DatesJob(const DatesDBJobInfo& jobInfo)
0078     : DBJob    (),
0079       m_jobInfo(jobInfo)
0080 {
0081 }
0082 
0083 DatesJob::~DatesJob()
0084 {
0085 }
0086 
0087 void DatesJob::run()
0088 {
0089     if (m_jobInfo.isFoldersJob())
0090     {
0091         const QVariantList& values = CoreDbAccess().db()->getAllCreationDates();
0092 
0093         QHash<QDateTime, int> dateNumberHash;
0094         QHash<QDateTime, int>::iterator it;
0095 
0096         Q_FOREACH (const QVariant& value, values)
0097         {
0098             if (!value.isNull())
0099             {
0100                 QDateTime dateTime = value.toDateTime();
0101                 dateTime.setTimeSpec(Qt::UTC);
0102 
0103                 if (!dateTime.isValid())
0104                 {
0105                     continue;
0106                 }
0107 
0108                 it = dateNumberHash.find(dateTime);
0109 
0110                 if (it == dateNumberHash.end())
0111                 {
0112                     dateNumberHash.insert(dateTime, 1);
0113                 }
0114                 else
0115                 {
0116                     it.value()++;
0117                 }
0118             }
0119         }
0120 
0121         Q_EMIT foldersData(dateNumberHash);
0122     }
0123     else
0124     {
0125         ItemLister lister;
0126         lister.setListOnlyAvailable(m_jobInfo.isListAvailableImagesOnly());
0127 
0128         // Send data every 200 images to be more responsive
0129 
0130         ItemListerJobPartsSendingReceiver receiver(this, 200);
0131         lister.listDateRange(&receiver, m_jobInfo.startDate(), m_jobInfo.endDate());
0132 
0133         // Send rest
0134 
0135         receiver.sendData();
0136     }
0137 
0138     Q_EMIT signalDone();
0139 }
0140 
0141 // ----------------------------------------------
0142 
0143 GPSJob::GPSJob(const GPSDBJobInfo& jobInfo)
0144     : DBJob    (),
0145       m_jobInfo(jobInfo)
0146 {
0147 }
0148 
0149 GPSJob::~GPSJob()
0150 {
0151 }
0152 
0153 void GPSJob::run()
0154 {
0155     if (m_jobInfo.isDirectQuery())
0156     {
0157         QList<QVariant> imagesInfoFromArea =
0158                 CoreDbAccess().db()->getImageIdsFromArea(m_jobInfo.lat1(),
0159                                                          m_jobInfo.lat2(),
0160                                                          m_jobInfo.lng1(),
0161                                                          m_jobInfo.lng2(),
0162                                                          0,
0163                                                          QLatin1String("rating"));
0164 
0165         Q_EMIT directQueryData(imagesInfoFromArea);
0166     }
0167     else
0168     {
0169         ItemLister lister;
0170         lister.setListOnlyAvailable(m_jobInfo.isListAvailableImagesOnly());
0171 
0172         // Send data every 200 images to be more responsive
0173 
0174         ItemListerJobPartsSendingReceiver receiver(this, 200);
0175         lister.listAreaRange(&receiver,
0176                              m_jobInfo.lat1(),
0177                              m_jobInfo.lat2(),
0178                              m_jobInfo.lng1(),
0179                              m_jobInfo.lng2());
0180         // send rest
0181 
0182         receiver.sendData();
0183     }
0184 
0185     Q_EMIT signalDone();
0186 }
0187 
0188 // ----------------------------------------------
0189 
0190 TagsJob::TagsJob(const TagsDBJobInfo& jobInfo)
0191     : DBJob    (),
0192       m_jobInfo(jobInfo)
0193 {
0194 }
0195 
0196 TagsJob::~TagsJob()
0197 {
0198 }
0199 
0200 void TagsJob::run()
0201 {
0202     if      (m_jobInfo.isFoldersJob())
0203     {
0204         const QHash<int, int>& tagNumberHash = CoreDbAccess().db()->getNumberOfImagesInTags();
0205 
0206         //qCDebug(DIGIKAM_DBJOB_LOG) << tagNumberHash;
0207 
0208         Q_EMIT foldersData(tagNumberHash);
0209     }
0210     else if (m_jobInfo.isFaceFoldersJob())
0211     {
0212         QString property;
0213         QHash<int, int> counts;
0214         QMap<QString, QHash<int, int> > facesNumberMap;
0215 
0216         property = ImageTagPropertyName::autodetectedFace();
0217         counts   = CoreDbAccess().db()->getNumberOfImagesInTagProperties(property);
0218 
0219         if (!counts.contains(FaceTags::unknownPersonTagId()))
0220         {
0221             counts[FaceTags::unknownPersonTagId()] = 0;
0222         }
0223 
0224         facesNumberMap.insert(property, counts);
0225 
0226         property = ImageTagPropertyName::autodetectedPerson();
0227         counts   = CoreDbAccess().db()->getNumberOfImagesInTagProperties(property);
0228 
0229         facesNumberMap.insert(property, counts);
0230 
0231         property = ImageTagPropertyName::ignoredFace();
0232         counts   = CoreDbAccess().db()->getNumberOfImagesInTagProperties(property);
0233 
0234         facesNumberMap.insert(property, counts);
0235 
0236         property = ImageTagPropertyName::tagRegion();
0237         counts   = CoreDbAccess().db()->getNumberOfImagesInTagProperties(property);
0238 
0239         Q_FOREACH (int tagId, FaceTags::allPersonTags())
0240         {
0241             if (!counts.contains(tagId))
0242             {
0243                 counts[tagId] = 0;
0244             }
0245         }
0246 
0247         facesNumberMap.insert(property, counts);
0248 
0249         Q_EMIT faceFoldersData(facesNumberMap);
0250     }
0251     else
0252     {
0253         ItemLister lister;
0254         lister.setRecursive(m_jobInfo.isRecursive());
0255         lister.setListOnlyAvailable(m_jobInfo.isListAvailableImagesOnly());
0256 
0257         // Send data every 200 images to be more responsive
0258 
0259         ItemListerJobPartsSendingReceiver receiver(this, 200);
0260 
0261         if (!m_jobInfo.specialTag().isNull())
0262         {
0263             QString searchXml = lister.tagSearchXml(m_jobInfo.tagsIds().constFirst(),
0264                                                     m_jobInfo.specialTag(),
0265                                                     m_jobInfo.isRecursive());
0266             lister.listImageTagPropertySearch(&receiver, searchXml);
0267         }
0268         else
0269         {
0270             lister.listTag(&receiver, m_jobInfo.tagsIds());
0271         }
0272 
0273         // Finish sending
0274 
0275         receiver.sendData();
0276     }
0277 
0278     Q_EMIT signalDone();
0279 }
0280 
0281 // ----------------------------------------------
0282 
0283 SearchesJob::SearchesJob(const SearchesDBJobInfo& jobInfo)
0284     : DBJob    (),
0285       m_jobInfo(jobInfo),
0286       m_iface  (nullptr)
0287 {
0288 }
0289 
0290 SearchesJob::SearchesJob(const SearchesDBJobInfo& jobInfo,
0291                          const QSet<qlonglong>::const_iterator& begin,
0292                          const QSet<qlonglong>::const_iterator& end,
0293                          HaarIface* iface)
0294     : DBJob    (),
0295       m_jobInfo(jobInfo),
0296       m_begin  (begin),
0297       m_end    (end),
0298       m_iface  (iface)
0299 {
0300 }
0301 
0302 SearchesJob::~SearchesJob()
0303 {
0304 }
0305 
0306 void SearchesJob::run()
0307 {
0308     m_jobInfo.isDuplicatesJob() ? runFindDuplicates() : runSearches();
0309 }
0310 
0311 void SearchesJob::runSearches()
0312 {
0313     QList<SearchInfo> infos;
0314 
0315     Q_FOREACH (int id, m_jobInfo.searchIds())
0316     {
0317         infos << CoreDbAccess().db()->getSearchInfo(id);
0318     }
0319 
0320     ItemLister lister;
0321     lister.setListOnlyAvailable(m_jobInfo.isListAvailableImagesOnly());
0322 
0323     // Send data every 200 images to be more responsive
0324 
0325     ItemListerJobPartsSendingReceiver receiver(this, 200);
0326 
0327     Q_FOREACH (const SearchInfo& info, infos)
0328     {
0329         if (info.type == DatabaseSearch::HaarSearch)
0330         {
0331             lister.listHaarSearch(&receiver, info.query);
0332         }
0333         else
0334         {
0335             bool ok;
0336             qlonglong referenceImageId = info.name.toLongLong(&ok);
0337 
0338             if (ok)
0339             {
0340                 lister.listSearch(&receiver, info.query, 0, referenceImageId);
0341             }
0342             else
0343             {
0344                 lister.listSearch(&receiver, info.query, 0, -1);
0345             }
0346         }
0347 
0348         if (!receiver.hasError)
0349         {
0350             receiver.sendData();
0351         }
0352     }
0353 
0354     Q_EMIT signalDone();
0355 }
0356 
0357 void SearchesJob::runFindDuplicates()
0358 {
0359     if (m_jobInfo.imageIds().isEmpty())
0360     {
0361         qCDebug(DIGIKAM_DBJOB_LOG) << "No image ids passed for duplicates search";
0362 
0363         Q_EMIT signalDuplicatesResults(HaarIface::DuplicatesResultsMap());
0364         return;
0365     }
0366 
0367     DuplicatesProgressObserver observer(this);
0368 
0369     if (!m_iface)
0370     {
0371         qCDebug(DIGIKAM_DBJOB_LOG) << "Invalid HaarIface pointer";
0372 
0373         Q_EMIT signalDuplicatesResults(HaarIface::DuplicatesResultsMap());
0374         return;
0375     }
0376 
0377     auto restriction = static_cast<HaarIface::DuplicatesSearchRestrictions>(m_jobInfo.searchResultRestriction());
0378     auto results     = m_iface->findDuplicates(m_jobInfo.imageIds(),
0379                                                m_begin,
0380                                                m_end,
0381                                                m_jobInfo.refImageSelectionMethod(),
0382                                                m_jobInfo.refImageIds(),
0383                                                m_jobInfo.minThreshold(),
0384                                                m_jobInfo.maxThreshold(),
0385                                                restriction,
0386                                                &observer);
0387 
0388     Q_EMIT signalDuplicatesResults(results);
0389 }
0390 
0391 bool SearchesJob::isCanceled() const
0392 {
0393     return m_cancel;
0394 }
0395 
0396 } // namespace Digikam
0397 
0398 #include "moc_dbjob.cpp"