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

paulmthompson / WhiskerToolbox / 14913265839

08 May 2025 06:17PM UTC coverage: 20.55% (-0.2%) from 20.758%
14913265839

push

github

paulmthompson
use datatype return from data manager.

0 of 42 new or added lines in 1 file covered. (0.0%)

78 existing lines in 1 file now uncovered.

471 of 2292 relevant lines covered (20.55%)

1.91 hits per line

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

3.91
/src/WhiskerToolbox/DataManager/DataManager.cpp
1

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

12
#include "AnalogTimeSeries/Analog_Time_Series_Loader.hpp"
13
#include "DigitalTimeSeries/Digital_Event_Series_Loader.hpp"
14
#include "DigitalTimeSeries/Digital_Interval_Series_Loader.hpp"
15
#include "Lines/IO/CSV/Line_Data_CSV.hpp"
16
#include "Masks/IO/HDF5/Mask_Data_HDF5.hpp"
17
#include "Media/Video_Data_Loader.hpp"
18
#include "Points/IO/CSV/Point_Data_CSV.hpp"
19

20
#include "loaders/binary_loaders.hpp"
21
#include "transforms/data_transforms.hpp"
22
#include "transforms/Masks/mask_area.hpp"
23

24
#include "TimeFrame.hpp"
25

26
#include "nlohmann/json.hpp"
27
#include "utils/string_manip.hpp"
28

29
#include <filesystem>
30
#include <fstream>
31
#include <iostream>
32
#include <optional>
33
#include <regex>
34

35
using namespace nlohmann;
36

37
DataManager::DataManager() {
2✔
38
    _times["time"] = std::make_shared<TimeFrame>();
6✔
39
    _data["media"] = std::make_shared<MediaData>();
6✔
40

41
    setTimeFrame("media", "time");
10✔
42
    _output_path = std::filesystem::current_path();
2✔
43
}
2✔
44

45
void DataManager::setTimeFrame(std::string const & data_key, std::string const & time_key) {
2✔
46
    //Check that data_key is in _data
47
    if (_data.find(data_key) == _data.end()) {
2✔
48
        std::cerr << "Data key not found in DataManager: " << data_key << std::endl;
×
49
        return;
×
50
    }
51

52
    //Check that time_key is in _times
53
    if (_times.find(time_key) == _times.end()) {
2✔
54
        std::cerr << "Time key not found in DataManager: " << time_key << std::endl;
×
55
        return;
×
56
    }
57

58
    _time_frames[data_key] = time_key;
2✔
59
}
60

NEW
61
DM_DataType stringToDataType(std::string const & data_type_str) {
×
NEW
62
    if (data_type_str == "video") return DM_DataType::Video;
×
NEW
63
    if (data_type_str == "points") return DM_DataType::Points;
×
NEW
64
    if (data_type_str == "mask") return DM_DataType::Mask;
×
NEW
65
    if (data_type_str == "line") return DM_DataType::Line;
×
NEW
66
    if (data_type_str == "analog") return DM_DataType::Analog;
×
NEW
67
    if (data_type_str == "digital_event") return DM_DataType::DigitalEvent;
×
NEW
68
    if (data_type_str == "digital_interval") return DM_DataType::DigitalInterval;
×
NEW
69
    if (data_type_str == "tensor") return DM_DataType::Tensor;
×
NEW
70
    if (data_type_str == "time") return DM_DataType::Time;
×
NEW
71
    return DM_DataType::Unknown;
×
72
}
73

74
std::optional<std::string> processFilePath(
×
75
        std::string const & file_path,
76
        std::filesystem::path const & base_path) {
77
    std::filesystem::path full_path = file_path;
×
78

79
    // Check for wildcard character
80
    if (file_path.find('*') != std::string::npos) {
×
81
        // Convert wildcard pattern to regex
82
        std::string const pattern = std::regex_replace(full_path.string(), std::regex("\\*"), ".*");
×
83
        std::regex const regex_pattern(pattern);
×
84

85
        // Iterate through the directory to find matching files
UNCOV
86
        for (auto const & entry: std::filesystem::directory_iterator(base_path)) {
×
87
            std::cout << "Checking " << entry.path().string() << " with full path " << full_path << std::endl;
×
UNCOV
88
            if (std::regex_match(entry.path().string(), regex_pattern)) {
×
UNCOV
89
                std::cout << "Loading file " << entry.path().string() << std::endl;
×
90
                return entry.path().string();
×
91
            }
UNCOV
92
        }
×
93
        return std::nullopt;
×
UNCOV
94
    } else {
×
95
        // Check if the file path is relative
96
        if (!std::filesystem::path(file_path).is_absolute()) {
×
UNCOV
97
            full_path = base_path / file_path;
×
98
        }
99
        // Check for the presence of the file
100
        if (std::filesystem::exists(full_path)) {
×
101
            std::cout << "Loading file " << full_path.string() << std::endl;
×
102
            return full_path.string();
×
103
        } else {
UNCOV
104
            return std::nullopt;
×
105
        }
106
    }
107
}
×
108

