From 046064ed451debcb6684954d5bde2994bc17068b Mon Sep 17 00:00:00 2001 From: Ryan Slominski Date: Mon, 23 Feb 2026 14:41:05 -0500 Subject: [PATCH 1/7] Add risk assessment metadata Fixes #6 --- container/oracle/initdb.d/02_ddl.sql | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/container/oracle/initdb.d/02_ddl.sql b/container/oracle/initdb.d/02_ddl.sql index 70caa1c..2bf51f9 100644 --- a/container/oracle/initdb.d/02_ddl.sql +++ b/container/oracle/initdb.d/02_ddl.sql @@ -58,6 +58,14 @@ CREATE TABLE SIM_OWNER.SOFTWARE HOME_URL VARCHAR2(256 CHAR) NULL, MAINTAINER_USERNAME_CSV VARCHAR2(256 CHAR) NULL, ARCHIVED_YN VARCHAR2(1 CHAR) DEFAULT 'N' NOT NULL, + OPS_IMPACT SMALLINT DEFAULT 1 NOT NULL, + PAST_DOWNTIME SMALLINT DEFAULT 1 NOT NULL, + DEBUGGABILITY_N_TESTING SMALLINT DEFAULT 1 NOT NULL, + CODE_COMPLEXITY SMALLINT DEFAULT 1 NOT NULL, + DOCUMENTATION_GAPS SMALLINT DEFAULT 1 NOT NULL, + ESOTERICISM SMALLINT DEFAULT 1 NOT NULL, + DOWN_PROBABILITY SMALLINT GENERATED ALWAYS AS ((PAST_DOWNTIME + DEBUGGABILITY_N_TESTING + CODE_COMPLEXITY + DOCUMENTATION_GAPS + ESOTERICISM) / 5) VIRTUAL, + RISK_SCORE SMALLINT GENERATED ALWAYS AS (((PAST_DOWNTIME + DEBUGGABILITY_N_TESTING + CODE_COMPLEXITY + DOCUMENTATION_GAPS + ESOTERICISM) / 5) + OPS_IMPACT) VIRTUAL, CONSTRAINT SOFTWARE_PK PRIMARY KEY (SOFTWARE_ID), CONSTRAINT SOFTWARE_AK1 UNIQUE (NAME, REPOSITORY_ID), CONSTRAINT SOFTWARE_FK1 FOREIGN KEY (REPOSITORY_ID) REFERENCES SIM_OWNER.REPOSITORY (REPOSITORY_ID), From 933713a97d762b7b2c51bc903dd93e235a1a7981 Mon Sep 17 00:00:00 2001 From: Ryan Slominski Date: Mon, 23 Feb 2026 15:16:27 -0500 Subject: [PATCH 2/7] Add mappings --- container/oracle/initdb.d/02_ddl.sql | 22 ++++++++----- .../converter/DowntimeRiskConverter.java | 25 ++++++++++++++ .../converter/OpsImpactConverter.java | 25 ++++++++++++++ .../jlab/sim/persistence/entity/Software.java | 22 +++++++++++++ .../persistence/enumeration/DowntimeRisk.java | 33 +++++++++++++++++++ .../persistence/enumeration/OpsImpact.java | 28 ++++++++++++++++ .../webapp/WEB-INF/tags/directory-page.tag | 2 ++ 7 files changed, 149 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/jlab/sim/persistence/converter/DowntimeRiskConverter.java create mode 100644 src/main/java/org/jlab/sim/persistence/converter/OpsImpactConverter.java create mode 100644 src/main/java/org/jlab/sim/persistence/enumeration/DowntimeRisk.java create mode 100644 src/main/java/org/jlab/sim/persistence/enumeration/OpsImpact.java diff --git a/container/oracle/initdb.d/02_ddl.sql b/container/oracle/initdb.d/02_ddl.sql index 2bf51f9..bfaf958 100644 --- a/container/oracle/initdb.d/02_ddl.sql +++ b/container/oracle/initdb.d/02_ddl.sql @@ -58,19 +58,25 @@ CREATE TABLE SIM_OWNER.SOFTWARE HOME_URL VARCHAR2(256 CHAR) NULL, MAINTAINER_USERNAME_CSV VARCHAR2(256 CHAR) NULL, ARCHIVED_YN VARCHAR2(1 CHAR) DEFAULT 'N' NOT NULL, - OPS_IMPACT SMALLINT DEFAULT 1 NOT NULL, - PAST_DOWNTIME SMALLINT DEFAULT 1 NOT NULL, - DEBUGGABILITY_N_TESTING SMALLINT DEFAULT 1 NOT NULL, - CODE_COMPLEXITY SMALLINT DEFAULT 1 NOT NULL, - DOCUMENTATION_GAPS SMALLINT DEFAULT 1 NOT NULL, - ESOTERICISM SMALLINT DEFAULT 1 NOT NULL, + OPS_IMPACT SMALLINT DEFAULT 3 NOT NULL, + PAST_DOWNTIME SMALLINT DEFAULT 3 NOT NULL, + DEBUGGABILITY_N_TESTING SMALLINT DEFAULT 3 NOT NULL, + CODE_COMPLEXITY SMALLINT DEFAULT 3 NOT NULL, + DOCUMENTATION_GAPS SMALLINT DEFAULT 3 NOT NULL, + ESOTERICISM SMALLINT DEFAULT 3 NOT NULL, DOWN_PROBABILITY SMALLINT GENERATED ALWAYS AS ((PAST_DOWNTIME + DEBUGGABILITY_N_TESTING + CODE_COMPLEXITY + DOCUMENTATION_GAPS + ESOTERICISM) / 5) VIRTUAL, - RISK_SCORE SMALLINT GENERATED ALWAYS AS (((PAST_DOWNTIME + DEBUGGABILITY_N_TESTING + CODE_COMPLEXITY + DOCUMENTATION_GAPS + ESOTERICISM) / 5) + OPS_IMPACT) VIRTUAL, + DOWNTIME_RISK SMALLINT GENERATED ALWAYS AS (((PAST_DOWNTIME + DEBUGGABILITY_N_TESTING + CODE_COMPLEXITY + DOCUMENTATION_GAPS + ESOTERICISM) / 5) + OPS_IMPACT) VIRTUAL, CONSTRAINT SOFTWARE_PK PRIMARY KEY (SOFTWARE_ID), CONSTRAINT SOFTWARE_AK1 UNIQUE (NAME, REPOSITORY_ID), CONSTRAINT SOFTWARE_FK1 FOREIGN KEY (REPOSITORY_ID) REFERENCES SIM_OWNER.REPOSITORY (REPOSITORY_ID), CONSTRAINT SOFTWARE_CK1 CHECK (TYPE IN ('APP', 'LIB', 'SCRIPT', 'PLUGIN')), - CONSTRAINT SOFTWARE_CK2 CHECK (ARCHIVED_YN IN ('Y', 'N')) + CONSTRAINT SOFTWARE_CK2 CHECK (ARCHIVED_YN IN ('Y', 'N')), + CONSTRAINT SOFTWARE_CK3 CHECK (OPS_IMPACT IN (1, 2, 3, 4, 5)), + CONSTRAINT SOFTWARE_CK4 CHECK (PAST_DOWNTIME IN (1, 2, 3, 4, 5)), + CONSTRAINT SOFTWARE_CK5 CHECK (DEBUGGABILITY_N_TESTING IN (1, 2, 3, 4, 5)), + CONSTRAINT SOFTWARE_CK6 CHECK (CODE_COMPLEXITY IN (1, 2, 3, 4, 5)), + CONSTRAINT SOFTWARE_CK7 CHECK (DOCUMENTATION_GAPS IN (1, 2, 3, 4, 5)), + CONSTRAINT SOFTWARE_CK8 CHECK (ESOTERICISM IN (1, 2, 3, 4, 5)) ); CREATE TABLE SIM_OWNER.TOPIC diff --git a/src/main/java/org/jlab/sim/persistence/converter/DowntimeRiskConverter.java b/src/main/java/org/jlab/sim/persistence/converter/DowntimeRiskConverter.java new file mode 100644 index 0000000..3b22d4d --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/converter/DowntimeRiskConverter.java @@ -0,0 +1,25 @@ +package org.jlab.sim.persistence.converter; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import org.jlab.sim.persistence.enumeration.DowntimeRisk; + +@Converter(autoApply = true) +public class DowntimeRiskConverter implements AttributeConverter { + + @Override + public Integer convertToDatabaseColumn(DowntimeRisk obj) { + if (obj == null) { + return null; + } + return obj.getValue(); + } + + @Override + public DowntimeRisk convertToEntityAttribute(Integer value) { + if (value == null) { + return null; + } + return DowntimeRisk.fromValue(value); + } +} diff --git a/src/main/java/org/jlab/sim/persistence/converter/OpsImpactConverter.java b/src/main/java/org/jlab/sim/persistence/converter/OpsImpactConverter.java new file mode 100644 index 0000000..aa78bde --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/converter/OpsImpactConverter.java @@ -0,0 +1,25 @@ +package org.jlab.sim.persistence.converter; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import org.jlab.sim.persistence.enumeration.OpsImpact; + +@Converter(autoApply = true) +public class OpsImpactConverter implements AttributeConverter { + + @Override + public Integer convertToDatabaseColumn(OpsImpact obj) { + if (obj == null) { + return null; + } + return obj.getValue(); + } + + @Override + public OpsImpact convertToEntityAttribute(Integer value) { + if (value == null) { + return null; + } + return OpsImpact.fromValue(value); + } +} diff --git a/src/main/java/org/jlab/sim/persistence/entity/Software.java b/src/main/java/org/jlab/sim/persistence/entity/Software.java index 2d5affb..363ea81 100644 --- a/src/main/java/org/jlab/sim/persistence/entity/Software.java +++ b/src/main/java/org/jlab/sim/persistence/entity/Software.java @@ -6,6 +6,8 @@ import java.io.Serializable; import java.math.BigInteger; import java.util.*; +import org.jlab.sim.persistence.enumeration.DowntimeRisk; +import org.jlab.sim.persistence.enumeration.OpsImpact; import org.jlab.sim.persistence.enumeration.SoftwareType; import org.jlab.smoothness.persistence.util.YnStringToBoolean; @@ -60,6 +62,14 @@ public class Software implements Serializable { @Convert(converter = YnStringToBoolean.class) private boolean archived; + @NotNull + @Column(name = "OPS_IMPACT", nullable = false) + private OpsImpact impact; + + @NotNull + @Column(name = "DOWNTIME_RISK", nullable = false) + private DowntimeRisk risk; + @OneToMany(fetch = FetchType.EAGER) @JoinColumn( name = "SOFTWARE_ID", @@ -201,6 +211,18 @@ public void setArchivedSynced(boolean archivedSynced) { this.archivedSynced = archivedSynced; } + public OpsImpact getImpact() { + return impact; + } + + public void setImpact(OpsImpact impact) { + this.impact = impact; + } + + public DowntimeRisk getRisk() { + return risk; + } + @Override public boolean equals(Object o) { if (!(o instanceof Software)) return false; diff --git a/src/main/java/org/jlab/sim/persistence/enumeration/DowntimeRisk.java b/src/main/java/org/jlab/sim/persistence/enumeration/DowntimeRisk.java new file mode 100644 index 0000000..757460e --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/enumeration/DowntimeRisk.java @@ -0,0 +1,33 @@ +package org.jlab.sim.persistence.enumeration; + +public enum DowntimeRisk { + Negligible(1), + Minimal(2), + Low(3), + Minor(4), + Moderate(5), + Significant(6), + High(7), + Major(8), + Critical(9), + Catastrophic(10); + + private int value; + + DowntimeRisk(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static DowntimeRisk fromValue(int value) { + for (DowntimeRisk obj : DowntimeRisk.values()) { + if (obj.value == value) { + return obj; + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} diff --git a/src/main/java/org/jlab/sim/persistence/enumeration/OpsImpact.java b/src/main/java/org/jlab/sim/persistence/enumeration/OpsImpact.java new file mode 100644 index 0000000..d1f23a5 --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/enumeration/OpsImpact.java @@ -0,0 +1,28 @@ +package org.jlab.sim.persistence.enumeration; + +public enum OpsImpact { + MARGINAL(1), + LOW(2), + MEDIUM(3), + HIGH(4), + CRITICAL(5); + + private int value; + + OpsImpact(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static OpsImpact fromValue(int value) { + for (OpsImpact obj : OpsImpact.values()) { + if (obj.value == value) { + return obj; + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} diff --git a/src/main/webapp/WEB-INF/tags/directory-page.tag b/src/main/webapp/WEB-INF/tags/directory-page.tag index 825c47a..c024590 100644 --- a/src/main/webapp/WEB-INF/tags/directory-page.tag +++ b/src/main/webapp/WEB-INF/tags/directory-page.tag @@ -108,6 +108,7 @@ Description Repo Maintainers + Downtime Risk @@ -147,6 +148,7 @@ + From 4086b9a65b942bf923dbe32270b64065dff9bdac Mon Sep 17 00:00:00 2001 From: Ryan Slominski Date: Mon, 23 Feb 2026 16:37:58 -0500 Subject: [PATCH 3/7] Basic impl --- .../converter/CodeComplexityConverter.java | 25 +++++ .../DebugTestDifficultyConverter.java | 26 +++++ .../converter/DocumentationGapsConverter.java | 25 +++++ .../DowntimeProbabilityConverter.java | 26 +++++ .../converter/EsotericismConverter.java | 25 +++++ .../converter/PastDowntimeRateConverter.java | 25 +++++ .../jlab/sim/persistence/entity/Software.java | 72 +++++++++++++- .../enumeration/CodeComplexity.java | 28 ++++++ .../enumeration/DebugTestDifficulty.java | 28 ++++++ .../enumeration/DocumentationGaps.java | 28 ++++++ .../enumeration/DowntimeProbability.java | 28 ++++++ .../persistence/enumeration/Esotericism.java | 28 ++++++ .../enumeration/PastDowntimeRate.java | 28 ++++++ .../webapp/WEB-INF/tags/directory-page.tag | 96 ++++++++++++++++++- src/main/webapp/resources/js/directory.js | 22 +++++ 15 files changed, 504 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/jlab/sim/persistence/converter/CodeComplexityConverter.java create mode 100644 src/main/java/org/jlab/sim/persistence/converter/DebugTestDifficultyConverter.java create mode 100644 src/main/java/org/jlab/sim/persistence/converter/DocumentationGapsConverter.java create mode 100644 src/main/java/org/jlab/sim/persistence/converter/DowntimeProbabilityConverter.java create mode 100644 src/main/java/org/jlab/sim/persistence/converter/EsotericismConverter.java create mode 100644 src/main/java/org/jlab/sim/persistence/converter/PastDowntimeRateConverter.java create mode 100644 src/main/java/org/jlab/sim/persistence/enumeration/CodeComplexity.java create mode 100644 src/main/java/org/jlab/sim/persistence/enumeration/DebugTestDifficulty.java create mode 100644 src/main/java/org/jlab/sim/persistence/enumeration/DocumentationGaps.java create mode 100644 src/main/java/org/jlab/sim/persistence/enumeration/DowntimeProbability.java create mode 100644 src/main/java/org/jlab/sim/persistence/enumeration/Esotericism.java create mode 100644 src/main/java/org/jlab/sim/persistence/enumeration/PastDowntimeRate.java diff --git a/src/main/java/org/jlab/sim/persistence/converter/CodeComplexityConverter.java b/src/main/java/org/jlab/sim/persistence/converter/CodeComplexityConverter.java new file mode 100644 index 0000000..6b18bfe --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/converter/CodeComplexityConverter.java @@ -0,0 +1,25 @@ +package org.jlab.sim.persistence.converter; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import org.jlab.sim.persistence.enumeration.CodeComplexity; + +@Converter(autoApply = true) +public class CodeComplexityConverter implements AttributeConverter { + + @Override + public Integer convertToDatabaseColumn(CodeComplexity obj) { + if (obj == null) { + return null; + } + return obj.getValue(); + } + + @Override + public CodeComplexity convertToEntityAttribute(Integer value) { + if (value == null) { + return null; + } + return CodeComplexity.fromValue(value); + } +} diff --git a/src/main/java/org/jlab/sim/persistence/converter/DebugTestDifficultyConverter.java b/src/main/java/org/jlab/sim/persistence/converter/DebugTestDifficultyConverter.java new file mode 100644 index 0000000..9191878 --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/converter/DebugTestDifficultyConverter.java @@ -0,0 +1,26 @@ +package org.jlab.sim.persistence.converter; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import org.jlab.sim.persistence.enumeration.DebugTestDifficulty; + +@Converter(autoApply = true) +public class DebugTestDifficultyConverter + implements AttributeConverter { + + @Override + public Integer convertToDatabaseColumn(DebugTestDifficulty obj) { + if (obj == null) { + return null; + } + return obj.getValue(); + } + + @Override + public DebugTestDifficulty convertToEntityAttribute(Integer value) { + if (value == null) { + return null; + } + return DebugTestDifficulty.fromValue(value); + } +} diff --git a/src/main/java/org/jlab/sim/persistence/converter/DocumentationGapsConverter.java b/src/main/java/org/jlab/sim/persistence/converter/DocumentationGapsConverter.java new file mode 100644 index 0000000..1d1e24e --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/converter/DocumentationGapsConverter.java @@ -0,0 +1,25 @@ +package org.jlab.sim.persistence.converter; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import org.jlab.sim.persistence.enumeration.DocumentationGaps; + +@Converter(autoApply = true) +public class DocumentationGapsConverter implements AttributeConverter { + + @Override + public Integer convertToDatabaseColumn(DocumentationGaps obj) { + if (obj == null) { + return null; + } + return obj.getValue(); + } + + @Override + public DocumentationGaps convertToEntityAttribute(Integer value) { + if (value == null) { + return null; + } + return DocumentationGaps.fromValue(value); + } +} diff --git a/src/main/java/org/jlab/sim/persistence/converter/DowntimeProbabilityConverter.java b/src/main/java/org/jlab/sim/persistence/converter/DowntimeProbabilityConverter.java new file mode 100644 index 0000000..fe3bd8c --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/converter/DowntimeProbabilityConverter.java @@ -0,0 +1,26 @@ +package org.jlab.sim.persistence.converter; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import org.jlab.sim.persistence.enumeration.DowntimeProbability; + +@Converter(autoApply = true) +public class DowntimeProbabilityConverter + implements AttributeConverter { + + @Override + public Integer convertToDatabaseColumn(DowntimeProbability obj) { + if (obj == null) { + return null; + } + return obj.getValue(); + } + + @Override + public DowntimeProbability convertToEntityAttribute(Integer value) { + if (value == null) { + return null; + } + return DowntimeProbability.fromValue(value); + } +} diff --git a/src/main/java/org/jlab/sim/persistence/converter/EsotericismConverter.java b/src/main/java/org/jlab/sim/persistence/converter/EsotericismConverter.java new file mode 100644 index 0000000..59ebcea --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/converter/EsotericismConverter.java @@ -0,0 +1,25 @@ +package org.jlab.sim.persistence.converter; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import org.jlab.sim.persistence.enumeration.Esotericism; + +@Converter(autoApply = true) +public class EsotericismConverter implements AttributeConverter { + + @Override + public Integer convertToDatabaseColumn(Esotericism obj) { + if (obj == null) { + return null; + } + return obj.getValue(); + } + + @Override + public Esotericism convertToEntityAttribute(Integer value) { + if (value == null) { + return null; + } + return Esotericism.fromValue(value); + } +} diff --git a/src/main/java/org/jlab/sim/persistence/converter/PastDowntimeRateConverter.java b/src/main/java/org/jlab/sim/persistence/converter/PastDowntimeRateConverter.java new file mode 100644 index 0000000..ecf917f --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/converter/PastDowntimeRateConverter.java @@ -0,0 +1,25 @@ +package org.jlab.sim.persistence.converter; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import org.jlab.sim.persistence.enumeration.PastDowntimeRate; + +@Converter(autoApply = true) +public class PastDowntimeRateConverter implements AttributeConverter { + + @Override + public Integer convertToDatabaseColumn(PastDowntimeRate obj) { + if (obj == null) { + return null; + } + return obj.getValue(); + } + + @Override + public PastDowntimeRate convertToEntityAttribute(Integer value) { + if (value == null) { + return null; + } + return PastDowntimeRate.fromValue(value); + } +} diff --git a/src/main/java/org/jlab/sim/persistence/entity/Software.java b/src/main/java/org/jlab/sim/persistence/entity/Software.java index 363ea81..f17fd71 100644 --- a/src/main/java/org/jlab/sim/persistence/entity/Software.java +++ b/src/main/java/org/jlab/sim/persistence/entity/Software.java @@ -6,9 +6,7 @@ import java.io.Serializable; import java.math.BigInteger; import java.util.*; -import org.jlab.sim.persistence.enumeration.DowntimeRisk; -import org.jlab.sim.persistence.enumeration.OpsImpact; -import org.jlab.sim.persistence.enumeration.SoftwareType; +import org.jlab.sim.persistence.enumeration.*; import org.jlab.smoothness.persistence.util.YnStringToBoolean; @Entity @@ -66,10 +64,34 @@ public class Software implements Serializable { @Column(name = "OPS_IMPACT", nullable = false) private OpsImpact impact; + @NotNull + @Column(name = "DOWN_PROBABILITY", nullable = false) + private DowntimeProbability probability; + @NotNull @Column(name = "DOWNTIME_RISK", nullable = false) private DowntimeRisk risk; + @NotNull + @Column(name = "PAST_DOWNTIME", nullable = false) + private PastDowntimeRate rate; + + @NotNull + @Column(name = "DEBUGGABILITY_N_TESTING", nullable = false) + private DebugTestDifficulty difficulty; + + @NotNull + @Column(name = "CODE_COMPLEXITY", nullable = false) + private CodeComplexity complexity; + + @NotNull + @Column(name = "DOCUMENTATION_GAPS", nullable = false) + private DocumentationGaps gaps; + + @NotNull + @Column(name = "ESOTERICISM", nullable = false) + private Esotericism esotericism; + @OneToMany(fetch = FetchType.EAGER) @JoinColumn( name = "SOFTWARE_ID", @@ -219,10 +241,54 @@ public void setImpact(OpsImpact impact) { this.impact = impact; } + public DowntimeProbability getProbability() { + return probability; + } + public DowntimeRisk getRisk() { return risk; } + public PastDowntimeRate getRate() { + return rate; + } + + public void setRate(PastDowntimeRate rate) { + this.rate = rate; + } + + public DebugTestDifficulty getDifficulty() { + return difficulty; + } + + public void setDifficulty(DebugTestDifficulty difficulty) { + this.difficulty = difficulty; + } + + public CodeComplexity getComplexity() { + return complexity; + } + + public void setComplexity(CodeComplexity complexity) { + this.complexity = complexity; + } + + public DocumentationGaps getGaps() { + return gaps; + } + + public void setGaps(DocumentationGaps gaps) { + this.gaps = gaps; + } + + public Esotericism getEsotericism() { + return esotericism; + } + + public void setEsotericism(Esotericism esotericism) { + this.esotericism = esotericism; + } + @Override public boolean equals(Object o) { if (!(o instanceof Software)) return false; diff --git a/src/main/java/org/jlab/sim/persistence/enumeration/CodeComplexity.java b/src/main/java/org/jlab/sim/persistence/enumeration/CodeComplexity.java new file mode 100644 index 0000000..fbb5702 --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/enumeration/CodeComplexity.java @@ -0,0 +1,28 @@ +package org.jlab.sim.persistence.enumeration; + +public enum CodeComplexity { + Simple(1), + Low(2), + Medium(3), + High(4), + Complex(5); + + private int value; + + CodeComplexity(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static CodeComplexity fromValue(int value) { + for (CodeComplexity obj : CodeComplexity.values()) { + if (obj.value == value) { + return obj; + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} diff --git a/src/main/java/org/jlab/sim/persistence/enumeration/DebugTestDifficulty.java b/src/main/java/org/jlab/sim/persistence/enumeration/DebugTestDifficulty.java new file mode 100644 index 0000000..8c5c33c --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/enumeration/DebugTestDifficulty.java @@ -0,0 +1,28 @@ +package org.jlab.sim.persistence.enumeration; + +public enum DebugTestDifficulty { + Easy(1), + Low(2), + Medium(3), + High(4), + Hard(5); + + private int value; + + DebugTestDifficulty(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static DebugTestDifficulty fromValue(int value) { + for (DebugTestDifficulty obj : DebugTestDifficulty.values()) { + if (obj.value == value) { + return obj; + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} diff --git a/src/main/java/org/jlab/sim/persistence/enumeration/DocumentationGaps.java b/src/main/java/org/jlab/sim/persistence/enumeration/DocumentationGaps.java new file mode 100644 index 0000000..ca2922f --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/enumeration/DocumentationGaps.java @@ -0,0 +1,28 @@ +package org.jlab.sim.persistence.enumeration; + +public enum DocumentationGaps { + Few(1), + Low(2), + Medium(3), + High(4), + Many(5); + + private int value; + + DocumentationGaps(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static DocumentationGaps fromValue(int value) { + for (DocumentationGaps obj : DocumentationGaps.values()) { + if (obj.value == value) { + return obj; + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} diff --git a/src/main/java/org/jlab/sim/persistence/enumeration/DowntimeProbability.java b/src/main/java/org/jlab/sim/persistence/enumeration/DowntimeProbability.java new file mode 100644 index 0000000..8c7a92d --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/enumeration/DowntimeProbability.java @@ -0,0 +1,28 @@ +package org.jlab.sim.persistence.enumeration; + +public enum DowntimeProbability { + MARGINAL(1), + LOW(2), + MEDIUM(3), + HIGH(4), + CRITICAL(5); + + private int value; + + DowntimeProbability(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static DowntimeProbability fromValue(int value) { + for (DowntimeProbability obj : DowntimeProbability.values()) { + if (obj.value == value) { + return obj; + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} diff --git a/src/main/java/org/jlab/sim/persistence/enumeration/Esotericism.java b/src/main/java/org/jlab/sim/persistence/enumeration/Esotericism.java new file mode 100644 index 0000000..a8e9c0c --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/enumeration/Esotericism.java @@ -0,0 +1,28 @@ +package org.jlab.sim.persistence.enumeration; + +public enum Esotericism { + Marginal(1), + Low(2), + Medium(3), + High(4), + Critical(5); + + private int value; + + Esotericism(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Esotericism fromValue(int value) { + for (Esotericism obj : Esotericism.values()) { + if (obj.value == value) { + return obj; + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} diff --git a/src/main/java/org/jlab/sim/persistence/enumeration/PastDowntimeRate.java b/src/main/java/org/jlab/sim/persistence/enumeration/PastDowntimeRate.java new file mode 100644 index 0000000..39e2a38 --- /dev/null +++ b/src/main/java/org/jlab/sim/persistence/enumeration/PastDowntimeRate.java @@ -0,0 +1,28 @@ +package org.jlab.sim.persistence.enumeration; + +public enum PastDowntimeRate { + Marginal(1), + Low(2), + Medium(3), + High(4), + Critical(5); + + private int value; + + PastDowntimeRate(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static PastDowntimeRate fromValue(int value) { + for (PastDowntimeRate obj : PastDowntimeRate.values()) { + if (obj.value == value) { + return obj; + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } +} diff --git a/src/main/webapp/WEB-INF/tags/directory-page.tag b/src/main/webapp/WEB-INF/tags/directory-page.tag index c024590..e609c83 100644 --- a/src/main/webapp/WEB-INF/tags/directory-page.tag +++ b/src/main/webapp/WEB-INF/tags/directory-page.tag @@ -122,7 +122,15 @@ data-note="${fn:escapeXml(software.note)}" data-repo-id="${software.repository.repositoryId}" data-maintainer-csv="${fn:escapeXml(software.maintainerUsernameCsv)}" - data-url="${fn:escapeXml(software.homeUrl)}"> + data-url="${fn:escapeXml(software.homeUrl)}" + data-risk="${fn:escapeXml(software.risk.value)}" + data-probability="${fn:escapeXml(software.probability.value)}" + data-impact="${fn:escapeXml(software.impact.value)}" + data-rate="${fn:escapeXml(software.rate.value)}" + data-difficulty="${fn:escapeXml(software.difficulty.value)}" + data-complexity="${fn:escapeXml(software.complexity.value)}" + data-gaps="${fn:escapeXml(software.gaps.value)}" + data-esotericism="${fn:escapeXml(software.esotericism.value)}"> @@ -148,7 +156,10 @@ - + + + () + @@ -166,4 +177,83 @@ - \ No newline at end of file + +
+
+
    +
  • +
    + +
    +
    + +
    +
  • +
