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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ coverage.xml
*.py,cover
.hypothesis/
.pytest_cache/
UnitTests/testResults/

# Translations
*.mo
Expand Down
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,7 @@
"MD025": {
"front_matter_title": ""
}
}
},
"esbonio.server.enabled": true,
"restructuredtext.languageServer.disabled": true
}
Binary file added RFEM/Reports/favicon32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
260 changes: 260 additions & 0 deletions RFEM/Reports/html.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
#################################
## DEFINITION:
## This feature allows the user to create an output HTML file with the results.
## The results are in the same form as result tables in RFEM.
## The output file is written in HTML and consists of embedded tabular data.
## It will also include dropdown menu with all tables/files.
## Result files are language dependent, so parsing based on strings is impossible.
#################################

from os import listdir, walk, path, getcwd, system
from RFEM.initModel import ExportResultTablesToCsv
from re import findall, match
from shutil import copy

columns = 0

def __HTMLheadAndHeader(modelName, fileNames):
output = ['<head>',
'<script src="htmlScript.js"></script>',
'<meta http-equiv="Content-Type" content="text/html; charset=utf-8">',
'<title>Results</title>',
'<link rel="icon" sizes="32x32" href="favicon32.png">',
'</head>',
'<header>',
'<link rel="stylesheet" href="htmlStyles.css">',
'<div class="head">',
'<img alt="Dlubal logo" width="77" height="77" src="favicon32.png" align="right">',
'<h2>Result report</h2>',
f'<p>Model: {modelName}</p>',
'</div>',
'<div class="head2">',
'<input type="text" list="filter" id="fl" onfocus="this.value=\'\'" onchange="showPanel()" placeholder="Find result table">',
'<datalist id="filter">']
for f in fileNames:
output.append(f'<option>{f}</option>')
output += ['</datalist>',
'<button class="button" onclick="switchButtonDown()">&#9664;</button>',
'<button class="button" onclick="switchButtonUp()">&#9654;</button>',
'</div>',
'</header>',
'',
'<div class="tabContainer">']
for f in fileNames:
output.append(f'<iframe src="{f}.html" loading="lazy" frameBorder="0"></iframe>')
return output

def __HTMLfooter():
return ['<script>atTheEnd()</script>',
'</div>']

def __isEmpty(dividedLine):
# returns True if all strings are empty
return not any (dividedLine)

def __numberOfPOsitiveItems(dividedLine):
# return number of list items which are not empty
counter = 0
for i in dividedLine:
if i:
counter += 1

return counter

def __isWords(word):
isWords = False
dividedWords = word.split(' ')

for oneWord in dividedWords:
if oneWord and bool(match(r"[A-Z]", oneWord[0])) and oneWord.isalpha():
isWords = True
else:
break

return isWords

def __tableHeader(dividedLine_1, dividedLine_2):
# define htnl of header lines, rowspan and colspan
# parameters are lists of strings
global columns
columns = max(len(dividedLine_1), len(dividedLine_2))
output = ['<div class="tabPanel">',
'<link rel="stylesheet" href="htmlStyles.css">',
'<table class="responsive-table">',
'<thead>',
'<tr>']
if __isEmpty(dividedLine_1):
__emptyLine()
else:
for i in range(columns):
if dividedLine_1[i] and not dividedLine_2[i]:
output.append(f'<th rowspan="2">{dividedLine_1[i]}</th>')
elif not dividedLine_1[i] and not dividedLine_2[i]:
output.append('<th></th>')
elif dividedLine_1[i] and dividedLine_2[i]:
colspan = 1
for ii in range(i+1, columns):
if ii == columns-1 and not dividedLine_1[ii] and __isWords(dividedLine_2[ii]):
break
if not dividedLine_1[ii] and dividedLine_2[ii]:
colspan += 1
else:
break
output.append(f'<th colspan="{colspan}">{dividedLine_1[i]}</th>')
i += colspan-1
elif i==columns-1 and not dividedLine_1[i] and dividedLine_2[i]:
output.append('<th></th>')
output += ['</tr>', '<tr>']

for y in range(columns):
if not dividedLine_1[y] and not dividedLine_2[y]:
output.append('<th></th>')
elif dividedLine_1[y] and not dividedLine_2[y]:
pass
else:
output.append(f'<th>{dividedLine_2[y]}</th>')

output += ['</tr>', '</thead>']
return output

def __tableSubHeader(dividedLine):
# sub header; one liner; in the body of table
return f'<th align="left" colspan="{columns}">{dividedLine}</th>'

def __emptyLine():
# define html of empty lines
return f'<th colspan="{columns}"></th>'

def __otherLines(dividedLine):
# define html of other lines
colspan = 1
output = []
for c in range(columns):
if colspan == 1:
sCheckIfDigit = dividedLine[c].replace('.','',1)
sCheckIfDigit = sCheckIfDigit.replace(',','',1)
sCheckIfDigit = sCheckIfDigit.replace('-','',1)
sCheckIfDigit = sCheckIfDigit.replace('+','',1)
align = 'left'
tag = 'td'
if c == 0:
align = 'center'
elif sCheckIfDigit.isdigit():
align = 'right'
elif bool(match(r"^\D+\.$", dividedLine[c])) or "|" in dividedLine[c]:
for cc in range(c+1, columns-1):
if not dividedLine[cc]:
colspan += 1
else:
break
else:
if '<sub>' in dividedLine[c] or '[' in dividedLine[c]:
tag = 'th'
align = 'center'
output.append(f'<{tag} colspan="{colspan}"align="{align}">{dividedLine[c]}</{tag}>')
else:
colspan -= 1
return output

def __writeToFile(TargetFilePath, output):
# Write into html file
with open(TargetFilePath, "w", encoding="utf-8") as f:
for line in output:
while '_' in line:
beginId = line.find('_')
endBySpace = line[beginId:].find(' ')
endByArrow = line.rfind('<')
if endBySpace == -1:
line = line[:beginId]+'<sub>'+line[beginId+1:endByArrow]+'</sub>'+line[endByArrow:]
else:
endId = min(endBySpace + beginId, endByArrow)
line = line[:beginId]+'<sub>'+line[beginId+1:endId]+'</sub>'+line[endId:]
while '^' in line:
beginId = line.find('^')
endBySpace = line[beginId:].find(' ')
endByArrow = line.rfind('<')
if endBySpace == -1:
line = line[:beginId]+'<sup>'+line[beginId+1:endByArrow]+'</sup>'+line[endByArrow:]
else:
endId = min(endBySpace + beginId, endByArrow)
line = line[:beginId]+'<sup>'+line[beginId+1:endId]+'</sup>'+line[endId:]
f.write(line+'\n')

def ExportResultTablesToHtml(TargetFolderPath: str):
# Create collection of source files
ExportResultTablesToCsv(TargetFolderPath)

modelName = next(walk(TargetFolderPath))[1][0]
dirList = listdir(path.join(TargetFolderPath, modelName))
# Parse CSV file names into LC and CO, analysis type, types of object (nodes, lines, members, surfaces),
# and tabs (such as summary, Global Deformations, or Support Forces)

fileNames = []

dirList.sort()

print('')

for fileName in dirList:
cats = fileName[:-4].split('_')

fileNameCapitalized = ''
for c in cats:
if findall('[0-9]+', c):
fileNameCapitalized += c+' '
else:
fileNameCapitalized += c.capitalize()+' '
fileNameCapitalized = fileNameCapitalized[:-1]
fileNames.append(fileNameCapitalized)

with open(path.join(TargetFolderPath, modelName, fileName), mode='r', encoding='utf-8-sig') as f:
lines = f.readlines()
# 1st header always consists of first 2 lines
line_1 = lines[0].split(';')
line_1[-1] = line_1[-1].rstrip('\n')
line_2 = lines[1].split(';')
line_2[-1] = line_2[-1].rstrip('\n')

output = __tableHeader(line_1, line_2)
output.append('<tbody>')

for line in lines[2:]:
output.append('<tr>')
dividedLine = line.split(';')
dividedLine[-1] = dividedLine[-1].rstrip('\n')

# check if number of columns is always same
#assert columns-len(dividedLine) == 0

if __isEmpty(dividedLine):
# if empty line
output.append(__emptyLine())
elif __numberOfPOsitiveItems(dividedLine) == 1 and dividedLine[0]:
# add empty line, these values doesn't make sence
output.append(__emptyLine())
elif __numberOfPOsitiveItems(dividedLine) == 1 and dividedLine[1]:
# if only one string in the list, it is sub header consisting of only 1 line
output.append(__tableSubHeader(dividedLine[1]))
else:
output += __otherLines(dividedLine)

output += ['</tr>', '</tbody>', '</table>', '</div>']

__writeToFile(path.join(TargetFolderPath, fileNameCapitalized+'.html'), output)

indexOutput = ['<div class="tabContainer">']
indexOutput += __HTMLfooter()
indexOutput = __HTMLheadAndHeader(modelName, fileNames) + indexOutput

# Copy basic files
dirname = path.join(getcwd(), path.dirname(__file__))
copy(path.join(dirname, 'htmlStyles.css'), TargetFolderPath)
copy(path.join(dirname, 'htmlScript.js'), TargetFolderPath)
copy(path.join(dirname, 'favicon32.png'), TargetFolderPath)

with open(path.join(TargetFolderPath,'index.html'), "w", encoding="utf-8") as f:
for line in indexOutput:
f.write(line+'\n')

# Open result html page
system(f"start {path.join(TargetFolderPath, 'index.html')}")
53 changes: 53 additions & 0 deletions RFEM/Reports/htmlScript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
function showPanel(){
var framePanels = document.getElementsByTagName("iframe");
var datalist = document.getElementById('filter');
var searchField = document.getElementById('fl');
var searchFieldTableName = searchField.value;
for (let i = 0; i < datalist.options.length; i++) {
if (searchFieldTableName == datalist.options[i].label){
framePanels[i].style.display="block";
}
else{
framePanels[i].style.display="none";
}
}
searchField.blur();
};
function switchButtonDown(){
var datalist = document.getElementById('filter');
var searchField = document.getElementById('fl');
var searchFieldTableName = searchField.value;
var index = 0;
for (let i = 0; i < datalist.options.length; i++) {
if (searchFieldTableName == datalist.options[i].label){
index = i;
break;
}
}
index = index - 1;
index = Math.max(0, index);
searchField.value = datalist.options[index].label;
showPanel();
};
function switchButtonUp(){
var datalist = document.getElementById('filter');
var searchField = document.getElementById('fl');
var searchFieldTableName = searchField.value;
var index = 0;
for (let i = 0; i < datalist.options.length; i++) {
if (searchFieldTableName == datalist.options[i].label){
index = i;
break;
}
}
index = index + 1
index = Math.min(datalist.options.length-1, index);
searchField.value = datalist.options[index].label;
showPanel();
};
function atTheEnd(){
var datalist = document.getElementById('filter');
var searchField = document.getElementById('fl');
searchField.value = datalist.options[0].label;
showPanel();
};
Loading