Skip to content

Commit

Permalink
Feature/html report UI upgrade (#134)
Browse files Browse the repository at this point in the history
This feature adds the following updates to the HTML report.

1. Filter items by status (Ok, Missing, Partial, Warning, Justified)
2. Hide/Unhide Issues.
3. Search in issues.
  • Loading branch information
kedarnn authored Dec 11, 2024
1 parent 3105b8e commit e2439ad
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 16 deletions.
29 changes: 16 additions & 13 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,23 @@

### 0.9.21-dev

* `lobster-html-report` has the following updates.
* Filter items by status (Ok, Missing, Partial, Warning, Justified)
* Hide/Unhide Issues.
* Search in issues and detailed report.

* Add support to view version for lobster tools for following tools:
- `lobster-ci-report`
- `lobster-codebeamer`
- `lobster-cpp`
- `lobster-cpptest`
- `lobster-gtest`
- `lobster-html-report`
- `lobster-json`
- `lobster-online-report`
- `lobster-python`
- `lobster-report`
- `lobster-trlc`

### 0.9.20

Expand All @@ -24,19 +40,6 @@
* `lobster-codebeamer`
* `lobster-report`

* Add support to view version for lobster tools for following tools:
- `lobster-ci-report`
- `lobster-codebeamer`
- `lobster-cpp`
- `lobster-cpptest`
- `lobster-gtest`
- `lobster-html-report`
- `lobster-json`
- `lobster-online-report`
- `lobster-python`
- `lobster-report`
- `lobster-trlc`

* `lobster-gtest` accepts XML nodes other than `testcase`, but ignores them.

### 0.9.19
Expand Down
5 changes: 5 additions & 0 deletions lobster/html/htmldoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ def __init__(self, title, subtitle):
}
self.scripts = []
self.body = []
self.css_files = []

def add_line(self, line):
assert isinstance(line, str)
Expand Down Expand Up @@ -293,6 +294,10 @@ def render(self):
rv.append(" %s: %s;" % (attr, value))
rv.append("}")
rv.append("</style>")

# add css files that are appended to self.files
for css_file in self.css_files:
rv.append(f"<link rel='stylesheet' href={css_file}>")
rv.append("</head>")
rv.append("<body>")

Expand Down
139 changes: 139 additions & 0 deletions lobster/tools/core/html_report/assets/html_report.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
.button {
background-color: #818589;
border: none;
border-radius: 5px;
color: white;
padding: 12px 25px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin: 4px 2px;
cursor: pointer
}

.button active:before {
content: ;
position: absolute;
left: 0;
top: 0;
display: inline-block;
width: 0;
height: 0;
border-style: solid;
border-width: 15px 15px 0 0;
border-color: #333 transparent transparent transparent
}

.buttonActive.button {
text-decoration: none;
border: 5px solid #000000
}

.buttonOK {
background-color: #04AA6D;
color: white;
border: 2px solid #04AA6D;
border-radius: 5px
}

.buttonOK:hover {
background-color: #026641;
color: white;
border: 2px solid #026641
}

.buttonActive.buttonOK {
text-decoration: none;
border: 5px solid #026641
}

.buttonPartial {
background-color: #17a2b8;
color: white;
border: 2px solid #17a2b8;
border-radius: 5px
}

.buttonPartial:hover {
background-color: #0e616e;
color: white;
border: 2px solid #0e616e
}

.buttonActive.buttonPartial {
text-decoration: none;
border: 5px solid #0e616e
}

.buttonMissing {
background-color: #f44336;
color: white;
border: 2px solid #f44336;
border-radius: 5px
}

.buttonMissing:hover {
background-color: #a91409;
color: white;
border: 2px solid #a91409
}

.buttonActive.buttonMissing {
text-decoration: none;
border: 5px solid #a91409
}

.buttonJustified {
background-color: #6c757d;
color: white;
border: 2px solid #6c757d;
border-radius: 5px
}

.buttonJustified:hover {
background-color: #41464b;
color: white;
border: 2px solid #41464b
}