+
+
    +
  • +
    + +
    +
    + +
    +
  • +
  • +
    + +
    +
    + +
    +
  • +
+
+
    +
  • +
    + +
    +
    + +
    +
  • +
  • +
    + +
    +
    + +
    +
  • +
  • +
    + +
    +
    + +
    +
  • +
  • +
    + +
    +
    + +
    +
  • +
  • +
    + +
    +
    + +
    +
  • +
+
+ +
+
+
\ No newline at end of file diff --git a/src/main/webapp/resources/js/directory.js b/src/main/webapp/resources/js/directory.js index 614a8b8..d8cd66f 100644 --- a/src/main/webapp/resources/js/directory.js +++ b/src/main/webapp/resources/js/directory.js @@ -209,10 +209,32 @@ $(document).on("change", ".change-submit", function () { let formId = $(this).attr("form"); document.getElementById(formId).submit(); }); +$(document).on("click", ".risk-dialog-opener", function () { + let $tr = $(this).closest("tr"); + + $("#risk-score").text($tr.attr("data-risk")); + $("#impact").text($tr.attr("data-impact")); + $("#probability").text($tr.attr("data-probability")); + $("#rate").text($tr.attr("data-rate")); + $("#complexity").text($tr.attr("data-complexity")); + $("#difficulty").text($tr.attr("data-difficulty")); + $("#gaps").text($tr.attr("data-gaps")); + $("#esotericism").text($tr.attr("data-esotericism")); + $("#risk-dialog").dialog("open"); + return false; +}); $(function(){ $("#row-topics").select2({ tags: true }); $("#topic-select").select2(); + + $("#risk-dialog").dialog({ + autoOpen: false, + width: 640, + height: 480, + modal: true, + draggable: false + }); }); \ No newline at end of file From f562dd47612b1875fa278f3b70e02d87b8570227 Mon Sep 17 00:00:00 2001 From: Ryan Slominski Date: Mon, 23 Feb 2026 16:58:36 -0500 Subject: [PATCH 4/7] Use selects --- .../persistence/enumeration/OpsImpact.java | 10 +++--- .../presentation/controller/Directory.java | 11 ++++-- .../webapp/WEB-INF/tags/directory-page.tag | 36 +++++++++++++++---- src/main/webapp/resources/js/directory.js | 12 +++---- 4 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/jlab/sim/persistence/enumeration/OpsImpact.java b/src/main/java/org/jlab/sim/persistence/enumeration/OpsImpact.java index d1f23a5..f5e9893 100644 --- a/src/main/java/org/jlab/sim/persistence/enumeration/OpsImpact.java +++ b/src/main/java/org/jlab/sim/persistence/enumeration/OpsImpact.java @@ -1,11 +1,11 @@ package org.jlab.sim.persistence.enumeration; public enum OpsImpact { - MARGINAL(1), - LOW(2), - MEDIUM(3), - HIGH(4), - CRITICAL(5); + Marginal(1), + Low(2), + Medium(3), + High(4), + Critical(5); private int value; diff --git a/src/main/java/org/jlab/sim/presentation/controller/Directory.java b/src/main/java/org/jlab/sim/presentation/controller/Directory.java index cbeb1fd..cbf5d4d 100644 --- a/src/main/java/org/jlab/sim/presentation/controller/Directory.java +++ b/src/main/java/org/jlab/sim/presentation/controller/Directory.java @@ -18,8 +18,7 @@ import org.jlab.sim.persistence.entity.Repository; import org.jlab.sim.persistence.entity.Software; import org.jlab.sim.persistence.entity.Topic; -import org.jlab.sim.persistence.enumeration.Include; -import org.jlab.sim.persistence.enumeration.SoftwareType; +import org.jlab.sim.persistence.enumeration.*; import org.jlab.sim.presentation.util.Parameter; import org.jlab.smoothness.business.service.JPAService; import org.jlab.smoothness.presentation.util.Paginator; @@ -107,6 +106,14 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) request.setAttribute("includeList", includeList); request.setAttribute("softwareList", softwareList); request.setAttribute("paginator", paginator); + request.setAttribute("riskList", Arrays.asList(DowntimeRisk.values())); + request.setAttribute("impactList", Arrays.asList(OpsImpact.values())); + request.setAttribute("probabilityList", Arrays.asList(DowntimeProbability.values())); + request.setAttribute("rateList", Arrays.asList(PastDowntimeRate.values())); + request.setAttribute("difficultyList", Arrays.asList(DebugTestDifficulty.values())); + request.setAttribute("complexityList", Arrays.asList(CodeComplexity.values())); + request.setAttribute("gapList", Arrays.asList(DocumentationGaps.values())); + request.setAttribute("esotericismList", Arrays.asList(Esotericism.values())); if ("Y".equals(request.getParameter("loose"))) { request.getRequestDispatcher("/WEB-INF/views/directory-loose.jsp").forward(request, response); diff --git a/src/main/webapp/WEB-INF/tags/directory-page.tag b/src/main/webapp/WEB-INF/tags/directory-page.tag index e609c83..4c5ceb5 100644 --- a/src/main/webapp/WEB-INF/tags/directory-page.tag +++ b/src/main/webapp/WEB-INF/tags/directory-page.tag @@ -197,7 +197,11 @@
- +
  • @@ -216,7 +220,11 @@
    - +
  • @@ -224,7 +232,11 @@
    - +
  • @@ -232,7 +244,11 @@
    - +
  • @@ -240,7 +256,11 @@
    - +
  • @@ -248,7 +268,11 @@
    - +
  • diff --git a/src/main/webapp/resources/js/directory.js b/src/main/webapp/resources/js/directory.js index d8cd66f..d7600a2 100644 --- a/src/main/webapp/resources/js/directory.js +++ b/src/main/webapp/resources/js/directory.js @@ -213,13 +213,13 @@ $(document).on("click", ".risk-dialog-opener", function () { let $tr = $(this).closest("tr"); $("#risk-score").text($tr.attr("data-risk")); - $("#impact").text($tr.attr("data-impact")); + $("#impact").val($tr.attr("data-impact")); $("#probability").text($tr.attr("data-probability")); - $("#rate").text($tr.attr("data-rate")); - $("#complexity").text($tr.attr("data-complexity")); - $("#difficulty").text($tr.attr("data-difficulty")); - $("#gaps").text($tr.attr("data-gaps")); - $("#esotericism").text($tr.attr("data-esotericism")); + $("#rate").val($tr.attr("data-rate")); + $("#complexity").val($tr.attr("data-complexity")); + $("#difficulty").val($tr.attr("data-difficulty")); + $("#gaps").val($tr.attr("data-gaps")); + $("#esotericism").val($tr.attr("data-esotericism")); $("#risk-dialog").dialog("open"); return false; }); From 36c9a1bbd4ba2eb6922a1059fd10f9eed9303f02 Mon Sep 17 00:00:00 2001 From: Ryan Slominski Date: Tue, 24 Feb 2026 07:45:47 -0500 Subject: [PATCH 5/7] Risk dialog --- .../enumeration/DowntimeProbability.java | 10 +- .../webapp/WEB-INF/tags/directory-page.tag | 148 ++++++++++-------- src/main/webapp/resources/js/directory.js | 10 +- 3 files changed, 94 insertions(+), 74 deletions(-) diff --git a/src/main/java/org/jlab/sim/persistence/enumeration/DowntimeProbability.java b/src/main/java/org/jlab/sim/persistence/enumeration/DowntimeProbability.java index 8c7a92d..38a4db6 100644 --- a/src/main/java/org/jlab/sim/persistence/enumeration/DowntimeProbability.java +++ b/src/main/java/org/jlab/sim/persistence/enumeration/DowntimeProbability.java @@ -1,11 +1,11 @@ package org.jlab.sim.persistence.enumeration; public enum DowntimeProbability { - MARGINAL(1), - LOW(2), - MEDIUM(3), - HIGH(4), - CRITICAL(5); + Marginal(1), + Low(2), + Medium(3), + High(4), + Critical(5); private int value; diff --git a/src/main/webapp/WEB-INF/tags/directory-page.tag b/src/main/webapp/WEB-INF/tags/directory-page.tag index 4c5ceb5..875ce5f 100644 --- a/src/main/webapp/WEB-INF/tags/directory-page.tag +++ b/src/main/webapp/WEB-INF/tags/directory-page.tag @@ -178,19 +178,26 @@ -
    +
    -
      + Explanation +
      • - + +
      + Risk = Impact + Probability
      • @@ -209,73 +216,82 @@
        - + +
        +
      • +
      +
      Probability = AVG(Risk Factors)
      +
      +   +
      + Risk Factors +
        +
      • +
        + +
        +
        + +
        +
      • +
      • +
        + +
        +
        + +
        +
      • +
      • +
        + +
        +
        + +
        +
      • +
      • +
        + +
        +
        + +
        +
      • +
      • +
        + +
        +
        +
      -
        -
      • -
        - -
        -
        - -
        -
      • -
      • -
        - -
        -
        - -
        -
      • -
      • -
        - -
        -
        - -
        -
      • -
      • -
        - -
        -
        - -
        -
      • -
      • -
        - -
        -
        - -
        -
      • -
      diff --git a/src/main/webapp/resources/js/directory.js b/src/main/webapp/resources/js/directory.js index d7600a2..1d7bf02 100644 --- a/src/main/webapp/resources/js/directory.js +++ b/src/main/webapp/resources/js/directory.js @@ -212,9 +212,13 @@ $(document).on("change", ".change-submit", function () { $(document).on("click", ".risk-dialog-opener", function () { let $tr = $(this).closest("tr"); - $("#risk-score").text($tr.attr("data-risk")); + $("#risk-dialog").dialog("option", "title", $tr.attr("data-name") + " - Downtime Risk Assessment"); + + $("#risk-score").val($tr.attr("data-risk")); + $("#probability").val($tr.attr("data-probability")); + $("#risk-score-visible").text($("#risk-score option:selected").text()); + $("#impact").val($tr.attr("data-impact")); - $("#probability").text($tr.attr("data-probability")); $("#rate").val($tr.attr("data-rate")); $("#complexity").val($tr.attr("data-complexity")); $("#difficulty").val($tr.attr("data-difficulty")); @@ -233,7 +237,7 @@ $(function(){ $("#risk-dialog").dialog({ autoOpen: false, width: 640, - height: 480, + height: 580, modal: true, draggable: false }); From e97a91ac7ebf90f20ec757b56b6f3958f2090a67 Mon Sep 17 00:00:00 2001 From: Ryan Slominski Date: Tue, 24 Feb 2026 08:15:17 -0500 Subject: [PATCH 6/7] Editable controls --- .../webapp/WEB-INF/tags/directory-page.tag | 6 +++-- src/main/webapp/resources/css/directory.css | 4 +++ src/main/webapp/resources/js/directory.js | 26 ++++++++++++++++++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/WEB-INF/tags/directory-page.tag b/src/main/webapp/WEB-INF/tags/directory-page.tag index 875ce5f..95835b5 100644 --- a/src/main/webapp/WEB-INF/tags/directory-page.tag +++ b/src/main/webapp/WEB-INF/tags/directory-page.tag @@ -180,7 +180,7 @@
    - Explanation + Guidance
    • @@ -216,7 +216,8 @@
      - @@ -293,6 +294,7 @@
    +
    diff --git a/src/main/webapp/resources/css/directory.css b/src/main/webapp/resources/css/directory.css index f0d23f3..a5d3094 100644 --- a/src/main/webapp/resources/css/directory.css +++ b/src/main/webapp/resources/css/directory.css @@ -52,4 +52,8 @@ td:nth-child(5) { .topic-scroll-wrap { overflow: auto; height: 60px; +} +#risk-update-button { + float: left; + margin-left: 0; } \ No newline at end of file diff --git a/src/main/webapp/resources/js/directory.js b/src/main/webapp/resources/js/directory.js index 1d7bf02..f04b0d7 100644 --- a/src/main/webapp/resources/js/directory.js +++ b/src/main/webapp/resources/js/directory.js @@ -217,13 +217,35 @@ $(document).on("click", ".risk-dialog-opener", function () { $("#risk-score").val($tr.attr("data-risk")); $("#probability").val($tr.attr("data-probability")); $("#risk-score-visible").text($("#risk-score option:selected").text()); + $("#probability-visible").text($("#probability option:selected").text()); $("#impact").val($tr.attr("data-impact")); $("#rate").val($tr.attr("data-rate")); - $("#complexity").val($tr.attr("data-complexity")); $("#difficulty").val($tr.attr("data-difficulty")); + $("#complexity").val($tr.attr("data-complexity")); $("#gaps").val($tr.attr("data-gaps")); $("#esotericism").val($tr.attr("data-esotericism")); + + let editable = $("#logout-form").length; + + if(editable) { + $("#risk-update-button").removeAttr("disabled"); + $("#impact").removeAttr("disabled"); + $("#rate").removeAttr("disabled"); + $("#difficulty").removeAttr("disabled"); + $("#complexity").removeAttr("disabled"); + $("#gaps").removeAttr("disabled"); + $("#esotericism").removeAttr("disabled"); + } else { + $("#risk-update-button").attr("disabled", "disabled"); + $("#impact").attr("disabled", "disabled"); + $("#rate").attr("disabled", "disabled"); + $("#difficulty").attr("disabled", "disabled"); + $("#complexity").attr("disabled", "disabled"); + $("#gaps").attr("disabled", "disabled"); + $("#esotericism").attr("disabled", "disabled"); + } + $("#risk-dialog").dialog("open"); return false; }); @@ -236,7 +258,9 @@ $(function(){ $("#risk-dialog").dialog({ autoOpen: false, + minWidth: 640, width: 640, + minHeight: 580, height: 580, modal: true, draggable: false From 71fbb24ca23fd5b30d82bef5a23f54a4065350e3 Mon Sep 17 00:00:00 2001 From: Ryan Slominski Date: Tue, 24 Feb 2026 08:44:56 -0500 Subject: [PATCH 7/7] Persist changes --- .../sim/business/service/SoftwareService.java | 34 ++++++- .../jlab/sim/persistence/entity/Software.java | 4 +- .../controller/ajax/EditSoftwareRisk.java | 88 +++++++++++++++++++ .../webapp/WEB-INF/tags/directory-page.tag | 1 + src/main/webapp/resources/js/directory.js | 58 ++++++++++++ 5 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/jlab/sim/presentation/controller/ajax/EditSoftwareRisk.java diff --git a/src/main/java/org/jlab/sim/business/service/SoftwareService.java b/src/main/java/org/jlab/sim/business/service/SoftwareService.java index 34a3d7e..0ffc027 100644 --- a/src/main/java/org/jlab/sim/business/service/SoftwareService.java +++ b/src/main/java/org/jlab/sim/business/service/SoftwareService.java @@ -12,8 +12,7 @@ import org.jlab.sim.persistence.entity.Repository; import org.jlab.sim.persistence.entity.Software; import org.jlab.sim.persistence.entity.SoftwareTopic; -import org.jlab.sim.persistence.enumeration.Include; -import org.jlab.sim.persistence.enumeration.SoftwareType; +import org.jlab.sim.persistence.enumeration.*; import org.jlab.smoothness.business.exception.UserFriendlyException; import org.jlab.smoothness.business.service.JPAService; @@ -122,6 +121,37 @@ public void editSoftware( softwareTopicService.set(software, topicArray); } + @RolesAllowed({"sim-admin", "acg"}) + public void editSoftwareRisk( + BigInteger softwareId, + OpsImpact impact, + PastDowntimeRate rate, + DebugTestDifficulty difficulty, + CodeComplexity complexity, + DocumentationGaps gaps, + Esotericism esotericism) + throws UserFriendlyException { + + if (softwareId == null) { + throw new UserFriendlyException("softwareId cannot be empty"); + } + + Software software = find(softwareId); + + if (software == null) { + throw new UserFriendlyException("software not found with id: " + softwareId); + } + + software.setImpact(impact); + software.setRate(rate); + software.setDifficulty(difficulty); + software.setComplexity(complexity); + software.setGaps(gaps); + software.setEsotericism(esotericism); + + edit(software); + } + @RolesAllowed({"sim-admin", "acg"}) public void removeSoftware(BigInteger softwareId) throws UserFriendlyException { if (softwareId == null) { diff --git a/src/main/java/org/jlab/sim/persistence/entity/Software.java b/src/main/java/org/jlab/sim/persistence/entity/Software.java index f17fd71..0ce9ebc 100644 --- a/src/main/java/org/jlab/sim/persistence/entity/Software.java +++ b/src/main/java/org/jlab/sim/persistence/entity/Software.java @@ -65,11 +65,11 @@ public class Software implements Serializable { private OpsImpact impact; @NotNull - @Column(name = "DOWN_PROBABILITY", nullable = false) + @Column(name = "DOWN_PROBABILITY", nullable = false, updatable = false) private DowntimeProbability probability; @NotNull - @Column(name = "DOWNTIME_RISK", nullable = false) + @Column(name = "DOWNTIME_RISK", nullable = false, updatable = false) private DowntimeRisk risk; @NotNull diff --git a/src/main/java/org/jlab/sim/presentation/controller/ajax/EditSoftwareRisk.java b/src/main/java/org/jlab/sim/presentation/controller/ajax/EditSoftwareRisk.java new file mode 100644 index 0000000..1fd31c1 --- /dev/null +++ b/src/main/java/org/jlab/sim/presentation/controller/ajax/EditSoftwareRisk.java @@ -0,0 +1,88 @@ +package org.jlab.sim.presentation.controller.ajax; + +import jakarta.ejb.EJB; +import jakarta.ejb.EJBAccessException; +import jakarta.json.Json; +import jakarta.json.stream.JsonGenerator; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.jlab.sim.business.service.SoftwareService; +import org.jlab.sim.persistence.enumeration.*; +import org.jlab.smoothness.business.exception.UserFriendlyException; +import org.jlab.smoothness.business.util.ExceptionUtil; +import org.jlab.smoothness.presentation.util.ParamConverter; + +@WebServlet( + name = "EditSoftwareRisk", + urlPatterns = {"/ajax/edit-software-risk"}) +public class EditSoftwareRisk extends HttpServlet { + + private static final Logger logger = Logger.getLogger(EditSoftwareRisk.class.getName()); + + @EJB SoftwareService softwareService; + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + String stat = "ok"; + String error = null; + String name = null; + + try { + BigInteger softwareId = ParamConverter.convertBigInteger(request, "softwareId"); + + Integer impactValue = ParamConverter.convertInteger(request, "impact"); + Integer rateValue = ParamConverter.convertInteger(request, "rate"); + Integer difficultyValue = ParamConverter.convertInteger(request, "difficulty"); + Integer complexityValue = ParamConverter.convertInteger(request, "complexity"); + Integer gapsValue = ParamConverter.convertInteger(request, "gaps"); + Integer esotericismValue = ParamConverter.convertInteger(request, "esotericism"); + + OpsImpact impact = OpsImpact.fromValue(impactValue); + PastDowntimeRate rate = PastDowntimeRate.fromValue(rateValue); + DebugTestDifficulty difficulty = DebugTestDifficulty.fromValue(difficultyValue); + CodeComplexity complexity = CodeComplexity.fromValue(complexityValue); + DocumentationGaps gaps = DocumentationGaps.fromValue(gapsValue); + Esotericism esotericism = Esotericism.fromValue(esotericismValue); + + // Since name is key in other repos, must delete and add new for name changes. + softwareService.editSoftwareRisk( + softwareId, impact, rate, difficulty, complexity, gaps, esotericism); + } catch (UserFriendlyException e) { + stat = "fail"; + error = "Unable to edit Software Risk: " + e.getUserMessage(); + } catch (EJBAccessException e) { + stat = "fail"; + error = "Unable to edit Software: Not authenticated / authorized (do you need to re-login?)"; + } catch (RuntimeException e) { + stat = "fail"; + error = "Unable to edit Software Risk"; + logger.log(Level.SEVERE, "Unable to edit Software Risk", e); + Throwable rootCause = ExceptionUtil.getRootCause(e); + if ("OracleDatabaseException".equals(rootCause.getClass().getSimpleName())) { + error = "Oracle Database Exception - make sure name doesn't already exist: " + name; + } + } + + response.setContentType("application/json"); + + OutputStream out = response.getOutputStream(); + + try (JsonGenerator gen = Json.createGenerator(out)) { + gen.writeStartObject().write("stat", stat); + if (error != null) { + gen.write("error", error); + } + gen.writeEnd(); + } + } +} diff --git a/src/main/webapp/WEB-INF/tags/directory-page.tag b/src/main/webapp/WEB-INF/tags/directory-page.tag index 95835b5..264b210 100644 --- a/src/main/webapp/WEB-INF/tags/directory-page.tag +++ b/src/main/webapp/WEB-INF/tags/directory-page.tag @@ -180,6 +180,7 @@
    + Guidance
    • diff --git a/src/main/webapp/resources/js/directory.js b/src/main/webapp/resources/js/directory.js index f04b0d7..b5a7c0a 100644 --- a/src/main/webapp/resources/js/directory.js +++ b/src/main/webapp/resources/js/directory.js @@ -209,9 +209,67 @@ $(document).on("change", ".change-submit", function () { let formId = $(this).attr("form"); document.getElementById(formId).submit(); }); +$(document).on("click", "#risk-update-button", function() { + var softwareId = $("#risk-software-id").val(), + impact = $("#impact").val(), + rate = $("#rate").val(), + difficulty = $("#difficulty").val(), + complexity = $("#complexity").val(), + gaps = $("#gaps").val(), + esotericism = $("#esotericism").val(), + $updateButton = $("#risk-update-button"), + reloading = false; + + $updateButton + .height($updateButton.height()) + .width($updateButton.width()) + .empty().append('
      '); + $(".dialog-close-button").attr("disabled", "disabled"); + $(".ui-dialog-titlebar button").attr("disabled", "disabled"); + + var request = jQuery.ajax({ + url: jlab.contextPath + "/ajax/edit-software-risk", + type: "POST", + data: { + softwareId: softwareId, + impact: impact, + rate: rate, + difficulty: difficulty, + complexity: complexity, + gaps: gaps, + esotericism: esotericism + }, + dataType: "json" + }); + + request.done(function(json) { + if (json.stat === 'ok') { + reloading = true; + window.location.reload(); + } else { + alert(json.error); + } + }); + + request.fail(function(xhr, textStatus) { + window.console && console.log('Unable to edit software; Text Status: ' + textStatus + ', Ready State: ' + xhr.readyState + ', HTTP Status Code: ' + xhr.status); + alert('Unable to Save: Server unavailable or unresponsive'); + }); + + request.always(function() { + if (!reloading) { + $updateButton.empty().text("Update"); + $updateButton.removeAttr("disabled"); + $(".ui-dialog-titlebar button").removeAttr("disabled"); + + } + }); +}); $(document).on("click", ".risk-dialog-opener", function () { let $tr = $(this).closest("tr"); + $("#risk-software-id").val($tr.attr("data-id")); + $("#risk-dialog").dialog("option", "title", $tr.attr("data-name") + " - Downtime Risk Assessment"); $("#risk-score").val($tr.attr("data-risk"));