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

paulmthompson / WhiskerToolbox / 16121066516

07 Jul 2025 03:19PM UTC coverage: 74.129% (-0.07%) from 74.196%
16121066516

push

github

paulmthompson
delete intervals in datamanager widget

0 of 20 new or added lines in 2 files covered. (0.0%)

139 existing lines in 5 files now uncovered.

13106 of 17680 relevant lines covered (74.13%)

1064.63 hits per line

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

40.26
/src/WhiskerToolbox/DataManager/DataManager.cpp
1
#include "DataManager.hpp"
2
#include "AnalogTimeSeries/Analog_Time_Series.hpp"
3
#include "DigitalTimeSeries/Digital_Event_Series.hpp"
4
#include "DigitalTimeSeries/Digital_Interval_Series.hpp"
5
#include "Lines/Line_Data.hpp"
6
#include "Masks/Mask_Data.hpp"
7
#include "Media/Media_Data.hpp"
8
#include "Media/Image_Data.hpp"
9
#include "Media/Video_Data.hpp"
10
#include "Points/Point_Data.hpp"
11
#include "Tensors/Tensor_Data.hpp"
12

13
#include "AnalogTimeSeries/IO/JSON/Analog_Time_Series_JSON.hpp"
14
#include "DigitalTimeSeries/IO/CSV/Digital_Interval_Series_CSV.hpp"
15
#include "DigitalTimeSeries/IO/JSON/Digital_Event_Series_JSON.hpp"
16
#include "DigitalTimeSeries/IO/JSON/Digital_Interval_Series_JSON.hpp"
17
#include "Lines/IO/JSON/Line_Data_JSON.hpp"
18
#include "Masks/IO/JSON/Mask_Data_JSON.hpp"
19
#include "Media/Video_Data_Loader.hpp"
20
#include "Points/IO/JSON/Point_Data_JSON.hpp"
21
#include "Tensors/IO/numpy/Tensor_Data_numpy.hpp"
22

23
#include "loaders/binary_loaders.hpp"
24
#include "transforms/Masks/mask_area.hpp"
25

26
#include "TimeFrame.hpp"
27
#include "TimeFrame/TimeFrameV2.hpp"
28

29
#include "nlohmann/json.hpp"
30
#include "utils/string_manip.hpp"
31

32
#include <filesystem>
33
#include <fstream>
34
#include <iostream>
35
#include <optional>
36
#include <regex>
37

38
using namespace nlohmann;
39

40
DataManager::DataManager() {
64✔
41
    _times["time"] = std::make_shared<TimeFrame>();
192✔
42
    _data["media"] = std::make_shared<MediaData>();
192✔
43

44
    setTimeFrame("media", "time");
320✔
45
    _output_path = std::filesystem::current_path();
64✔
46
}
64✔
47

48
bool DataManager::setTime(std::string const & key, std::shared_ptr<TimeFrame> timeframe, bool overwrite) {
28✔
49

50
    if (!timeframe) {
28✔
51
        std::cerr << "Error: Cannot register a nullptr TimeFrame for key: " << key << std::endl;
1✔
52
        return false;
1✔
53
    }
54

55
    if (_times.find(key) != _times.end()) {
27✔
56
        if (overwrite) {
2✔
57
            _times[key] = std::move(timeframe);
×
UNCOV
58
            return true;
×
59
        } else {
60
            std::cerr << "Error: Time key already exists in DataManager: " << key << std::endl;
2✔
61
            return false;
2✔
62
        }
63
    }
64

65
    _times[key] = std::move(timeframe);
25✔
66
    return true;
25✔
67
}
68

69
std::shared_ptr<TimeFrame> DataManager::getTime() {
1✔
70
    return _times["time"];
3✔
71
};
72

73
std::shared_ptr<TimeFrame> DataManager::getTime(std::string const & key) {
11✔
74
    if (_times.find(key) != _times.end()) {
11✔
75
        return _times[key];
9✔
76
    }
77
    return nullptr;
2✔
78
};
79

80
bool DataManager::removeTime(std::string const & key) {
×
81
    if (_times.find(key) == _times.end()) {
×
82
        std::cerr << "Error: could not find time key in DataManager: " << key << std::endl;
×
UNCOV
83
        return false;
×
84
    }
85

86
    auto it = _times.find(key);
×
87
    _times.erase(it);
×
UNCOV
88
    return true;
×
89
}
90