.buttonActive.buttonJustified {
text-decoration: none;
border: 5px solid #41464b
}

.buttonWarning {
background-color: #ffbf00;
color: white;
border: 2px solid #ffbf00;
border-radius: 5px
}

.buttonWarning:hover {
background-color: #997300;
color: white;
border: 2px solid #997300
}

.buttonActive.buttonWarning {
text-decoration: none;
border: 5px solid #997300
}

.buttonBlue {
background-color: #0000ff;
color: white;
border: 2px solid #0000ff;
border-radius: 5px
}

.buttonBlue:hover {
background-color: #000099;
color: white;
border: 2px solid #000099
}

.buttonActive.buttonBlue {
text-decoration: none;
border: 5px solid #000099
}
87 changes: 87 additions & 0 deletions lobster/tools/core/html_report/assets/html_report.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
function buttonFilter(filter) {
var elms = document.getElementsByTagName("div");
var issue_elms = document.getElementsByClassName("issue");
for (i = 0; i < elms.length; i++) {
if (elms[i].id.startsWith("item-")) {
console.log("elms[i].className ", elms[i].className)
if (filter == "all") {
elms[i].style.display = "block";
} else if (elms[i].className == "item-" + filter) {
elms[i].style.display = "block";
} else {
elms[i].style.display = "none";
}
}
}
// filter the issues list based on the issue filter button clicked
for (i = 0; i < issue_elms.length; i++) {
console.log("log ", issue_elms[i].className)
if (filter == "all") {
issue_elms[i].style.display = "list-item";
} else if (issue_elms[i].className == "issue issue-" + filter) {
issue_elms[i].style.display = "list-item";
} else {
issue_elms[i].style.display = "none";
}
}
activeButton(filter);
//call the search filering which could have been overwritten by the current filtering
searchItem();
}


function activeButton(filter) {
var elms = document.getElementsByTagName("button");
console.log("the click buitton is " + filter);
for (i = 0; i < elms.length; i++) {
if (elms[i].className.includes("buttonActive")) {
console.log("elem active found : " + elms[i].className);
elms[i].className = elms[i].className.replace("buttonActive", "");
} else if (elms[i].className.toLowerCase().includes("button" + filter.toLowerCase())) {
console.log("elem to be activated found : " + elms[i].className);
elms[i].className = elms[i].className + " buttonActive";
}
}
}


function ToggleIssues() {
var div_issue = document.getElementById("issues-section");
if (div_issue.style.display == "block" || div_issue.style.display == "") {
div_issue.style.display = "none";
document.getElementById("BtnToggleIssue").innerHTML = "Show Issues";
document.getElementById("BtnToggleIssue").className = document.getElementById("BtnToggleIssue").className + " buttonActive";
} else {
div_issue.style = 'display: block; flex-direction: column; height: 200px;' +
'overflow:auto;';
document.getElementById("BtnToggleIssue").innerHTML = "Hide Issues";
document.getElementById("BtnToggleIssue").className = document.getElementById("BtnToggleIssue").className.replace("buttonActive", "");
}
}


function searchItem() {
var input = document.getElementById('search').value
input = input.toLowerCase();

var divs = document.getElementsByClassName('item-name');
for (i = 0; i < divs.length; i++) {
var title = divs[i].parentNode.getAttribute("title");
// get requirement name: 2nd part when we cut the long string with /svg
var reqname = divs[i].innerHTML.toLowerCase().split("</svg>").pop();
reqname = reqname.split(" ").pop();
if (reqname.includes(input)) {
// the search pattern has been found, if this elem has the title "hidden-not-matching", put it back to diplayed
if (title) {
if (title.startsWith("hidden-not-matching")) {
divs[i].parentNode.style.display = "block";
}
}
divs[i].parentNode.setAttribute("title", "matching-" + input)
} else {
// not maching, we hide
divs[i].parentNode.setAttribute("title", "hidden-not-matching")
divs[i].parentNode.style.display = "none";
}
}
}
55 changes: 52 additions & 3 deletions lobster/tools/core/html_report/html_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
# You should have received a copy of the GNU Affero General Public
# License along with this program. If not, see
# <https://www.gnu.org/licenses/>.

