Skip to content

Commit 65f05c4

Browse files
authored
improve functional logs messages, show non connected equipments (#129)
Signed-off-by: jamal-khey <[email protected]>
1 parent a1bf1a3 commit 65f05c4

File tree

6 files changed

+158
-37
lines changed

6 files changed

+158
-37
lines changed

src/main/java/org/gridsuite/securityanalysis/server/dto/ContingencyInfos.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@ public class ContingencyInfos {
2727
private String id;
2828
private Contingency contingency;
2929
private Set<String> notFoundElements;
30+
private Set<String> notConnectedElements;
3031

3132
public ContingencyInfos(Contingency contingency) {
32-
this(contingency, Set.of());
33+
this(contingency, Set.of(), Set.of());
3334
}
3435

35-
public ContingencyInfos(Contingency contingency, Set<String> notFoundElements) {
36-
this(contingency == null ? null : contingency.getId(), contingency, notFoundElements);
36+
public ContingencyInfos(Contingency contingency, Set<String> notFoundElements, Set<String> notConnectedElements) {
37+
this(contingency == null ? null : contingency.getId(), contingency, notFoundElements, notConnectedElements);
3738
}
3839
}
3940

src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisWorkerService.java

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import org.springframework.stereotype.Service;
2929
import org.springframework.util.CollectionUtils;
3030

31-
import java.util.ArrayList;
3231
import java.util.Collections;
3332
import java.util.List;
3433
import java.util.Objects;
@@ -116,11 +115,10 @@ protected CompletableFuture<SecurityAnalysisResult> getCompletableFuture(Securit
116115
protected void preRun(SecurityAnalysisRunContext runContext) {
117116
LOGGER.info("Run security analysis on contingency lists: {}", runContext.getContingencyListNames().stream().map(LogUtils::sanitizeParam).toList());
118117

119-
// enrich context
120118
List<ContingencyInfos> contingencies = observer.observe("contingencies.fetch", runContext,
121-
() -> runContext.getContingencyListNames().stream()
122-
.map(contingencyListName -> actionsService.getContingencyList(contingencyListName, runContext.getNetworkUuid(), runContext.getVariantId()))
123-
.flatMap(List::stream)
119+
() -> runContext.getContingencyListNames()
120+
.stream()
121+
.flatMap(contingencyListName -> actionsService.getContingencyList(contingencyListName, runContext.getNetworkUuid(), runContext.getVariantId()).stream())
124122
.toList());
125123

126124
runContext.setContingencies(contingencies);
@@ -129,24 +127,8 @@ protected void preRun(SecurityAnalysisRunContext runContext) {
129127
@Override
130128
protected void postRun(SecurityAnalysisRunContext runContext, AtomicReference<ReportNode> rootReportNode, SecurityAnalysisResult ignoredResult) {
131129
if (runContext.getReportInfos().reportUuid() != null) {
132-
List<ReportNode> notFoundElementReports = new ArrayList<>();
133-
runContext.getContingencies().stream()
134-
.filter(contingencyInfos -> !CollectionUtils.isEmpty(contingencyInfos.getNotFoundElements()))
135-
.forEach(contingencyInfos -> {
136-
String elementsIds = String.join(", ", contingencyInfos.getNotFoundElements());
137-
notFoundElementReports.add(ReportNode.newRootReportNode()
138-
.withMessageTemplate("contingencyElementNotFound_" + contingencyInfos.getId() + notFoundElementReports.size(),
139-
String.format("Cannot find the following equipments %s in contingency %s", elementsIds, contingencyInfos.getId()))
140-
.withSeverity(TypedValue.WARN_SEVERITY)
141-
.build());
142-
});
143-
if (!CollectionUtils.isEmpty(notFoundElementReports)) {
144-
ReportNode elementNotFoundSubReporter = runContext.getReportNode().newReportNode()
145-
.withMessageTemplate(runContext.getReportInfos().reportUuid().toString() + "notFoundElements", "Elements not found")
146-
.add();
147-
notFoundElementReports.forEach(r -> elementNotFoundSubReporter.newReportNode()
148-
.withMessageTemplate(r.getMessageKey(), r.getMessageTemplate()).add());
149-
}
130+
logContingencyEquipmentsNotConnected(runContext);
131+
logContingencyEquipmentsNotFound(runContext);
150132
}
151133
super.postRun(runContext, rootReportNode, ignoredResult);
152134
}
@@ -177,4 +159,53 @@ public Consumer<Message<String>> consumeRun() {
177159
public Consumer<Message<String>> consumeCancel() {
178160
return super.consumeCancel();
179161
}
162+
163+
private static void logContingencyEquipmentsNotFound(SecurityAnalysisRunContext runContext) {
164+
List<ContingencyInfos> contingencyInfosList = runContext.getContingencies().stream()
165+
.filter(contingencyInfos -> !CollectionUtils.isEmpty(contingencyInfos.getNotFoundElements())).toList();
166+
167+
if (contingencyInfosList.isEmpty()) {
168+
return;
169+
}
170+
171+
ReportNode elementsNotFoundSubReporter = runContext.getReportNode().newReportNode()
172+
.withMessageTemplate("notFoundEquipments", "Equipments not found")
173+
.add();
174+
175+
contingencyInfosList.forEach(contingencyInfos -> {
176+
String elementsIds = String.join(", ", contingencyInfos.getNotFoundElements());
177+
elementsNotFoundSubReporter.newReportNode()
178+
.withMessageTemplate("contingencyEquipmentNotFound",
179+
"Cannot find the following equipments ${elementsIds} in contingency ${contingencyId}")
180+
.withUntypedValue("elementsIds", elementsIds)
181+
.withUntypedValue("contingencyId", contingencyInfos.getId())
182+
.withSeverity(TypedValue.WARN_SEVERITY)
183+
.add();
184+
});
185+
}
186+
187+
private void logContingencyEquipmentsNotConnected(SecurityAnalysisRunContext runContext) {
188+
List<ContingencyInfos> contingencyInfosList = runContext.getContingencies().stream()
189+
.filter(contingencyInfos -> !CollectionUtils.isEmpty(contingencyInfos.getNotConnectedElements())).toList();
190+
191+
if (contingencyInfosList.isEmpty()) {
192+
return;
193+
}
194+
195+
ReportNode elementsNotConnectedSubReporter = runContext.getReportNode().newReportNode()
196+
.withMessageTemplate("notConnectedEquipments", "Equipments not connected")
197+
.add();
198+
199+
contingencyInfosList.forEach(contingencyInfos -> {
200+
String elementsIds = String.join(", ", contingencyInfos.getNotConnectedElements());
201+
elementsNotConnectedSubReporter.newReportNode()
202+
.withMessageTemplate("contingencyEquipmentNotConnected",
203+
"The following equipments ${elementsIds} in contingency ${contingencyId} are not connected")
204+
.withUntypedValue("elementsIds", elementsIds)
205+
.withUntypedValue("contingencyId", contingencyInfos.getId())
206+
.withSeverity(TypedValue.WARN_SEVERITY)
207+
.add();
208+
});
209+
}
210+
180211
}

src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisControllerTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
import static com.powsybl.ws.commons.computation.service.NotificationService.*;
7575
import static org.gridsuite.securityanalysis.server.service.SecurityAnalysisService.COMPUTATION_TYPE;
7676
import static org.gridsuite.securityanalysis.server.util.DatabaseQueryUtils.assertRequestsCount;
77+
import static org.gridsuite.securityanalysis.server.util.TestUtils.assertLogMessage;
7778
import static org.hamcrest.MatcherAssert.assertThat;
7879
import static org.junit.Assert.*;
7980
import static org.mockito.ArgumentMatchers.any;
@@ -727,6 +728,33 @@ public void runWithReportTest() throws Exception {
727728
assertThat(RESULT, new MatcherJson<>(mapper, securityAnalysisResult));
728729
}
729730

731+
@Test
732+
public void runWithReportTestElementsNotFoundAndNotConnected() throws Exception {
733+
MvcResult mvcResult;
734+
String resultAsString;
735+
736+
Network network = EurostagTutorialExample1Factory.create(new NetworkFactoryImpl());
737+
given(networkStoreService.getNetwork(NETWORK_UUID, PreloadingStrategy.COLLECTION)).willReturn(network);
738+
739+
mvcResult = mockMvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/run?reportType=SecurityAnalysis&contingencyListName=" + CONTINGENCY_LIST_NAME + "&provider=testProvider" + "&reportUuid=" + REPORT_UUID + "&reporterId=" + UUID.randomUUID() + "&loadFlowParametersUuid=" + UUID.randomUUID()).contentType(MediaType.APPLICATION_JSON)
740+
.header(HEADER_USER_ID, "testUserId")
741+
.contentType(MediaType.APPLICATION_JSON))
742+
.andExpectAll(
743+
status().isOk(),
744+
content().contentType(MediaType.APPLICATION_JSON))
745+
.andReturn();
746+
747+
resultAsString = mvcResult.getResponse().getContentAsString();
748+
SecurityAnalysisResult securityAnalysisResult = mapper.readValue(resultAsString, SecurityAnalysisResult.class);
749+
assertThat(RESULT, new MatcherJson<>(mapper, securityAnalysisResult));
750+
751+
assertLogMessage("Equipments not found", "notFoundEquipments", reportService);
752+
assertLogMessage("Cannot find the following equipments wrongId1, wrongId2 in contingency l1", "contingencyEquipmentNotFound", reportService);
753+
754+
assertLogMessage("Equipments not connected", "notConnectedEquipments", reportService);
755+
assertLogMessage("The following equipments notConnectedId1 in contingency l4 are not connected", "contingencyEquipmentNotConnected", reportService);
756+
}
757+
730758
@Test
731759
public void getProvidersTest() throws Exception {
732760
MvcResult mvcResult;

src/test/java/org/gridsuite/securityanalysis/server/SecurityAnalysisProviderMock.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,17 @@ public class SecurityAnalysisProviderMock implements SecurityAnalysisProvider {
4646
static final String CONTINGENCY_LIST_NAME_VARIANT = "listVariant";
4747

4848
static final List<ContingencyInfos> CONTINGENCIES = List.of(
49-
new ContingencyInfos(new Contingency("l1", new BranchContingency("l1")), Set.of("wrongId1, wrongId2")),
50-
new ContingencyInfos(new Contingency("l2", new GeneratorContingency("l2")), Set.of("wrongId1, wrongId2")),
51-
new ContingencyInfos(new Contingency("l3", new BusbarSectionContingency("l3")), Set.of("wrongId1, wrongId2")),
52-
new ContingencyInfos(new Contingency("l4", new LineContingency("l4")), Set.of("wrongId1, wrongId2")),
49+
new ContingencyInfos(new Contingency("l1", new BranchContingency("l1")), Set.of("wrongId1, wrongId2"), Set.of()),
50+
new ContingencyInfos(new Contingency("l2", new GeneratorContingency("l2")), Set.of("wrongId1, wrongId2"), Set.of()),
51+
new ContingencyInfos(new Contingency("l3", new BusbarSectionContingency("l3")), Set.of("wrongId1, wrongId2"), Set.of()),
52+
new ContingencyInfos(new Contingency("l4", new LineContingency("l4")), Set.of("wrongId1, wrongId2"), Set.of("notConnectedId1")),
5353
//new Contingency("l5", new LoadContingency("l5")), //ContingencyElementDeserializer does not handle LOAD
54-
new ContingencyInfos(new Contingency("l6", new HvdcLineContingency("l6")), Set.of("wrongId1, wrongId2")),
55-
new ContingencyInfos(new Contingency("l7", new DanglingLineContingency("l7")), Set.of("wrongId1, wrongId2")),
56-
new ContingencyInfos(new Contingency("l8", new ShuntCompensatorContingency("l8")), Set.of("wrongId1, wrongId2")),
57-
new ContingencyInfos(new Contingency("l9", new TwoWindingsTransformerContingency("l9")), Set.of("wrongId1, wrongId2")),
58-
new ContingencyInfos(new Contingency("la", new ThreeWindingsTransformerContingency("l0")), Set.of("wrongId1, wrongId2")), // Contingencies are reordered by id
59-
new ContingencyInfos(new Contingency("lb", new StaticVarCompensatorContingency("la")), Set.of("wrongId1, wrongId2"))
54+
new ContingencyInfos(new Contingency("l6", new HvdcLineContingency("l6")), Set.of("wrongId1, wrongId2"), Set.of()),
55+
new ContingencyInfos(new Contingency("l7", new DanglingLineContingency("l7")), Set.of("wrongId1, wrongId2"), Set.of()),
56+
new ContingencyInfos(new Contingency("l8", new ShuntCompensatorContingency("l8")), Set.of("wrongId1, wrongId2"), Set.of()),
57+
new ContingencyInfos(new Contingency("l9", new TwoWindingsTransformerContingency("l9")), Set.of("wrongId1, wrongId2"), Set.of()),
58+
new ContingencyInfos(new Contingency("la", new ThreeWindingsTransformerContingency("l0")), Set.of("wrongId1, wrongId2"), Set.of()), // Contingencies are reordered by id
59+
new ContingencyInfos(new Contingency("lb", new StaticVarCompensatorContingency("la")), Set.of("wrongId1, wrongId2"), Set.of())
6060
);
6161

6262
static final List<Contingency> FAILED_CONTINGENCIES = List.of(

src/test/java/org/gridsuite/securityanalysis/server/service/ActionsServiceTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public class ActionsServiceTest {
5858
private static final String VERY_LARGE_LIST_NAME = "veryLargelist";
5959

6060
public static final String WRONG_ID = "wrongID";
61-
private static final ContingencyInfos CONTINGENCY = new ContingencyInfos(new Contingency("c1", new BranchContingency("b1")), Set.of(WRONG_ID));
61+
private static final ContingencyInfos CONTINGENCY = new ContingencyInfos(new Contingency("c1", new BranchContingency("b1")), Set.of(WRONG_ID), Set.of());
6262

6363
private static final ContingencyInfos CONTINGENCY_VARIANT = new ContingencyInfos(new Contingency("c2", new BranchContingency("b2")));
6464

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package org.gridsuite.securityanalysis.server.util;
2+
3+
import com.powsybl.commons.report.ReportNode;
4+
import com.powsybl.ws.commons.computation.service.ReportService;
5+
import org.apache.commons.text.StringSubstitutor;
6+
import org.mockito.ArgumentCaptor;
7+
8+
import java.util.Iterator;
9+
import java.util.Optional;
10+
import java.util.UUID;
11+
12+
import static org.junit.Assert.*;
13+
import static org.mockito.ArgumentMatchers.any;
14+
import static org.mockito.Mockito.atLeast;
15+
import static org.mockito.Mockito.verify;
16+
17+
public final class TestUtils {
18+
19+
private TestUtils() {
20+
}
21+
22+
public static void assertLogNthMessage(String expectedMessage, String reportKey, ReportService reportService, int rank) {
23+
ArgumentCaptor<ReportNode> reporterCaptor = ArgumentCaptor.forClass(ReportNode.class);
24+
verify(reportService, atLeast(1)).sendReport(any(UUID.class), reporterCaptor.capture());
25+
assertNotNull(reporterCaptor.getValue());
26+
Optional<String> message = getMessageFromReporter(reportKey, reporterCaptor.getValue(), rank);
27+
assertTrue(message.isPresent());
28+
assertEquals(expectedMessage, message.get().trim());
29+
}
30+
31+
public static void assertLogMessage(String expectedMessage, String reportKey, ReportService reportService) {
32+
assertLogNthMessage(expectedMessage, reportKey, reportService, 1);
33+
}
34+
35+
private static Optional<String> getMessageFromReporter(String reportKey, ReportNode reporterModel, int rank) {
36+
Optional<String> message = Optional.empty();
37+
38+
Iterator<ReportNode> reportsIterator = reporterModel.getChildren().iterator();
39+
int nbTimes = 0;
40+
while (message.isEmpty() && reportsIterator.hasNext()) {
41+
ReportNode report = reportsIterator.next();
42+
if (report.getMessageKey().equals(reportKey)) {
43+
nbTimes++;
44+
if (nbTimes == rank) {
45+
message = Optional.of(formatReportMessage(report, reporterModel));
46+
}
47+
}
48+
}
49+
50+
Iterator<ReportNode> reportersIterator = reporterModel.getChildren().iterator();
51+
while (message.isEmpty() && reportersIterator.hasNext()) {
52+
message = getMessageFromReporter(reportKey, reportersIterator.next(), rank);
53+
}
54+
55+
return message;
56+
}
57+
58+
private static String formatReportMessage(ReportNode report, ReportNode reporterModel) {
59+
return new StringSubstitutor(reporterModel.getValues()).replace(new StringSubstitutor(report.getValues()).replace(report.getMessageTemplate()));
60+
}
61+
}

0 commit comments

Comments
 (0)