• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

paulmthompson / WhiskerToolbox / 17598819761

10 Sep 2025 12:00AM UTC coverage: 71.842% (+0.07%) from 71.771%
17598819761

push

github

paulmthompson
fix build error on clang linux

4 of 4 new or added lines in 1 file covered. (100.0%)

143 existing lines in 3 files now uncovered.

37074 of 51605 relevant lines covered (71.84%)

1305.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.89
/src/WhiskerToolbox/TableViewerWidget/PaginatedTableModel.cpp
1
#include "PaginatedTableModel.hpp"
2

3
#include "DataManager/DataManager.hpp"
4
#include "DataManager/utils/TableView/TableInfo.hpp"
5
#include "DataManager/utils/TableView/core/TableViewBuilder.h"
6
#include "DataManager/utils/TableView/TableRegistry.hpp"
7
#include "DataManager/utils/TableView/interfaces/IRowSelector.h"
8
#include "DataManager/utils/TableView/adapters/DataManagerExtension.h"
9
// removed duplicate include
10

11
#include <QDebug>
12
#include <algorithm>
13
#include <iomanip>
14
#include <sstream>
15

16
PaginatedTableModel::PaginatedTableModel(QObject * parent)
28✔
17
    : QAbstractTableModel(parent) {
28✔
18
}
28✔
19

20
PaginatedTableModel::~PaginatedTableModel() = default;
56✔
21

22
void PaginatedTableModel::setSourceTable(std::unique_ptr<IRowSelector> row_selector,
22✔
23
                                        std::vector<ColumnInfo> column_infos,
24
                                        std::shared_ptr<DataManager> data_manager) {
25
    beginResetModel();
22✔
26
    
27
    _source_row_selector = std::move(row_selector);
22✔
28
    _column_infos = std::move(column_infos);
22✔
29
    _data_manager = std::move(data_manager);
22✔
30
    _complete_table_view.reset();
22✔
31
    
32
    // Calculate total rows and extract column names
33
    if (_source_row_selector) {
22✔
34
        _total_rows = _source_row_selector->getRowCount();
22✔
35
    } else {
36
        _total_rows = 0;
×
37
    }
38
    
39
    _column_names.clear();
22✔
40
    for (auto const & col_info : _column_infos) {
70✔
41
        _column_names.push_back(QString::fromStdString(col_info.name));
48✔
42
    }
43
    
44
    // Clear cache
45
    _page_cache.clear();
22✔
46
    
47
    endResetModel();
22✔
48
}
22✔
49

50
void PaginatedTableModel::setTableView(std::shared_ptr<TableView> table_view) {
6✔
51
    beginResetModel();
6✔
52
    
53
    _complete_table_view = std::move(table_view);
6✔
54
    _source_row_selector.reset();
6✔
55
    _column_infos.clear();
6✔
56
    _data_manager.reset();
6✔
57
    
58
    if (_complete_table_view) {
6✔
59
        _total_rows = _complete_table_view->getRowCount();
6✔
60
        auto names = _complete_table_view->getColumnNames();
6✔
61
        _column_names.clear();
6✔
62
        for (auto const & name : names) {
27✔
63
            _column_names.push_back(QString::fromStdString(name));
21✔
64
        }
65
    } else {
6✔
66
        _total_rows = 0;
×
67
        _column_names.clear();
×
68
    }
69
    
70
    // Clear cache
71
    _page_cache.clear();
6✔
72
    
73
    endResetModel();
6✔
74
}
6✔
75

76
void PaginatedTableModel::clearTable() {
176✔
77
    beginResetModel();
176✔
78
    _source_row_selector.reset();
176✔
79
    _column_infos.clear();
176✔
80
    _data_manager.reset();
176✔
81
    _complete_table_view.reset();
176✔
82
    _total_rows = 0;
176✔
83
    _column_names.clear();
176✔
84
    _page_cache.clear();
176✔
85
    endResetModel();
176✔
86
}
176✔
87