import os.path
import argparse
import html
Expand Down Expand Up @@ -374,8 +373,43 @@ def write_html(fd, report, dot, high_contrast):
print("> please install Graphviz (https://graphviz.org)")
doc.add_line('</div>')

### Filtering
doc.add_heading(2, "Filtering", "filtering-options")
doc.add_heading(3, "Item Filters")
doc.add_line('<div id = "btnFilterItem">')
doc.add_line('<button class="button buttonAll buttonActive" '
'onclick="buttonFilter(\'all\')"> Show All </button>')

doc.add_line('<button class ="button buttonOK" '
'onclick="buttonFilter(\'ok\')" > OK </button>')

doc.add_line('<button class ="button buttonMissing" '
'onclick="buttonFilter(\'missing\')" > Missing </button>')

doc.add_line('<button class ="button buttonPartial" '
'onclick="buttonFilter(\'partial\')" > Partial </button>')

doc.add_line('<button class ="button buttonJustified" '
'onclick="buttonFilter(\'justified\')" > Justified </button>')

doc.add_line('<button class ="button buttonWarning" '
'onclick="buttonFilter(\'warning\')" > Warning </button>')
doc.add_line("</div>")

doc.add_heading(3, "Show Issues")
doc.add_line('<div id = "ContainerBtnToggleIssue">')
doc.add_line('<button class ="button buttonBlue" id="BtnToggleIssue" '
'onclick="ToggleIssues()"> Show Issues </button>')
doc.add_line('</div>')

doc.add_heading(3, "Search", "search")
doc.add_line('<input type="text" id="search" placeholder="Search..." '
'onkeyup="searchItem()">')
doc.add_line('<div id="search-sec-id"')

### Issues
doc.add_heading(2, "Issues", "issues")
doc.add_line('<div id="issues-section" style="display:none">')
has_issues = False
for item in sorted(report.items.values(),
key = lambda x: x.location.sorting_key()):
Expand All @@ -385,13 +419,15 @@ def write_html(fd, report, dot, high_contrast):
if not has_issues:
has_issues = True
doc.add_line("<ul>")
doc.add_line("<li>%s: %s</li>" %
(xref_item(item),
doc.add_line("<li class=\"issue issue-%s\">%s: %s</li>" %
(item.tracing_status.name.lower(),
xref_item(item),
message))
if has_issues:
doc.add_line("</ul>")
else:
doc.add_line("<div>No traceability issues found.</div>")
doc.add_line("</div>")

### Report
file_heading = None
Expand Down Expand Up @@ -447,6 +483,19 @@ def write_html(fd, report, dot, high_contrast):
write_item_box_end(doc)
else:
doc.add_line("No items recorded at this level.")
# Closing tag for id #search-sec-id
doc.add_line("</div>")

# Add the css from assets
dir_path = os.path.dirname(os.path.abspath(__file__))
file_path = dir_path + "/assets/html_report.css"
doc.css_files.append(file_path)

# Add javascript from assets/html_report.js file
dir_path = os.path.dirname(os.path.abspath(__file__))
file_path = dir_path + "/assets/html_report.js"
with open(file_path, "r", encoding="UTF-8") as scripts:
doc.scripts.append("".join(scripts.readlines()))

### STM
# doc.add_heading(2, "Software traceability matrix", "matrix")
Expand Down
3 changes: 3 additions & 0 deletions packages/lobster-core/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@
"lobster.tools.core.html_report",
"lobster.tools.core.online_report",
"lobster.tools.core.report"],
package_data={
"lobster.tools.core.html_report":["assets/*"]
},
install_requires=[],
python_requires=">=3.7, <4",
classifiers=[
Expand Down
Loading

0 comments on commit e2439ad

Please sign in to comment.