10#include <QStandardItemModel>
23 gDetectorConstruction(dc),
27 dbhost = gopts->getScalarString(
"sql");
28 experiment = gopts->getScalarString(
"experiment");
34 std::vector<std::string> dirs = {
46 if (sqlite3_open_v2(dbPath.value().c_str(), &db, SQLITE_OPEN_READONLY,
nullptr) != SQLITE_OK || !isGeometryTableValid()) {
52 log->
info(1,
"Opened database: " + dbhost,
" found at ", dbPath.value());
59 experimentModel->blockSignals(
true);
63 bool expFound =
false;
64 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
65 QStandardItem* expItem = experimentModel->item(i, 0);
66 if (expItem && expItem->text() == QString::fromStdString(experiment)) {
67 expItem->setCheckState(Qt::Checked);
77 applyGSystemSelections();
80 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
81 QStandardItem* expItem = experimentModel->item(i, 0);
82 for (
int j = 0; j < expItem->rowCount(); ++j) {
83 QStandardItem* sysItem = expItem->child(j, 0);
84 updateSystemItemAppearance(sysItem);
89 experimentModel->blockSignals(
false);
96bool DBSelectView::isGeometryTableValid()
const {
100 sqlite3_stmt* stmt =
nullptr;
101 const char* sql_query =
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='geometry'";
104 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) != SQLITE_OK) {
105 log->
error(ERR_GSQLITEERROR,
"SQL Error: Failed to check geometry table existence:", sqlite3_errmsg(db));
108 bool tableExists =
false;
109 if (sqlite3_step(stmt) == SQLITE_ROW) {
110 tableExists = sqlite3_column_int(stmt, 0) > 0;
112 sqlite3_finalize(stmt);
118 sql_query =
"SELECT COUNT(*) FROM geometry";
119 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) != SQLITE_OK) {
120 log->
error(ERR_GSQLITEERROR,
"SQL Error: Failed to count rows in geometry table:", sqlite3_errmsg(db));
123 bool hasData =
false;
124 if (sqlite3_step(stmt) == SQLITE_ROW) {
125 hasData = sqlite3_column_int(stmt, 0) > 0;
127 sqlite3_finalize(stmt);
132void DBSelectView::applyGSystemSelections() {
136 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
137 QStandardItem* expItem = experimentModel->item(i, 0);
142 if (expItem->text() == QString::fromStdString(experiment)) {
143 expItem->setCheckState(Qt::Checked);
147 for (
int j = 0; j < expItem->rowCount(); ++j) {
148 QStandardItem* sysItem = expItem->child(j, 0);
149 QStandardItem* varItem = expItem->child(j, 2);
150 QStandardItem* runItem = expItem->child(j, 3);
151 if (!sysItem || !varItem || !runItem)
154 std::string sysName = sysItem->text().toStdString();
155 bool systemFound =
false;
157 for (
auto const& gsys : gsystems) {
158 if (gsys->getName() == sysName) {
160 sysItem->setCheckState(Qt::Checked);
163 QStringList availableVariations = getAvailableVariations(sysName);
164 QString selectedVar = QString::fromStdString(gsys->getVariation());
165 if (availableVariations.contains(selectedVar))
166 varItem->setData(selectedVar, Qt::EditRole);
167 else if (!availableVariations.isEmpty())
168 varItem->setData(availableVariations.first(), Qt::EditRole);
169 varItem->setData(availableVariations, Qt::UserRole);
172 QStringList availableRuns = getAvailableRuns(sysName);
173 QString selectedRun = QString::number(gsys->getRunno());
174 if (availableRuns.contains(selectedRun))
175 runItem->setData(selectedRun, Qt::EditRole);
176 else if (!availableRuns.isEmpty())
177 runItem->setData(availableRuns.first(), Qt::EditRole);
178 runItem->setData(availableRuns, Qt::UserRole);
180 updateSystemItemAppearance(sysItem);
187 sysItem->setCheckState(Qt::Unchecked);
193void DBSelectView::setupUI() {
194 auto mainLayout =
new QVBoxLayout(
this);
195 mainLayout->setContentsMargins(10, 10, 10, 10);
196 mainLayout->setSpacing(10);
199 auto headerLayout =
new QHBoxLayout();
201 auto labelLayout =
new QVBoxLayout();
203 titleLabel =
new QLabel(
"Experiment Selection",
this);
204 QFont titleFont(
"Avenir", 20, QFont::Bold);
205 titleLabel->setFont(titleFont);
206 titleLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
207 labelLayout->addWidget(titleLabel);
209 experimentHeaderLabel =
new QLabel(
"",
this);
210 experimentHeaderLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
211 experimentHeaderLabel->setWordWrap(
true);
212 experimentHeaderLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
213 labelLayout->addWidget(experimentHeaderLabel);
215 headerLayout->addLayout(labelLayout);
216 headerLayout->addStretch();
218 reloadButton =
new QPushButton(
"Reload",
this);
219 reloadButton->setEnabled(
false);
220 headerLayout->addWidget(reloadButton);
223 mainLayout->addLayout(headerLayout);
226 experimentTree =
new QTreeView(
this);
227 experimentTree->setStyleSheet(
"QTreeView { alternate-background-color: #f0f0f0; }");
228 experimentTree->setSelectionMode(QAbstractItemView::SingleSelection);
229 experimentTree->setSelectionBehavior(QAbstractItemView::SelectRows);
230 experimentTree->header()->show();
232 experimentModel =
new QStandardItemModel(
this);
233 experimentModel->setHorizontalHeaderLabels(QStringList() <<
"exp/system" <<
"volumes" <<
"variation" <<
"run");
235 experimentTree->setModel(experimentModel);
238 experimentTree->setItemDelegateForColumn(2,
new ComboDelegate(
this));
239 experimentTree->setItemDelegateForColumn(3,
new ComboDelegate(
this));
241 mainLayout->addWidget(experimentTree);
243 connect(experimentModel, &QStandardItemModel::itemChanged,
244 this, &DBSelectView::onItemChanged);
247void DBSelectView::loadExperiments() {
248 experimentModel->clear();
250 sqlite3_stmt* stmt =
nullptr;
251 const char* sql_query =
"SELECT DISTINCT experiment FROM geometry";
253 int rc = sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr);
254 if (rc != SQLITE_OK) {
255 log->
error(ERR_GSQLITEERROR,
"Failed to prepare experiment query:", sqlite3_errmsg(db));
259 while (sqlite3_step(stmt) == SQLITE_ROW) {
260 const char* expText =
reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
262 QString expName = QString::fromUtf8(expText);
265 experiment = expName.toStdString();
267 auto* expItem =
new QStandardItem(expName);
268 expItem->setFlags(expItem->flags() & ~Qt::ItemIsEditable);
269 expItem->setCheckable(
true);
270 expItem->setCheckState(Qt::Unchecked);
273 auto* dummyEntries =
new QStandardItem(
"");
274 auto* dummyVar =
new QStandardItem(
"");
275 auto* dummyRun =
new QStandardItem(
"");
277 loadSystemsForExperiment(expItem);
279 experimentModel->appendRow(QList<QStandardItem*>() << expItem << dummyEntries << dummyVar << dummyRun);
283 sqlite3_finalize(stmt);
286void DBSelectView::loadSystemsForExperiment(QStandardItem* experimentItem) {
287 sqlite3_stmt* stmt =
nullptr;
288 const char* sql_query =
"SELECT DISTINCT system FROM geometry WHERE experiment = ?";
290 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) == SQLITE_OK) {
292 sqlite3_bind_text(stmt, 1, experiment.c_str(), -1, SQLITE_TRANSIENT);
294 while (sqlite3_step(stmt) == SQLITE_ROW) {
295 const char* sysText =
reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
297 auto* sysItem =
new QStandardItem(QString::fromUtf8(sysText));
298 sysItem->setFlags(sysItem->flags() & ~Qt::ItemIsEditable);
299 sysItem->setCheckable(
true);
300 sysItem->setCheckState(Qt::Unchecked);
303 auto* entriesItem =
new QStandardItem(
"");
306 auto* varItem =
new QStandardItem();
307 QStringList varList = getAvailableVariations(sysText);
308 if (!varList.isEmpty())
309 varItem->setData(varList.first(), Qt::EditRole);
311 varItem->setData(
"", Qt::EditRole);
312 varItem->setData(varList, Qt::UserRole);
313 varItem->setBackground(QColor(
"lightblue"));
316 auto* runItem =
new QStandardItem();
317 QStringList runList = getAvailableRuns(sysText);
318 if (!runList.isEmpty())
319 runItem->setData(runList.first(), Qt::EditRole);
321 runItem->setData(
"", Qt::EditRole);
322 runItem->setData(runList, Qt::UserRole);
323 runItem->setBackground(QColor(
"lightgreen"));
325 QList<QStandardItem*> rowItems;
326 rowItems << sysItem << entriesItem << varItem << runItem;
327 experimentItem->appendRow(rowItems);
332 sqlite3_finalize(stmt);
335int DBSelectView::getGeometryCount(
const std::string& system,
const std::string& variation,
int run)
const {
337 std::string query =
"SELECT COUNT(*) FROM geometry WHERE experiment = ? AND system = ? AND variation = ? AND run = ?";
339 sqlite3_stmt* stmt =
nullptr;
340 if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt,
nullptr) == SQLITE_OK) {
341 sqlite3_bind_text(stmt, 1, experiment.c_str(), -1, SQLITE_TRANSIENT);
342 sqlite3_bind_text(stmt, 2, system.c_str(), -1, SQLITE_TRANSIENT);
343 sqlite3_bind_text(stmt, 3, variation.c_str(), -1, SQLITE_TRANSIENT);
344 sqlite3_bind_int(stmt, 4, run);
346 if (sqlite3_step(stmt) == SQLITE_ROW) {
347 count = sqlite3_column_int(stmt, 0);
351 log->
error(ERR_GSQLITEERROR,
"SQL Error: Failed togetGeometryCounte:", sqlite3_errmsg(db));
354 sqlite3_finalize(stmt);
358QStringList DBSelectView::getAvailableVariations(
const std::string& system)
const {
360 sqlite3_stmt* stmt =
nullptr;
361 const char* sql_query =
"SELECT DISTINCT variation FROM geometry WHERE system = ?";
363 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) == SQLITE_OK) {
364 sqlite3_bind_text(stmt, 1, system.c_str(), -1, SQLITE_TRANSIENT);
366 while (sqlite3_step(stmt) == SQLITE_ROW) {
367 const char* varText =
reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
369 varList << QString::fromUtf8(varText);
374 sqlite3_finalize(stmt);
378QStringList DBSelectView::getAvailableRuns(
const std::string& system) {
380 sqlite3_stmt* stmt =
nullptr;
381 const char* sql_query =
"SELECT DISTINCT run FROM geometry WHERE system = ?";
383 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) == SQLITE_OK) {
384 sqlite3_bind_text(stmt, 1, system.c_str(), -1, SQLITE_TRANSIENT);
386 while (sqlite3_step(stmt) == SQLITE_ROW) {
387 int runVal = sqlite3_column_int(stmt, 0);
388 runList << QString::number(runVal);
392 sqlite3_finalize(stmt);
396bool DBSelectView::systemAvailable(
const std::string& system,
const std::string& variation,
int run) {
397 std::string query =
"SELECT COUNT(*) FROM geometry WHERE system = ? AND variation = ? AND run = ?";
398 sqlite3_stmt* stmt =
nullptr;
400 if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt,
nullptr) != SQLITE_OK) {
401 log->
error(ERR_GSQLITEERROR,
"SQL Error:systemAvailable: prepare failed:e:", sqlite3_errmsg(db));
404 sqlite3_bind_text(stmt, 1, system.c_str(), -1, SQLITE_TRANSIENT);
405 sqlite3_bind_text(stmt, 2, variation.c_str(), -1, SQLITE_TRANSIENT);
406 sqlite3_bind_int(stmt, 3, run);
408 bool available =
false;
409 if (sqlite3_step(stmt) == SQLITE_ROW) {
410 int count = sqlite3_column_int(stmt, 0);
411 available = (count > 0);
414 sqlite3_finalize(stmt);
418QIcon DBSelectView::createStatusIcon(
const QColor& color) {
419 QPixmap pixmap(12, 12);
421 return QIcon(pixmap);
424void DBSelectView::updateSystemItemAppearance(QStandardItem* systemItem) {
425 QStandardItem* parentItem = systemItem->parent();
430 int row = systemItem->row();
431 QStandardItem* varItem = parentItem->child(row, 2);
432 QStandardItem* runItem = parentItem->child(row, 3);
434 QString varStr = varItem ? varItem->data(Qt::EditRole).toString() :
"";
435 QString runStr = runItem ? runItem->data(Qt::EditRole).toString() :
"";
437 int run = runStr.toInt();
438 QString expStr = parentItem->text();
441 experiment = expStr.toStdString();
443 std::string systemName = systemItem->text().toStdString();
444 std::string variation = varStr.toStdString();
446 int count = getGeometryCount(systemName, variation, run);
449 QStandardItem* entriesItem = parentItem->child(row, 1);
451 entriesItem->setText(QString::number(count));
455 bool available = (count > 0);
456 QColor statusColor = available ? QColor(
"green") : QColor(
"red");
457 systemItem->setIcon(createStatusIcon(statusColor));
460 systemItem->setData(QColor(
"white"), Qt::BackgroundRole);
461 systemItem->setData(QColor(
"black"), Qt::ForegroundRole);
464void DBSelectView::updateExperimentHeader() {
465 QStandardItem* selectedExp =
nullptr;
468 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
469 QStandardItem* expItem = experimentModel->item(i, 0);
470 if (expItem && expItem->checkState() == Qt::Checked) {
471 selectedExp = expItem;
477 int totalSystems = selectedExp->rowCount();
478 experimentHeaderLabel->setText(QString(
"Total systems for experiment \"%1\": %2")
479 .arg(selectedExp->text()).arg(totalSystems));
482 experimentHeaderLabel->setText(
"");
486 experimentModel->setHorizontalHeaderLabels(QStringList() <<
"exp/system" <<
"volumes" <<
"variation" <<
"run");
489void DBSelectView::onItemChanged(QStandardItem* item) {
490 if (m_ignoreItemChange || !item)
494 m_ignoreItemChange =
true;
497 if (!item->parent()) {
498 if (item->checkState() == Qt::Checked) {
500 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
501 QStandardItem* expItem = experimentModel->item(i, 0);
503 expItem->setCheckState(Qt::Unchecked);
505 updateExperimentHeader();
509 for (
int i = 0; i < item->rowCount(); ++i) {
510 QStandardItem* sysItem = item->child(i, 0);
512 sysItem->setCheckState(Qt::Unchecked);
514 updateExperimentHeader();
519 if (item->column() == 0) {
520 updateSystemItemAppearance(item);
522 else if (item->column() == 2 || item->column() == 3) {
523 QStandardItem* sysItem = item->parent()->child(item->row(), 0);
524 updateSystemItemAppearance(sysItem);
528 m_ignoreItemChange =
false;
541 for (
int i = 0; i < experimentModel->rowCount(); i++) {
542 QStandardItem* expItem = experimentModel->item(i, 0);
546 for (
int j = 0; j < expItem->rowCount(); j++) {
547 QStandardItem* sysItem = expItem->child(j, 0);
548 QStandardItem* varItem = expItem->child(j, 2);
549 QStandardItem* runItem = expItem->child(j, 3);
551 if (!sysItem || !varItem || !runItem)
554 if (sysItem->checkState() == Qt::Checked) {
555 std::string systemName = sysItem->text().toStdString();
556 std::string variation = varItem->data(Qt::EditRole).toString().toStdString();
557 int run = runItem->data(Qt::EditRole).toInt();
559 log->
info(2,
SFUNCTION_NAME,
": adding systemName: ", systemName,
" , variation: ", variation,
", for run:", run);
561 updatedSystems.emplace_back(
562 std::make_shared<GSystem>(
575 return updatedSystems;
578void DBSelectView::updateModifiedUI() {
580 updateExperimentHeader();
583 titleLabel->setText(
"Experiment Selection* (modified)");
585 titleLabel->setText(
"Experiment Selection");
587 reloadButton->setEnabled(modified);
590 experimentTree->resizeColumnToContents(0);
591 experimentTree->setColumnWidth(1, 100);
592 experimentTree->setColumnWidth(2, 150);
593 experimentTree->setColumnWidth(3, 150);
594 experimentTree->header()->setStretchLastSection(
false);
595 experimentTree->expandAll();
603 for (
auto& gsys : reloaded_system) {
Item delegate that edits a cell using a QComboBox populated from Qt::UserRole.
SystemList get_gsystems()
Build and return the list of selected systems as a SystemList.
DBSelectView(const std::shared_ptr< GOptions > &gopts, GDetectorConstruction *dc, QWidget *parent=nullptr)
Construct the view and populate the experiment/system model from the database.
void reload_geometry()
Slot invoked by the Reload button to reload geometry based on current selections.
std::shared_ptr< GLogger > log
void reload_geometry(SystemList sl)
void info(int level, Args &&... args) const
void error(int exit_code, Args &&... args) const
constexpr const char * DBSELECT_LOGGER
Logger name used by the dbselect module.
#define GSYSTEMSQLITETFACTORYLABEL
#define ERR_EXPERIMENTNOTFOUND
std::vector< SystemPtr > SystemList
SystemList getSystems(const std::shared_ptr< GOptions > &gopts)
std::filesystem::path gemc_root()
std::optional< std::string > searchForFileInLocations(const std::vector< std::string > &locations, std::string_view filename)