Add rendering collect bundle files to report tool
This update adds the collect bundle to report tool. The collect bundle will show after the system info and results section. The layout for collect bundle will be the same as the results section. Test Plan: PASS: Verify the collect bundle section is shown below results section PASS: Verify all collect bundle folders are shown PASS: Verify menus are levelled with '+'/'-' function to show/hide PASS: Verify menus are colored dark green and items are light green PASS: Verify empty folders are showing grey with disabled click PASS: Verify empty files are showing grey with disabled click PASS: Verify '.log', '.conf', '.info', '.json', '.alarm', '.pid', '.list', '.lock', '.txt' files can be directly viewed when opened PASS: Verify handling of files that are not in the above extension PASS: Verify a new window is opened if the file is viewable PASS: Verify the file will be loaded appropriately PASS: Verify a download popup is opened if the file is not viewable PASS: Verify index.html is in a reasonable size PASS: Verify index.html loading time is reasonable PASS: Verify the generated html with css content has no error in console Story: 2010533 Task: 49191 Change-Id: I71c4c6b39ca68464baf09c7d1708348e30989fda Signed-off-by: Lance Xu <lance.xu@windriver.com>
This commit is contained in:
parent
944f847b43
commit
40d4e80404
|
@ -6,17 +6,20 @@
|
|||
#
|
||||
########################################################################
|
||||
#
|
||||
# This file contains the Render function
|
||||
# The Rendering tool visualizes the collect bundle and generates index.html file
|
||||
# This file contains the Render function.
|
||||
# The Rendering tool visualizes the collect bundle and generates
|
||||
# an index.html file
|
||||
#
|
||||
########################################################################
|
||||
|
||||
from datetime import datetime
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
|
||||
def extract_section(log_contents, start_phrase):
|
||||
"""extract the correlated or plugin content of the summary
|
||||
"""Extract the correlated or plugin content of the summary
|
||||
|
||||
Parameters:
|
||||
log_contents (string): content of the log
|
||||
|
@ -32,7 +35,7 @@ def extract_section(log_contents, start_phrase):
|
|||
|
||||
|
||||
def remove_timestamp(text):
|
||||
"""remove timestamp of summary message
|
||||
"""Remove timestamp of summary message
|
||||
|
||||
Parameters:
|
||||
text (string): the summary message
|
||||
|
@ -51,7 +54,7 @@ def remove_timestamp(text):
|
|||
|
||||
|
||||
def remove_emptyinfo(text):
|
||||
""" remove 'INFO' text of summary message
|
||||
"""Remove 'INFO' text of summary message
|
||||
|
||||
Parameters:
|
||||
text (string): the summary message
|
||||
|
@ -66,7 +69,7 @@ def remove_emptyinfo(text):
|
|||
|
||||
|
||||
def process_section(section, title):
|
||||
"""return text with timestamp and INFO: removed
|
||||
"""Return text with timestamp and INFO: removed
|
||||
|
||||
Parameters:
|
||||
section (string): the message of the correlated/plugins section
|
||||
|
@ -79,7 +82,7 @@ def process_section(section, title):
|
|||
|
||||
|
||||
def classify_node(data):
|
||||
"""classify node type in system_info summary
|
||||
"""Classify node type in system_info summary
|
||||
|
||||
Parameters:
|
||||
data (string): the summary of system_info
|
||||
|
@ -92,7 +95,7 @@ def classify_node(data):
|
|||
|
||||
|
||||
def controller_sort(x):
|
||||
"""sort the controller, place the controller-0 first
|
||||
"""Sort the controller, place the controller-0 first
|
||||
|
||||
Parameters:
|
||||
x (list): list of controller info
|
||||
|
@ -101,7 +104,7 @@ def controller_sort(x):
|
|||
|
||||
|
||||
def html_css():
|
||||
"""static css code of the rendering tool
|
||||
"""Static css code of the rendering tool
|
||||
|
||||
iframe, textarea: the content panel showing information
|
||||
#show-worker: the show more worker button
|
||||
|
@ -193,7 +196,7 @@ def html_css():
|
|||
margin-top: 10px;
|
||||
}}
|
||||
|
||||
.content-item, .content-itemtwo {{
|
||||
.content-item, .content-itemtwo, .content-itemthree {{
|
||||
display: none;
|
||||
}}
|
||||
|
||||
|
@ -218,6 +221,31 @@ def html_css():
|
|||
color: #2F4F4F;
|
||||
}}
|
||||
|
||||
.caret {{
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
color: #00857e;
|
||||
font-weight: bold;
|
||||
}}
|
||||
|
||||
.caret::before {{
|
||||
content: '+';
|
||||
color: #2F4F4F;
|
||||
margin-right: 6px;
|
||||
}}
|
||||
|
||||
.caret-down::before {{
|
||||
color: #2F4F4F;
|
||||
content: '-';
|
||||
}}
|
||||
|
||||
.text-color {{
|
||||
color: #00ada4;
|
||||
}}
|
||||
|
||||
.nested {{ display: none; }}
|
||||
.active {{ display: block; }}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
"""
|
||||
|
@ -225,7 +253,7 @@ def html_css():
|
|||
|
||||
|
||||
def html_script():
|
||||
"""static script code
|
||||
"""Static script code
|
||||
|
||||
Functions:
|
||||
toggleContent: show content in System Info section
|
||||
|
@ -234,6 +262,7 @@ def html_script():
|
|||
showContentStorage: display content of selected storage item
|
||||
showContentWorker: display content of selected worker item
|
||||
showContentTwo: display content of result section
|
||||
toggleTree: show the collect bundle
|
||||
"""
|
||||
html_content_script = """
|
||||
<script>
|
||||
|
@ -323,6 +352,20 @@ def html_script():
|
|||
}}
|
||||
}}
|
||||
|
||||
function showContentThree(event, contentId) {{
|
||||
event.preventDefault();
|
||||
|
||||
const contentItems = document.querySelectorAll('.content-itemthree');
|
||||
contentItems.forEach(item => {{
|
||||
item.style.display = 'none';
|
||||
}});
|
||||
|
||||
const selectedContent = document.getElementById(contentId);
|
||||
if (selectedContent) {{
|
||||
selectedContent.style.display = 'block';
|
||||
}}
|
||||
}}
|
||||
|
||||
function toggleContent(option, menuItem) {{
|
||||
const contentDiv = document.getElementById(option);
|
||||
const icon = menuItem.querySelector('.icon');
|
||||
|
@ -395,6 +438,48 @@ def html_script():
|
|||
}}
|
||||
}}
|
||||
|
||||
function toggleTree() {{
|
||||
var toggler = document.getElementsByClassName('caret');
|
||||
for (var i = 0; i < toggler.length; i++) {{
|
||||
var nested = toggler[i].parentElement.querySelector('.nested');
|
||||
var isEmpty = nested.querySelectorAll('li').length === 0;
|
||||
|
||||
|
||||
if (!isEmpty) {{
|
||||
toggler[i].addEventListener('click', function() {{
|
||||
this.parentElement.querySelector('.nested').classList.toggle('active');
|
||||
this.classList.toggle('caret-down');
|
||||
this.parentElement.classList.toggle('text-color');
|
||||
}});
|
||||
}} else {{
|
||||
toggler[i].style.color = '#808080';
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
|
||||
// Call the function when the page loads to initialize the tree behavior
|
||||
toggleTree();
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {{
|
||||
var hash = window.location.hash;
|
||||
var sectionA = document.getElementById('content-one');
|
||||
var sectionB = document.getElementById('content-two');
|
||||
var collectSections = document.querySelectorAll('[id*="collect"]');
|
||||
|
||||
collectSections.forEach(function(section) {{
|
||||
section.classList.add('hidden');
|
||||
}});
|
||||
|
||||
if (hash.includes("collect")) {{
|
||||
sectionA.classList.add('hidden');
|
||||
sectionB.classList.add('hidden');
|
||||
var matchingElement = document.querySelector(hash);
|
||||
if (matchingElement) {{
|
||||
matchingElement.classList.remove('hidden');
|
||||
}}
|
||||
}}
|
||||
}});
|
||||
|
||||
</script>
|
||||
</html>
|
||||
"""
|
||||
|
@ -402,7 +487,7 @@ def html_script():
|
|||
|
||||
|
||||
def html_info(sys_section):
|
||||
"""system info part generation
|
||||
"""System info part generation
|
||||
reads from plugin/system_info and show by different types
|
||||
order: controller, storage(if there exists), worker(if there exists)
|
||||
|
||||
|
@ -440,7 +525,7 @@ def html_info(sys_section):
|
|||
|
||||
html_content_one += """
|
||||
<body>
|
||||
<div class="container-menu">
|
||||
<div id="content-one" class="container-menu">
|
||||
<div class="menu">
|
||||
<ul>
|
||||
<a href="#" class="menuTitle" onclick="location.reload()">System Information</a>
|
||||
|
@ -505,7 +590,7 @@ def html_info(sys_section):
|
|||
|
||||
|
||||
def html_result(log_contents, output_dir):
|
||||
"""result part generation in the menu-content style
|
||||
"""Result part generation in the menu-content style
|
||||
generates correlated results, plugin results, and the items under them
|
||||
subitems for plugins and correlated results under separate menus
|
||||
|
||||
|
@ -540,7 +625,7 @@ def html_result(log_contents, output_dir):
|
|||
html_content_two = ""
|
||||
|
||||
html_content_two += """
|
||||
<div class="container-menu">
|
||||
<div id="content-two" class="container-menu">
|
||||
<div class="menu">
|
||||
<ul>
|
||||
<li>
|
||||
|
@ -562,7 +647,7 @@ def html_result(log_contents, output_dir):
|
|||
for item in plugin_items:
|
||||
html_content_two += f'<li><a href="#" class="toggle-sign" onclick="showContentTwo(event, \'{item["id"]}\')">{item["name"]}</a></li>'
|
||||
|
||||
html_content_two += """</ul></li></ul></div>"""
|
||||
html_content_two += "</ul></li><hr>" + generate_collect() + "</ul></div>"
|
||||
html_content_two += """<div class="content">"""
|
||||
|
||||
for item in correlated_items:
|
||||
|
@ -576,12 +661,84 @@ def html_result(log_contents, output_dir):
|
|||
html_content_two += """
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
"""
|
||||
|
||||
return html_content_two
|
||||
|
||||
|
||||
def generate_collect():
|
||||
os.chdir('../../')
|
||||
current_directory = Path('.')
|
||||
finalstr = "<li>"
|
||||
for item in current_directory.iterdir():
|
||||
if item.is_dir() and item.name != "report_analysis":
|
||||
temp_item = re.sub(r'[^a-zA-Z0-9]', '', item.name)
|
||||
finalstr += f'<a href="#collect{temp_item}" target="_blank" class="menuTitle">{item.name}</a>'
|
||||
finalstr += "</li>"
|
||||
return finalstr
|
||||
|
||||
|
||||
def html_collect():
|
||||
"""Collect bundle code generation
|
||||
|
||||
Calls a helper function to to generate the collect bundle
|
||||
"""
|
||||
current_directory = Path('.')
|
||||
tree_html = ""
|
||||
content_html = "<div class='content'>"
|
||||
target_dir = current_directory.resolve()
|
||||
newtree_html, newcontent_html = generate_directory_tree(current_directory, target_dir, 0)
|
||||
tree_html += newtree_html
|
||||
content_html += newcontent_html
|
||||
content_html += "</div>"
|
||||
html_content_three = """<div class="container-menu"><div class="menu" style="max-height: 90vh">
|
||||
""" + tree_html + "</div>" + content_html + "</div></body>"
|
||||
return html_content_three
|
||||
|
||||
|
||||
def generate_directory_tree(directory_path, target_dir, is_top_level):
|
||||
"""Helper function for Collect bundle generation
|
||||
|
||||
Parameters:
|
||||
directory_path(Path): the path of the directory in each call
|
||||
target_dir(string): the path of the file/folder
|
||||
is_top_level(bool): if the level is the top level of the collect bundle
|
||||
"""
|
||||
directory_name = directory_path.name
|
||||
tree_html = ""
|
||||
content_html = ""
|
||||
approved_list = ['.log', '.conf', '.info', '.json', '.alarm', '.pid', '.list', '.lock', '.txt']
|
||||
if is_top_level == 1:
|
||||
temp_name = re.sub(r'[^a-zA-Z0-9]', '', directory_name)
|
||||
tree_html = f'<li id=collect{temp_name}><span class="caret">{directory_name}</span><ul class="nested">'
|
||||
if is_top_level > 1:
|
||||
tree_html = f'<li><span class="caret">{directory_name}</span><ul class="nested">'
|
||||
|
||||
for item in directory_path.iterdir():
|
||||
try:
|
||||
if item.is_dir() and item.name != "report_analysis":
|
||||
nested_tree_html, nested_content_html = generate_directory_tree(item, target_dir, is_top_level + 1)
|
||||
tree_html += nested_tree_html
|
||||
content_html += nested_content_html
|
||||
elif item.is_file():
|
||||
if os.path.getsize(item) == 0:
|
||||
tree_html += f'<li><a style="color: #808080">{item}</a></li>'
|
||||
else:
|
||||
if item.name.endswith(tuple(approved_list)):
|
||||
tree_html += f'<li><a href="#" class="toggle-sign" onclick="showContentThree(event, \'{item.name}\')">{item.name}</a></li>'
|
||||
content_html += f'<div class="content-itemthree" id="{item.name}"><h2>{item.name}</h2><iframe src="{target_dir}/{item}"></iframe></div>'
|
||||
else:
|
||||
if not item.name.endswith(".tgz"):
|
||||
tree_html += f'<li><a href="../{item}" target="_blank">{item}</a></li>'
|
||||
# if it's permission error, just skip reading the file or folder
|
||||
except PermissionError as e:
|
||||
continue
|
||||
if is_top_level:
|
||||
tree_html += '</ul></li>'
|
||||
|
||||
return tree_html, content_html
|
||||
|
||||
|
||||
# main
|
||||
def main(input_dir, output_dir):
|
||||
reportlog_path = os.path.join(output_dir, 'report.log')
|
||||
|
@ -596,8 +753,9 @@ def main(input_dir, output_dir):
|
|||
html_file = os.path.abspath(os.path.join(output_dir, 'index.html'))
|
||||
|
||||
sys_section = sysinfo_contents.strip().split("\n\n")
|
||||
html_content = html_css() + html_info(sys_section) + html_result(log_contents, output_dir) + html_script()
|
||||
html_content = html_css() + html_info(sys_section) + html_result(log_contents, output_dir)
|
||||
html_content = html_content.format()
|
||||
html_content += html_collect() + html_script()
|
||||
|
||||
# write the HTML content to file
|
||||
with open(html_file, "w") as file:
|
||||
|
|
Loading…
Reference in New Issue