91
bool DataManager::setTimeFrame(std::string const & data_key, std::string const & time_key) {
124✔
92
    if (_data.find(data_key) == _data.end()) {
124✔
93
        std::cerr << "Error: Data key not found in DataManager: " << data_key << std::endl;
1✔
94
        return false;
1✔
95
    }
96

97
    if (_times.find(time_key) == _times.end()) {
123✔
98
        std::cerr << "Error: Time key not found in DataManager: " << time_key << std::endl;
1✔
99
        return false;
1✔
100
    }
101

102
    _time_frames[data_key] = time_key;
122✔
103
    return true;
122✔
104
}
105

106
std::string DataManager::getTimeFrame(std::string const & data_key) {
9✔
107
    // check if data_key exists
108
    if (_data.find(data_key) == _data.end()) {
9✔
109
        std::cerr << "Error: Data key not found in DataManager: " << data_key << std::endl;
1✔
110
        return "";
3✔
111
    }
112

113
    // check if data key has time frame
114
    if (_time_frames.find(data_key) == _time_frames.end()) {
8✔
115
        std::cerr << "Error: Data key "
116
                  << data_key
117
                  << " exists, but not assigned to a TimeFrame" << std::endl;
×
UNCOV
118
        return "";
×
119
    }
120

121
    return _time_frames[data_key];
8✔
122
}
123

124
std::vector<std::string> DataManager::getTimeFrameKeys() {
9✔
125
    std::vector<std::string> keys;
9✔
126
    keys.reserve(_times.size());
9✔
127
    for (auto const & [key, value]: _times) {
27✔
128

129
        keys.push_back(key);
18✔
130
    }
131
    return keys;
9✔
UNCOV
132
}
×
133

134
int DataManager::addCallbackToData(std::string const & key, ObserverCallback callback) {
7✔
135

136
    int id = -1;
7✔
137

138
    if (_data.find(key) != _data.end()) {
7✔
139
        auto data = _data[key];
6✔
140

141
        id = std::visit([callback](auto & x) {
12✔
142
            return x.get()->addObserver(callback);
6✔
143
        },
144
                        data);
145
    }
6✔
146

147
    return id;
7✔
148
}
149

150
bool DataManager::removeCallbackFromData(std::string const & key, int callback_id) {
4✔
151
    if (_data.find(key) != _data.end()) {
4✔
152
        auto data = _data[key];
3✔
153

154
        std::visit([callback_id](auto & x) {
6✔
155
            x.get()->removeObserver(callback_id);
3✔
156
        },
3✔
157
                   data);
158

159
        return true;
3✔
160
    }
3✔
161

162
    return false;
1✔
163
}
164

165
void DataManager::addObserver(ObserverCallback callback) {
5✔
166
    _observers.push_back(std::move(callback));
5✔
167
}
5✔
168

169
void DataManager::_notifyObservers() {
62✔
170
    for (auto & observer: _observers) {
70✔
171
        observer();
8✔
172
    }
173
}
62✔
174

175
std::vector<std::string> DataManager::getAllKeys() {
5✔
176
    std::vector<std::string> keys;
5✔
177
    keys.reserve(_data.size());
5✔
178
    for (auto const & [key, value]: _data) {
17✔
179

180
        keys.push_back(key);
12✔
181
    }
182
    return keys;
5✔
UNCOV
183
}
×
184

185
std::optional<DataTypeVariant> DataManager::getDataVariant(std::string const & key) {
×
186
    if (_data.find(key) != _data.end()) {
×
UNCOV
187
        return _data[key];
×
188
    }
UNCOV
189
    return std::nullopt;
×
190
}
191

192
void DataManager::setData(std::string const & key, DataTypeVariant data) {
1✔
193
    _data[key] = data;
1✔
194
    setTimeFrame(key, "time");
3✔
195
    _notifyObservers();
1✔
196
}
1✔
197

