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

paulmthompson / WhiskerToolbox / 17826420298

18 Sep 2025 02:24AM UTC coverage: 71.942% (-0.002%) from 71.944%
17826420298

push

github

paulmthompson
Merge branch 'main' of https://github.com/paulmthompson/WhiskerToolbox

110 of 158 new or added lines in 12 files covered. (69.62%)

222 existing lines in 9 files now uncovered.

39625 of 55079 relevant lines covered (71.94%)

1301.53 hits per line

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

86.57
/src/WhiskerToolbox/GroupManagementWidget/GroupManager.cpp
1
#include "GroupManager.hpp"
2

3
#include "DataManager/Entity/EntityGroupManager.hpp"
4

5
#include <QDebug>
6

7
QVector<QColor> const GroupManager::DEFAULT_COLORS = {
8
        QColor(31, 119, 180), // Blue
9
        QColor(255, 127, 14), // Orange
10
        QColor(44, 160, 44),  // Green
11
        QColor(214, 39, 40),  // Red
12
        QColor(148, 103, 189),// Purple
13
        QColor(140, 86, 75),  // Brown
14
        QColor(227, 119, 194),// Pink
15
        QColor(127, 127, 127),// Gray
16
        QColor(188, 189, 34), // Olive
17
        QColor(23, 190, 207)  // Cyan
18
};
19

20
GroupManager::GroupManager(EntityGroupManager * entity_group_manager, QObject * parent)
34✔
21
    : QObject(parent),
22
      m_entity_group_manager(entity_group_manager),
34✔
23
      m_next_group_id(1) {
34✔
24
    // Assert that we have a valid EntityGroupManager
25
    Q_ASSERT(m_entity_group_manager != nullptr);
34✔
26
}
34✔
27

28
int GroupManager::createGroup(QString const & name) {
19✔
29
    QColor const color = getNextDefaultColor();
19✔
30
    return createGroup(name, color);
38✔
31
}
32

33
int GroupManager::createGroup(QString const & name, QColor const & color) {
19✔
34

35
    GroupId const entity_group_id = m_entity_group_manager->createGroup(name.toStdString());
57✔
36

37
    auto const group_id = static_cast<int>(entity_group_id);
19✔
38
    m_group_colors[group_id] = color;
19✔
39

40
    qDebug() << "GroupManager: Created group" << group_id << "with name" << name;
19✔
41

42
    emit groupCreated(group_id);
19✔
43
    return group_id;
19✔
44
}
45

46
bool GroupManager::removeGroup(int group_id) {
3✔
47
    auto entity_group_id = static_cast<GroupId>(group_id);
3✔
48

49
    if (!m_entity_group_manager->deleteGroup(entity_group_id)) {
3✔
50
        return false;
1✔
51
    }
52

53
    m_group_colors.remove(group_id);
2✔
54

55
    qDebug() << "GroupManager: Removed group" << group_id;
2✔
56

57
    emit groupRemoved(group_id);
2✔
58
    return true;
2✔
59
}
60

61
std::optional<GroupManager::Group> GroupManager::getGroup(int group_id) const {
19✔
62
    auto entity_group_id = static_cast<GroupId>(group_id);
19✔
63

64
    auto descriptor = m_entity_group_manager->getGroupDescriptor(entity_group_id);
19✔
65
    if (!descriptor.has_value()) {
19✔
UNCOV
66
        return std::nullopt;
×
67
    }
68

69
    Group group(group_id,
19✔
70
                QString::fromStdString(descriptor->name),
38✔
71
                m_group_colors.value(group_id, QColor(128, 128, 128)));
57✔
72

73
    return group;
19✔
74
}
19✔
75

76
bool GroupManager::setGroupName(int group_id, QString const & name) {
11✔
77
    auto entity_group_id = static_cast<GroupId>(group_id);
11✔
78

79
    // Get current descriptor to preserve description
80
    auto descriptor = m_entity_group_manager->getGroupDescriptor(entity_group_id);
11✔
81
    if (!descriptor.has_value()) {
11✔
UNCOV
82
        return false;
×
83
    }
84

85
    // Avoid redundant updates/signals if the name is unchanged
86
    if (QString::fromStdString(descriptor->name) == name) {
11✔
87
        return true;
7✔
88
    }
89

90
    if (!m_entity_group_manager->updateGroup(entity_group_id, name.toStdString(), descriptor->description)) {
4✔
UNCOV
91
        return false;
×
92
    }
93

94
    qDebug() << "GroupManager: Updated group" << group_id << "name to" << name;
4✔
95

96
    emit groupModified(group_id);
4✔
97
    return true;
4✔
98
}
11✔
99

100
bool GroupManager::setGroupColor(int group_id, QColor const & color) {
2✔
101
    auto entity_group_id = static_cast<GroupId>(group_id);
2✔
102

103
    if (!m_entity_group_manager->hasGroup(entity_group_id)) {
2✔
UNCOV
104
        return false;
×
105
    }
106

107
    m_group_colors[group_id] = color;
2✔
108

109
    qDebug() << "GroupManager: Updated group" << group_id << "color";
2✔
110

111
    emit groupModified(group_id);
2✔
112
    return true;
2✔
113
}
114

115
QMap<int, GroupManager::Group> GroupManager::getGroups() const {
66✔
116
    QMap<int, Group> result;
66✔
117

118
    auto group_ids = m_entity_group_manager->getAllGroupIds();
66✔
119
    for (GroupId const entity_group_id: group_ids) {
81✔
120
        auto descriptor = m_entity_group_manager->getGroupDescriptor(entity_group_id);
15✔
121
        if (descriptor.has_value()) {
15✔
122
            auto group_id = static_cast<int>(entity_group_id);
15✔
123
            Group const group(group_id,
15✔
124
                              QString::fromStdString(descriptor->name),
30✔
125
                              m_group_colors.value(group_id, QColor(128, 128, 128)));
45✔
126
            result[group_id] = group;
15✔
127
        }
15✔
128
    }
15✔
129

130
    return result;
66✔
131
}
66✔
132

133
// ===== EntityId-based API =====
134
bool GroupManager::assignEntitiesToGroup(int group_id, std::unordered_set<EntityId> const & entity_ids) {
13✔
135
    auto entity_group_id = static_cast<GroupId>(group_id);
13✔
136

137
    if (!m_entity_group_manager->hasGroup(entity_group_id)) {
13✔
UNCOV
138
        return false;
×
139
    }
140

141
    std::vector<EntityId> const entity_vector(entity_ids.begin(), entity_ids.end());
39✔
142

143
    std::size_t const added_count = m_entity_group_manager->addEntitiesToGroup(entity_group_id, entity_vector);
13✔
144

145
    qDebug() << "GroupManager: Assigned" << added_count << "entities to group" << group_id;
13✔
146
    if (added_count > 0) {
13✔
147
        emit groupModified(group_id);
11✔
148
    }
149

150
    return added_count > 0;
13✔
151
}
13✔
152

153
bool GroupManager::removeEntitiesFromGroup(int group_id, std::unordered_set<EntityId> const & entity_ids) {
6✔
154
    auto entity_group_id = static_cast<GroupId>(group_id);
6✔
155

156
    if (!m_entity_group_manager->hasGroup(entity_group_id)) {
6✔
UNCOV
157
        return false;
×
158
    }
159

160
    std::vector<EntityId> const entity_vector(entity_ids.begin(), entity_ids.end());
18✔
161

162
    std::size_t const removed_count = m_entity_group_manager->removeEntitiesFromGroup(entity_group_id, entity_vector);
6✔
163

164
    if (removed_count > 0) {
6✔
165
        qDebug() << "GroupManager: Removed" << removed_count << "entities from group" << group_id;
4✔
166
        emit groupModified(group_id);
4✔
167
    }
168

169
    return removed_count > 0;
6✔
170
}
6✔
171

172
void GroupManager::ungroupEntities(std::unordered_set<EntityId> const & entity_ids) {
1✔
173
    std::unordered_set<int> affected_groups;
1✔
174

175
    // For each entity, find which groups it belongs to and remove it
176
    for (EntityId const entity_id: entity_ids) {
2✔
177
        auto group_ids = m_entity_group_manager->getGroupsContainingEntity(entity_id);
1✔
178
        for (GroupId const entity_group_id: group_ids) {
3✔
179
            auto const group_id = static_cast<int>(entity_group_id);
2✔
180
            affected_groups.insert(group_id);
2✔
181

182
            std::vector<EntityId> const single_entity = {entity_id};
6✔
183
            m_entity_group_manager->removeEntitiesFromGroup(entity_group_id, single_entity);
2✔
184
        }
2✔
185
    }
1✔
186

187
    if (!affected_groups.empty()) {
1✔
188
        qDebug() << "GroupManager: Ungrouped" << entity_ids.size() << "entities from" << affected_groups.size() << "groups";
1✔
189
        for (int const gid: affected_groups) {
3✔
190
            emit groupModified(gid);
2✔
191
        }
192
    }
193
}
2✔
194

195
int GroupManager::getEntityGroup(EntityId id) const {
24✔
196
    auto group_ids = m_entity_group_manager->getGroupsContainingEntity(id);
24✔
197

198
    // Return the first group (assuming entities belong to at most one group for now)
199
    if (!group_ids.empty()) {
24✔
200
        return static_cast<int>(group_ids[0]);
4✔
201
    }
202

203
    return -1;// Not in any group
20✔
204
}
24✔
205

UNCOV
206
QColor GroupManager::getEntityColor(EntityId id, QColor const & default_color) const {
×
NEW
207
    int const group_id = getEntityGroup(id);
×
208
    if (group_id == -1) {
×
UNCOV
209
        return default_color;
×
210
    }
211

UNCOV
212
    return m_group_colors.value(group_id, default_color);
×
213
}
214

215
int GroupManager::getGroupMemberCount(int group_id) const {
22✔
216
    auto entity_group_id = static_cast<GroupId>(group_id);
22✔
217
    return static_cast<int>(m_entity_group_manager->getGroupSize(entity_group_id));
22✔
218
}
219

220
void GroupManager::clearAllGroups() {
×
UNCOV
221
    qDebug() << "GroupManager: Clearing all groups";
×
222

223
    // Clear EntityGroupManager
UNCOV
224
    m_entity_group_manager->clear();
×
225

226
    // Clear our color mappings
UNCOV
227
    m_group_colors.clear();
×
228

UNCOV
229
    m_next_group_id = 1;
×
230

231
    // Note: We don't emit specific signals here since everything is being cleared
UNCOV
232
}
×
233

234
QColor GroupManager::getNextDefaultColor() const {
19✔
235
    if (DEFAULT_COLORS.isEmpty()) {
19✔
NEW
236
        return {128, 128, 128};// Fallback gray
×
237
    }
238

239
    // Cycle through the default colors based on current group count
240
    auto group_ids = m_entity_group_manager->getAllGroupIds();
19✔
241
    auto color_index = static_cast<int>(group_ids.size()) % DEFAULT_COLORS.size();
19✔
242
    return DEFAULT_COLORS[color_index];
19✔
243
}
19✔
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