10#include <QStandardItemModel>
27 gDetectorConstruction(dc),
31 dbhost = gopts->getScalarString(
"sql");
32 experiment = gopts->getScalarString(
"experiment");
38 std::vector<std::string> dirs = {
46 log->
warning(
"Failed to find database file <", dbhost,
">. Setup tab will start empty.");
48 experimentModel->blockSignals(
true);
49 experimentModel->setHorizontalHeaderLabels(QStringList() <<
"exp/system" <<
"volumes" <<
"variation" <<
"run");
50 experimentModel->blockSignals(
false);
53 experimentHeaderLabel->setText(QString(
"Database \"%1\" was not found.").arg(QString::fromStdString(dbhost)));
58 if (sqlite3_open_v2(dbPath.value().c_str(), &db, SQLITE_OPEN_READONLY,
nullptr) != SQLITE_OK || !isGeometryTableValid()) {
61 log->
warning(
"Failed to open or validate database <", dbhost,
">. Setup tab will start empty.");
63 experimentModel->blockSignals(
true);
64 experimentModel->setHorizontalHeaderLabels(QStringList() <<
"exp/system" <<
"volumes" <<
"variation" <<
"run");
65 experimentModel->blockSignals(
false);
68 experimentHeaderLabel->setText(QString(
"Database \"%1\" could not be opened or validated.").arg(QString::fromStdString(dbhost)));
72 log->
info(1,
"Opened database: " + dbhost,
" found at ", dbPath.value());
79 experimentModel->blockSignals(
true);
83 bool expFound =
false;
84 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
85 QStandardItem* expItem = experimentModel->item(i, 0);
86 if (expItem && expItem->text() == QString::fromStdString(experiment)) {
87 expItem->setCheckState(Qt::Checked);
97 applyGSystemSelections();
100 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
101 QStandardItem* expItem = experimentModel->item(i, 0);
102 for (
int j = 0; j < expItem->rowCount(); ++j) {
103 QStandardItem* sysItem = expItem->child(j, 0);
104 updateSystemItemAppearance(sysItem);
109 experimentModel->blockSignals(
false);
114 QTimer::singleShot(0,
this, &DBSelectView::resizeExperimentColumns);
117bool DBSelectView::isGeometryTableValid()
const {
121 sqlite3_stmt* stmt =
nullptr;
122 const char* sql_query =
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='geometry'";
125 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) != SQLITE_OK) {
126 log->
error(ERR_GSQLITEERROR,
"SQL Error: Failed to check geometry table existence:", sqlite3_errmsg(db));
129 bool tableExists =
false;
130 if (sqlite3_step(stmt) == SQLITE_ROW) {
131 tableExists = sqlite3_column_int(stmt, 0) > 0;
133 sqlite3_finalize(stmt);
139 sql_query =
"SELECT COUNT(*) FROM geometry";
140 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) != SQLITE_OK) {
141 log->
error(ERR_GSQLITEERROR,
"SQL Error: Failed to count rows in geometry table:", sqlite3_errmsg(db));
144 bool hasData =
false;
145 if (sqlite3_step(stmt) == SQLITE_ROW) {
146 hasData = sqlite3_column_int(stmt, 0) > 0;
148 sqlite3_finalize(stmt);
153void DBSelectView::applyGSystemSelections() {
157 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
158 QStandardItem* expItem = experimentModel->item(i, 0);
163 if (expItem->text() == QString::fromStdString(experiment)) {
164 expItem->setCheckState(Qt::Checked);
168 for (
int j = 0; j < expItem->rowCount(); ++j) {
169 QStandardItem* sysItem = expItem->child(j, 0);
170 QStandardItem* varItem = expItem->child(j, 2);
171 QStandardItem* runItem = expItem->child(j, 3);
172 if (!sysItem || !varItem || !runItem)
175 std::string sysName = sysItem->text().toStdString();
176 std::string rowVariation = varItem->data(Qt::EditRole).toString().toStdString();
177 bool systemFound =
false;
179 for (
auto const& gsys : gsystems) {
180 if (gsys->getName() == sysName && gsys->getVariation() == rowVariation) {
182 sysItem->setCheckState(Qt::Checked);
185 QStringList availableRuns = getAvailableRuns(sysName, rowVariation);
186 QString selectedRun = QString::number(gsys->getRunno());
187 if (availableRuns.contains(selectedRun))
188 runItem->setData(selectedRun, Qt::EditRole);
189 else if (!availableRuns.isEmpty())
190 runItem->setData(availableRuns.first(), Qt::EditRole);
191 runItem->setData(availableRuns, Qt::UserRole);
193 updateSystemItemAppearance(sysItem);
200 sysItem->setCheckState(Qt::Unchecked);
206void DBSelectView::setupUI() {
207 auto mainLayout =
new QVBoxLayout(
this);
208 mainLayout->setContentsMargins(10, 10, 10, 10);
209 mainLayout->setSpacing(10);
212 auto headerLayout =
new QHBoxLayout();
214 auto labelLayout =
new QVBoxLayout();
216 titleLabel =
new QLabel(
"Experiment Selection",
this);
217 QFont titleFont(
"Avenir", 20, QFont::Bold);
218 titleLabel->setFont(titleFont);
219 titleLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
220 labelLayout->addWidget(titleLabel);
222 experimentHeaderLabel =
new QLabel(
"",
this);
223 experimentHeaderLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
224 experimentHeaderLabel->setWordWrap(
true);
225 experimentHeaderLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
226 labelLayout->addWidget(experimentHeaderLabel);
228 headerLayout->addLayout(labelLayout);
229 headerLayout->addStretch();
231 reloadButton =
new QPushButton(
"Reload",
this);
232 reloadButton->setEnabled(
false);
233 headerLayout->addWidget(reloadButton);
236 mainLayout->addLayout(headerLayout);
239 experimentTree =
new QTreeView(
this);
240 experimentTree->setAlternatingRowColors(
true);
241 experimentTree->setSelectionMode(QAbstractItemView::SingleSelection);
242 experimentTree->setSelectionBehavior(QAbstractItemView::SelectRows);
243 experimentTree->header()->show();
245 experimentModel =
new QStandardItemModel(
this);
246 experimentModel->setHorizontalHeaderLabels(QStringList() <<
"exp/system" <<
"volumes" <<
"variation" <<
"run");
248 experimentTree->setModel(experimentModel);
251 experimentTree->setItemDelegateForColumn(3,
new ComboDelegate(
this));
253 mainLayout->addWidget(experimentTree);
255 connect(experimentModel, &QStandardItemModel::itemChanged,
256 this, &DBSelectView::onItemChanged);
259void DBSelectView::loadExperiments() {
260 experimentModel->clear();
263 const std::string configuredExperiment = experiment;
264 sqlite3_stmt* stmt =
nullptr;
265 const char* sql_query =
"SELECT DISTINCT experiment FROM geometry";
267 int rc = sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr);
268 if (rc != SQLITE_OK) {
269 log->
error(ERR_GSQLITEERROR,
"Failed to prepare experiment query:", sqlite3_errmsg(db));
273 while (sqlite3_step(stmt) == SQLITE_ROW) {
274 const char* expText =
reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
276 QString expName = QString::fromUtf8(expText);
279 experiment = expName.toStdString();
281 auto* expItem =
new QStandardItem(expName);
282 expItem->setFlags(expItem->flags() & ~Qt::ItemIsEditable);
283 expItem->setCheckable(
true);
284 expItem->setCheckState(Qt::Unchecked);
287 auto* dummyEntries =
new QStandardItem(
"");
288 auto* dummyVar =
new QStandardItem(
"");
289 auto* dummyRun =
new QStandardItem(
"");
291 loadSystemsForExperiment(expItem);
293 experimentModel->appendRow(QList<QStandardItem*>() << expItem << dummyEntries << dummyVar << dummyRun);
297 sqlite3_finalize(stmt);
298 experiment = configuredExperiment;
301void DBSelectView::loadSystemsForExperiment(QStandardItem* experimentItem) {
302 sqlite3_stmt* stmt =
nullptr;
303 const char* sql_query =
"SELECT DISTINCT system, variation FROM geometry WHERE experiment = ? ORDER BY system, variation";
305 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) == SQLITE_OK) {
307 sqlite3_bind_text(stmt, 1, experiment.c_str(), -1, SQLITE_TRANSIENT);
309 while (sqlite3_step(stmt) == SQLITE_ROW) {
310 const char* sysText =
reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
311 const char* varText =
reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 1));
312 if (sysText && varText) {
313 auto* sysItem =
new QStandardItem(QString::fromUtf8(sysText));
314 sysItem->setFlags(sysItem->flags() & ~Qt::ItemIsEditable);
315 sysItem->setCheckable(
true);
316 sysItem->setCheckState(Qt::Unchecked);
319 auto* entriesItem =
new QStandardItem(
"");
322 auto* varItem =
new QStandardItem();
323 QString variation = QString::fromUtf8(varText);
324 QStringList varList{variation};
325 varItem->setData(variation, Qt::EditRole);
326 varItem->setData(varList, Qt::UserRole);
327 varItem->setFlags(varItem->flags() & ~Qt::ItemIsEditable);
330 auto* runItem =
new QStandardItem();
331 QStringList runList = getAvailableRuns(sysText, varText);
332 if (!runList.isEmpty())
333 runItem->setData(runList.first(), Qt::EditRole);
335 runItem->setData(
"", Qt::EditRole);
336 runItem->setData(runList, Qt::UserRole);
338 QList<QStandardItem*> rowItems;
339 rowItems << sysItem << entriesItem << varItem << runItem;
340 experimentItem->appendRow(rowItems);
345 sqlite3_finalize(stmt);
348int DBSelectView::getGeometryCount(
const std::string& system,
const std::string& variation,
int run)
const {
349 if (!db) {
return 0; }
352 std::string query =
"SELECT COUNT(*) FROM geometry WHERE experiment = ? AND system = ? AND variation = ? AND run = ?";
354 sqlite3_stmt* stmt =
nullptr;
355 if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt,
nullptr) == SQLITE_OK) {
356 sqlite3_bind_text(stmt, 1, experiment.c_str(), -1, SQLITE_TRANSIENT);
357 sqlite3_bind_text(stmt, 2, system.c_str(), -1, SQLITE_TRANSIENT);
358 sqlite3_bind_text(stmt, 3, variation.c_str(), -1, SQLITE_TRANSIENT);
359 sqlite3_bind_int(stmt, 4, run);
361 if (sqlite3_step(stmt) == SQLITE_ROW) {
362 count = sqlite3_column_int(stmt, 0);
366 log->
error(ERR_GSQLITEERROR,
"SQL Error: Failed togetGeometryCounte:", sqlite3_errmsg(db));
369 sqlite3_finalize(stmt);
373QStringList DBSelectView::getAvailableRuns(
const std::string& system,
const std::string& variation)
const {
375 if (!db) {
return runList; }
377 sqlite3_stmt* stmt =
nullptr;
378 const char* sql_query =
"SELECT DISTINCT run FROM geometry WHERE experiment = ? AND system = ? AND variation = ? ORDER BY run";
380 if (sqlite3_prepare_v2(db, sql_query, -1, &stmt,
nullptr) == SQLITE_OK) {
381 sqlite3_bind_text(stmt, 1, experiment.c_str(), -1, SQLITE_TRANSIENT);
382 sqlite3_bind_text(stmt, 2, system.c_str(), -1, SQLITE_TRANSIENT);
383 sqlite3_bind_text(stmt, 3, variation.c_str(), -1, SQLITE_TRANSIENT);
385 while (sqlite3_step(stmt) == SQLITE_ROW) {
386 int runVal = sqlite3_column_int(stmt, 0);
387 runList << QString::number(runVal);
391 sqlite3_finalize(stmt);
395bool DBSelectView::systemAvailable(
const std::string& system,
const std::string& variation,
int run) {
396 if (!db) {
return false; }
398 std::string query =
"SELECT COUNT(*) FROM geometry WHERE system = ? AND variation = ? AND run = ?";
399 sqlite3_stmt* stmt =
nullptr;
401 if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt,
nullptr) != SQLITE_OK) {
402 log->
error(ERR_GSQLITEERROR,
"SQL Error:systemAvailable: prepare failed:e:", sqlite3_errmsg(db));
405 sqlite3_bind_text(stmt, 1, system.c_str(), -1, SQLITE_TRANSIENT);
406 sqlite3_bind_text(stmt, 2, variation.c_str(), -1, SQLITE_TRANSIENT);
407 sqlite3_bind_int(stmt, 3, run);
409 bool available =
false;
410 if (sqlite3_step(stmt) == SQLITE_ROW) {
411 int count = sqlite3_column_int(stmt, 0);
412 available = (count > 0);
415 sqlite3_finalize(stmt);
419QIcon DBSelectView::createStatusIcon(
const QColor& color) {
420 QPixmap pixmap(12, 12);
422 return QIcon(pixmap);
425void DBSelectView::updateSystemItemAppearance(QStandardItem* systemItem) {
426 QStandardItem* parentItem = systemItem->parent();
431 int row = systemItem->row();
432 QStandardItem* varItem = parentItem->child(row, 2);
433 QStandardItem* runItem = parentItem->child(row, 3);
435 QString varStr = varItem ? varItem->data(Qt::EditRole).toString() :
"";
436 QString runStr = runItem ? runItem->data(Qt::EditRole).toString() :
"";
438 int run = runStr.toInt();
439 QString expStr = parentItem->text();
442 experiment = expStr.toStdString();
444 std::string systemName = systemItem->text().toStdString();
445 std::string variation = varStr.toStdString();
447 int count = getGeometryCount(systemName, variation, run);
450 QStandardItem* entriesItem = parentItem->child(row, 1);
452 entriesItem->setText(QString::number(count));
456 bool available = (count > 0);
457 QColor statusColor = available ? QColor(
"green") : QColor(
"red");
458 systemItem->setIcon(createStatusIcon(statusColor));
461 systemItem->setData(QVariant(), Qt::BackgroundRole);
462 systemItem->setData(QVariant(), Qt::ForegroundRole);
465void DBSelectView::updateExperimentHeader() {
466 QStandardItem* selectedExp =
nullptr;
469 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
470 QStandardItem* expItem = experimentModel->item(i, 0);
471 if (expItem && expItem->checkState() == Qt::Checked) {
472 selectedExp = expItem;
478 int totalSystems = selectedExp->rowCount();
479 experimentHeaderLabel->setText(QString(
"Total systems for experiment \"%1\": %2")
480 .arg(selectedExp->text()).arg(totalSystems));
483 experimentHeaderLabel->setText(
"");
487 experimentModel->setHorizontalHeaderLabels(QStringList() <<
"exp/system" <<
"volumes" <<
"variation" <<
"run");
490void DBSelectView::onItemChanged(QStandardItem* item) {
491 if (m_ignoreItemChange || !item)
495 m_ignoreItemChange =
true;
498 if (!item->parent()) {
499 if (item->checkState() == Qt::Checked) {
501 for (
int i = 0; i < experimentModel->rowCount(); ++i) {
502 QStandardItem* expItem = experimentModel->item(i, 0);
504 expItem->setCheckState(Qt::Unchecked);
506 updateExperimentHeader();
510 for (
int i = 0; i < item->rowCount(); ++i) {
511 QStandardItem* sysItem = item->child(i, 0);
513 sysItem->setCheckState(Qt::Unchecked);
515 updateExperimentHeader();
520 if (item->column() == 0) {
521 if (item->checkState() == Qt::Checked) {
522 for (
int i = 0; i < item->parent()->rowCount(); ++i) {
523 QStandardItem* sibling = item->parent()->child(i, 0);
524 if (sibling && sibling != item && sibling->text() == item->text()) {
525 sibling->setCheckState(Qt::Unchecked);
529 updateSystemItemAppearance(item);
531 else if (item->column() == 2 || item->column() == 3) {
532 QStandardItem* sysItem = item->parent()->child(item->row(), 0);
533 updateSystemItemAppearance(sysItem);
537 m_ignoreItemChange =
false;
550 for (
int i = 0; i < experimentModel->rowCount(); i++) {
551 QStandardItem* expItem = experimentModel->item(i, 0);
555 for (
int j = 0; j < expItem->rowCount(); j++) {
556 QStandardItem* sysItem = expItem->child(j, 0);
557 QStandardItem* varItem = expItem->child(j, 2);
558 QStandardItem* runItem = expItem->child(j, 3);
560 if (!sysItem || !varItem || !runItem)
563 if (sysItem->checkState() == Qt::Checked) {
564 std::string systemName = sysItem->text().toStdString();
565 std::string variation = varItem->data(Qt::EditRole).toString().toStdString();
566 int run = runItem->data(Qt::EditRole).toInt();
567 std::string expName = expItem->text().toStdString();
571 updatedSystems.emplace_back(
572 std::make_shared<GSystem>(
585 return updatedSystems;
588void DBSelectView::updateModifiedUI() {
590 updateExperimentHeader();
593 titleLabel->setText(
"Experiment Selection* (modified)");
595 titleLabel->setText(
"Experiment Selection");
597 reloadButton->setEnabled(modified);
600 resizeExperimentColumns();
603void DBSelectView::resizeExperimentColumns() {
604 if (!experimentTree) {
return; }
606 experimentTree->resizeColumnToContents(0);
607 experimentTree->setColumnWidth(1, 100);
608 experimentTree->setColumnWidth(2, 150);
609 experimentTree->setColumnWidth(3, 150);
610 experimentTree->header()->setStretchLastSection(
false);
611 experimentTree->expandAll();
619 if (!reloaded_system.empty()) {
620 std::ostringstream gsystemYaml;
622 for (
size_t i = 0; i < reloaded_system.size(); ++i) {
623 const auto& gsys = reloaded_system[i];
624 if (i > 0) { gsystemYaml <<
", "; }
625 gsystemYaml <<
"{name: " << gsys->getName()
626 <<
", factory: " << gsys->getFactoryName()
627 <<
", variation: " << gsys->getVariation();
629 gsystemYaml <<
", annotations: " << gsys->getAnnotations();
637 for (
auto& gsys : reloaded_system) {
Item delegate that edits a cell using a QComboBox populated from Qt::UserRole.
void geometryReloaded()
Emitted after detector construction has reloaded geometry from the selected systems.
SystemList get_gsystems()
Build and return the list of selected systems as a SystemList.
void geometryAboutToReload()
Emitted immediately before detector construction replaces the current geometry.
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 warning(Args &&... args) const
void info(int level, Args &&... args) const
void error(int exit_code, Args &&... args) const
void setOptionValueFromString(const std::string &optionName, const std::string &possibleYamlNode)
constexpr const char * DBSELECT_LOGGER
Logger name used by the dbselect module.
#define GSYSTEMSQLITETFACTORYLABEL
#define ERR_EXPERIMENTNOTFOUND
std::vector< SystemPtr > SystemList
#define UNINITIALIZEDSTRINGQUANTITY
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)