From 5da85e53ec71365cd20d50664abf20d4c4d064d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=C5=A1an=20Poizl?= Date: Fri, 8 Apr 2022 21:41:38 +0200 Subject: [PATCH] Add FITS files indexing support to database --- database.cpp | 101 +++++++++++++++++++++++++++++++++++++++++++++++- database.h | 11 ++++++ loadrunable.cpp | 19 ++++++++- loadrunable.h | 2 + mainwindow.cpp | 1 + 5 files changed, 132 insertions(+), 2 deletions(-) diff --git a/database.cpp b/database.cpp index 4d0e0e2..ce676e8 100644 --- a/database.cpp +++ b/database.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include "loadrunable.h" Database::Database(QObject *parent) : QObject(parent) { @@ -23,7 +25,11 @@ bool Database::init() m_database.setDatabaseName(dir.absoluteFilePath("database.db")); if(m_database.open()) { - m_database.exec("CREATE TABLE IF NOT EXISTS files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE);"); + m_database.exec("PRAGMA foreign_keys = ON"); + m_database.exec("CREATE TABLE IF NOT EXISTS files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE)"); + m_database.exec("CREATE TABLE IF NOT EXISTS fits_files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE, mtime DATETIME)"); + m_database.exec("CREATE TABLE IF NOT EXISTS fits_headers (id INTEGER PRIMARY KEY AUTOINCREMENT, id_file INTEGER," + "key VARCHAR(81), value VARCHAR(81), comment VARCHAR(81), FOREIGN KEY(id_file) REFERENCES fits_files(id) ON DELETE CASCADE)"); QSqlError error = m_database.lastError(); if(error.type() == QSqlError::NoError) @@ -34,6 +40,17 @@ bool Database::init() m_unmarkQuery.prepare("DELETE FROM files WHERE file = (?)"); m_isMarkedQuery = QSqlQuery(m_database); m_isMarkedQuery.prepare("SELECT * FROM files WHERE file = (:name)"); + + m_insertFile = QSqlQuery(m_database); + m_insertFile.prepare("INSERT INTO fits_files (file, mtime) VALUES (?, ?)"); + m_insertFitsHeader = QSqlQuery(m_database); + m_insertFitsHeader.prepare("INSERT INTO fits_headers (id_file, key, value, comment) VALUES (?, ?, ?, ?)"); + m_checkFile = QSqlQuery(m_database); + m_checkFile.prepare("SELECT id,mtime FROM fits_files WHERE file=?"); + m_headerKeywords = QSqlQuery(m_database); + m_headerKeywords.prepare("SELECT DISTINCT key FROM fits_headers"); + m_deleteFile = QSqlQuery(m_database); + m_deleteFile.prepare("DELETE FROM fits_files WHERE id=?"); return true; } @@ -90,3 +107,85 @@ bool Database::checkError() return false; } } + +void Database::indexDir(const QDir &dir) +{ + //m_database.exec("DROP TABLE fits_files"); + m_database.transaction(); + if(indexDir2(dir)) + m_database.commit(); + else + m_database.rollback(); +} + +QStringList Database::getFitsKeywords() +{ + m_headerKeywords.exec(); + QStringList keywords; + while(m_headerKeywords.next()) + { + keywords << m_headerKeywords.value(0).toString(); + } + return keywords; +} + +bool Database::indexDir2(const QDir &dir) +{ + static QStringList nameFilters = {"*.fit", "*.fits"}; + QFileInfoList files = dir.entryInfoList(nameFilters, QDir::Files); + QStringList dirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + for(const QString &d : dirs) + { + if(!indexDir2(dir.filePath(d))) + return false; + } + ImageInfoData info; + for(const QFileInfo &file : files) + { + QString filePath = file.absoluteFilePath(); + QString mtime = file.fileTime(QFileDevice::FileModificationTime).toString(Qt::ISODate); + m_checkFile.bindValue(0, filePath); + m_checkFile.exec(); + if(m_checkFile.next()) + { + if(m_checkFile.value(1).toString() == file.fileTime(QFileDevice::FileModificationTime).toString(Qt::ISODate)) + continue; + else + { + m_deleteFile.bindValue(0, m_checkFile.value(0).toLongLong()); + m_deleteFile.exec(); + } + } + + readFITSHeader(filePath, info); + m_insertFile.bindValue(0, filePath); + m_insertFile.bindValue(1, mtime); + if(!m_insertFile.exec()) + { + qDebug() << m_insertFile.lastError(); + return false; + } + qlonglong last_id = m_insertFile.lastInsertId().toLongLong(); + QVariantList file_id, keys, values, comments; + for(auto &record : info.fitsHeader) + { + file_id << last_id; + keys << QString(record.key); + values << record.value.toString(); + comments << QString(record.comment); + } + m_insertFitsHeader.bindValue(0, file_id); + m_insertFitsHeader.bindValue(1, keys); + m_insertFitsHeader.bindValue(2, values); + m_insertFitsHeader.bindValue(3, comments); + if(!m_insertFitsHeader.execBatch()) + { + qDebug() << m_insertFitsHeader.lastError(); + return false; + } + + qDebug() << filePath << last_id; + info.fitsHeader.clear(); + } + return true; +} diff --git a/database.h b/database.h index 933bb91..cb30fdc 100644 --- a/database.h +++ b/database.h @@ -4,6 +4,7 @@ #include #include #include +#include class Database : public QObject { @@ -12,6 +13,12 @@ class Database : public QObject QSqlQuery m_markQuery; QSqlQuery m_unmarkQuery; QSqlQuery m_isMarkedQuery; + + QSqlQuery m_insertFile; + QSqlQuery m_insertFitsHeader; + QSqlQuery m_checkFile; + QSqlQuery m_headerKeywords; + QSqlQuery m_deleteFile; public: explicit Database(QObject *parent = 0); bool init(); @@ -19,7 +26,11 @@ public: bool unmark(const QString &filename); bool isMarked(const QString &filename); QStringList getMarkedFiles(); + + void indexDir(const QDir &dir); + QStringList getFitsKeywords(); protected: + bool indexDir2(const QDir &dir); bool checkError(); }; diff --git a/loadrunable.cpp b/loadrunable.cpp index 832aeb5..94c12db 100644 --- a/loadrunable.cpp +++ b/loadrunable.cpp @@ -127,7 +127,7 @@ bool loadRAW(QString path, ImageInfoData &info, RawImage **image) return true; } -void loadFITSHeader(fitsfile *file, ImageInfoData &info) +int loadFITSHeader(fitsfile *file, ImageInfoData &info) { int nexist; int status = 0; @@ -159,7 +159,12 @@ void loadFITSHeader(fitsfile *file, ImageInfoData &info) status = 0; info.fitsHeader.append({key, var, comm}); } + else + { + return status; + } } + return status; } bool loadFITS(QString path, ImageInfoData &info, RawImage **image) @@ -350,3 +355,15 @@ void LoadRunable::run() QMetaObject::invokeMethod(m_receiver, "imageLoaded", Qt::QueuedConnection, Q_ARG(void*, rawImage), Q_ARG(ImageInfoData, info)); } + +bool readFITSHeader(const QString &path, ImageInfoData &info) +{ + fitsfile *fr; + int status = 0; + fits_open_diskfile(&fr, path.toLocal8Bit().data(), READONLY, &status); + + status = loadFITSHeader(fr, info); + + fits_close_file(fr, &status); + return status == 0; +} diff --git a/loadrunable.h b/loadrunable.h index 1443f16..ca6a5f6 100644 --- a/loadrunable.h +++ b/loadrunable.h @@ -5,6 +5,8 @@ #include #include "imageinfo.h" +bool readFITSHeader(const QString &path, ImageInfoData &info); + class Image; class LoadRunable : public QRunnable diff --git a/mainwindow.cpp b/mainwindow.cpp index 0e8c1be..04fac6f 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -158,6 +158,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) MainWindow::~MainWindow() { + delete m_database; } void MainWindow::keyPressEvent(QKeyEvent *event)