88
void PaginatedTableModel::setPageSize(size_t page_size) {
32✔
89
    if (page_size == 0 || page_size == _page_size) return;
32✔
90
    
91
    beginResetModel();
32✔
92
    _page_size = page_size;
32✔
93
    _page_cache.clear(); // Clear cache since page boundaries changed
32✔
94
    endResetModel();
32✔
95
}
96

97
int PaginatedTableModel::rowCount(QModelIndex const & parent) const {
690✔
98
    if (parent.isValid()) return 0;
690✔
99
    return static_cast<int>(_total_rows);
690✔
100
}
101

102
int PaginatedTableModel::columnCount(QModelIndex const & parent) const {
812✔
103
    if (parent.isValid()) return 0;
812✔
104
    return static_cast<int>(_column_names.size());
812✔
105
}
106

107
QVariant PaginatedTableModel::data(QModelIndex const & index, int role) const {
322✔
108
    if (role != Qt::DisplayRole || !index.isValid()) return {};
322✔
109
    if (index.row() < 0 || index.column() < 0) return {};
118✔
110
    if (index.row() >= static_cast<int>(_total_rows)) return {};
118✔
111
    if (index.column() >= _column_names.size()) return {};
118✔
112

113
    auto const row = static_cast<size_t>(index.row());
118✔
114
    auto const column_name = _column_names[index.column()].toStdString();
118✔
115

116
    try {
117
        if (_complete_table_view) {
118✔
118
            // Use the complete table view directly
119
            return formatValue(_complete_table_view, column_name, row);
26✔
120
        } else {
121
            // Use pagination with mini tables
122
            auto [mini_table, local_row] = getMiniTableForRow(row);
92✔
123
            if (!mini_table || local_row >= mini_table->getRowCount()) {
92✔
124
                return QString("Error");
×
125
            }
126
            return formatValue(mini_table, column_name, local_row);
92✔
127
        }
92✔
128
    } catch (std::exception const & e) {
×
129
        qDebug() << "Error accessing data at row" << row << "column" << column_name.c_str() << ":" << e.what();
×
130
        return QString("Error");
×
131
    }
×
132
}
118✔
133

134
QVariant PaginatedTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
4,925✔
135
    if (role != Qt::DisplayRole) return {};
4,925✔
136
    if (orientation == Qt::Horizontal) {
1,265✔
137
        if (section >= 0 && section < _column_names.size()) {
158✔
138
            return _column_names[section];
158✔
139
        }
140
        return {};
×
141
    }
142
    return section + 1; // 1-based row numbering
1,107✔
143
}
144

145
std::pair<std::shared_ptr<TableView>, size_t> PaginatedTableModel::getMiniTableForRow(size_t row_index) const {
92✔
146
    if (!_source_row_selector || !_data_manager) {
92✔
147
        return {nullptr, 0};
×
148
    }
149
    
150
    // Calculate which page this row belongs to
151
    size_t const page_number = row_index / _page_size;
92✔
152
    size_t const page_start_row = page_number * _page_size;
92✔
153
    size_t const local_row = row_index - page_start_row;
92✔
154
    
155
    // Check cache first
156
    auto cache_it = _page_cache.find(page_number);
92✔
157
    if (cache_it != _page_cache.end()) {
92✔
158
        return {cache_it->second, local_row};
77✔
159
    }
160
    
161
    // Create mini table for this page
162
    size_t const actual_page_size = std::min(_page_size, _total_rows - page_start_row);
15✔
163
    auto mini_table = createMiniTable(page_start_row, actual_page_size);
15✔
164
    
165
    if (mini_table) {
15✔
166
        // Cache the mini table
167
        _page_cache[page_number] = mini_table;
15✔
168
        // Diagnostics: track number of pages materialized
169
        ++_materialized_page_count;
15✔
170
        cleanupCache();
15✔
171
    }
172
    
173
    return {mini_table, local_row};
15✔
174
}
15✔
175

