Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 64 additions & 2 deletions src/integration/java/org/jlab/sim/SoftwareIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ void editorAddsEditsScoresAndRemovesSoftware() throws Exception {
assertEquals("N", row.attr("data-archived"));
assertEquals("3", row.attr("data-impact"), "new software scores Medium");
assertEquals("", row.attr("data-assessed-date"), "new software is not assessed");
assertEquals("Not assessed", impactCell(row).text());
assertEquals("Not assessed", levelCell(row).text());
assertEquals("Not assessed", attentionCell(row).text());

Expand Down Expand Up @@ -142,6 +143,7 @@ void editorAddsEditsScoresAndRemovesSoftware() throws Exception {
assertEquals("4", row.attr("data-attention"), "Impact 5 and Fragility 5 is Severe");
assertEquals(Sim.EDITOR, row.attr("data-assessed-by"));
assertFalse(row.attr("data-assessed-date").isEmpty(), "assessed date");
assertEquals("5 Critical", impactCell(row).text());
assertEquals("2 Significant*", levelCell(row).text(), "the override, marked");
assertEquals("Severe", attentionCell(row).text());

Expand Down Expand Up @@ -304,6 +306,42 @@ void directoryFiltersByLevelAttentionFragilityAndAssessment() throws Exception {
assertTrue(message.contains("Not Assessed"), message);
}

/**
* The directory lists the most important software first: assessed software by Ops Impact, then by
* Attention, then by name, and after it the software nobody has assessed.
*/
@Test
void directoryListsHighestOpsImpactFirst() throws Exception {
String token = Sim.token(Sim.EDITOR);
String prefix = newName();
String unassessed = addNamed(token, prefix + "-a");
String mediumRobust = addNamed(token, prefix + "-b");
String critical = addNamed(token, prefix + "-c");
String mediumFragile = addNamed(token, prefix + "-d");
String criticalToo = addNamed(token, prefix + "-e");

// Impact, complexity and the other factors: attention is Low for 3 with 1, High for 3 with 5,
// and Moderate for 5 with 1
scoreNamed(token, mediumRobust, 3, 1);
scoreNamed(token, critical, 5, 1);
scoreNamed(token, mediumFragile, 3, 5);
scoreNamed(token, criticalToo, 5, 1);

String filter = "/directory?softwareName=" + Sim.encodeParam(prefix + "-*");

assertEquals(
List.of(critical, criticalToo, mediumFragile, mediumRobust, unassessed),
Sim.page(filter, null).select("tbody tr[data-name]").eachAttr("data-name"),
"Ops Impact, then Attention, then name, then unassessed");

assertEquals(
List.of(mediumFragile, mediumRobust),
Sim.page(filter + "&max=2&offset=2", null)
.select("tbody tr[data-name]")
.eachAttr("data-name"),
"the second page of two carries on in the same order");
}

@Test
void archivedSoftwareIsListedOnlyWhenIncluded() throws Exception {
String name = newName();
Expand Down Expand Up @@ -356,6 +394,26 @@ private String newName() {
return name;
}

/** Adds an app of this name, which the test's cleanup removes, and returns the name. */
private String addNamed(String token, String name) throws Exception {
added.add(name);
assertOk(add(token, name, "APP", "N"));
return name;
}

/** Scores the software of this name: Code Complexity 1, and the other four factors the same. */
private static void scoreNamed(String token, String name, int impact, int others)
throws Exception {
assertOk(
score(
token,
row(name).attr("data-id"),
impact,
1,
others,
List.of(Map.entry("levelOverride", ""), Map.entry("levelNote", ""))));
}

private HttpResponse<String> add(
String token, String name, String type, String archived, String... topics) throws Exception {
List<Map.Entry<String, String>> form = new ArrayList<>();
Expand Down Expand Up @@ -396,12 +454,16 @@ private static HttpResponse<String> score(
return Sim.post("/ajax/edit-software-risk", token, form);
}

private static Element impactCell(Element row) {
return row.selectFirst("td.ops-impact");
}

private static Element levelCell(Element row) {
return row.select("td").get(5);
return row.selectFirst("td.sqa-level");
}

private static Element attentionCell(Element row) {
return row.select("td").get(6);
return row.selectFirst("td.attention");
}

/** The software's row in the directory, or null if the directory doesn't list it. */
Expand Down
16 changes: 13 additions & 3 deletions src/main/java/org/jlab/sim/business/service/SoftwareService.java
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ public List<Software> filterList(
Attention attention,
boolean fragile,
Boolean assessed,
SoftwareOrder order,
int offset,
int max) {
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
Expand Down Expand Up @@ -253,9 +254,18 @@ public List<Software> filterList(
}

List<Order> orders = new ArrayList<>();
Path<String> p0 = root.get("name");
Order o0 = cb.asc(p0);
orders.add(o0);
if (order == SoftwareOrder.IMPACT) {
// Unassessed software shows no impact or attention, so it comes after all that is assessed.
// Its inputs are still the defaults, which leaves it in name order.
orders.add(
cb.asc(
cb.<Integer>selectCase().when(cb.isNull(root.get(ASSESSED_DATE)), 1).otherwise(0)));
orders.add(cb.desc(root.get("impact")));
orders.add(cb.desc(root.get("attention")));
}
orders.add(cb.asc(root.get("name")));
// Two repositories may hold software of the same name, and pages need a fixed order
orders.add(cb.asc(root.get("softwareId")));
cq.orderBy(orders);
return getEntityManager()
.createQuery(cq)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.jlab.sim.persistence.enumeration;

/** The order of a list of software. */
public enum SoftwareOrder {
/** By name, A to Z */
NAME,
/**
* The most important first: assessed software by Ops Impact from Critical down, then by Attention
* from Severe down, then by name; then the software nobody has assessed, by name
*/
IMPACT
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response)
attention,
fragile,
assessed,
SoftwareOrder.IMPACT,
offset,
maxPerPage);

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/jlab/sim/presentation/controller/Sync.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.jlab.sim.persistence.entity.Repository;
import org.jlab.sim.persistence.entity.Software;
import org.jlab.sim.persistence.enumeration.Include;
import org.jlab.sim.persistence.enumeration.SoftwareOrder;
import org.jlab.sim.persistence.view.SoftwareDiff;
import org.jlab.smoothness.business.exception.UserFriendlyException;
import org.jlab.smoothness.presentation.util.ParamConverter;
Expand Down Expand Up @@ -100,6 +101,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response)
null,
false,
null,
SoftwareOrder.NAME,
0,
Integer.MAX_VALUE);

Expand Down
9 changes: 8 additions & 1 deletion src/main/webapp/WEB-INF/tags/directory-page.tag
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
<th>Description</th>
<th>Repo</th>
<th>Maintainers</th>
<th>Ops Impact</th>
<th>SQA Level</th>
<th>Attention</th>
</tr>
Expand Down Expand Up @@ -214,13 +215,19 @@
</td>
<td><c:out value="${software.repository.name}"/></td>
<td class="maintainers"><c:forEach items="${software.maintainerUsernameList}" var="username"><div><c:out value="${username}"/></div></c:forEach></td>
<td class="ops-impact">
<a class="risk-dialog-opener${software.assessed ? '' : ' not-assessed'}" href="#"><c:choose>
<c:when test="${software.assessed}"><c:out value="${software.impact.value} ${software.impact}"/></c:when>
<c:otherwise>Not assessed</c:otherwise>
</c:choose></a>
</td>
<td class="sqa-level">
<a class="risk-dialog-opener${software.assessed ? '' : ' not-assessed'}" href="#"><c:choose>
<c:when test="${software.assessed}"><c:out value="${software.effectiveControlLevel.value} ${software.effectiveControlLevel}"/><c:if test="${not empty software.controlLevelOverride}"><span class="override-marker" title="Overridden from ${software.controlLevel.value} ${software.controlLevel}: ${fn:escapeXml(software.controlLevelNote)}">*</span></c:if></c:when>
<c:otherwise>Not assessed</c:otherwise>
</c:choose></a>
</td>
<td>
<td class="attention">
<a class="risk-dialog-opener${software.assessed ? '' : ' not-assessed'}" href="#"><c:choose>
<c:when test="${software.assessed}"><c:out value="${software.attention}"/></c:when>
<c:otherwise>Not assessed</c:otherwise>
Expand Down
3 changes: 2 additions & 1 deletion src/main/webapp/resources/css/directory.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ td:nth-child(3) {
td.maintainers {
white-space: nowrap;
}
/* Keep "2 Significant" on one line; "Not assessed" may wrap, as under Attention */
/* Keep "5 Critical" and "2 Significant" on one line; "Not assessed" may wrap, as under Attention */
td.ops-impact .risk-dialog-opener:not(.not-assessed),
td.sqa-level .risk-dialog-opener:not(.not-assessed) {
white-space: nowrap;
}
Expand Down
Loading