109
bool checkRequiredFields(json const & item, std::vector<std::string> const & requiredFields) {
×
110
    for (auto const & field: requiredFields) {
×
UNCOV
111
        if (!item.contains(field)) {
×
UNCOV
112
            std::cerr << "Error: Missing required field \"" << field << "\" in JSON item." << std::endl;
×
113
            return false;
×
114
        }
115
    }
UNCOV
116
    return true;
×
117
}
118

UNCOV
119
int DataManager::addCallbackToData(std::string const & key, ObserverCallback callback) {
×
120

UNCOV
121
    int id = -1;
×
122

123
    if (_data.find(key) != _data.end()) {
×
124
        auto data = _data[key];
×
125

126
        id = std::visit([callback](auto & x) {
×
UNCOV
127
            return x.get()->addObserver(callback);
×
128
        },
129
                        data);
UNCOV
130
    }
×
131

132
    return id;
×
133
}
134

UNCOV
135
void DataManager::removeCallbackFromData(std::string const & key, int callback_id) {
×
136
    if (_data.find(key) != _data.end()) {
×
137
        auto data = _data[key];
×
138

139
        std::visit([callback_id](auto & x) {
×
140
            x.get()->removeObserver(callback_id);
×
UNCOV
141
        },
×
142
                   data);
143
    }
×
UNCOV
144
}
×
145

UNCOV
146
void checkOptionalFields(json const & item, std::vector<std::string> const & optionalFields) {
×
UNCOV
147
    for (auto const & field: optionalFields) {
×
148
        if (!item.contains(field)) {
×
149
            std::cout << "Warning: Optional field \"" << field << "\" is missing in JSON item." << std::endl;
×
150
        }
151
    }
152
}
×
153