UNCOV
198
std::optional<std::string> processFilePath(
×
199
        std::string const & file_path,
200
        std::filesystem::path const & base_path) {
UNCOV
201
    std::filesystem::path full_path = file_path;
×
202

203
    // Check for wildcard character
UNCOV
204
    if (file_path.find('*') != std::string::npos) {
×
205
        // Convert wildcard pattern to regex
206
        std::string const pattern = std::regex_replace(full_path.string(), std::regex("\\*"), ".*");
×
UNCOV
207
        std::regex const regex_pattern(pattern);
×
208

209
        // Iterate through the directory to find matching files
210
        for (auto const & entry: std::filesystem::directory_iterator(base_path)) {
×
211
            std::cout << "Checking " << entry.path().string() << " with full path " << full_path << std::endl;
×
212
            if (std::regex_match(entry.path().string(), regex_pattern)) {
×
213
                std::cout << "Loading file " << entry.path().string() << std::endl;
×
UNCOV
214
                return entry.path().string();
×
215
            }
216
        }
×
217
        return std::nullopt;
×
UNCOV
218
    } else {
×
219
        // Check if the file path is relative
220
        if (!std::filesystem::path(file_path).is_absolute()) {
×
UNCOV
221
            full_path = base_path / file_path;
×
222
        }
223
        // Check for the presence of the file
224
        if (std::filesystem::exists(full_path)) {
×
225
            std::cout << "Loading file " << full_path.string() << std::endl;
×
UNCOV
226
            return full_path.string();
×
227
        } else {
UNCOV
228
            return std::nullopt;
×
229
        }
230
    }
UNCOV
231
}
×
232

233
bool checkRequiredFields(json const & item, std::vector<std::string> const & requiredFields) {
×
234
    for (auto const & field: requiredFields) {
×
235
        if (!item.contains(field)) {
×
236
            std::cerr << "Error: Missing required field \"" << field << "\" in JSON item." << std::endl;
×
UNCOV
237
            return false;
×
238
        }
239
    }
UNCOV
240
    return true;
×
241
}
242

243
void checkOptionalFields(json const & item, std::vector<std::string> const & optionalFields) {
×
244
    for (auto const & field: optionalFields) {
×
245
        if (!item.contains(field)) {
×
UNCOV
246
            std::cout << "Warning: Optional field \"" << field << "\" is missing in JSON item." << std::endl;
×
247
        }
248
    }
UNCOV
249
}
×
250

251
DM_DataType stringToDataType(std::string const & data_type_str) {
×
252
    if (data_type_str == "video") return DM_DataType::Video;
×
253
    if (data_type_str == "images") return DM_DataType::Images;
×
254
    if (data_type_str == "points") return DM_DataType::Points;
×
255
    if (data_type_str == "mask") return DM_DataType::Mask;
×
256
    if (data_type_str == "line") return DM_DataType::Line;
×
257
    if (data_type_str == "analog") return DM_DataType::Analog;
×
258
    if (data_type_str == "digital_event") return DM_DataType::DigitalEvent;
×
259
    if (data_type_str == "digital_interval") return DM_DataType::DigitalInterval;
×
260
    if (data_type_str == "tensor") return DM_DataType::Tensor;
×
261
    if (data_type_str == "time") return DM_DataType::Time;
×
UNCOV
262
    return DM_DataType::Unknown;
×
263
}
264

