Files
geMoldInsight/venv/lib/python3.11/site-packages/vtkmodules/web/vtkjs_helper.py
T

284 lines
11 KiB
Python
Raw Normal View History

2026-02-12 23:22:11 +08:00
import base64
import json
import re
import os
import shutil
import sys
import zipfile
try:
import zlib
compression = zipfile.ZIP_DEFLATED
except:
compression = zipfile.ZIP_STORED
# -----------------------------------------------------------------------------
def convertDirectoryToZipFile(directoryPath):
if os.path.isfile(directoryPath):
return
zipFilePath = "%s.zip" % directoryPath
zf = zipfile.ZipFile(zipFilePath, mode="w")
try:
for dirName, subdirList, fileList in os.walk(directoryPath):
for fname in fileList:
fullPath = os.path.join(dirName, fname)
relPath = "%s" % (os.path.relpath(fullPath, directoryPath))
zf.write(fullPath, arcname=relPath, compress_type=compression)
finally:
zf.close()
shutil.rmtree(directoryPath)
shutil.move(zipFilePath, directoryPath)
# -----------------------------------------------------------------------------
def addDataToViewer(dataPath, srcHtmlPath, disableGirder=False):
if os.path.isfile(dataPath) and os.path.exists(srcHtmlPath):
dstDir = os.path.dirname(dataPath)
dstHtmlPath = os.path.join(dstDir, "%s.html" % os.path.basename(dataPath)[:-6])
# Extract data as base64
with open(dataPath, "rb") as data:
dataContent = data.read()
base64Content = base64.b64encode(dataContent)
base64Content = base64Content.decode().replace("\n", "")
# Create new output file
with open(srcHtmlPath, mode="r", encoding="utf-8") as srcHtml:
with open(dstHtmlPath, mode="w", encoding="utf-8") as dstHtml:
for line in srcHtml:
if disableGirder and "</title>" in line:
dstHtml.write(
"""
<script>
// Force reloading the page if we want to disable girder before anything else.
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('noGirder') != 'true') {
urlParams.set('noGirder', 'true');
window.location.search = urlParams;
}
</script>
"""
)
if "</body>" in line:
dstHtml.write("<script>\n")
dstHtml.write('var contentToLoad = "%s";\n\n' % base64Content)
dstHtml.write(
'Glance.importBase64Dataset("%s" , contentToLoad, glanceInstance.proxyManager);\n'
% os.path.basename(dataPath)
)
dstHtml.write("glanceInstance.showApp();\n")
dstHtml.write("</script>\n")
dstHtml.write(line)
# -----------------------------------------------------------------------------
def numericSorted(l):
"""Numerically sort a list of strings."""
# pattern to split name into numeric and non-numeric parts
splitter_pattern = re.compile('([0-9]+|[^0-9]+)')
def keyfunc(name):
"""Sorting key for numeric sorting."""
split_name = re.findall(splitter_pattern, name)
# one-liner to convert numeric parts into integers
split_name = list(map(lambda x: int(x) if x.isdigit() else x, split_name))
# ensure that list begins with a string to avoid string<->int compare
if split_name and isinstance(split_name[0], int):
split_name.insert(0, '')
return split_name
# return the numerically sorted list
return sorted(l, key=keyfunc)
# -----------------------------------------------------------------------------
def zipAllTimeSteps(directoryPath):
if os.path.isfile(directoryPath):
return
class UrlCounterDict(dict):
Counter = 0
def GetUrlName(self, name):
if name not in self.keys():
self[name] = str(objNameToUrls.Counter)
self.Counter = self.Counter + 1
return self[name]
def InitIndex(sourcePath, destObj):
with open(sourcePath, "r") as sourceFile:
sourceData = sourceFile.read()
sourceObj = json.loads(sourceData)
for key in sourceObj:
destObj[key] = sourceObj[key]
# remove vtkHttpDataSetReader information
for obj in destObj["scene"]:
obj.pop(obj["type"])
obj.pop("type")
def getUrlToNameDictionary(indexObj):
urls = {}
for obj in indexObj["scene"]:
urls[obj[obj["type"]]["url"]] = obj["name"]
return urls
def addDirectoryToZip(
dirname, zipobj, storedData, rootIdx, timeStep, objNameToUrls
):
# Update root index.json file from index.json of this timestep
with open(os.path.join(dirname, "index.json"), "r") as currentIdxFile:
currentIdx = json.loads(currentIdxFile.read())
urlToName = getUrlToNameDictionary(currentIdx)
rootTimeStepSection = rootIdx["animation"]["timeSteps"][timeStep]
for key in currentIdx:
if key == "scene" or key == "version":
continue
rootTimeStepSection[key] = currentIdx[key]
for obj in currentIdx["scene"]:
objName = obj["name"]
rootTimeStepSection[objName] = {}
rootTimeStepSection[objName]["actor"] = obj["actor"]
rootTimeStepSection[objName]["actorRotation"] = obj["actorRotation"]
rootTimeStepSection[objName]["mapper"] = obj["mapper"]
rootTimeStepSection[objName]["property"] = obj["property"]
# For every object in the current timestep
for folder in sorted(os.listdir(dirname)):
currentItem = os.path.join(dirname, folder)
if os.path.isdir(currentItem) is False:
continue
# Write all data array of the current timestep in the archive
for filename in os.listdir(os.path.join(currentItem, "data")):
fullpath = os.path.join(currentItem, "data", filename)
if os.path.isfile(fullpath) and filename not in storedData:
storedData.add(filename)
relPath = os.path.join("data", filename)
zipobj.write(fullpath, arcname=relPath, compress_type=compression)
# Write the index.json containing pointers to these data arrays
# while replacing every basepath as '../../data'
objIndexFilePath = os.path.join(dirname, folder, "index.json")
with open(objIndexFilePath, "r") as objIndexFile:
objIndexObjData = json.loads(objIndexFile.read())
for elm in objIndexObjData.keys():
try:
if "ref" in objIndexObjData[elm].keys():
objIndexObjData[elm]["ref"]["basepath"] = "../../data"
if "arrays" in objIndexObjData[elm].keys():
for array in objIndexObjData[elm]["arrays"]:
array["data"]["ref"]["basepath"] = "../../data"
except AttributeError:
continue
currentObjName = urlToName[folder]
objIndexRelPath = os.path.join(
objNameToUrls.GetUrlName(currentObjName), str(timeStep), "index.json"
)
zipobj.writestr(
objIndexRelPath,
json.dumps(objIndexObjData, indent=2),
compress_type=compression,
)
# ---
zipFilePath = "%s.zip" % directoryPath
currentDirectory = os.path.abspath(os.path.join(directoryPath, os.pardir))
rootIndexPath = os.path.join(currentDirectory, "index.json")
rootIndexFile = open(rootIndexPath, "r")
rootIndexObj = json.loads(rootIndexFile.read())
zf = zipfile.ZipFile(zipFilePath, mode="w")
try:
# We copy the scene from an index of a specific timestep to the root index
# Scenes should all have the same objects so only do it for the first one
isSceneInitialized = False
# currentlyAddedData set stores hashes of every data we already added to the
# vtkjs archive to prevent data duplication
currentlyAddedData = set()
# Regex that folders storing timestep data from paraview should follow
reg = re.compile(r"^" + os.path.basename(directoryPath) + r"\.[0-9]+$")
# We assume an object will not be deleted from a timestep to another so we create a generic index.json for each object
genericIndexObj = {}
genericIndexObj["series"] = []
timeStep = 0
for item in rootIndexObj["animation"]["timeSteps"]:
genericIndexObj["series"].append({})
genericIndexObj["series"][timeStep]["url"] = str(timeStep)
genericIndexObj["series"][timeStep]["timeStep"] = float(item["time"])
timeStep = timeStep + 1
# Keep track of the url for every object
objNameToUrls = UrlCounterDict()
timeStep = 0
# zip all timestep directories
for folder in numericSorted(os.listdir(currentDirectory)):
fullPath = os.path.join(currentDirectory, folder)
if os.path.isdir(fullPath) and reg.match(folder):
if not isSceneInitialized:
InitIndex(os.path.join(fullPath, "index.json"), rootIndexObj)
isSceneInitialized = True
addDirectoryToZip(
fullPath,
zf,
currentlyAddedData,
rootIndexObj,
timeStep,
objNameToUrls,
)
shutil.rmtree(fullPath)
timeStep = timeStep + 1
# Write every index.json holding time information for each object
for name in objNameToUrls:
zf.writestr(
os.path.join(objNameToUrls[name], "index.json"),
json.dumps(genericIndexObj, indent=2),
compress_type=compression,
)
# Update root index.json urls and write it in the archive
for obj in rootIndexObj["scene"]:
obj["id"] = obj["name"]
obj["type"] = "vtkHttpDataSetSeriesReader"
obj["vtkHttpDataSetSeriesReader"] = {}
obj["vtkHttpDataSetSeriesReader"]["url"] = objNameToUrls[obj["name"]]
zf.writestr(
"index.json", json.dumps(rootIndexObj, indent=2), compress_type=compression
)
os.remove(rootIndexPath)
finally:
zf.close()
shutil.move(zipFilePath, directoryPath)
# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------
if __name__ == "__main__":
if len(sys.argv) < 2:
print(
"Usage: directoryToFile /path/to/directory.vtkjs [/path/to/ParaViewGlance.html]"
)
else:
fileName = sys.argv[1]
convertDirectoryToZipFile(fileName)
if len(sys.argv) == 3:
addDataToViewer(fileName, sys.argv[2])