154
std::vector<DataInfo> load_data_from_json_config(DataManager * dm, std::string const & json_filepath) {
×
UNCOV
155
    std::vector<DataInfo> data_info_list;
×
156
    // Open JSON file
157
    std::ifstream ifs(json_filepath);
×
UNCOV
158
    if (!ifs.is_open()) {
×
159
        std::cerr << "Failed to open JSON file: " << json_filepath << std::endl;
×
160
        return data_info_list;
×
161
    }
162

163
    // Parse JSON
UNCOV
164
    json j;
×
165
    ifs >> j;
×
166

167
    // get base path of filepath
168
    std::filesystem::path const base_path = std::filesystem::path(json_filepath).parent_path();
×
169

170
    // Iterate through JSON array
171
    for (auto const & item: j) {
×
172

173
        if (!checkRequiredFields(item, {"data_type", "name", "filepath"})) {
×
UNCOV
174
            continue;// Exit if any required field is missing
×
175
        }
176

177
        std::string const data_type_str = item["data_type"];
×
178
        auto const data_type = stringToDataType(data_type_str);
×
NEW
179
        if (data_type == DM_DataType::Unknown) {
×
UNCOV
180
            std::cout << "Unknown data type: " << data_type_str << std::endl;
×
181
            continue;
×
182
        }
183

184
        std::string const name = item["name"];
×
185

186
        auto file_exists = processFilePath(item["filepath"], base_path);
×
187
        if (!file_exists) {
×
UNCOV
188
            std::cerr << "File does not exist: " << item["filepath"] << std::endl;
×
UNCOV
189
            continue;
×
190
        }
191

192
        std::string const file_path = file_exists.value();
×
193

194
        switch (data_type) {
×
NEW
195
            case DM_DataType::Video: {
×
196
                // Create VideoData object
197
                auto video_data = load_video_into_VideoData(file_path);
×
198

199
                // Add VideoData to DataManager
200
                dm->setMedia(video_data);
×
201

202
                data_info_list.push_back({name, "VideoData", ""});
×
UNCOV
203
                break;
×
UNCOV
204
            }
×
NEW
205
            case DM_DataType::Points: {
×
206

207
                auto point_data = load_into_PointData(file_path, item);
×
208

UNCOV
209
                dm->setData<PointData>(name, point_data);
×
210

UNCOV
211
                std::string const color = item.value("color", "#0000FF");
×
UNCOV
212
                data_info_list.push_back({name, "PointData", color});
×
213
                break;
×
UNCOV
214
            }
×
NEW
215
            case DM_DataType::Mask: {
×
216

217
                auto mask_data = load_into_MaskData(file_path, item);
×
218

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

222
                data_info_list.push_back({name, "MaskData", color});
×
223

224
                if (item.contains("operations")) {
×
225

226
                    for (auto const & operation: item["operations"]) {
×
227

228
                        std::string const operation_type = operation["type"];
×
229

230
                        if (operation_type == "area") {
×
UNCOV
231
                            std::cout << "Calculating area for mask: " << name << std::endl;
×
232
                            auto area_data = area(dm->getData<MaskData>(name).get());
×
233
                            std::string const output_name = name + "_area";
×
UNCOV
234
                            dm->setData<AnalogTimeSeries>(output_name, area_data);
×
235
                        }
×
UNCOV
236
                    }
×
237
                }
UNCOV
238
                break;
×
239
            }
×
NEW
240
            case DM_DataType::Line: {
×
241

UNCOV
242
                auto line_map = load_line_csv(file_path);
×
243

244
                //Get the whisker name from the filename using filesystem
245
                auto whisker_filename = std::filesystem::path(file_path).filename().string();
×
246

247
                //Remove .csv suffix from filename
248
                auto whisker_name = remove_extension(whisker_filename);
×
249

UNCOV
250
                dm->setData<LineData>(whisker_name, std::make_shared<LineData>(line_map));
×
251

252
                std::string const color = item.value("color", "0000FF");
×
253

UNCOV
254
                data_info_list.push_back({name, "LineData", color});
×
255

UNCOV
256
                break;
×
UNCOV
257
            }
×
NEW
258
            case DM_DataType::Analog: {
×
259

UNCOV
260
                auto analog_time_series = load_into_AnalogTimeSeries(file_path, item);
×
261

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

265
                    dm->setData<AnalogTimeSeries>(channel_name, analog_time_series[channel]);
×
266

267
                    if (item.contains("clock")) {
×
UNCOV
268
                        std::string const clock = item["clock"];
×
269
                        dm->setTimeFrame(channel_name, clock);
×
270
                    }
×
271
                }
×
UNCOV
272
                break;
×
273
            }
×
NEW
274
            case DM_DataType::DigitalEvent: {
×
275

276
                auto digital_event_series = load_into_DigitalEventSeries(file_path, item);
×
277

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

281
                    dm->setData<DigitalEventSeries>(channel_name, digital_event_series[channel]);
×
282

283
                    if (item.contains("clock")) {
×
284
                        std::string const clock = item["clock"];
×
285
                        dm->setTimeFrame(channel_name, clock);
×
286
                    }
×
287
                }
×
UNCOV
288
                break;
×
289
            }
×
NEW
290
            case DM_DataType::DigitalInterval: {
×
291

292
                auto digital_interval_series = load_into_DigitalIntervalSeries(file_path, item);
×
UNCOV
293
                dm->setData<DigitalIntervalSeries>(name, digital_interval_series);
×
294

UNCOV
295
                break;
×
296
            }
×
NEW
297
            case DM_DataType::Tensor: {
×
298

299
                if (item["format"] == "numpy") {
×
300

301
                    TensorData tensor_data;
×
302
                    loadNpyToTensorData(file_path, tensor_data);
×
303

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

306
                } else {
×
UNCOV
307
                    std::cout << "Format " << item["format"] << " not found for " << name << std::endl;
×
308
                }
309
                break;
×
310
            }
NEW
311
            case DM_DataType::Time: {
×
312

UNCOV
313
                if (item["format"] == "uint16") {
×
314

315
                    int const channel = item["channel"];
×
UNCOV
316
                    std::string const transition = item["transition"];
×
317

UNCOV
318
                    int const header_size = item.value("header_size", 0);
×
319

320
                    auto opts = Loader::BinaryAnalogOptions{.file_path = file_path,
×
UNCOV
321
                                                            .header_size_bytes = static_cast<size_t>(header_size)};
×
322
                    auto data = readBinaryFile<uint16_t>(opts);
×
323

324
                    auto digital_data = Loader::extractDigitalData(data, channel);
×
UNCOV
325
                    auto events = Loader::extractEvents(digital_data, transition);
×
326

327
                    // convert to int with std::transform
328
                    std::vector<int> events_int;
×
329
                    events_int.reserve(events.size());
×
UNCOV
330
                    for (auto e: events) {
×
331
                        events_int.push_back(static_cast<int>(e));
×
332
                    }
333
                    std::cout << "Loaded " << events_int.size() << " events for " << name << std::endl;
×
334

335
                    auto timeframe = std::make_shared<TimeFrame>(events_int);
×
UNCOV
336
                    dm->setTime(name, timeframe);
×
337
                }
×
338

UNCOV
339
                if (item["format"] == "uint16_length") {
×
340

341
                    int const header_size = item.value("header_size", 0);
×
342

343
                    auto opts = Loader::BinaryAnalogOptions{.file_path = file_path,
×
344
                                                            .header_size_bytes = static_cast<size_t>(header_size)};
×
UNCOV
345
                    auto data = readBinaryFile<uint16_t>(opts);
×
346

UNCOV
347
                    std::vector<int> t(data.size());
×
348
                    std::iota(std::begin(t), std::end(t), 0);
×
349

350
                    std::cout << "Total of " << t.size() << " timestamps for " << name << std::endl;
×
351

352
                    auto timeframe = std::make_shared<TimeFrame>(t);
×
UNCOV
353
                    dm->setTime(name, timeframe);
×
354
                }
×
UNCOV
355
                break;
×
356
            }
357
            default:
×
358
                std::cout << "Unsupported data type: " << data_type_str << std::endl;
×
UNCOV
359
                continue;
×
360
        }
