2 * Copyright (C) 2011, Jamie Thompson
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 3 of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public
15 * License along with this program; If not, see
16 * <http://www.gnu.org/licenses/>.
19 #include "SyncerThread.h"
21 #include "DBBackends/AllBackends.h"
22 #include "EventProcessors/Hasher.h"
23 #include "EventProcessors/Writer.h"
25 #include "Attachment.h"
26 #include "EventPreventer.h"
28 #include "EventTypes/EventFromFileList.h"
29 #include "EventTypes/iEvent.h"
30 #include "EventLogBackupManager.h"
31 #include "EventParsers/Factory.h"
32 #include "EventParsers/iEventParser.h"
39 #include <QSharedPointer>
43 typedef QPair<QFileInfo, uint> EventFileInfo;
45 QDebug operator<<(QDebug, QList<Attachment*> &);
46 QFileInfoList FindEvents(QFileInfoList);
48 SyncerThread::SyncerThread(Settings &settings) :
49 QThread(), m_Settings(settings)
55 SyncerThread::~SyncerThread()
59 m_Condition.wakeOne();
65 void SyncerThread::Sync()
74 m_Condition.wakeOne();
78 #include "NumberToNameLookup.h"
80 void SyncerThread::run()
84 EventPreventer preventEvents(CurrentSettings());
85 EventLogBackupManager backupManager(CurrentSettings());
87 if(CurrentSettings().Mode() == Settings::MODE_EXPORT)
89 qDebug() << "Exporting events";
91 // Temp - Remove directory first so it's always just this export for now
92 QDir().rmpath(CurrentSettings().Directory());
94 DBBackends::AllBackends allBackends(CurrentSettings());
95 NumberToNameLookup lookup; // Prepare the telephone-address book ID lookup.
96 EventProcessors::Writer eventWriter(CurrentSettings(), lookup);
97 QObject::connect(&eventWriter, SIGNAL(EventProcessed(int,int)), this, SIGNAL(EventProcessed(int,int)));
98 allBackends.Process(eventWriter);
99 QObject::disconnect(&eventWriter, SIGNAL(EventProcessed(int,int)), this, SIGNAL(EventProcessed(int,int)));
103 qDebug() << "Importing events";
105 backupManager.CreateBackup();
107 qDebug() << "Scanning filesystem";
109 // Open chosen directory and grab *all* files within (we filter at
111 QFileInfoList candidates(FindEvents(
112 QDir(CurrentSettings().Directory()).entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot)));
114 // We're going to be storing hashes to match up events on disk to
115 // those in the DB. Assuming 1 event per file is a good starting point.
116 // Each file can provide multiple events, so sotre the hash of the
117 // event, the file information, and the record index.
118 uint totalEvents(candidates.count());
119 QHash<QPair<QString, uint>, iHashable::Hash> hashesByPath;
120 hashesByPath.reserve(totalEvents);
121 QHash<iHashable::Hash, QPair<QString, uint> > pathsByHashes;
122 pathsByHashes.reserve(totalEvents);
124 qDebug() << "Hashing events";
126 // Work our way through the candidates...
128 foreach(QFileInfo fileInfo, candidates)
133 EventTypes::EventFromFileList fileEvents(ProcessFile(fileInfo.absoluteFilePath()));
135 foreach(EventTypes::EventFromFile eventFromFile, fileEvents)
137 const uint hash(eventFromFile.first->HashCode());
138 pathsByHashes.insert(hash, QPair<QString, iHashable::Hash>(fileInfo.absoluteFilePath(), eventFromFile.second));
139 hashesByPath.insert(QPair<QString, iHashable::Hash>(fileInfo.absoluteFilePath(), eventFromFile.second), hash);
144 catch(std::runtime_error& exception)
146 qDebug() << exception.what() << endl;
149 emit EventProcessed(idx, candidates.count());
153 qDebug() << "Determining new events";
155 // Calculate the new events by removing the hashes of events already
157 QSet<iHashable::Hash> newHashes(QSet<iHashable::Hash>::fromList( pathsByHashes.keys()));
159 //EventHasher eventHasher;
160 //ProcessDBEvents(eventHasher);
161 DBBackends::AllBackends allBackends(CurrentSettings());
162 EventProcessors::Hasher eventHasher;
163 allBackends.Process(eventHasher);
165 foreach(iHashable::Hash hash, eventHasher.m_Hashes)
166 newHashes.remove(hash);
168 qDebug() << QString("%1 new hashes").arg(newHashes.size()) << endl;
169 foreach(iHashable::Hash hash, newHashes)
170 qDebug() << hash << endl;
172 // Now an optimisation: group the new hashes by the files they come
173 // from. This enables each file to only be parsed once and return
174 // all the required events from it.
175 QHash<QString, QList<iHashable::Hash> > newHashesByPath;
176 foreach(iHashable::Hash newHash, newHashes)
178 const QString ¤tPath(pathsByHashes.value(newHash).first);
179 if(newHashesByPath.contains(currentPath))
180 newHashesByPath[currentPath].append(newHash);
182 newHashesByPath[currentPath] = QList<iHashable::Hash>() << newHash;
185 qDebug() << "Scanning addressbook";
187 // Prepare the telephone-address book ID lookup.
188 NumberToNameLookup lookup;
190 qDebug() << "Importing new events";
192 // Re-parse the new events and insert them
193 allBackends.PreInsert();
196 foreach(QString filename, newHashesByPath.keys())
198 QList<uint> recordsToReturn;
199 foreach(iHashable::Hash newHash, newHashesByPath.value(filename))
200 recordsToReturn.append(pathsByHashes.value(newHash).second);
202 // Repeating an action that caused an exception last time
203 // shouldn't happen again, but just in case...
206 foreach(EventTypes::EventFromFile newEventFromFile, ProcessFile(filename, recordsToReturn))
208 // ...and insert it into the DB
211 allBackends.Insert(*newEventFromFile.first, lookup);
213 catch(const std::runtime_error &exception)
215 qDebug() << "Unable to insert event: " << exception.what();
218 emit EventProcessed(++idx, newHashes.count());
221 catch(const std::runtime_error &exception)
223 qDebug() << exception.what() << endl;
226 // Just to make sure the listeners are synced even if the
227 // earlier call is skipped due to errors...
228 emit EventProcessed(idx, newHashes.count());
231 allBackends.PostInsert(); // Perform any post-insert cleanup (i.e. reindexing)
233 // Need to find a better way of refreshing the conversations view...
234 QProcess::execute("pkill rtcom");
236 // Signal we completed successfully.
237 backupManager.UnlockCurrentBackup();
240 // May as well call this explicitly despite it being called by the
241 // destructor - it's harmless.
242 preventEvents.RestoreAccounts();
244 catch(std::runtime_error exception)
246 qDebug() << exception.what();
250 EventTypes::EventFromFileList SyncerThread::ProcessFile(const QString &path, const QList<uint> &recordsToReturn) const
252 qDebug() << path << endl;
253 QFile eventFile(path);
255 // If the file's ok, process it...
256 if (eventFile.open(QFile::ReadOnly))
258 // Identify type of file...
259 EventParsers::iEventParser * parser(EventParsers::Factory::CreateParser(CurrentSettings(), path));
261 // ...and grab the events from it (if it's a supported format)
263 return parser->ParseFile(eventFile, recordsToReturn);
265 return EventTypes::EventFromFileList();
268 throw std::runtime_error(QString("Unable to open: %1").arg(path).toStdString());
271 QFileInfoList FindEvents(QFileInfoList currentCandidate)
273 QFileInfoList foundEvents;
274 foreach(QFileInfo fileInfo, currentCandidate)
277 foundEvents.append(FindEvents(QDir(fileInfo.absoluteFilePath()).entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot)));
279 foundEvents.append(fileInfo);
285 QDebug operator<<(QDebug dbg, QList<Attachment*> &attachments)
287 dbg.nospace() << "Attachments" << "\n";
289 foreach(Attachment* attachment, attachments)
290 dbg.nospace() << *attachment << "\n";
292 dbg.nospace() << "\n";