265
std::vector<DataInfo> load_data_from_json_config(DataManager * dm, std::string const & json_filepath) {
×
UNCOV
266
    std::vector<DataInfo> data_info_list;
×
267
    // Open JSON file
268
    std::ifstream ifs(json_filepath);
×
269
    if (!ifs.is_open()) {
×
270
        std::cerr << "Failed to open JSON file: " << json_filepath << std::endl;
×
UNCOV
271
        return data_info_list;
×
272
    }
273

274
    // Parse JSON
275
    json j;
×
UNCOV
276
    ifs >> j;
×
277

278
    // get base path of filepath
UNCOV
279
    std::filesystem::path const base_path = std::filesystem::path(json_filepath).parent_path();
×
280

281
    // Iterate through JSON array
UNCOV
282
    for (auto const & item: j) {
×
283

284
        if (!checkRequiredFields(item, {"data_type", "name", "filepath"})) {
×
UNCOV
285
            continue;// Exit if any required field is missing
×
286
        }
287

288
        std::string const data_type_str = item["data_type"];
×
289
        auto const data_type = stringToDataType(data_type_str);
×
290
        if (data_type == DM_DataType::Unknown) {
×
291
            std::cout << "Unknown data type: " << data_type_str << std::endl;
×
UNCOV
292
            continue;
×
293
        }
294

UNCOV
295
        std::string const name = item["name"];
×
296

297
        auto file_exists = processFilePath(item["filepath"], base_path);
×
298
        if (!file_exists) {
×
299
            std::cerr << "File does not exist: " << item["filepath"] << std::endl;
×
UNCOV
300
            continue;
×
301
        }
302

UNCOV
303
        std::string const file_path = file_exists.value();
×
304

305
        switch (data_type) {
×
UNCOV
306
            case DM_DataType::Video: {
×
307

308
                auto video_data = load_video_into_VideoData(file_path);
×
UNCOV
309
                dm->setData<VideoData>("media", video_data);
×
310

311
                data_info_list.push_back({name, "VideoData", ""});
×
312
                break;
×
313
            }
×
UNCOV
314
            case DM_DataType::Images: {
×
315

316
                auto media = std::make_shared<ImageData>();
×
317
                media->LoadMedia(file_path);
×
UNCOV
318
                dm->setData<ImageData>("media", media);
×
319

320
                data_info_list.push_back({name, "ImageData", ""});
×
321
                break;
×
322
            }
×
UNCOV
323
            case DM_DataType::Points: {
×
324

UNCOV
325
                auto point_data = load_into_PointData(file_path, item);
×
326

UNCOV
327
                dm->setData<PointData>(name, point_data);
×
328

329
                std::string const color = item.value("color", "#0000FF");
×
330
                data_info_list.push_back({name, "PointData", color});
×
331
                break;
×
332
            }
×
UNCOV
333
            case DM_DataType::Mask: {
×
334

UNCOV
335
                auto mask_data = load_into_MaskData(file_path, item);
×
336

337
                std::string const color = item.value("color", "0000FF");
×
UNCOV
338
                dm->setData<MaskData>(name, mask_data);
×
339

UNCOV
340
                data_info_list.push_back({name, "MaskData", color});
×
341

UNCOV
342
                if (item.contains("operations")) {
×
343

UNCOV
344
                    for (auto const & operation: item["operations"]) {
×
345

UNCOV
346
                        std::string const operation_type = operation["type"];
×
347

348
                        if (operation_type == "area") {
×
349
                            std::cout << "Calculating area for mask: " << name << std::endl;
×
350
                            auto area_data = area(dm->getData<MaskData>(name).get());
×
351
                            std::string const output_name = name + "_area";
×
352
                            dm->setData<AnalogTimeSeries>(output_name, area_data);
×
353
                        }
×
UNCOV
354
                    }
×
355
                }
356
                break;
×
357
            }
×
UNCOV
358
            case DM_DataType::Line: {
×
359

UNCOV
360
                auto line_data = load_into_LineData(file_path, item);
×
361

UNCOV
362
                dm->setData<LineData>(name, line_data);
×
363

UNCOV
364
                std::string const color = item.value("color", "0000FF");
×
365

UNCOV
366
                data_info_list.push_back({name, "LineData", color});
×
367

368
                break;
×
369
            }
×
UNCOV
370
            case DM_DataType::Analog: {
×
371

UNCOV
372
                auto analog_time_series = load_into_AnalogTimeSeries(file_path, item);
×
373

374
                for (int channel = 0; channel < analog_time_series.size(); channel++) {
×
UNCOV
375
                    std::string const channel_name = name + "_" + std::to_string(channel);
×
376

UNCOV
377
                    dm->setData<AnalogTimeSeries>(channel_name, analog_time_series[channel]);
×
378

379
                    if (item.contains("clock")) {
×
380
                        std::string const clock = item["clock"];
×
381
                        dm->setTimeFrame(channel_name, clock);
×
382
                    }
×
383
                }
×
384
                break;
×
385
            }
×
UNCOV
386
            case DM_DataType::DigitalEvent: {
×
387

UNCOV
388
                auto digital_event_series = load_into_DigitalEventSeries(file_path, item);
×
389

390
                for (int channel = 0; channel < digital_event_series.size(); channel++) {
×
UNCOV
391
                    std::string const channel_name = name + "_" + std::to_string(channel);
×
392

UNCOV
393
                    dm->setData<DigitalEventSeries>(channel_name, digital_event_series[channel]);
×
394

395
                    if (item.contains("clock")) {
×
396
                        std::string const clock = item["clock"];
×
397
                        dm->setTimeFrame(channel_name, clock);
×
398
                    }
×
399
                }
×
400
                break;
×
401
            }
×
UNCOV
402
            case DM_DataType::DigitalInterval: {
×
403

404
                auto digital_interval_series = load_into_DigitalIntervalSeries(file_path, item);
×
UNCOV
405
                dm->setData<DigitalIntervalSeries>(name, digital_interval_series);
×
406

407
                break;
×
408
            }
×
UNCOV
409
            case DM_DataType::Tensor: {
×
410

UNCOV
411
                if (item["format"] == "numpy") {
×
412

413
                    TensorData tensor_data;
×
UNCOV
414
                    loadNpyToTensorData(file_path, tensor_data);
×
415

UNCOV
416
                    dm->setData<TensorData>(name, std::make_shared<TensorData>(tensor_data));
×
417

418
                } else {
×
UNCOV
419
                    std::cout << "Format " << item["format"] << " not found for " << name << std::endl;
×
420
                }
UNCOV
421
                break;
×
422
            }
UNCOV
423
            case DM_DataType::Time: {
×
424

UNCOV
425
                if (item["format"] == "uint16") {
×
426

427
                    int const channel = item["channel"];
×
UNCOV
428
                    std::string const transition = item["transition"];
×
429

UNCOV
430
                    int const header_size = item.value("header_size", 0);
×
431

432
                    auto opts = Loader::BinaryAnalogOptions{.file_path = file_path,
×
433
                                                            .header_size_bytes = static_cast<size_t>(header_size)};
×
UNCOV
434
                    auto data = Loader::readBinaryFile<uint16_t>(opts);
×
435

436
                    auto digital_data = Loader::extractDigitalData(data, channel);
×
UNCOV
437
                    auto events = Loader::extractEvents(digital_data, transition);
×
438

439
                    // convert to int with std::transform
440
                    std::vector<int> events_int;
×
441
                    events_int.reserve(events.size());
×
442
                    for (auto e: events) {
×
UNCOV
443
                        events_int.push_back(static_cast<int>(e));
×
444
                    }
UNCOV
445
                    std::cout << "Loaded " << events_int.size() << " events for " << name << std::endl;
×
446

447
                    auto timeframe = std::make_shared<TimeFrame>(events_int);
×
448
                    dm->setTime(name, timeframe, true);
×
UNCOV
449
                }
×
450

UNCOV
451
                if (item["format"] == "uint16_length") {
×
452

UNCOV
453
                    int const header_size = item.value("header_size", 0);
×
454

455
                    auto opts = Loader::BinaryAnalogOptions{.file_path = file_path,
×
456
                                                            .header_size_bytes = static_cast<size_t>(header_size)};
×
UNCOV
457
                    auto data = Loader::readBinaryFile<uint16_t>(opts);
×
458

459
                    std::vector<int> t(data.size());
×
UNCOV
460
                    std::iota(std::begin(t), std::end(t), 0);
×
461

UNCOV
462
                    std::cout << "Total of " << t.size() << " timestamps for " << name << std::endl;
×
463

464
                    auto timeframe = std::make_shared<TimeFrame>(t);
×
465
                    dm->setTime(name, timeframe, true);
×
UNCOV
466
                }
×
467

UNCOV
468
                if (item["format"] == "filename") {
×
469

470
                    // Get required parameters
471
                    std::string const folder_path = file_path; // file path is required argument
×
UNCOV
472
                    std::string const regex_pattern = item["regex_pattern"];
×
473

474
                    // Get optional parameters with defaults
475
                    std::string const file_extension = item.value("file_extension", "");
×
476
                    std::string const mode_str = item.value("mode", "found_values");
×
UNCOV
477
                    bool const sort_ascending = item.value("sort_ascending", true);
×
478

479
                    // Convert mode string to enum
480
                    FilenameTimeFrameMode mode = FilenameTimeFrameMode::FOUND_VALUES;
×
481
                    if (mode_str == "zero_to_max") {
×
482
                        mode = FilenameTimeFrameMode::ZERO_TO_MAX;
×
483
                    } else if (mode_str == "min_to_max") {
×
UNCOV
484
                        mode = FilenameTimeFrameMode::MIN_TO_MAX;
×
485
                    }
486

487
                    // Create options
488
                    FilenameTimeFrameOptions options;
×
489
                    options.folder_path = folder_path;
×
490
                    options.file_extension = file_extension;
×
491
                    options.regex_pattern = regex_pattern;
×
492
                    options.mode = mode;
×
UNCOV
493
                    options.sort_ascending = sort_ascending;
×
494

495
                    // Create TimeFrame from filenames
496
                    auto timeframe = createTimeFrameFromFilenames(options);
×
497
                    if (timeframe) {
×
UNCOV
498
                        dm->setTime(name, timeframe, true);
×
499
                        std::cout << "Created TimeFrame '" << name << "' from filenames in "
UNCOV
500
                                  << folder_path << std::endl;
×
501
                    } else {
502
                        std::cerr << "Error: Failed to create TimeFrame from filenames for "
UNCOV
503
                                  << name << std::endl;
×
504
                    }
505
                }
×
UNCOV
506
                break;
×
507
            }
508
            default:
×
509
                std::cout << "Unsupported data type: " << data_type_str << std::endl;
×
510
                continue;
×
511
        }
×
512
        if (item.contains("clock")) {
×
513
            std::string const clock = item["clock"];
×
514
            std::cout << "Setting time for " << name << " to " << clock << std::endl;
×
515
            dm->setTimeFrame(name, clock);
×
516
        }
×
UNCOV
517
    }
×
518

519
    return data_info_list;
UNCOV
520
}
×
521