176
std::shared_ptr<TableView> PaginatedTableModel::createMiniTable(size_t page_start_row, size_t page_size) const {
15✔
177
    if (!_source_row_selector || !_data_manager) {
15✔
178
        return nullptr;
×
179
    }
180
    
181
    try {
182
        // Get the data manager extension and table registry
183
        auto* table_registry = _data_manager->getTableRegistry();
15✔
184
        if (!table_registry) {
15✔
185
            qDebug() << "Failed to get table registry from data manager";
×
186
            return nullptr;
×
187
        }
188
        
189
        auto data_manager_extension = table_registry->getDataManagerExtension();
15✔
190
        if (!data_manager_extension) {
15✔
191
            qDebug() << "Failed to get data manager extension from table registry";
×
192
            return nullptr;
×
193
        }
194
        // Create a vector of indices for this window
195
        std::vector<size_t> window_indices;
15✔
196
        window_indices.reserve(page_size);
15✔
197
        for (size_t i = 0; i < page_size && (page_start_row + i) < _total_rows; ++i) {
914✔
198
            window_indices.push_back(page_start_row + i);
899✔
199
        }
200
        
201
        // Use the existing cloneRowSelectorFiltered pattern
202
        auto windowed_selector = [&]() -> std::unique_ptr<IRowSelector> {
30✔
203
            // Create filtered selector based on the type of the source selector
204
            if (auto indexSelector = dynamic_cast<IndexSelector const *>(_source_row_selector.get())) {
15✔
205
                std::vector<size_t> const & source_indices = indexSelector->getIndices();
×
206
                std::vector<size_t> filtered;
×
207
                filtered.reserve(window_indices.size());
×
208
                for (size_t const k : window_indices) {
×
209
                    if (k < source_indices.size()) {
×
210
                        filtered.push_back(source_indices[k]);
×
211
                    }
212
                }
213
                return std::make_unique<IndexSelector>(std::move(filtered));
×
214
            }
×
215
            
216
            if (auto timestampSelector = dynamic_cast<TimestampSelector const *>(_source_row_selector.get())) {
15✔
217
                auto const & timestamps = timestampSelector->getTimestamps();
7✔
218
                std::vector<TimeFrameIndex> filtered;
7✔
219
                filtered.reserve(window_indices.size());
7✔
220
                for (size_t const k : window_indices) {
455✔
221
                    if (k < timestamps.size()) {
448✔
222
                        filtered.push_back(timestamps[k]);
448✔
223
                    }
224
                }
225
                return std::make_unique<TimestampSelector>(std::move(filtered), timestampSelector->getTimeFrame());
7✔
226
            }
7✔
227
            
228
            if (auto intervalSelector = dynamic_cast<IntervalSelector const *>(_source_row_selector.get())) {
8✔
229
                auto const & intervals = intervalSelector->getIntervals();
8✔
230
                std::vector<TimeFrameInterval> filtered;
8✔
231
                filtered.reserve(window_indices.size());
8✔
232
                for (size_t const k : window_indices) {
459✔
233
                    if (k < intervals.size()) {
451✔
234
                        filtered.push_back(intervals[k]);
451✔
235
                    }
236
                }
237
                return std::make_unique<IntervalSelector>(std::move(filtered), intervalSelector->getTimeFrame());
8✔
238
            }
8✔
239
            
240
            return nullptr;
×
241
        }();
15✔
242
        
243
        if (!windowed_selector) {
15✔
244
            qDebug() << "Failed to create windowed selector for page starting at row" << page_start_row;
×
245
            return nullptr;
×
246
        }
247
        
248
        // Build a mini table with the windowed selector
249
        TableViewBuilder builder(data_manager_extension);
15✔
250
        builder.setRowSelector(std::move(windowed_selector));
15✔
251
        
252
        // Add all columns using the TableRegistry method
253
        for (auto const & col_info : _column_infos) {
93✔
254
            if (!table_registry->addColumnToBuilder(builder, col_info)) {
78✔
255
                qDebug() << "Failed to add column" << col_info.name.c_str() << "to mini table";
×
256
                return nullptr;
×
257
            }
258
        }
259
        
260
        auto mini_table_obj = builder.build();
15✔
261
        auto mini_table = std::make_shared<TableView>(std::move(mini_table_obj));
15✔
262
        mini_table->materializeAll();
15✔
263
        
264
        return mini_table;
15✔
265
    } catch (std::exception const & e) {
15✔
266
        qDebug() << "Exception creating mini table:" << e.what();
×
267
        return nullptr;
×
268
    }
×
269
}
270