×
361
        if (item.contains("clock")) {
×
UNCOV
362
            std::string const clock = item["clock"];
×
363
            std::cout << "Setting time for " << name << " to " << clock << std::endl;
×
UNCOV
364
            dm->setTimeFrame(name, clock);
×
365
        }
×
366
    }
×
367

368
    return data_info_list;
UNCOV
369
}
×
370

NEW
371
DM_DataType DataManager::getType(std::string const & key) const {
×
372
    auto it = _data.find(key);
×
373
    if (it != _data.end()) {
×
374
        if (std::holds_alternative<std::shared_ptr<MediaData>>(it->second)) {
×
NEW
375
            return DM_DataType::Video;
×
376
        } else if (std::holds_alternative<std::shared_ptr<PointData>>(it->second)) {
×
NEW
377
            return DM_DataType::Points;
×
378
        } else if (std::holds_alternative<std::shared_ptr<LineData>>(it->second)) {
×
NEW
379
            return DM_DataType::Line;
×
UNCOV
380
        } else if (std::holds_alternative<std::shared_ptr<MaskData>>(it->second)) {
×
NEW
381
            return DM_DataType::Mask;
×
382
        } else if (std::holds_alternative<std::shared_ptr<AnalogTimeSeries>>(it->second)) {
×
NEW
383
            return DM_DataType::Analog;
×
384
        } else if (std::holds_alternative<std::shared_ptr<DigitalEventSeries>>(it->second)) {
×
NEW
385
            return DM_DataType::DigitalEvent;
×
386
        } else if (std::holds_alternative<std::shared_ptr<DigitalIntervalSeries>>(it->second)) {
×
NEW
387
            return DM_DataType::DigitalInterval;
×
388
        } else if (std::holds_alternative<std::shared_ptr<TensorData>>(it->second)) {
×
NEW
389
            return DM_DataType::Tensor;
×
390
        }
NEW
391
        return DM_DataType::Unknown;
×
392
    }
NEW
393
    return DM_DataType::Unknown;
×
394
}
395

NEW
396
std::string convert_data_type_to_string(DM_DataType type) {
×
397
    switch (type) {
×
NEW
398
        case DM_DataType::Video:
×
399
            return "video";
×
NEW
400
        case DM_DataType::Points:
×
401
            return "points";
×
NEW
402
        case DM_DataType::Mask:
×
UNCOV
403
            return "mask";
×
NEW
404
        case DM_DataType::Line:
×
UNCOV
405
            return "line";
×
NEW
406
        case DM_DataType::Analog:
×
UNCOV
407
            return "analog";
×
NEW
408
        case DM_DataType::DigitalEvent:
×
UNCOV
409
            return "digital_event";
×
NEW
410
        case DM_DataType::DigitalInterval:
×
UNCOV
411
            return "digital_interval";
×
NEW
412
        case DM_DataType::Tensor:
×
UNCOV
413
            return "tensor";
×
NEW
414
        case DM_DataType::Time:
×
UNCOV
415
            return "time";
×
UNCOV
416
        default:
×
UNCOV
417
            return "unknown";
×
418
    }
419
}
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