522
DM_DataType DataManager::getType(std::string const & key) const {
22✔
523
    auto it = _data.find(key);
22✔
524
    if (it != _data.end()) {
22✔
525
        if (std::holds_alternative<std::shared_ptr<MediaData>>(it->second)) {
19✔
526
            //Dynamic cast to videodata or image data
527

528
            auto media_data = std::get<std::shared_ptr<MediaData>>(it->second);
15✔
529
            if (dynamic_cast<VideoData *>(media_data.get()) != nullptr) {
15✔
530
                return DM_DataType::Video;
7✔
531
            } else if (dynamic_cast<ImageData *>(media_data.get()) != nullptr) {
8✔
532
                return DM_DataType::Images;
7✔
533
            } else {
534
                return DM_DataType::Video; //Old behavior
1✔
535
            }
536
        } else if (std::holds_alternative<std::shared_ptr<PointData>>(it->second)) {
19✔
537
            return DM_DataType::Points;
2✔
538
        } else if (std::holds_alternative<std::shared_ptr<LineData>>(it->second)) {
2✔
539
            return DM_DataType::Line;
1✔
540
        } else if (std::holds_alternative<std::shared_ptr<MaskData>>(it->second)) {
1✔
541
            return DM_DataType::Mask;
1✔
542
        } else if (std::holds_alternative<std::shared_ptr<AnalogTimeSeries>>(it->second)) {
×
543
            return DM_DataType::Analog;
×
544
        } else if (std::holds_alternative<std::shared_ptr<DigitalEventSeries>>(it->second)) {
×
545
            return DM_DataType::DigitalEvent;
×
546
        } else if (std::holds_alternative<std::shared_ptr<DigitalIntervalSeries>>(it->second)) {
×
547
            return DM_DataType::DigitalInterval;
×
548
        } else if (std::holds_alternative<std::shared_ptr<TensorData>>(it->second)) {
×
UNCOV
549
            return DM_DataType::Tensor;
×
550
        }
UNCOV
551
        return DM_DataType::Unknown;
×
552
    }
553
    return DM_DataType::Unknown;
3✔
554
}
555