271
void PaginatedTableModel::cleanupCache() const {
15✔
272
    while (_page_cache.size() > MAX_CACHED_PAGES) {
15✔
273
        // Remove the first (oldest) entry
274
        _page_cache.erase(_page_cache.begin());
×
275
    }
276
}
15✔
277

278
QString PaginatedTableModel::formatValue(std::shared_ptr<TableView> const & mini_table, 
118✔
279
                                       std::string const & column_name, 
280
                                       size_t local_row) {
281
    if (!mini_table || local_row >= mini_table->getRowCount()) {
118✔
282
        return {"N/A"};
×
283
    }
284
    
285
    try {
286
        return mini_table->visitColumnData(column_name, [local_row](auto const & vec) -> QString {
236✔
287
            using VecT = std::decay_t<decltype(vec)>;
288
            using ElemT = typename VecT::value_type;
289

290
            if (local_row >= vec.size()) {
118✔
291
                if constexpr (std::is_same_v<ElemT, double>) return {"NaN"};
×
292
                if constexpr (std::is_same_v<ElemT, int>) return {"NaN"};
×
293
                if constexpr (std::is_same_v<ElemT, long long>) return {"NaN"};
294
                if constexpr (std::is_same_v<ElemT, bool>) return {"false"};
×
UNCOV
295
                return {"N/A"};
×
296
            }
297

298
            if constexpr (std::is_same_v<ElemT, double>) {
299
                return QString::number(vec[local_row], 'f', 3);
44✔
300
            } else if constexpr (std::is_same_v<ElemT, int>) {
301
                return QString::number(vec[local_row]);
26✔
302
            } else if constexpr (std::is_same_v<ElemT, int64_t>) {
303
                return QString::number(static_cast<int64_t>(vec[local_row]));
3✔
304
            } else if constexpr (std::is_same_v<ElemT, bool>) {
305
                return vec[local_row] ? QStringLiteral("true") : QStringLiteral("false");
78✔
306
            } else if constexpr (
307
                std::is_same_v<ElemT, std::vector<double>> ||
308
                std::is_same_v<ElemT, std::vector<int>> ||
309
                std::is_same_v<ElemT, std::vector<float>>
310
            ) {
311
                return joinVector(vec[local_row]);
6✔
312
            } else {
313
                return {"?"};
×
314
            }
315
        });
118✔
UNCOV
316
    } catch (...) {
×
UNCOV
317
        return {"Error"};
×
UNCOV
318
    }
×
319
}
320

321
template<typename T>
322
QString PaginatedTableModel::joinVector(std::vector<T> const & values) {
6✔
323
    if (values.empty()) return QString();
6✔
324
    QString out;
6✔
325
    out.reserve(static_cast<int>(values.size() * 4));
6✔
326
    for (size_t i = 0; i < values.size(); ++i) {
21✔
327
        if constexpr (std::is_same_v<T, double>) {
328
            out += QString::number(values[i], 'f', 3);
6✔
329
        } else if constexpr (std::is_same_v<T, float>) {
330
            out += QString::number(static_cast<double>(values[i]), 'f', 3);
9✔
331
        } else {
UNCOV
332
            out += QString::number(values[i]);
×
333
        }
334
        if (i + 1 < values.size()) out += ",";
15✔
335
    }
336
    return out;
6✔
337
}
6✔
338

339
// Explicit instantiations
340
template QString PaginatedTableModel::joinVector<double>(std::vector<double> const &);
341
template QString PaginatedTableModel::joinVector<int>(std::vector<int> const &);
342
template QString PaginatedTableModel::joinVector<float>(std::vector<float> const &);
343

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc