Skip to content

Commit ebfa69b

Browse files
committed
Add option for users to coment
1 parent a2ac471 commit ebfa69b

5 files changed

Lines changed: 590 additions & 84 deletions

File tree

backend/src/main/java/com/apexgrid/transformertracker/model/Inspection.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,18 @@ public class Inspection {
7575
@Column(name = "severity", columnDefinition = "text")
7676
private String severity;
7777

78+
// Optional per-box comments entered by users
79+
@Column(name = "comment", columnDefinition = "text")
80+
private String comment;
81+
7882
// History of per-box severity arrays aligned with boundingBoxHistory/faultTypeHistory snapshots
7983
@Column(name = "severityhistory", columnDefinition = "text")
8084
private String severityHistory;
8185

86+
// History of per-box comments aligned with boundingBoxHistory snapshots
87+
@Column(name = "commenthistory", columnDefinition = "text")
88+
private String commentHistory;
89+
8290
// History of timestamps (ISO-8601 strings) when each snapshot was archived
8391
@Column(name = "timestamphistory", columnDefinition = "text")
8492
private String timestampHistory;
@@ -151,9 +159,15 @@ public class Inspection {
151159
public String getSeverity() { return severity; }
152160
public void setSeverity(String severity) { this.severity = severity; }
153161

162+
public String getComment() { return comment; }
163+
public void setComment(String comment) { this.comment = comment; }
164+
154165
public String getSeverityHistory() { return severityHistory; }
155166
public void setSeverityHistory(String severityHistory) { this.severityHistory = severityHistory; }
156167

168+
public String getCommentHistory() { return commentHistory; }
169+
public void setCommentHistory(String commentHistory) { this.commentHistory = commentHistory; }
170+
157171
public String getTimestampHistory() { return timestampHistory; }
158172
public void setTimestampHistory(String timestampHistory) { this.timestampHistory = timestampHistory; }
159173
}

backend/src/main/java/com/apexgrid/transformertracker/web/InspectionController.java

Lines changed: 128 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ public ResponseEntity<?> exportInspection(@PathVariable String id) {
112112
JsonNode currentFaults = parseJsonNode(mapper, inspection.getFaultTypes());
113113
JsonNode currentAnnotated = parseJsonNode(mapper, inspection.getAnnotatedBy());
114114
JsonNode currentSeverity = parseJsonNode(mapper, inspection.getSeverity());
115+
JsonNode currentComments = parseJsonNode(mapper, inspection.getComment());
115116

116117
ObjectNode currentNode = root.putObject("current");
117118
putNullable(currentNode, "timestamp",
@@ -120,15 +121,17 @@ public ResponseEntity<?> exportInspection(@PathVariable String id) {
120121
currentNode.set("faultTypes", cloneNode(currentFaults));
121122
currentNode.set("annotatedBy", cloneNode(currentAnnotated));
122123
currentNode.set("severity", cloneNode(currentSeverity));
124+
currentNode.set("comments", cloneNode(currentComments));
123125

124126
ArrayNode boxHistory = asArrayNode(parseJsonNode(mapper, inspection.getBoundingBoxHistory()));
125127
ArrayNode faultHistory = asArrayNode(parseJsonNode(mapper, inspection.getFaultTypeHistory()));
126128
ArrayNode annotatedHistory = asArrayNode(parseJsonNode(mapper, inspection.getAnnotatedByHistory()));
127129
ArrayNode severityHistory = asArrayNode(parseJsonNode(mapper, inspection.getSeverityHistory()));
130+
ArrayNode commentHistory = asArrayNode(parseJsonNode(mapper, inspection.getCommentHistory()));
128131
ArrayNode timestampHistory = asArrayNode(parseJsonNode(mapper, inspection.getTimestampHistory()));
129132

130133
ArrayNode history = mapper.createArrayNode();
131-
int snapshotCount = maxSize(boxHistory, faultHistory, annotatedHistory, severityHistory, timestampHistory);
134+
int snapshotCount = maxSize(boxHistory, faultHistory, annotatedHistory, severityHistory, commentHistory, timestampHistory);
132135
for (int index = 0; index < snapshotCount; index++) {
133136
ObjectNode entry = mapper.createObjectNode();
134137
String ts = extractText(timestampHistory, index);
@@ -138,6 +141,7 @@ public ResponseEntity<?> exportInspection(@PathVariable String id) {
138141
entry.set("faultTypes", cloneNode(snapshotValue(faultHistory, index)));
139142
entry.set("annotatedBy", cloneNode(snapshotValue(annotatedHistory, index)));
140143
entry.set("severity", cloneNode(snapshotValue(severityHistory, index)));
144+
entry.set("comments", cloneNode(snapshotValue(commentHistory, index)));
141145
history.add(entry);
142146
}
143147

@@ -149,26 +153,29 @@ public ResponseEntity<?> exportInspection(@PathVariable String id) {
149153
currentEntry.set("faultTypes", cloneNode(currentFaults));
150154
currentEntry.set("annotatedBy", cloneNode(currentAnnotated));
151155
currentEntry.set("severity", cloneNode(currentSeverity));
156+
currentEntry.set("comments", cloneNode(currentComments));
152157
history.add(currentEntry);
153158

154159
root.set("history", history);
155160

156161
byte[] metadataBytes = mapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(root);
157162

158163
StringBuilder csvBuilder = new StringBuilder();
159-
csvBuilder.append("timestamp,isCurrent,boundingBoxes,faultTypes,annotatedBy,severity\n");
164+
csvBuilder.append("timestamp,isCurrent,boundingBoxes,faultTypes,annotatedBy,severity,comments\n");
160165
for (JsonNode entry : history) {
161166
String timestamp = entry.path("timestamp").isNull() ? "" : entry.path("timestamp").asText("");
162167
String boxesJson = mapper.writeValueAsString(entry.path("boundingBoxes"));
163168
String faultsJson = mapper.writeValueAsString(entry.path("faultTypes"));
164169
String annotatedJson = mapper.writeValueAsString(entry.path("annotatedBy"));
165170
String severityJson = mapper.writeValueAsString(entry.path("severity"));
171+
String commentsJson = mapper.writeValueAsString(entry.path("comments"));
166172
csvBuilder.append('"').append(csvEscape(timestamp)).append('"').append(',');
167173
csvBuilder.append(entry.path("isCurrent").asBoolean(false) ? "true" : "false").append(',');
168174
csvBuilder.append('"').append(csvEscape(boxesJson)).append('"').append(',');
169175
csvBuilder.append('"').append(csvEscape(faultsJson)).append('"').append(',');
170176
csvBuilder.append('"').append(csvEscape(annotatedJson)).append('"').append(',');
171-
csvBuilder.append('"').append(csvEscape(severityJson)).append('"').append('\n');
177+
csvBuilder.append('"').append(csvEscape(severityJson)).append('"').append(',');
178+
csvBuilder.append('"').append(csvEscape(commentsJson)).append('"').append('\n');
172179
}
173180
byte[] csvBytes = csvBuilder.toString().getBytes(StandardCharsets.UTF_8);
174181

@@ -435,6 +442,7 @@ private void archivePreviousAnalysis(Inspection i, String annotatedBy) throws Ex
435442
String boxesJson = i.getBoundingBoxes();
436443
String faultsJson = i.getFaultTypes();
437444
String severityJson = i.getSeverity();
445+
String commentJson = i.getComment();
438446
if ((boxesJson == null || boxesJson.isBlank()) && (faultsJson == null || faultsJson.isBlank())) {
439447
return; // nothing to archive
440448
}
@@ -563,6 +571,29 @@ private void archivePreviousAnalysis(Inspection i, String annotatedBy) throws Ex
563571
severityHist.addNull();
564572
}
565573

574+
// commentHistory as array of snapshots aligned with boxes/faults
575+
ArrayNode commentHist;
576+
String commentHistJson = i.getCommentHistory();
577+
if (commentHistJson != null && !commentHistJson.isBlank()) {
578+
try {
579+
var node = mapper.readTree(commentHistJson);
580+
commentHist = node instanceof ArrayNode ? (ArrayNode) node : mapper.createArrayNode();
581+
} catch (Exception ex) {
582+
commentHist = mapper.createArrayNode();
583+
}
584+
} else {
585+
commentHist = mapper.createArrayNode();
586+
}
587+
if (commentJson != null && !commentJson.isBlank()) {
588+
try {
589+
commentHist.add(mapper.readTree(commentJson));
590+
} catch (Exception ex) {
591+
commentHist.addNull();
592+
}
593+
} else {
594+
commentHist.addNull();
595+
}
596+
566597
// timestampHistory records when each snapshot was archived
567598
ArrayNode timestampHist;
568599
String timestampHistJson = i.getTimestampHistory();
@@ -583,6 +614,7 @@ private void archivePreviousAnalysis(Inspection i, String annotatedBy) throws Ex
583614
i.setFaultTypeHistory(faultHist.toString());
584615
i.setAnnotatedByHistory(annotatedHist.toString());
585616
i.setSeverityHistory(severityHist.toString());
617+
i.setCommentHistory(commentHist.toString());
586618
i.setTimestampHistory(timestampHist.toString());
587619
}
588620

@@ -786,6 +818,8 @@ public ResponseEntity<?> clearAnalysis(@PathVariable String id) {
786818
i.setAnnotatedByHistory(null);
787819
i.setSeverity(null);
788820
i.setSeverityHistory(null);
821+
i.setComment(null);
822+
i.setCommentHistory(null);
789823
i.setTimestampHistory(null);
790824
// analyzed image dimensions removed; nothing to clear
791825
} catch (Exception ignore) { }
@@ -882,6 +916,21 @@ public ResponseEntity<?> removeBox(@PathVariable String id,
882916
} catch (Exception ignore) { /* ignore malformed severity */ }
883917
}
884918

919+
// Update comment array to maintain alignment
920+
String commentJson = i.getComment();
921+
if (commentJson != null && !commentJson.isBlank()) {
922+
try {
923+
JsonNode commentNode = mapper.readTree(commentJson);
924+
if (commentNode instanceof ArrayNode) {
925+
ArrayNode commentArr = (ArrayNode) commentNode;
926+
if (index >= 0 && index < commentArr.size()) {
927+
commentArr.remove(index);
928+
i.setComment(commentArr.isEmpty() ? null : commentArr.toString());
929+
}
930+
}
931+
} catch (Exception ignore) { /* ignore malformed comment */ }
932+
}
933+
885934
repo.save(i);
886935
return ResponseEntity.ok(Map.of("ok", true, "boundingBoxes", arr, "faultTypes", i.getFaultTypes()));
887936
} catch (Exception e) {
@@ -976,6 +1025,21 @@ public ResponseEntity<?> removeBoxByValue(@PathVariable String id,
9761025
} catch (Exception ignore) { /* ignore malformed severity */ }
9771026
}
9781027

1028+
// Update comment alignment
1029+
String commentJson = i.getComment();
1030+
if (commentJson != null && !commentJson.isBlank()) {
1031+
try {
1032+
JsonNode commentNode = mapper.readTree(commentJson);
1033+
if (commentNode instanceof ArrayNode) {
1034+
ArrayNode commentArr = (ArrayNode) commentNode;
1035+
if (matchIdx >= 0 && matchIdx < commentArr.size()) {
1036+
commentArr.remove(matchIdx);
1037+
i.setComment(commentArr.isEmpty() ? null : commentArr.toString());
1038+
}
1039+
}
1040+
} catch (Exception ignore) { /* ignore malformed comment */ }
1041+
}
1042+
9791043
repo.save(i);
9801044
return ResponseEntity.ok(Map.of("ok", true, "boundingBoxes", arr, "faultTypes", i.getFaultTypes()));
9811045
} catch (Exception e) {
@@ -997,6 +1061,14 @@ public ResponseEntity<?> addBox(@PathVariable String id,
9971061
double w = ((Number)payload.getOrDefault("w", 0)).doubleValue();
9981062
double h = ((Number)payload.getOrDefault("h", 0)).doubleValue();
9991063
String faultType = String.valueOf(payload.getOrDefault("faultType", "none"));
1064+
Object rawComment = payload.get("comment");
1065+
String commentValue = null;
1066+
if (rawComment instanceof String) {
1067+
String trimmed = ((String) rawComment).trim();
1068+
if (!trimmed.isEmpty()) {
1069+
commentValue = trimmed;
1070+
}
1071+
}
10001072

10011073
ObjectMapper mapper = new ObjectMapper();
10021074
ArrayNode boxesArr;
@@ -1065,8 +1137,28 @@ public ResponseEntity<?> addBox(@PathVariable String id,
10651137
sevArr.addNull(); // User-added boxes have null severity
10661138
i.setSeverity(sevArr.toString());
10671139

1140+
// Update comment array aligned with boxes
1141+
ArrayNode commentArr;
1142+
String commentJson = i.getComment();
1143+
if (commentJson != null && !commentJson.isBlank()) {
1144+
try {
1145+
var node = mapper.readTree(commentJson);
1146+
commentArr = node instanceof ArrayNode ? (ArrayNode) node : mapper.createArrayNode();
1147+
} catch (Exception ex) {
1148+
commentArr = mapper.createArrayNode();
1149+
}
1150+
} else {
1151+
commentArr = mapper.createArrayNode();
1152+
}
1153+
if (commentValue != null) {
1154+
commentArr.add(commentValue);
1155+
} else {
1156+
commentArr.addNull();
1157+
}
1158+
i.setComment(commentArr.isEmpty() ? null : commentArr.toString());
1159+
10681160
repo.save(i);
1069-
return ResponseEntity.ok(Map.of("ok", true, "boundingBoxes", boxesArr, "faultTypes", ftArr));
1161+
return ResponseEntity.ok(Map.of("ok", true, "boundingBoxes", boxesArr, "faultTypes", ftArr, "comments", commentArr));
10701162
} catch (Exception e) {
10711163
return ResponseEntity.internalServerError().body(Map.of("error", "Failed to add box"));
10721164
}
@@ -1091,6 +1183,7 @@ public ResponseEntity<?> bulkUpdateBoxes(@PathVariable String id,
10911183
var boxesPayload = payload.get("boundingBoxes");
10921184
var faultsPayload = payload.get("faultTypes");
10931185
var annotatedByPayload = payload.get("annotatedBy");
1186+
var commentsPayload = payload.get("comments");
10941187
var tuneModelPayload = payload.get("tuneModel");
10951188
boolean tuneModel = true;
10961189
if (tuneModelPayload instanceof Boolean) {
@@ -1137,14 +1230,41 @@ public ResponseEntity<?> bulkUpdateBoxes(@PathVariable String id,
11371230
}
11381231
}
11391232

1140-
String finalBoxesJson = finalBoxes.toString();
1141-
String finalFaultsJson = finalFaults.toString();
1142-
String finalAnnotatedJson = finalAnnotated.toString();
1233+
ArrayNode finalComments = mapper.createArrayNode();
1234+
if (commentsPayload instanceof List) {
1235+
for (Object commentObj : (List<?>) commentsPayload) {
1236+
if (commentObj == null) {
1237+
finalComments.addNull();
1238+
} else {
1239+
String text = commentObj.toString().trim();
1240+
finalComments.add(text.isEmpty() ? null : text);
1241+
}
1242+
}
1243+
}
1244+
while (finalComments.size() < finalBoxes.size()) {
1245+
finalComments.addNull();
1246+
}
1247+
while (finalComments.size() > finalBoxes.size()) {
1248+
finalComments.remove(finalComments.size() - 1);
1249+
}
1250+
boolean hasCommentValue = false;
1251+
for (JsonNode node : finalComments) {
1252+
if (node != null && !node.isNull() && !node.asText("").isBlank()) {
1253+
hasCommentValue = true;
1254+
break;
1255+
}
1256+
}
1257+
1258+
String finalBoxesJson = finalBoxes.toString();
1259+
String finalFaultsJson = finalFaults.toString();
1260+
String finalAnnotatedJson = finalAnnotated.toString();
1261+
String finalCommentsJson = hasCommentValue ? finalComments.toString() : null;
11431262

11441263
// Persist final state
11451264
i.setBoundingBoxes(finalBoxesJson);
11461265
i.setFaultTypes(finalFaultsJson);
11471266
i.setAnnotatedBy(finalAnnotatedJson);
1267+
i.setComment(finalCommentsJson);
11481268
repo.save(i);
11491269

11501270
if (tuneModel) {
@@ -1159,7 +1279,7 @@ public ResponseEntity<?> bulkUpdateBoxes(@PathVariable String id,
11591279
);
11601280
}
11611281

1162-
return ResponseEntity.ok(Map.of("ok", true, "boundingBoxes", finalBoxes, "faultTypes", finalFaults));
1282+
return ResponseEntity.ok(Map.of("ok", true, "boundingBoxes", finalBoxes, "faultTypes", finalFaults, "comments", finalComments));
11631283
} catch (Exception e) {
11641284
return ResponseEntity.internalServerError().body(Map.of("error", "Bulk update failed"));
11651285
}

0 commit comments

Comments
 (0)