556
// ========== TimeFrameV2 Implementation ==========
557

558
bool DataManager::setTimeV2(std::string const & key, AnyTimeFrame timeframe, bool overwrite) {
18✔
559
    if (_times_v2.find(key) != _times_v2.end()) {
18✔
560
        if (overwrite) {
6✔
561
            _times_v2[key] = std::move(timeframe);
6✔
562
            return true;
6✔
563
        } else {
564
            std::cerr << "Error: TimeFrameV2 key already exists in DataManager: " << key << std::endl;
×
UNCOV
565
            return false;
×
566
        }
567
    }
568

569
    _times_v2[key] = std::move(timeframe);
12✔
570
    return true;
12✔
571
}
572

573
std::optional<AnyTimeFrame> DataManager::getTimeV2(std::string const & key) {
15✔
574
    if (_times_v2.find(key) != _times_v2.end()) {
15✔
575
        return _times_v2[key];
12✔
576
    }
577
    return std::nullopt;
3✔
578
}
579

580
bool DataManager::removeTimeV2(std::string const & key) {
2✔
581
    if (_times_v2.find(key) == _times_v2.end()) {
2✔
582
        std::cerr << "Error: could not find TimeFrameV2 key in DataManager: " << key << std::endl;
1✔
583
        return false;
1✔
584
    }
585

586
    auto it = _times_v2.find(key);
1✔
587
    _times_v2.erase(it);
1✔
588
    return true;
1✔
589
}
590

591
std::vector<std::string> DataManager::getTimeFrameV2Keys() {
3✔
592
    std::vector<std::string> keys;
3✔
593
    keys.reserve(_times_v2.size());
3✔
594
    for (auto const & [key, value]: _times_v2) {
7✔
595
        keys.push_back(key);
4✔
596
    }
597
    return keys;
3✔
UNCOV
598
}
×
599

