diff --git a/container/oracle/initdb.d/02_ddl.sql b/container/oracle/initdb.d/02_ddl.sql index 70caa1c..bfaf958 100644 --- a/container/oracle/initdb.d/02_ddl.sql +++ b/container/oracle/initdb.d/02_ddl.sql @@ -58,11 +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 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, + 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/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/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/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/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/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/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 2d5affb..0ce9ebc 100644 --- a/src/main/java/org/jlab/sim/persistence/entity/Software.java +++ b/src/main/java/org/jlab/sim/persistence/entity/Software.java @@ -6,7 +6,7 @@ import java.io.Serializable; import java.math.BigInteger; import java.util.*; -import org.jlab.sim.persistence.enumeration.SoftwareType; +import org.jlab.sim.persistence.enumeration.*; import org.jlab.smoothness.persistence.util.YnStringToBoolean; @Entity @@ -60,6 +60,38 @@ 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 = "DOWN_PROBABILITY", nullable = false, updatable = false) + private DowntimeProbability probability; + + @NotNull + @Column(name = "DOWNTIME_RISK", nullable = false, updatable = 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", @@ -201,6 +233,62 @@ public void setArchivedSynced(boolean archivedSynced) { this.archivedSynced = archivedSynced; } + public OpsImpact getImpact() { + return impact; + } + + 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..38a4db6 --- /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/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/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/OpsImpact.java b/src/main/java/org/jlab/sim/persistence/enumeration/OpsImpact.java new file mode 100644 index 0000000..f5e9893 --- /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/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/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/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 825c47a..264b210 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 @@ -121,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)}"> @@ -147,6 +156,10 @@ + + + () + @@ -164,4 +177,126 @@ - \ No newline at end of file + +
+
+ + Guidance +
    +
  • +
    + +
    +
    + + +
    +
  • +
+
+ Risk = Impact + Probability +
    +
  • +
    + +
    +
    + +
    +
  • +
  • +
    + +
    +
    + + +
    +
  • +
+
Probability = AVG(Risk Factors)
+
+   +
+ Risk Factors +
    +
  • +
    + +
    +
    + +
    +
  • +
  • +
    + +
    +
    + +
    +
  • +
  • +
    + +
    +
    + +
    +
  • +
  • +
    + +
    +
    + +
    +
  • +
  • +
    + +
    +
    + +
    +
  • +
+
+
+ + +
+
+
\ No newline at end of file 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 614a8b8..b5a7c0a 100644 --- a/src/main/webapp/resources/js/directory.js +++ b/src/main/webapp/resources/js/directory.js @@ -209,10 +209,118 @@ $(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")); + $("#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")); + $("#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; +}); $(function(){ $("#row-topics").select2({ tags: true }); $("#topic-select").select2(); + + $("#risk-dialog").dialog({ + autoOpen: false, + minWidth: 640, + width: 640, + minHeight: 580, + height: 580, + modal: true, + draggable: false + }); }); \ No newline at end of file