600
bool DataManager::createClockTimeFrame(std::string const & key, int64_t start_tick,
4✔
601
                                       int64_t num_samples, double sampling_rate_hz,
602
                                       bool overwrite) {
603
    auto clock_frame = TimeFrameUtils::createDenseClockTimeFrame(start_tick, num_samples, sampling_rate_hz);
4✔
604
    AnyTimeFrame any_frame = clock_frame;
4✔
605
    return setTimeV2(key, std::move(any_frame), overwrite);
8✔
606
}
4✔
607

608
bool DataManager::createCameraTimeFrame(std::string const & key, std::vector<int64_t> frame_indices,
4✔
609
                                        bool overwrite) {
610
    auto camera_frame = TimeFrameUtils::createSparseCameraTimeFrame(std::move(frame_indices));
4✔
611
    AnyTimeFrame any_frame = camera_frame;
4✔
612
    return setTimeV2(key, std::move(any_frame), overwrite);
8✔
613
}
4✔
614

615
bool DataManager::createDenseCameraTimeFrame(std::string const & key, int64_t start_frame,
1✔
616
                                             int64_t num_frames, bool overwrite) {
617
    auto camera_frame = TimeFrameUtils::createDenseCameraTimeFrame(start_frame, num_frames);
1✔
618
    AnyTimeFrame any_frame = camera_frame;
1✔
619
    return setTimeV2(key, std::move(any_frame), overwrite);
2✔
620
}
1✔
621

622
std::string convert_data_type_to_string(DM_DataType type) {
×
623
    switch (type) {
×
624
        case DM_DataType::Video:
×
625
            return "video";
×
626
        case DM_DataType::Images:
×
627
            return "images";
×
628
        case DM_DataType::Points:
×
629
            return "points";
×
630
        case DM_DataType::Mask:
×
631
            return "mask";
×
632
        case DM_DataType::Line:
×
633
            return "line";
×
634
        case DM_DataType::Analog:
×
635
            return "analog";
×
636
        case DM_DataType::DigitalEvent:
×
637
            return "digital_event";
×
638
        case DM_DataType::DigitalInterval:
×
639
            return "digital_interval";
×
640
        case DM_DataType::Tensor:
×
641
            return "tensor";
×
642
        case DM_DataType::Time:
×
643
            return "time";
×
644
        default:
×
UNCOV
645
            return "unknown";
×
646
    }
647
}
648

649
// ========== Enhanced AnalogTimeSeries Support Implementation ==========
650

651
bool DataManager::createAnalogTimeSeriesWithClock(std::string const & data_key,
3✔
652
                                                  std::string const & timeframe_key,
653
                                                  std::vector<float> analog_data,
654
                                                  int64_t start_tick,
655
                                                  double sampling_rate_hz,
656
                                                  bool overwrite) {
657
    // Create the clock timeframe
658
    if (!createClockTimeFrame(timeframe_key, start_tick,
3✔
659
                              static_cast<int64_t>(analog_data.size()),
3✔
660
                              sampling_rate_hz, overwrite)) {
UNCOV
661
        return false;
×
662
    }
663

664
    // Get the timeframe
665
    auto timeframe_opt = getTimeV2(timeframe_key);
3✔
666
    if (!timeframe_opt.has_value()) {
3✔
UNCOV
667
        return false;
×
668
    }
669

670
    // Create time vector with the actual tick values
671
    std::vector<TimeFrameIndex> time_vector;
3✔
672
    time_vector.reserve(analog_data.size());
3✔
673
    for (size_t i = 0; i < analog_data.size(); ++i) {
31,007✔
674
        time_vector.push_back(TimeFrameIndex(start_tick + static_cast<int64_t>(i)));
31,004✔
675
    }
676

677
    // Create the analog series
678
    auto series = std::make_shared<AnalogTimeSeries>(std::move(analog_data), std::move(time_vector));
3✔
679
    setDataV2(data_key, series, timeframe_opt.value(), timeframe_key);
3✔
680
    return true;
3✔
681
}
3✔
682

683
bool DataManager::createAnalogTimeSeriesWithCamera(std::string const & data_key,
2✔
684
                                                   std::string const & timeframe_key,
685
                                                   std::vector<float> analog_data,
686
                                                   std::vector<int64_t> frame_indices,
687
                                                   bool overwrite) {
688
    if (analog_data.size() != frame_indices.size()) {
2✔
689
        std::cerr << "Error: analog data and frame indices must have same size" << std::endl;
×
UNCOV
690
        return false;
×
691
    }
692

693
    // Save frame_indices before they're moved
694
    std::vector<int64_t> frame_indices_copy = frame_indices;
2✔
695

696
    // Create the camera timeframe
697
    if (!createCameraTimeFrame(timeframe_key, std::move(frame_indices), overwrite)) {
2✔
UNCOV
698
        return false;
×
699
    }
700

701
    // Get the timeframe
702
    auto timeframe_opt = getTimeV2(timeframe_key);
2✔
703
    if (!timeframe_opt.has_value()) {
2✔
UNCOV
704
        return false;
×
705
    }
706

707
    // Create time vector with the actual frame indices
708
    std::vector<TimeFrameIndex> time_vector;
2✔
709
    time_vector.reserve(analog_data.size());
2✔
710
    for (int64_t frame_idx: frame_indices_copy) {
107✔
711
        time_vector.push_back(TimeFrameIndex(frame_idx));
105✔
712
    }
713

714
    // Create the analog series
715
    auto series = std::make_shared<AnalogTimeSeries>(std::move(analog_data), std::move(time_vector));
2✔
716
    setDataV2(data_key, series, timeframe_opt.value(), timeframe_key);
2✔
717
    return true;
2✔
718
}
2✔
719

720
bool DataManager::createAnalogTimeSeriesWithDenseCamera(std::string const & data_key,
1✔
721
                                                        std::string const & timeframe_key,
722
                                                        std::vector<float> analog_data,
723
                                                        int64_t start_frame,
724
                                                        bool overwrite) {
725
    // Create the dense camera timeframe
726
    if (!createDenseCameraTimeFrame(timeframe_key, start_frame,
1✔
727
                                    static_cast<int64_t>(analog_data.size()), overwrite)) {
1✔
UNCOV
728
        return false;
×
729
    }
730

731
    // Get the timeframe
732
    auto timeframe_opt = getTimeV2(timeframe_key);
1✔
733
    if (!timeframe_opt.has_value()) {
1✔
UNCOV
734
        return false;
×
735
    }
736

737
    // Create time vector with the actual frame indices
738
    std::vector<TimeFrameIndex> time_vector;
1✔
739
    time_vector.reserve(analog_data.size());
1✔
740
    for (size_t i = 0; i < analog_data.size(); ++i) {
7✔
741
        time_vector.push_back(TimeFrameIndex(start_frame + static_cast<int64_t>(i)));
6✔
742
    }
743

744
    // Create the analog series
745
    auto series = std::make_shared<AnalogTimeSeries>(std::move(analog_data), std::move(time_vector));
1✔
746
    setDataV2(data_key, series, timeframe_opt.value(), timeframe_key);
1✔
747
    return true;
1✔
748
}
1✔
749

750
std::string DataManager::getAnalogCoordinateType(std::string const & data_key) {
5✔
751
    // Check if the data exists and is an AnalogTimeSeries
752
    if (_data.find(data_key) == _data.end()) {
5✔
UNCOV
753
        return "not_found";
×
754
    }
755

756
    if (!std::holds_alternative<std::shared_ptr<AnalogTimeSeries>>(_data[data_key])) {
5✔
UNCOV
757
        return "not_analog_timeseries";
×
758
    }
759

760
    auto series = std::get<std::shared_ptr<AnalogTimeSeries>>(_data[data_key]);
5✔
761
    if (!series) {
5✔
UNCOV
762
        return "null_series";
×
763
    }
764

765
    return series->getCoordinateType();
5✔
766
}
5✔
767

768
bool DataManager::analogUsesCoordinateTypeImpl(std::string const & data_key, std::string const & type_name) {
3✔
769
    // Check if the data exists and is an AnalogTimeSeries
770
    if (_data.find(data_key) == _data.end()) {
3✔
UNCOV
771
        return false;
×
772
    }
773

774
    if (!std::holds_alternative<std::shared_ptr<AnalogTimeSeries>>(_data[data_key])) {
3✔
UNCOV
775
        return false;
×
776
    }
777

778
    auto series = std::get<std::shared_ptr<AnalogTimeSeries>>(_data[data_key]);
3✔
779
    if (!series) {
3✔
UNCOV
780
        return false;
×
781
    }
782

783
    return series->getCoordinateType() == type_name;
3✔
784
}
3✔
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