diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 29ba9e0..c8c9157 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -28,6 +28,8 @@ jobs: run: | pip install --upgrade pip pip install hatch + - name: Install Graphviz + run: sudo apt-get install graphviz graphviz-dev - name: Install package run: | pip install . @@ -51,6 +53,8 @@ jobs: run: | pip install --upgrade pip pip install hatch + - name: Install Graphviz + run: sudo apt-get install graphviz graphviz-dev - name: Install package run: | pip install . @@ -71,6 +75,8 @@ jobs: run: | pip install --upgrade pip pip install hatch + - name: Install Graphviz + run: sudo apt-get install graphviz graphviz-dev - name: Install package run: | pip install . diff --git a/Parser_PoC/parse_preview.py b/Parser_PoC/parse_preview.py deleted file mode 100755 index 7f2683f..0000000 --- a/Parser_PoC/parse_preview.py +++ /dev/null @@ -1,368 +0,0 @@ -#!/usr/bin/env python3 - -import pygraphviz as pgv -from datetime import datetime -from isoduration import parse_duration -import yaml -from lxml import etree -import webbrowser -from pathlib import Path -import argparse - - -# ======= -# Classes -# ======= -class WcTask(): - - gv_kw = {'shape': 'box', - 'style': 'filled', - 'fontname': 'Fira Sans', - 'fillcolor': '#ffd8dc', - 'fontcolor': '#330005', - 'color': '#4F161D', - 'fontsize': 14, - 'penwidth': 2} - - def __init__(self, name, run_spec): - self.name = name - self.run_spec = run_spec - self.input = [] - self.output = [] - self.depends = [] - - -class WcData(): - - gv_kw = {'shape': 'ellipse', - 'style': 'filled', - 'fontname': 'Fira Sans', - 'fontcolor': '#001633', - 'fillcolor': '#d8e9ff', - 'color': '#001633', - 'fontsize': 14, - 'penwidth': 2} - - def __init__(self, name, run_spec): - self.name = name - rel_path = run_spec.get('rel_path') - abs_path = run_spec.get('abs_path') - if rel_path is None and abs_path is None: - raise ValueError(f"Error wheen trying to define data node {name}. " - f"Either rel_path or abs_path must be specified") - if rel_path and abs_path: - raise ValueError(f"Error wheen trying to define data node {name}. " - f"Only one of rel_path or abs_path must be specified") - self.abs_path = abs_path - self.rel_apth = rel_path - self.run_spec = run_spec - - def is_concrete(self): - return self.abs_path is not None - - -class WcCycle(): - - def __init__(self, name, spec, graph): - self.name = name - self.graph = graph - self.parse_spec(spec) - - def parse_spec(self, spec): - if (d:=spec.get('start_date')) is None: - self.start_date = self.graph.start_date - else: - self.start_date = datetime.fromisoformat(d) - - if (d:=spec.get('end_date')) is None: - self.end_date = self.graph.end_date - else: - self.end_date = datetime.fromisoformat(d) - - if (p:=spec.get('period')) is None: - self.period = None - else: - self.period = parse_duration(p) - - self.tasks = spec.get('tasks', []) - self.data = spec.get('data', []) - - -class WcGraph(): - - edge_gv_kw = {'color': '#77767B', - 'penwidth': 1.5} - cluster_kw = {'bgcolor': '#F6F5F4', - 'color': None, - 'fontsize': 16} - - def __init__(self, start_date, end_date, cycles, tasks, data, - *args, **kwargs): - - self.start_date = datetime.fromisoformat(start_date) - self.end_date = datetime.fromisoformat(end_date) - self.tasks_specs = tasks - self.data_specs = data - self.cycles = cycles - self.graph = pgv.AGraph(*args, fontname='Fira Sans', newrank=True, **kwargs) - - # Needed for date indexing - self.data = {k: {} for k in data.keys()} - self.tasks = {k: {} for k in tasks.keys()} - - @property - def cycles(self): - return self._cycles - @cycles.setter - def cycles(self, specs): - self._cycles = [] - for name, spec in specs.items(): - self._cycles.append(WcCycle(name, spec, self)) - - def add_task(self, name): - if name not in self.tasks_specs: - raise ValueError(f'{name} not declared as task') - if self.cycling_date not in self.tasks[name]: - task_node = WcTask(name, self.tasks_specs[name]) - self.tasks[name][self.cycling_date] = task_node - self.graph.add_node(task_node, label=name, - tooltip=yaml.dump(task_node.run_spec), - **task_node.gv_kw) - - def add_data(self, name): - if name not in self.data_specs: - raise ValueError(f'{name} not declared as data') - data_node = WcData(name, self.data_specs[name]) - if data_node.is_concrete(): - if not self.data[name]: - self.data[name] = data_node - self.graph.add_node(data_node, label=name, - tooltip=yaml.dump(data_node.run_spec), - **data_node.gv_kw) - elif self.cycling_date not in self.data[name]: - self.data[name][self.cycling_date] = data_node - self.graph.add_node(data_node, label=name, - tooltip=yaml.dump(data_node.run_spec), - **data_node.gv_kw) - - def get_task(self, spec): - if isinstance(spec, dict): - name, graph_spec = next(iter(spec.items())) - else: - name, graph_spec = spec, None - if graph_spec is None: - return self.tasks[name][self.cycling_date] - else: - date = graph_spec.get('date') - lag = graph_spec.get('lag') - if date and lag: - raise ValueError('graph_spec cannot contain both date and lag') - if not date and not lag: - raise ValueError('graph_spec must contain eiher date or lag') - if date: - return self.task['name'][date] - else: - dates = [*self.tasks[name].keys()] - if isinstance(lag, list): - nodes = [] - for lg in lag: - date = self.cycling_date + parse_duration(lg) - if date <= dates[-1] and date >= dates[0]: - nodes.append(self.tasks[name][date]) - return nodes - else: - date = self.cycling_date + parse_duration(lag) - if date <= dates[-1] and date >= dates[0]: - return self.tasks[name][date] - - def get_data(self, spec): - if isinstance(spec, dict): - name, graph_spec = next(iter(spec.items())) - else: - name, graph_spec = spec, None - if 'abs_path' in self.data_specs[name]: - if graph_spec: - raise ValueError("graph_spec cannot be provided to access data with abs_path") - return self.data[name] - elif graph_spec is None: - return self.data[name][self.cycling_date] - else: - date = graph_spec.get('date') - lag = graph_spec.get('lag') - if date and lag: - raise ValueError('graph_spec cannot contain both date and lag') - if not date and not lag: - raise ValueError('graph_spec must contain eiher date or lag') - if date: - return self.data[name][datetime.fromisoformat(date)] - else: - dates = [*self.data[name].keys()] - if isinstance(lag, list): - nodes = [] - for lg in lag: - date = self.cycling_date + parse_duration(lg) - if date <= dates[-1] and date >= dates[0]: - nodes.append(self.data[name][date]) - return nodes - else: - date = self.cycling_date + parse_duration(lag) - if date <= dates[-1] and date >= dates[0]: - return self.data[name][date] - - def add_edge(self, u, v): - if isinstance(u, list) and isinstance(v, list): - raise ValueError("Only origin or target of edge can be a list") - if isinstance(u, list): - for node in u: - self.graph.add_edge(node, v, **self.edge_gv_kw) - elif isinstance(v, list): - for node in v: - self.graph.add_edge(u, node, **self.edge_gv_kw) - else: - self.graph.add_edge(u, v, **self.edge_gv_kw) - - def prepare(self): - # Add concrete data nodes - for name, spec in self.data_specs.items(): - if 'abs_path' in spec: - self.add_data(name) - # Define all the other task and data nodes - for cycle in self.cycles: - self._add_nodes_from_cycle(cycle) - # Draw edges between nodes - for cycle in self.cycles: - self._add_edges_from_cycle(cycle) - - def _add_nodes_from_cycle(self, cycle): - self.cycling_date = cycle.start_date - p = cycle.period - do_parsing = True - while do_parsing: - # Tasks nodes with correponding output but not input - for name, task_graph_spec in cycle.tasks.items(): - # Task nodes - # note: input, output and dependencies added at second traversing - self.add_task(name) - # output nodes - for out_name in task_graph_spec.get('output', []): - self.add_data(out_name) - # Continue cycling - if not p: - do_parsing = False - else: - self.cycling_date += p - do_parsing = (self.cycling_date + p) <= cycle.end_date - - def _add_edges_from_cycle(self, cycle): - self.cycling_date = cycle.start_date - p = cycle.period - do_parsing = True - k = 0 - while do_parsing: - cluster = [] - for name, task_graph_spec in cycle.tasks.items(): - task_node = self.get_task(name) - cluster.append(task_node) - # add input nodes - for in_spec in task_graph_spec.get('input', []): - if (in_node := self.get_data(in_spec)): - if isinstance(in_node, list): - task_node.input.extend(in_node) - else: - task_node.input.append(in_node) - self.add_edge(in_node, task_node) - # add output nodes - for out_spec in task_graph_spec.get('output', []): - if (out_node := self.get_data(out_spec)): - task_node.output.append(out_node) - self.add_edge(task_node, out_node) - if not out_node.is_concrete(): - cluster.append(out_node) - # add dependencies - for dep_spec in task_graph_spec.get('depends', []): - if (dep_node := self.get_task(dep_spec)): - if isinstance(dep_node, list): - task_node.depends.extend(dep_node) - else: - task_node.depends.append(dep_node) - self.add_edge(dep_node, task_node) - # Add clsuter - d1 = self.cycling_date - dates = d1.isoformat() - if p: - d2 = self.cycling_date + p - dates += f" -- {d2.isoformat()}" - label = f"{cycle.name}\n{dates}" - self.graph.add_subgraph(cluster, name=f'cluster_{cycle.name}_{k}', - clusterrank='global', - label=label, tooltip=label, - **self.cluster_kw) - # Continue cycling - if not p: - do_parsing = False - else: - self.cycling_date += p - k += 1 - do_parsing = (self.cycling_date + p) <= cycle.end_date - - def draw(self, **kwargs): - # draw graphviz dot graph to svg file - self.graph.layout(prog='dot') - file_path = Path(f'./{self.graph.name}.svg') - self.graph.draw(path=file_path, format='svg', **kwargs) - - # Add interactive capabilities to the svg graph thanks to - # https://github.com/BartBrood/dynamic-SVG-from-Graphviz - - # Parse svg - svg = etree.parse(file_path) - svg_root = svg.getroot() - # Add 'onload' tag - svg_root.set('onload', 'addInteractivity(evt)') - # Add css style for interactivity - with open('svg-interactive-style.css') as f: - node = etree.Element('style') - node.text = f.read() - svg_root.append(node) - # Add scripts - with open('svg-interactive-script.js') as f: - node = etree.Element('script') - node.text = etree.CDATA(f.read()) - svg_root.append(node) - # write svg again - svg.write(file_path) - # open in browser - webbrowser.open(file_path.resolve().as_uri(), new=1) - - @classmethod - def from_yaml(cls, config): - config_path = Path(config) - config = yaml.safe_load(config_path.read_text()) - return cls(*map(config['scheduling'].get, ('start_date', 'end_date', 'graph')), - *map(config['runtime'].get, ('tasks', 'data')), - name=config_path.stem) - - -# ============ -# Main program -# ============ - -def main(): - - # Parse user input - # ================ - parser = argparse.ArgumentParser( - description='draw the graph specified in a weather and climate yaml format') - - parser.add_argument('config', help="path to yaml configuration file") - args = parser.parse_args() - - # Build and draw graph - # ==================== - WCG = WcGraph.from_yaml(args.config) - WCG.prepare() - WCG.draw() - - -if __name__ == '__main__': - main() diff --git a/Parser_PoC/svg-interactive-script.js b/Parser_PoC/svg-interactive-script.js deleted file mode 100644 index bd71e15..0000000 --- a/Parser_PoC/svg-interactive-script.js +++ /dev/null @@ -1,368 +0,0 @@ -function addInteractivity(evt) { - - var svg = evt.target; - var edges = document.getElementsByClassName('edge'); - var nodes = document.getElementsByClassName('node'); - var clusters = document.getElementsByClassName('cluster'); - var selectedElement, offset, transform, nodrag, origmousepos; - - svg.addEventListener('mousedown', startDrag); - svg.addEventListener('mousemove', drag); - svg.addEventListener('mouseup', endDrag); - svg.addEventListener('mouseleave', endDrag); - svg.addEventListener('touchstart', startDrag); - svg.addEventListener('touchmove', drag); - svg.addEventListener('touchend', endDrag); - svg.addEventListener('touchleave', endDrag); - svg.addEventListener('touchcancel', endDrag); - - for (var i = 0; i < edges.length; i++) { - edges[i].addEventListener('click', clickEdge); - } - - for (var i = 0; i < nodes.length; i++) { - nodes[i].addEventListener('click', clickNode); - } - - addToggleButtons(evt); - - function getMousePosition(evt) { - var CTM = svg.getScreenCTM(); - if (evt.touches) { evt = evt.touches[0]; } - return { - x: (evt.clientX - CTM.e) / CTM.a, - y: (evt.clientY - CTM.f) / CTM.d - }; - } - - function startDrag(evt) { - origmousepos = getMousePosition(evt); - nodrag=true; - selectedElement = evt.target.parentElement; - if (selectedElement){ - offset = getMousePosition(evt); - - // Make sure the first transform on the element is a translate transform - var transforms = selectedElement.transform.baseVal; - - if (transforms.length === 0 || transforms.getItem(0).type !== SVGTransform.SVG_TRANSFORM_TRANSLATE) { - // Create an transform that translates by (0, 0) - var translate = svg.createSVGTransform(); - translate.setTranslate(0, 0); - selectedElement.transform.baseVal.insertItemBefore(translate, 0); - } - - // Get initial translation - transform = transforms.getItem(0); - offset.x -= transform.matrix.e; - offset.y -= transform.matrix.f; - } - } - - function drag(evt) { - if (selectedElement) { - evt.preventDefault(); - var coord = getMousePosition(evt); - transform.setTranslate(coord.x - offset.x, coord.y - offset.y); - } - } - - function endDrag(evt) { - - //if statement to avoid the header section being affected of the translate (0,0) - if (selectedElement){ - if (selectedElement.classList.contains('header')){ - selectedElement = false; - } else { - selectedElement = false; - transform.setTranslate(0,0); - } - } - var currentmousepos=getMousePosition(evt); - if (currentmousepos.x===origmousepos.x|currentmousepos.y===origmousepos.y){ - nodrag=true; - } else { - nodrag=false; - } - - } - - function clickEdge() { - if (nodrag) { - if (this.classList.contains("edge-highlight")){ - this.classList.remove("edge-highlight"); - this.classList.remove("text-highlight-edges"); - } - else { - this.classList.add("edge-highlight"); - this.classList.add("text-highlight-edges"); - animateEdge(this); - } - } - } - - function clickNode() { - if (nodrag) { - var nodeName = this.childNodes[1].textContent; - // Escape special characters in the node name - var nodeNameEscaped = nodeName.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g, '\\$&'); - - var patroon = new RegExp('^' + nodeNameEscaped + '->|->' + nodeNameEscaped + '$|' + nodeNameEscaped + '--|--' + nodeNameEscaped + '$') - - if (this.classList.contains("node-highlight")) { - this.classList.remove("node-highlight"); - this.classList.remove("text-highlight-nodes"); - var edges = document.getElementsByClassName('edge'); - for (var i = 0; i < edges.length; i++) { - if (patroon.test(edges[i].childNodes[1].textContent)) { - edges[i].classList.remove("edge-highlight"); - edges[i].classList.remove("text-highlight-edges"); - } - } - } else { - this.classList.add("node-highlight"); - this.classList.add("text-highlight-nodes"); - var edges = document.getElementsByClassName('edge'); - for (var i = 0; i < edges.length; i++) { - if (patroon.test(edges[i].childNodes[1].textContent)) { - edges[i].classList.add("edge-highlight"); - edges[i].classList.add("text-highlight-edges"); - animateEdge(edges[i]); - } - } - } - } - } - - function animateEdge(edge){ - var path = edge.querySelector('path'); - var polygon = edge.querySelector('polygon'); - var length = path.getTotalLength(); - // Clear any previous transition - path.style.transition = path.style.WebkitTransition = 'none'; - if (polygon){polygon.style.transition = polygon.style.WebkitTransition = 'none';}; - // Set up the starting positions - path.style.strokeDasharray = length + ' ' + length; - path.style.strokeDashoffset = length; - if(polygon){polygon.style.opacity='0';}; - // Trigger a layout so styles are calculated & the browser - // picks up the starting position before animating - path.getBoundingClientRect(); - // Define our transition - path.style.transition = path.style.WebkitTransition = - 'stroke-dashoffset 2s ease-in-out'; - if (polygon){polygon.style.transition = polygon.style.WebkitTransition = - 'fill-opacity 1s ease-in-out 2s';}; - // Go! - path.style.strokeDashoffset = '0'; - if (polygon){setTimeout(function(){polygon.style.opacity='1';},2000)}; - } -} - -var svg = document.querySelector('svg'); -var viewBox = svg.viewBox.baseVal; -allShown=true; -function addToggleButtons(evt) { - var svg = evt.target; - classArray = []; - currentClassIndex = 0; - - var uniqueClasses = new Set(); - var elements = document.getElementsByTagName("g"); - - for (var i = 0; i < elements.length; i++) { - - var allclasses = elements[i].getAttribute("class"); - if (allclasses) { - var classes = allclasses.split(" "); - for (var j = 0; j < classes.length; j++) { - var currentClass = classes[j]; - if (currentClass !== "graph" && !uniqueClasses.has(currentClass)) { - uniqueClasses.add(currentClass); - classArray.push(currentClass); - } - } - } - } - classArray.sort(); - - var buttonContainer = document.createElementNS("http://www.w3.org/2000/svg", "g"); - buttonContainer.setAttribute("transform", "translate(10, -30)"); - buttonContainer.setAttributeNS(null, 'class', 'header'); - - - var header = document.createElementNS("http://www.w3.org/2000/svg", "text"); - header.setAttribute("x", 100); - header.setAttribute("y", -10); - header.setAttribute("text-anchor", "middle"); - header.setAttribute("font-size", "15px"); - header.setAttribute("fill", "black"); - header.textContent = "Toggle Visibility:"; - - var prevButton = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - prevButton.setAttribute("x", 0); - prevButton.setAttribute("y", 0); - prevButton.setAttribute("rx", 5); - prevButton.setAttribute("width", 20); - prevButton.setAttribute("height", 25); - prevButton.setAttribute("fill", "lightgray"); - prevButton.setAttribute("stroke", "black"); - prevButton.setAttribute("onclick", "prevClass()"); - - var prevButtonText = document.createElementNS("http://www.w3.org/2000/svg", "text"); - prevButtonText.setAttribute("id", "prevButtonText"); - prevButtonText.setAttribute("x", 10); - prevButtonText.setAttribute("y", 18); - prevButtonText.setAttribute("text-anchor", "middle"); - prevButtonText.setAttribute("font-size", "20px"); - prevButtonText.setAttribute("fill", "black"); - prevButtonText.setAttribute("onclick", "prevClass()"); - prevButtonText.textContent = "<"; - - var nextButton = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - nextButton.setAttribute("x", 300); - nextButton.setAttribute("y", 0); - nextButton.setAttribute("rx", 5); - nextButton.setAttribute("width", 20); - nextButton.setAttribute("height", 25); - nextButton.setAttribute("fill", "lightgray"); - nextButton.setAttribute("stroke", "black"); - nextButton.setAttribute("onclick", "nextClass()"); - - var nextButtonText = document.createElementNS("http://www.w3.org/2000/svg", "text"); - nextButtonText.setAttribute("id", "nextButtonText"); - nextButtonText.setAttribute("x", 310); - nextButtonText.setAttribute("y", 18); - nextButtonText.setAttribute("text-anchor", "middle"); - nextButtonText.setAttribute("font-size", "20px"); - nextButtonText.setAttribute("fill", "black"); - nextButtonText.setAttribute("onclick", "nextClass()"); - nextButtonText.textContent = ">"; - - var toggleButton = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - toggleButton.setAttribute("id", "toggleButton"); - toggleButton.setAttribute("x", 30); - toggleButton.setAttribute("y", 0); - toggleButton.setAttribute("rx", 5); - toggleButton.setAttribute("width", 260); - toggleButton.setAttribute("height", 25); - toggleButton.setAttribute("fill", "#007bbf"); - toggleButton.setAttribute("stroke", "black"); - toggleButton.setAttribute("onclick", "toggleVisibility(classArray[currentClassIndex])"); - - var toggleButtonText = document.createElementNS("http://www.w3.org/2000/svg", "text"); - toggleButtonText.setAttribute("id", "toggleButtonText"); - toggleButtonText.setAttribute("x", 160); - toggleButtonText.setAttribute("y", 18); - toggleButtonText.setAttribute("text-anchor", "middle"); - toggleButtonText.setAttribute("font-size", "17px"); - toggleButtonText.setAttribute("fill", "black"); - toggleButtonText.setAttribute("onclick", "toggleVisibility(classArray[currentClassIndex])"); - toggleButtonText.textContent = classArray[currentClassIndex]; - - var allButton = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - allButton.setAttribute("id", "allButton"); - allButton.setAttribute("x", 340); - allButton.setAttribute("y", 0); - allButton.setAttribute("width", 150); - allButton.setAttribute("height", 25); - allButton.setAttribute("fill", "#007bbf"); - allButton.setAttribute("stroke", "black"); - allButton.setAttribute("onclick", "toggleAll()"); - - var allButtonText = document.createElementNS("http://www.w3.org/2000/svg", "text"); - allButtonText.setAttribute("id", "allButtonText"); - allButtonText.setAttribute("x", 415); - allButtonText.setAttribute("y", 18); - allButtonText.setAttribute("text-anchor", "middle"); - allButtonText.setAttribute("font-size", "20px"); - allButtonText.setAttribute("fill", "black"); - allButtonText.setAttribute("onclick", "toggleAll()"); - allButtonText.textContent = "Hide/show All"; - - buttonContainer.appendChild(header); - buttonContainer.appendChild(prevButton); - buttonContainer.appendChild(prevButtonText); - buttonContainer.appendChild(nextButton); - buttonContainer.appendChild(nextButtonText); - buttonContainer.appendChild(toggleButton); - buttonContainer.appendChild(toggleButtonText); - buttonContainer.appendChild(allButton); - buttonContainer.appendChild(allButtonText); - adjustViewBox(svg); - svg.appendChild(buttonContainer); - updateButton(); -} - -function nextClass() { - currentClassIndex = (currentClassIndex + 1) % classArray.length; - updateButton(); -} - -function prevClass() { - currentClassIndex = (currentClassIndex - 1 + classArray.length) % classArray.length; - updateButton(); -} - -function updateButton() { - var buttonText = document.getElementById("toggleButtonText"); - var button = document.getElementById("toggleButton"); - buttonText.textContent = classArray[currentClassIndex]; - elements=document.getElementsByClassName(classArray[currentClassIndex]); - - if (elements[0].style.visibility === "hidden"){ - button.setAttribute("fill", "lightblue"); - } - else{ - button.setAttribute("fill", "#007bbf");}; -} - -function toggleVisibility(className) { - var elements = document.getElementsByClassName(className); - - - for (var i = 0; i < elements.length; i++) { - if (elements[i].style.visibility === "hidden") { - elements[i].style.visibility = "visible"; - - } else { - elements[i].style.visibility = "hidden"; - } - } - updateButton(); -} - -function toggleAll(){ - - for (var i = 0; i < classArray.length; i++) { - var elements = document.getElementsByClassName(classArray[i]); - for (var j = 0; j < elements.length; j++) { - if (allShown) { - elements[j].style.visibility = "hidden"; - - } else { - elements[j].style.visibility = "visible"; - } - } - - } - updateButton(); - //update showall button - var button = document.getElementById("allButton"); - if (allShown){ - button.setAttribute("fill", "lightblue"); - } - else{ - button.setAttribute("fill", "#007bbf"); - } - allShown = !allShown; -} - -function adjustViewBox(svg) { - var viewBoxParts = svg.getAttribute("viewBox").split(" "); - var newYMin = parseFloat(viewBoxParts[1]) - 60; // Adjust this value as needed - var newYMax = parseFloat(viewBoxParts[3]) + 60; // Adjust this value as needed - var newXMax = Math.max(parseFloat(viewBoxParts[2]),240); - var newViewBox = viewBoxParts[0] + " " + newYMin + " " + newXMax + " " + newYMax; - svg.setAttribute("viewBox", newViewBox); -} diff --git a/Parser_PoC/svg-interactive-style.css b/Parser_PoC/svg-interactive-style.css deleted file mode 100644 index 3a2d34f..0000000 --- a/Parser_PoC/svg-interactive-style.css +++ /dev/null @@ -1,70 +0,0 @@ -/* The first 2 styles are hiding longer description texts on */ -/* nodes and edges, that are shown when nodes are clicked */ -/* .edge text{ */ -/* opacity: 0; */ -/* } */ -/* .node text:not(:first-of-type){ */ -/* opacity: 0; */ -/* } */ - -.text-highlight-nodes text{ - opacity: 1 !important; - stroke-width: 10; - font-size: 20px; - font-weight: bold; - fill: black; -} -.text-highlight-edges text{ - opacity: 1 !important; - stroke-width: 10; - font-size: 20px; - font-weight: bold; - fill: Indigo; -} -.edge-highlight path{ - opacity: 1; - stroke-width: 10; - stroke: crimson; -} -.edge-highlight polygon{ - opacity: 1; - stroke-width: 10; - stroke: crimson; -} -.node-highlight polygon{ - opacity: 1; - stroke-width: 10; - stroke: crimson; - z-index:99999; -} -.node-highlight ellipse{ - opacity: 1; - stroke-width: 10; - stroke: crimson; - z-index:99999; -} -.node-highlight path{ - opacity: 1; - stroke-width: 10; - stroke: crimson; - z-index:99999; -} -.compass { - fill: #fff; - stroke: #000; - stroke-width: 1; -} -.button { - fill: #225EA8; - stroke: #0C2C84; - stroke-width: 0.5; - stroke-miterlimit:6; - stroke-linecap: round; -} -.button:hover { - stroke-width: 1; -} -.plus-minus { - fill: #fff; - pointer-events: none; -} diff --git a/Parser_PoC/test_config.svg b/Parser_PoC/test_config.svg deleted file mode 100644 index f0abfed..0000000 --- a/Parser_PoC/test_config.svg +++ /dev/null @@ -1,2835 +0,0 @@ - - - -test_config - - -cluster_icon_0 - - -icon -2025-01-01T00:00:00 -- 2025-03-01T00:00:00 - - - - -cluster_icon_1 - - -icon -2025-03-01T00:00:00 -- 2025-05-01T00:00:00 - - - - -cluster_icon_2 - - -icon -2025-05-01T00:00:00 -- 2025-07-01T00:00:00 - - - - -cluster_icon_3 - - -icon -2025-07-01T00:00:00 -- 2025-09-01T00:00:00 - - - - -cluster_icon_4 - - -icon -2025-09-01T00:00:00 -- 2025-11-01T00:00:00 - - - - -cluster_icon_7 - - -icon -2026-03-01T00:00:00 -- 2026-05-01T00:00:00 - - - - -cluster_icon_6 - - -icon -2026-01-01T00:00:00 -- 2026-03-01T00:00:00 - - - - -cluster_icon_9 - - -icon -2026-07-01T00:00:00 -- 2026-09-01T00:00:00 - - - - -cluster_icon_8 - - -icon -2026-05-01T00:00:00 -- 2026-07-01T00:00:00 - - - - -cluster_icon_11 - - -icon -2026-11-01T00:00:00 -- 2027-01-01T00:00:00 - - - - -cluster_icon_5 - - -icon -2025-11-01T00:00:00 -- 2026-01-01T00:00:00 - - - - -cluster_icon_10 - - -icon -2026-09-01T00:00:00 -- 2026-11-01T00:00:00 - - - - -cluster_init_0 - - -init -2025-01-01T00:00:00 - - - - -cluster_yearly_0 - - -yearly -2025-01-01T00:00:00 -- 2026-01-01T00:00:00 - - - - -cluster_yearly_1 - - -yearly -2026-01-01T00:00:00 -- 2027-01-01T00:00:00 - - - - - -<__main__.WcTask object at 0x7f96837f5100> - - -preproc - - - - - -<__main__.WcData object at 0x7f96837f5880> - - -icon input - - - - - -<__main__.WcTask object at 0x7f96837f5100>--<__main__.WcData object at 0x7f96837f5880> - - - - -<__main__.WcTask object at 0x7f96837f76e0> - - -ICON - - - - - -<__main__.WcData object at 0x7f96837f5880>--<__main__.WcTask object at 0x7f96837f76e0> - - - - -<__main__.WcTask object at 0x7f96837f4ec0> - - -store & clean 1 - - - - - -<__main__.WcData object at 0x7f96837f5880>--<__main__.WcTask object at 0x7f96837f4ec0> - - - - -<__main__.WcData object at 0x7f96837f4d40> - - -stream 1 - - - - - -<__main__.WcTask object at 0x7f96837f76e0>--<__main__.WcData object at 0x7f96837f4d40> - - - - -<__main__.WcData object at 0x7f96837f4da0> - - -stream 2 - - - - - -<__main__.WcTask object at 0x7f96837f76e0>--<__main__.WcData object at 0x7f96837f4da0> - - - - -<__main__.WcData object at 0x7f96837f4c50> - - -restart - - - - - -<__main__.WcTask object at 0x7f96837f76e0>--<__main__.WcData object at 0x7f96837f4c50> - - - - -<__main__.WcTask object at 0x7f96837f5280> - - -preproc - - - - - -<__main__.WcTask object at 0x7f96837f76e0>--<__main__.WcTask object at 0x7f96837f5280> - - - - -<__main__.WcData object at 0x7f9686552a20> - - -stored data 1 - - - - - -<__main__.WcTask object at 0x7f96837f4ec0>--<__main__.WcData object at 0x7f9686552a20> - - - - -<__main__.WcData object at 0x7f96837f4d40>--<__main__.WcTask object at 0x7f96837f4ec0> - - - - -<__main__.WcTask object at 0x7f96837f4e90> - - -postproc 1 - - - - - -<__main__.WcData object at 0x7f96837f4d40>--<__main__.WcTask object at 0x7f96837f4e90> - - - - -<__main__.WcTask object at 0x7f96837f6c30> - - -postproc 2 - - - - - -<__main__.WcData object at 0x7f96837f4da0>--<__main__.WcTask object at 0x7f96837f6c30> - - - - -<__main__.WcTask object at 0x7f96837f7290> - - -store & clean 2 - - - - - -<__main__.WcData object at 0x7f96837f4da0>--<__main__.WcTask object at 0x7f96837f7290> - - - - -<__main__.WcTask object at 0x7f96837f56d0> - - -ICON - - - - - -<__main__.WcData object at 0x7f96837f4c50>--<__main__.WcTask object at 0x7f96837f56d0> - - - - -<__main__.WcData object at 0x7f96837f4f50> - - -postout 1 - - - - - -<__main__.WcTask object at 0x7f96837f4e90>--<__main__.WcData object at 0x7f96837f4f50> - - - - -<__main__.WcData object at 0x7f96837f4f50>--<__main__.WcTask object at 0x7f96837f4ec0> - - - - -<__main__.WcTask object at 0x7f96837f4cb0> - - -preproc - - - - - -<__main__.WcData object at 0x7f96837f5070> - - -icon input - - - - - -<__main__.WcTask object at 0x7f96837f4cb0>--<__main__.WcData object at 0x7f96837f5070> - - - - -<__main__.WcData object at 0x7f96837f5070>--<__main__.WcTask object at 0x7f96837f56d0> - - - - -<__main__.WcTask object at 0x7f96837f4f80> - - -store & clean 1 - - - - - -<__main__.WcData object at 0x7f96837f5070>--<__main__.WcTask object at 0x7f96837f4f80> - - - - -<__main__.WcData object at 0x7f96837f4bf0> - - -stream 1 - - - - - -<__main__.WcTask object at 0x7f96837f56d0>--<__main__.WcData object at 0x7f96837f4bf0> - - - - -<__main__.WcData object at 0x7f96837f4d10> - - -stream 2 - - - - - -<__main__.WcTask object at 0x7f96837f56d0>--<__main__.WcData object at 0x7f96837f4d10> - - - - -<__main__.WcData object at 0x7f96837f51f0> - - -restart - - - - - -<__main__.WcTask object at 0x7f96837f56d0>--<__main__.WcData object at 0x7f96837f51f0> - - - - -<__main__.WcTask object at 0x7f96837f53a0> - - -preproc - - - - - -<__main__.WcTask object at 0x7f96837f56d0>--<__main__.WcTask object at 0x7f96837f53a0> - - - - -<__main__.WcTask object at 0x7f96837f4f80>--<__main__.WcData object at 0x7f9686552a20> - - - - -<__main__.WcData object at 0x7f96837f4bf0>--<__main__.WcTask object at 0x7f96837f4f80> - - - - -<__main__.WcTask object at 0x7f96837f50a0> - - -postproc 1 - - - - - -<__main__.WcData object at 0x7f96837f4bf0>--<__main__.WcTask object at 0x7f96837f50a0> - - - - -<__main__.WcData object at 0x7f96837f4d10>--<__main__.WcTask object at 0x7f96837f6c30> - - - - -<__main__.WcData object at 0x7f96837f4d10>--<__main__.WcTask object at 0x7f96837f7290> - - - - -<__main__.WcTask object at 0x7f96837f5220> - - -ICON - - - - - -<__main__.WcData object at 0x7f96837f51f0>--<__main__.WcTask object at 0x7f96837f5220> - - - - -<__main__.WcData object at 0x7f96837f4e00> - - -postout 1 - - - - - -<__main__.WcTask object at 0x7f96837f50a0>--<__main__.WcData object at 0x7f96837f4e00> - - - - -<__main__.WcData object at 0x7f96837f4e00>--<__main__.WcTask object at 0x7f96837f4f80> - - - - -<__main__.WcData object at 0x7f96837f4e30> - - -icon input - - - - - -<__main__.WcTask object at 0x7f96837f5280>--<__main__.WcData object at 0x7f96837f4e30> - - - - -<__main__.WcData object at 0x7f96837f4e30>--<__main__.WcTask object at 0x7f96837f5220> - - - - -<__main__.WcTask object at 0x7f96837f5130> - - -store & clean 1 - - - - - -<__main__.WcData object at 0x7f96837f4e30>--<__main__.WcTask object at 0x7f96837f5130> - - - - -<__main__.WcData object at 0x7f96837f55e0> - - -stream 1 - - - - - -<__main__.WcTask object at 0x7f96837f5220>--<__main__.WcData object at 0x7f96837f55e0> - - - - -<__main__.WcData object at 0x7f96837f5490> - - -stream 2 - - - - - -<__main__.WcTask object at 0x7f96837f5220>--<__main__.WcData object at 0x7f96837f5490> - - - - -<__main__.WcData object at 0x7f96837f4e60> - - -restart - - - - - -<__main__.WcTask object at 0x7f96837f5220>--<__main__.WcData object at 0x7f96837f4e60> - - - - -<__main__.WcTask object at 0x7f96837f5310> - - -preproc - - - - - -<__main__.WcTask object at 0x7f96837f5220>--<__main__.WcTask object at 0x7f96837f5310> - - - - -<__main__.WcTask object at 0x7f96837f5130>--<__main__.WcData object at 0x7f9686552a20> - - - - -<__main__.WcData object at 0x7f96837f55e0>--<__main__.WcTask object at 0x7f96837f5130> - - - - -<__main__.WcTask object at 0x7f96837f4ce0> - - -postproc 1 - - - - - -<__main__.WcData object at 0x7f96837f55e0>--<__main__.WcTask object at 0x7f96837f4ce0> - - - - -<__main__.WcData object at 0x7f96837f5490>--<__main__.WcTask object at 0x7f96837f6c30> - - - - -<__main__.WcData object at 0x7f96837f5490>--<__main__.WcTask object at 0x7f96837f7290> - - - - -<__main__.WcTask object at 0x7f96837f5400> - - -ICON - - - - - -<__main__.WcData object at 0x7f96837f4e60>--<__main__.WcTask object at 0x7f96837f5400> - - - - -<__main__.WcData object at 0x7f96837f5340> - - -postout 1 - - - - - -<__main__.WcTask object at 0x7f96837f4ce0>--<__main__.WcData object at 0x7f96837f5340> - - - - -<__main__.WcData object at 0x7f96837f5340>--<__main__.WcTask object at 0x7f96837f5130> - - - - -<__main__.WcData object at 0x7f96837f51c0> - - -icon input - - - - - -<__main__.WcTask object at 0x7f96837f53a0>--<__main__.WcData object at 0x7f96837f51c0> - - - - -<__main__.WcData object at 0x7f96837f51c0>--<__main__.WcTask object at 0x7f96837f5400> - - - - -<__main__.WcTask object at 0x7f96837f5730> - - -store & clean 1 - - - - - -<__main__.WcData object at 0x7f96837f51c0>--<__main__.WcTask object at 0x7f96837f5730> - - - - -<__main__.WcData object at 0x7f96837f5190> - - -stream 1 - - - - - -<__main__.WcTask object at 0x7f96837f5400>--<__main__.WcData object at 0x7f96837f5190> - - - - -<__main__.WcData object at 0x7f96837f5580> - - -stream 2 - - - - - -<__main__.WcTask object at 0x7f96837f5400>--<__main__.WcData object at 0x7f96837f5580> - - - - -<__main__.WcData object at 0x7f96837f4fb0> - - -restart - - - - - -<__main__.WcTask object at 0x7f96837f5400>--<__main__.WcData object at 0x7f96837f4fb0> - - - - -<__main__.WcTask object at 0x7f96837f5700> - - -preproc - - - - - -<__main__.WcTask object at 0x7f96837f5400>--<__main__.WcTask object at 0x7f96837f5700> - - - - -<__main__.WcTask object at 0x7f96837f5730>--<__main__.WcData object at 0x7f9686552a20> - - - - -<__main__.WcData object at 0x7f96837f5190>--<__main__.WcTask object at 0x7f96837f5730> - - - - -<__main__.WcTask object at 0x7f96837f5370> - - -postproc 1 - - - - - -<__main__.WcData object at 0x7f96837f5190>--<__main__.WcTask object at 0x7f96837f5370> - - - - -<__main__.WcData object at 0x7f96837f5580>--<__main__.WcTask object at 0x7f96837f6c30> - - - - -<__main__.WcData object at 0x7f96837f5580>--<__main__.WcTask object at 0x7f96837f7290> - - - - -<__main__.WcTask object at 0x7f96837f5520> - - -ICON - - - - - -<__main__.WcData object at 0x7f96837f4fb0>--<__main__.WcTask object at 0x7f96837f5520> - - - - -<__main__.WcData object at 0x7f96837f52b0> - - -postout 1 - - - - - -<__main__.WcTask object at 0x7f96837f5370>--<__main__.WcData object at 0x7f96837f52b0> - - - - -<__main__.WcData object at 0x7f96837f52b0>--<__main__.WcTask object at 0x7f96837f5730> - - - - -<__main__.WcData object at 0x7f96837f7d10> - - -icon input - - - - - -<__main__.WcTask object at 0x7f96837f5310>--<__main__.WcData object at 0x7f96837f7d10> - - - - -<__main__.WcData object at 0x7f96837f7d10>--<__main__.WcTask object at 0x7f96837f5520> - - - - -<__main__.WcTask object at 0x7f96837f5820> - - -store & clean 1 - - - - - -<__main__.WcData object at 0x7f96837f7d10>--<__main__.WcTask object at 0x7f96837f5820> - - - - -<__main__.WcData object at 0x7f96837f5670> - - -stream 1 - - - - - -<__main__.WcTask object at 0x7f96837f5520>--<__main__.WcData object at 0x7f96837f5670> - - - - -<__main__.WcData object at 0x7f96837f5850> - - -stream 2 - - - - - -<__main__.WcTask object at 0x7f96837f5520>--<__main__.WcData object at 0x7f96837f5850> - - - - -<__main__.WcData object at 0x7f96837f54c0> - - -restart - - - - - -<__main__.WcTask object at 0x7f96837f5520>--<__main__.WcData object at 0x7f96837f54c0> - - - - -<__main__.WcTask object at 0x7f96837f5af0> - - -preproc - - - - - -<__main__.WcTask object at 0x7f96837f5520>--<__main__.WcTask object at 0x7f96837f5af0> - - - - -<__main__.WcTask object at 0x7f96837f5820>--<__main__.WcData object at 0x7f9686552a20> - - - - -<__main__.WcData object at 0x7f96837f5670>--<__main__.WcTask object at 0x7f96837f5820> - - - - -<__main__.WcTask object at 0x7f96837f5250> - - -postproc 1 - - - - - -<__main__.WcData object at 0x7f96837f5670>--<__main__.WcTask object at 0x7f96837f5250> - - - - -<__main__.WcData object at 0x7f96837f5850>--<__main__.WcTask object at 0x7f96837f6c30> - - - - -<__main__.WcData object at 0x7f96837f5850>--<__main__.WcTask object at 0x7f96837f7290> - - - - -<__main__.WcTask object at 0x7f96837f5550> - - -ICON - - - - - -<__main__.WcData object at 0x7f96837f54c0>--<__main__.WcTask object at 0x7f96837f5550> - - - - -<__main__.WcData object at 0x7f96837f58b0> - - -postout 1 - - - - - -<__main__.WcTask object at 0x7f96837f5250>--<__main__.WcData object at 0x7f96837f58b0> - - - - -<__main__.WcData object at 0x7f96837f58b0>--<__main__.WcTask object at 0x7f96837f5820> - - - - -<__main__.WcTask object at 0x7f96837f6030> - - -preproc - - - - - -<__main__.WcData object at 0x7f96837f61b0> - - -icon input - - - - - -<__main__.WcTask object at 0x7f96837f6030>--<__main__.WcData object at 0x7f96837f61b0> - - - - -<__main__.WcTask object at 0x7f96837f5d60> - - -ICON - - - - - -<__main__.WcData object at 0x7f96837f61b0>--<__main__.WcTask object at 0x7f96837f5d60> - - - - -<__main__.WcTask object at 0x7f96837f5e20> - - -store & clean 1 - - - - - -<__main__.WcData object at 0x7f96837f61b0>--<__main__.WcTask object at 0x7f96837f5e20> - - - - -<__main__.WcData object at 0x7f96837f5fa0> - - -stream 1 - - - - - -<__main__.WcTask object at 0x7f96837f5d60>--<__main__.WcData object at 0x7f96837f5fa0> - - - - -<__main__.WcData object at 0x7f96837f6090> - - -stream 2 - - - - - -<__main__.WcTask object at 0x7f96837f5d60>--<__main__.WcData object at 0x7f96837f6090> - - - - -<__main__.WcData object at 0x7f96837f5b80> - - -restart - - - - - -<__main__.WcTask object at 0x7f96837f5d60>--<__main__.WcData object at 0x7f96837f5b80> - - - - -<__main__.WcTask object at 0x7f96837f6690> - - -preproc - - - - - -<__main__.WcTask object at 0x7f96837f5d60>--<__main__.WcTask object at 0x7f96837f6690> - - - - -<__main__.WcTask object at 0x7f96837f5e20>--<__main__.WcData object at 0x7f9686552a20> - - - - -<__main__.WcData object at 0x7f96837f5fa0>--<__main__.WcTask object at 0x7f96837f5e20> - - - - -<__main__.WcTask object at 0x7f96837f5fd0> - - -postproc 1 - - - - - -<__main__.WcData object at 0x7f96837f5fa0>--<__main__.WcTask object at 0x7f96837f5fd0> - - - - -<__main__.WcTask object at 0x7f96837f6840> - - -postproc 2 - - - - - -<__main__.WcData object at 0x7f96837f6090>--<__main__.WcTask object at 0x7f96837f6840> - - - - -<__main__.WcTask object at 0x7f96837f7200> - - -store & clean 2 - - - - - -<__main__.WcData object at 0x7f96837f6090>--<__main__.WcTask object at 0x7f96837f7200> - - - - -<__main__.WcTask object at 0x7f96837f62a0> - - -ICON - - - - - -<__main__.WcData object at 0x7f96837f5b80>--<__main__.WcTask object at 0x7f96837f62a0> - - - - -<__main__.WcData object at 0x7f96837f63c0> - - -postout 1 - - - - - -<__main__.WcTask object at 0x7f96837f5fd0>--<__main__.WcData object at 0x7f96837f63c0> - - - - -<__main__.WcData object at 0x7f96837f63c0>--<__main__.WcTask object at 0x7f96837f5e20> - - - - -<__main__.WcData object at 0x7f96837f5eb0> - - -icon input - - - - - -<__main__.WcTask object at 0x7f96837f5af0>--<__main__.WcData object at 0x7f96837f5eb0> - - - - -<__main__.WcTask object at 0x7f96837f5c10> - - -ICON - - - - - -<__main__.WcData object at 0x7f96837f5eb0>--<__main__.WcTask object at 0x7f96837f5c10> - - - - -<__main__.WcTask object at 0x7f96837f5f40> - - -store & clean 1 - - - - - -<__main__.WcData object at 0x7f96837f5eb0>--<__main__.WcTask object at 0x7f96837f5f40> - - - - -<__main__.WcData object at 0x7f96837f5d30> - - -stream 1 - - - - - -<__main__.WcTask object at 0x7f96837f5c10>--<__main__.WcData object at 0x7f96837f5d30> - - - - -<__main__.WcData object at 0x7f96837f5d00> - - -stream 2 - - - - - -<__main__.WcTask object at 0x7f96837f5c10>--<__main__.WcData object at 0x7f96837f5d00> - - - - -<__main__.WcData object at 0x7f96837f5b50> - - -restart - - - - - -<__main__.WcTask object at 0x7f96837f5c10>--<__main__.WcData object at 0x7f96837f5b50> - - - - -<__main__.WcTask object at 0x7f96837f6270> - - -preproc - - - - - -<__main__.WcTask object at 0x7f96837f5c10>--<__main__.WcTask object at 0x7f96837f6270> - - - - -<__main__.WcTask object at 0x7f96837f5f40>--<__main__.WcData object at 0x7f9686552a20> - - - - -<__main__.WcData object at 0x7f96837f5d30>--<__main__.WcTask object at 0x7f96837f5f40> - - - - -<__main__.WcTask object at 0x7f96837f5c70> - - -postproc 1 - - - - - -<__main__.WcData object at 0x7f96837f5d30>--<__main__.WcTask object at 0x7f96837f5c70> - - - - -<__main__.WcData object at 0x7f96837f5d00>--<__main__.WcTask object at 0x7f96837f6840> - - - - -<__main__.WcData object at 0x7f96837f5d00>--<__main__.WcTask object at 0x7f96837f7200> - - - - -<__main__.WcData object at 0x7f96837f5b50>--<__main__.WcTask object at 0x7f96837f5d60> - - - - -<__main__.WcData object at 0x7f96837f6000> - - -postout 1 - - - - - -<__main__.WcTask object at 0x7f96837f5c70>--<__main__.WcData object at 0x7f96837f6000> - - - - -<__main__.WcData object at 0x7f96837f6000>--<__main__.WcTask object at 0x7f96837f5f40> - - - - -<__main__.WcData object at 0x7f96837f70b0> - - -icon input - - - - - -<__main__.WcTask object at 0x7f96837f6690>--<__main__.WcData object at 0x7f96837f70b0> - - - - -<__main__.WcTask object at 0x7f96837f6420> - - -ICON - - - - - -<__main__.WcData object at 0x7f96837f70b0>--<__main__.WcTask object at 0x7f96837f6420> - - - - -<__main__.WcTask object at 0x7f96837f68a0> - - -store & clean 1 - - - - - -<__main__.WcData object at 0x7f96837f70b0>--<__main__.WcTask object at 0x7f96837f68a0> - - - - -<__main__.WcData object at 0x7f96837f6330> - - -stream 1 - - - - - -<__main__.WcTask object at 0x7f96837f6420>--<__main__.WcData object at 0x7f96837f6330> - - - - -<__main__.WcData object at 0x7f96837f66f0> - - -stream 2 - - - - - -<__main__.WcTask object at 0x7f96837f6420>--<__main__.WcData object at 0x7f96837f66f0> - - - - -<__main__.WcData object at 0x7f96837f66c0> - - -restart - - - - - -<__main__.WcTask object at 0x7f96837f6420>--<__main__.WcData object at 0x7f96837f66c0> - - - - -<__main__.WcTask object at 0x7f96837f6cf0> - - -preproc - - - - - -<__main__.WcTask object at 0x7f96837f6420>--<__main__.WcTask object at 0x7f96837f6cf0> - - - - -<__main__.WcTask object at 0x7f96837f68a0>--<__main__.WcData object at 0x7f9686552a20> - - - - -<__main__.WcData object at 0x7f96837f6330>--<__main__.WcTask object at 0x7f96837f68a0> - - - - -<__main__.WcTask object at 0x7f96837f6630> - - -postproc 1 - - - - - -<__main__.WcData object at 0x7f96837f6330>--<__main__.WcTask object at 0x7f96837f6630> - - - - -<__main__.WcData object at 0x7f96837f66f0>--<__main__.WcTask object at 0x7f96837f6840> - - - - -<__main__.WcData object at 0x7f96837f66f0>--<__main__.WcTask object at 0x7f96837f7200> - - - - -<__main__.WcTask object at 0x7f96837f6930> - - -ICON - - - - - -<__main__.WcData object at 0x7f96837f66c0>--<__main__.WcTask object at 0x7f96837f6930> - - - - -<__main__.WcData object at 0x7f96837f6960> - - -postout 1 - - - - - -<__main__.WcTask object at 0x7f96837f6630>--<__main__.WcData object at 0x7f96837f6960> - - - - -<__main__.WcData object at 0x7f96837f6960>--<__main__.WcTask object at 0x7f96837f68a0> - - - - -<__main__.WcData object at 0x7f96837f64e0> - - -icon input - - - - - -<__main__.WcTask object at 0x7f96837f6270>--<__main__.WcData object at 0x7f96837f64e0> - - - - -<__main__.WcData object at 0x7f96837f64e0>--<__main__.WcTask object at 0x7f96837f62a0> - - - - -<__main__.WcTask object at 0x7f96837f6240> - - -store & clean 1 - - - - - -<__main__.WcData object at 0x7f96837f64e0>--<__main__.WcTask object at 0x7f96837f6240> - - - - -<__main__.WcData object at 0x7f96837f62d0> - - -stream 1 - - - - - -<__main__.WcTask object at 0x7f96837f62a0>--<__main__.WcData object at 0x7f96837f62d0> - - - - -<__main__.WcData object at 0x7f96837f6600> - - -stream 2 - - - - - -<__main__.WcTask object at 0x7f96837f62a0>--<__main__.WcData object at 0x7f96837f6600> - - - - -<__main__.WcData object at 0x7f96837f6150> - - -restart - - - - - -<__main__.WcTask object at 0x7f96837f62a0>--<__main__.WcData object at 0x7f96837f6150> - - - - -<__main__.WcTask object at 0x7f96837f69f0> - - -preproc - - - - - -<__main__.WcTask object at 0x7f96837f62a0>--<__main__.WcTask object at 0x7f96837f69f0> - - - - -<__main__.WcTask object at 0x7f96837f6240>--<__main__.WcData object at 0x7f9686552a20> - - - - -<__main__.WcData object at 0x7f96837f62d0>--<__main__.WcTask object at 0x7f96837f6240> - - - - -<__main__.WcTask object at 0x7f96837f6780> - - -postproc 1 - - - - - -<__main__.WcData object at 0x7f96837f62d0>--<__main__.WcTask object at 0x7f96837f6780> - - - - -<__main__.WcData object at 0x7f96837f6600>--<__main__.WcTask object at 0x7f96837f6840> - - - - -<__main__.WcData object at 0x7f96837f6600>--<__main__.WcTask object at 0x7f96837f7200> - - - - -<__main__.WcData object at 0x7f96837f6150>--<__main__.WcTask object at 0x7f96837f6420> - - - - -<__main__.WcData object at 0x7f96837f6720> - - -postout 1 - - - - - -<__main__.WcTask object at 0x7f96837f6780>--<__main__.WcData object at 0x7f96837f6720> - - - - -<__main__.WcData object at 0x7f96837f6720>--<__main__.WcTask object at 0x7f96837f6240> - - - - -<__main__.WcData object at 0x7f96837f6fc0> - - -icon input - - - - - -<__main__.WcTask object at 0x7f96837f6cf0>--<__main__.WcData object at 0x7f96837f6fc0> - - - - -<__main__.WcTask object at 0x7f96837f6ab0> - - -ICON - - - - - -<__main__.WcData object at 0x7f96837f6fc0>--<__main__.WcTask object at 0x7f96837f6ab0> - - - - -<__main__.WcTask object at 0x7f96837f6cc0> - - -store & clean 1 - - - - - -<__main__.WcData object at 0x7f96837f6fc0>--<__main__.WcTask object at 0x7f96837f6cc0> - - - - -<__main__.WcData object at 0x7f96837f6e70> - - -stream 1 - - - - - -<__main__.WcTask object at 0x7f96837f6ab0>--<__main__.WcData object at 0x7f96837f6e70> - - - - -<__main__.WcData object at 0x7f96837f6c90> - - -stream 2 - - - - - -<__main__.WcTask object at 0x7f96837f6ab0>--<__main__.WcData object at 0x7f96837f6c90> - - - - -<__main__.WcData object at 0x7f96837f6ba0> - - -restart - - - - - -<__main__.WcTask object at 0x7f96837f6ab0>--<__main__.WcData object at 0x7f96837f6ba0> - - - - -<__main__.WcTask object at 0x7f96837f6cc0>--<__main__.WcData object at 0x7f9686552a20> - - - - -<__main__.WcData object at 0x7f96837f6e70>--<__main__.WcTask object at 0x7f96837f6cc0> - - - - -<__main__.WcTask object at 0x7f96837f6d20> - - -postproc 1 - - - - - -<__main__.WcData object at 0x7f96837f6e70>--<__main__.WcTask object at 0x7f96837f6d20> - - - - -<__main__.WcData object at 0x7f96837f6c90>--<__main__.WcTask object at 0x7f96837f6840> - - - - -<__main__.WcData object at 0x7f96837f6c90>--<__main__.WcTask object at 0x7f96837f7200> - - - - -<__main__.WcData object at 0x7f96837f6ff0> - - -postout 1 - - - - - -<__main__.WcTask object at 0x7f96837f6d20>--<__main__.WcData object at 0x7f96837f6ff0> - - - - -<__main__.WcData object at 0x7f96837f6ff0>--<__main__.WcTask object at 0x7f96837f6cc0> - - - - -<__main__.WcData object at 0x7f96837f5be0> - - -icon input - - - - - -<__main__.WcTask object at 0x7f96837f5700>--<__main__.WcData object at 0x7f96837f5be0> - - - - -<__main__.WcData object at 0x7f96837f5be0>--<__main__.WcTask object at 0x7f96837f5550> - - - - -<__main__.WcTask object at 0x7f96837f5ac0> - - -store & clean 1 - - - - - -<__main__.WcData object at 0x7f96837f5be0>--<__main__.WcTask object at 0x7f96837f5ac0> - - - - -<__main__.WcTask object at 0x7f96837f5550>--<__main__.WcTask object at 0x7f96837f6030> - - - - -<__main__.WcData object at 0x7f96837f59d0> - - -stream 1 - - - - - -<__main__.WcTask object at 0x7f96837f5550>--<__main__.WcData object at 0x7f96837f59d0> - - - - -<__main__.WcData object at 0x7f96837f5a30> - - -stream 2 - - - - - -<__main__.WcTask object at 0x7f96837f5550>--<__main__.WcData object at 0x7f96837f5a30> - - - - -<__main__.WcData object at 0x7f96837f5610> - - -restart - - - - - -<__main__.WcTask object at 0x7f96837f5550>--<__main__.WcData object at 0x7f96837f5610> - - - - -<__main__.WcTask object at 0x7f96837f5ac0>--<__main__.WcData object at 0x7f9686552a20> - - - - -<__main__.WcData object at 0x7f96837f59d0>--<__main__.WcTask object at 0x7f96837f5ac0> - - - - -<__main__.WcTask object at 0x7f96837f5a60> - - -postproc 1 - - - - - -<__main__.WcData object at 0x7f96837f59d0>--<__main__.WcTask object at 0x7f96837f5a60> - - - - -<__main__.WcData object at 0x7f96837f5a30>--<__main__.WcTask object at 0x7f96837f6c30> - - - - -<__main__.WcData object at 0x7f96837f5a30>--<__main__.WcTask object at 0x7f96837f7290> - - - - -<__main__.WcData object at 0x7f96837f5610>--<__main__.WcTask object at 0x7f96837f5c10> - - - - -<__main__.WcData object at 0x7f96837f5bb0> - - -postout 1 - - - - - -<__main__.WcTask object at 0x7f96837f5a60>--<__main__.WcData object at 0x7f96837f5bb0> - - - - -<__main__.WcData object at 0x7f96837f5bb0>--<__main__.WcTask object at 0x7f96837f5ac0> - - - - -<__main__.WcData object at 0x7f96837f6de0> - - -icon input - - - - - -<__main__.WcTask object at 0x7f96837f69f0>--<__main__.WcData object at 0x7f96837f6de0> - - - - -<__main__.WcData object at 0x7f96837f6de0>--<__main__.WcTask object at 0x7f96837f6930> - - - - -<__main__.WcTask object at 0x7f96837f69c0> - - -store & clean 1 - - - - - -<__main__.WcData object at 0x7f96837f6de0>--<__main__.WcTask object at 0x7f96837f69c0> - - - - -<__main__.WcData object at 0x7f96837f6ea0> - - -stream 1 - - - - - -<__main__.WcTask object at 0x7f96837f6930>--<__main__.WcData object at 0x7f96837f6ea0> - - - - -<__main__.WcData object at 0x7f96837f6c60> - - -stream 2 - - - - - -<__main__.WcTask object at 0x7f96837f6930>--<__main__.WcData object at 0x7f96837f6c60> - - - - -<__main__.WcData object at 0x7f96837f6900> - - -restart - - - - - -<__main__.WcTask object at 0x7f96837f6930>--<__main__.WcData object at 0x7f96837f6900> - - - - -<__main__.WcTask object at 0x7f96837f69c0>--<__main__.WcData object at 0x7f9686552a20> - - - - -<__main__.WcData object at 0x7f96837f6ea0>--<__main__.WcTask object at 0x7f96837f69c0> - - - - -<__main__.WcTask object at 0x7f96837f6b40> - - -postproc 1 - - - - - -<__main__.WcData object at 0x7f96837f6ea0>--<__main__.WcTask object at 0x7f96837f6b40> - - - - -<__main__.WcData object at 0x7f96837f6c60>--<__main__.WcTask object at 0x7f96837f6840> - - - - -<__main__.WcData object at 0x7f96837f6c60>--<__main__.WcTask object at 0x7f96837f7200> - - - - -<__main__.WcData object at 0x7f96837f6900>--<__main__.WcTask object at 0x7f96837f6ab0> - - - - -<__main__.WcData object at 0x7f96837f6db0> - - -postout 1 - - - - - -<__main__.WcTask object at 0x7f96837f6b40>--<__main__.WcData object at 0x7f96837f6db0> - - - - -<__main__.WcData object at 0x7f96837f6db0>--<__main__.WcTask object at 0x7f96837f69c0> - - - - -<__main__.WcTask object at 0x7f96837f5940> - - -Extpar - - - - - -<__main__.WcData object at 0x7f96837f7c50> - - -extpar file - - - - - -<__main__.WcTask object at 0x7f96837f5940>--<__main__.WcData object at 0x7f96837f7c50> - - - - -<__main__.WcData object at 0x7f96837f7c50>--<__main__.WcTask object at 0x7f96837f5100> - - - - -<__main__.WcData object at 0x7f96837f7c50>--<__main__.WcTask object at 0x7f96837f4cb0> - - - - -<__main__.WcData object at 0x7f96837f7c50>--<__main__.WcTask object at 0x7f96837f5280> - - - - -<__main__.WcData object at 0x7f96837f7c50>--<__main__.WcTask object at 0x7f96837f53a0> - - - - -<__main__.WcData object at 0x7f96837f7c50>--<__main__.WcTask object at 0x7f96837f5310> - - - - -<__main__.WcData object at 0x7f96837f7c50>--<__main__.WcTask object at 0x7f96837f6030> - - - - -<__main__.WcData object at 0x7f96837f7c50>--<__main__.WcTask object at 0x7f96837f5af0> - - - - -<__main__.WcData object at 0x7f96837f7c50>--<__main__.WcTask object at 0x7f96837f6690> - - - - -<__main__.WcData object at 0x7f96837f7c50>--<__main__.WcTask object at 0x7f96837f6270> - - - - -<__main__.WcData object at 0x7f96837f7c50>--<__main__.WcTask object at 0x7f96837f6cf0> - - - - -<__main__.WcData object at 0x7f96837f7c50>--<__main__.WcTask object at 0x7f96837f5700> - - - - -<__main__.WcData object at 0x7f96837f7c50>--<__main__.WcTask object at 0x7f96837f69f0> - - - - -<__main__.WcData object at 0x7f96837f65a0> - - -postout 2 - - - - - -<__main__.WcTask object at 0x7f96837f6c30>--<__main__.WcData object at 0x7f96837f65a0> - - - - -<__main__.WcData object at 0x7f96837f65a0>--<__main__.WcTask object at 0x7f96837f7290> - - - - -<__main__.WcData object at 0x7f9686b40110> - - -stored data 2 - - - - - -<__main__.WcTask object at 0x7f96837f7290>--<__main__.WcData object at 0x7f9686b40110> - - - - -<__main__.WcData object at 0x7f96837f6e10> - - -postout 2 - - - - - -<__main__.WcTask object at 0x7f96837f6840>--<__main__.WcData object at 0x7f96837f6e10> - - - - -<__main__.WcData object at 0x7f96837f6e10>--<__main__.WcTask object at 0x7f96837f7200> - - - - -<__main__.WcTask object at 0x7f96837f7200>--<__main__.WcData object at 0x7f9686b40110> - - - - -<__main__.WcData object at 0x7f9686b8dfa0> - - -grid file - - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f5100> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f76e0> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f4cb0> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f56d0> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f5280> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f5220> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f53a0> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f5400> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f5310> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f5520> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f6030> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f5d60> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f5af0> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f5c10> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f6690> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f6420> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f6270> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f62a0> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f6cf0> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f6ab0> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f5700> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f5550> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f69f0> - - - - -<__main__.WcData object at 0x7f9686b8dfa0>--<__main__.WcTask object at 0x7f96837f6930> - - - - -<__main__.WcData object at 0x7f96868c0bf0> - - -obs data - - - - - -<__main__.WcData object at 0x7f96868c0bf0>--<__main__.WcTask object at 0x7f96837f5940> - - - - -<__main__.WcData object at 0x7f9686b209b0> - - -ERA5 - - - - - -<__main__.WcData object at 0x7f9686b209b0>--<__main__.WcTask object at 0x7f96837f5100> - - - - -<__main__.WcData object at 0x7f9686b209b0>--<__main__.WcTask object at 0x7f96837f4cb0> - - - - -<__main__.WcData object at 0x7f9686b209b0>--<__main__.WcTask object at 0x7f96837f5280> - - - - -<__main__.WcData object at 0x7f9686b209b0>--<__main__.WcTask object at 0x7f96837f53a0> - - - - -<__main__.WcData object at 0x7f9686b209b0>--<__main__.WcTask object at 0x7f96837f5310> - - - - -<__main__.WcData object at 0x7f9686b209b0>--<__main__.WcTask object at 0x7f96837f6030> - - - - -<__main__.WcData object at 0x7f9686b209b0>--<__main__.WcTask object at 0x7f96837f5af0> - - - - -<__main__.WcData object at 0x7f9686b209b0>--<__main__.WcTask object at 0x7f96837f6690> - - - - -<__main__.WcData object at 0x7f9686b209b0>--<__main__.WcTask object at 0x7f96837f6270> - - - - -<__main__.WcData object at 0x7f9686b209b0>--<__main__.WcTask object at 0x7f96837f6cf0> - - - - -<__main__.WcData object at 0x7f9686b209b0>--<__main__.WcTask object at 0x7f96837f5700> - - - - -<__main__.WcData object at 0x7f9686b209b0>--<__main__.WcTask object at 0x7f96837f69f0> - - - - \ No newline at end of file diff --git a/Parser_PoC/test_config.yaml b/Parser_PoC/test_config.yaml deleted file mode 100644 index be27813..0000000 --- a/Parser_PoC/test_config.yaml +++ /dev/null @@ -1,157 +0,0 @@ -scheduling: - start_date: &root_start_date '2025-01-01T00:00' - end_date: &root_end_date '2027-01-01T00:00' - graph: - init: - tasks: - Extpar: - input: - - obs data - output: - - extpar file - icon: - period: 'P2M' - # period: 'P3M' - # period: 'P4M' - # period: 'P6M' - tasks: - preproc: - input: - - grid file - - extpar file: - date: *root_start_date - - ERA5 - output: - - icon input - depends: - - ICON: - lag: '-P4M' - # lag: '-P6M' - # lag: '-P8M' - # lag: '-P12M' - ICON: - input: - - grid file - - icon input - - restart: - lag: '-P2M' - # lag: '-P3M' - # lag: '-P4M' - # lag: '-P6M' - output: - - stream 1 - - stream 2 - - restart - postproc 1: - input: - - stream 1 - output: - - postout 1 - store & clean 1: - input: - - postout 1 - - stream 1 - - icon input - output: - - stored data 1 - yearly: - period: 'P1Y' - tasks: - postproc 2: - input: - - stream 2: - lag: ['P0M', 'P2M', 'P4M', 'P6M', 'P8M', 'P10M'] - # lag: ['P0M', 'P3M', 'P6M', 'P9M'] - # lag: ['P0M', 'P4M', 'P8M'] - # lag: ['P0M', 'P6M'] - output: - - postout 2 - store & clean 2: - input: - - postout 2 - - stream 2: - lag: ['P0M', 'P2M', 'P4M', 'P6M', 'P8M', 'P10M'] - # lag: ['P0M', 'P3M', 'P6M', 'P9M'] - # lag: ['P0M', 'P4M', 'P8M'] - # lag: ['P0M', 'P6M'] - output: - - stored data 2 -runtime: - # Each task and piece of data (input and output of tasks) used to - # define the graph is described in that section - tasks: - root: - # All tasks inherit the root task properties - host: santis - account: g110 - Extpar: - plugin: extpar - exe: path/to/exe - config: path/to/namelists/dir - uenv: - squashfs: path/to/squashfs - mount_point: runtime/mount/point - nodes: 1 - walltime: '00:02:00' - preproc: - plugin: AiiDA Shell - nodes: 4 - walltime: '00:02:00' - config: path/to/config/dir - uenv: - squashfs: path/to/squashfs - mount_point: runtime/mount/point - ICON: - plugin: ICON - nodes: 40 - walltime: '24:00:00' - config: path/to/namelists/dir - uenv: - squashfs: path/to/squashfs - mount_point: runtime/mount/point - exe: path/to/icon - postproc 1: - plugin: AiiDA Shell - nodes: 2 - walltime: '00:05:00' - src: path/to/src/dir - exe: "python3 main_script_ocn.py" - conda_env: path/to/yaml/env/file - uenv: - squashfs: path/to/squashfs - mount_point: runtime/mount/point - postproc 2: - plugin: AiiDA Shell - nodes: 4 - walltime: '00:05:00' - src: path/to/src/dir - exe: "python3 main_script_atm.py" - conda_env: path/to/yaml/env/file - uenv: - squashfs: path/to/squashfs - mount_point: runtime/mount/point - store & clean 1: - plugin: AiiDA Shell - nodes: 1 - walltime: '00:01:00' - scipt: path/to/script - store & clean 2: - plugin: AiiDA Shell - nodes: 1 - walltime: '00:01:00' - scipt: path/to/script - - data: - # Properties of data nodes - grid file: {type: file, abs_path: /path/to/grid/file.nc} - obs data: {type: dir, abs_path: /some/where} - ERA5: {type: dir, abs_path: /some/where/else} - extpar file: {type: file, rel_path: output} - icon input: {rel_path: output} - restart: {type: ICON_restart, format: ncdf, rel_path: restart} - stream 1: {type: ICON_output_stream, rel_path: output_1} - stream 2: {type: ICON_output_stream, rel_path: output_2} - postout 1: {'rel_path': output} - postout 2: {'rel_path': output} - stored data 1: {'abs_path': /store/some/where_1} - stored data 2: {'abs_path': /store/some/where_2} diff --git a/pyproject.toml b/pyproject.toml index c3997f1..23211ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,9 @@ dependencies = [ "pydantic", "pydantic-yaml", "aiida-core>=2.5", - "termcolor" + "termcolor", + "pygraphviz", + "lxml" ] [project.urls] Repository = "https://github.com/C2SM/Sirocco.git" diff --git a/src/sirocco/core.py b/src/sirocco/core.py index 9d73f28..2b25bb0 100644 --- a/src/sirocco/core.py +++ b/src/sirocco/core.py @@ -250,6 +250,7 @@ class Workflow: """Internal reprensentation of a workflow""" def __init__(self, workflow_config: ConfigWorkflow) -> None: + self.name = workflow_config.name self.tasks = Store() self.data = Store() self.cycles = Store() diff --git a/src/sirocco/svg-interactive-script.js b/src/sirocco/svg-interactive-script.js new file mode 100644 index 0000000..45ef88f --- /dev/null +++ b/src/sirocco/svg-interactive-script.js @@ -0,0 +1,195 @@ +// https://github.com/BartBrood/Interactive-Graphviz-Diagrams + +// MIT License + +// Copyright (c) 2024 BartBrood + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +function addInteractivity(evt) { + + var svg = evt.target; + var edges = document.getElementsByClassName('edge'); + var nodes = document.getElementsByClassName('node'); + var clusters = document.getElementsByClassName('cluster'); + var selectedElement, offset, transform, nodrag, origmousepos; + + svg.addEventListener('mousedown', startDrag); + svg.addEventListener('mousemove', drag); + svg.addEventListener('mouseup', endDrag); + svg.addEventListener('mouseleave', endDrag); + svg.addEventListener('touchstart', startDrag); + svg.addEventListener('touchmove', drag); + svg.addEventListener('touchend', endDrag); + svg.addEventListener('touchleave', endDrag); + svg.addEventListener('touchcancel', endDrag); + + for (var i = 0; i < edges.length; i++) { + edges[i].addEventListener('click', clickEdge); + } + + for (var i = 0; i < nodes.length; i++) { + nodes[i].addEventListener('click', clickNode); + } + + var svg = document.querySelector('svg'); + var viewBox = svg.viewBox.baseVal; + adjustViewBox(svg); + + function getMousePosition(evt) { + var CTM = svg.getScreenCTM(); + if (evt.touches) { evt = evt.touches[0]; } + return { + x: (evt.clientX - CTM.e) / CTM.a, + y: (evt.clientY - CTM.f) / CTM.d + }; + } + + function startDrag(evt) { + origmousepos = getMousePosition(evt); + nodrag=true; + selectedElement = evt.target.parentElement; + if (selectedElement){ + offset = getMousePosition(evt); + + // Make sure the first transform on the element is a translate transform + var transforms = selectedElement.transform.baseVal; + + if (transforms.length === 0 || transforms.getItem(0).type !== SVGTransform.SVG_TRANSFORM_TRANSLATE) { + // Create an transform that translates by (0, 0) + var translate = svg.createSVGTransform(); + translate.setTranslate(0, 0); + selectedElement.transform.baseVal.insertItemBefore(translate, 0); + } + + // Get initial translation + transform = transforms.getItem(0); + offset.x -= transform.matrix.e; + offset.y -= transform.matrix.f; + } + } + + function drag(evt) { + if (selectedElement) { + evt.preventDefault(); + var coord = getMousePosition(evt); + transform.setTranslate(coord.x - offset.x, coord.y - offset.y); + } + } + + function endDrag(evt) { + + //if statement to avoid the header section being affected by the translate (0,0) + if (selectedElement){ + if (selectedElement.classList.contains('header')){ + selectedElement = false; + } else { + selectedElement = false; + transform.setTranslate(0,0); + } + } + var currentmousepos=getMousePosition(evt); + if (currentmousepos.x===origmousepos.x|currentmousepos.y===origmousepos.y){ + nodrag=true; + } else { + nodrag=false; + } + + } + + function clickEdge() { + if (nodrag) { + if (this.classList.contains("edge-highlight")){ + this.classList.remove("edge-highlight"); + this.classList.remove("text-highlight-edges"); + } + else { + this.classList.add("edge-highlight"); + this.classList.add("text-highlight-edges"); + animateEdge(this); + } + } + } + + function clickNode() { + if (nodrag) { + var nodeName = this.childNodes[1].textContent; + // Escape special characters in the node name + var nodeNameEscaped = nodeName.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g, '\\$&'); + + var patroon = new RegExp('^' + nodeNameEscaped + '->|->' + nodeNameEscaped + '$|' + nodeNameEscaped + '--|--' + nodeNameEscaped + '$') + + if (this.classList.contains("node-highlight")) { + this.classList.remove("node-highlight"); + this.classList.remove("text-highlight-nodes"); + var edges = document.getElementsByClassName('edge'); + for (var i = 0; i < edges.length; i++) { + if (patroon.test(edges[i].childNodes[1].textContent)) { + edges[i].classList.remove("edge-highlight"); + edges[i].classList.remove("text-highlight-edges"); + } + } + } else { + this.classList.add("node-highlight"); + this.classList.add("text-highlight-nodes"); + var edges = document.getElementsByClassName('edge'); + for (var i = 0; i < edges.length; i++) { + if (patroon.test(edges[i].childNodes[1].textContent)) { + edges[i].classList.add("edge-highlight"); + edges[i].classList.add("text-highlight-edges"); + animateEdge(edges[i]); + } + } + } + } + } + + function animateEdge(edge){ + var path = edge.querySelector('path'); + var polygon = edge.querySelector('polygon'); + var length = path.getTotalLength(); + // Clear any previous transition + path.style.transition = path.style.WebkitTransition = 'none'; + if (polygon){polygon.style.transition = polygon.style.WebkitTransition = 'none';}; + // Set up the starting positions + path.style.strokeDasharray = length + ' ' + length; + path.style.strokeDashoffset = length; + if(polygon){polygon.style.opacity='0';}; + // Trigger a layout so styles are calculated & the browser + // picks up the starting position before animating + path.getBoundingClientRect(); + // Define our transition + path.style.transition = path.style.WebkitTransition = + 'stroke-dashoffset 2s ease-in-out'; + if (polygon){polygon.style.transition = polygon.style.WebkitTransition = + 'fill-opacity 1s ease-in-out 2s';}; + // Go! + path.style.strokeDashoffset = '0'; + if (polygon){setTimeout(function(){polygon.style.opacity='1';},2000)}; + } +} + +function adjustViewBox(svg) { + var viewBoxParts = svg.getAttribute("viewBox").split(" "); + var newYMin = parseFloat(viewBoxParts[1]) - 30; // Adjust this value as needed + var newYMax = parseFloat(viewBoxParts[3]) + 30; // Adjust this value as needed + var newXMax = Math.max(parseFloat(viewBoxParts[2]),240); + var newViewBox = viewBoxParts[0] + " " + newYMin + " " + newXMax + " " + newYMax; + svg.setAttribute("viewBox", newViewBox); +} diff --git a/src/sirocco/svg-interactive-style.css b/src/sirocco/svg-interactive-style.css new file mode 100644 index 0000000..465db6e --- /dev/null +++ b/src/sirocco/svg-interactive-style.css @@ -0,0 +1,85 @@ +/* https://github.com/BartBrood/Interactive-Graphviz-Diagrams */ + +/* MIT License */ + +/* Copyright (c) 2024 BartBrood */ + +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ + +/* The above copyright notice and this permission notice shall be included in all */ +/* copies or substantial portions of the Software. */ + +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ + + +/* The first 2 styles are hiding longer description texts on */ +/* nodes and edges, that are shown when nodes are clicked */ +/* .edge text{ */ +/* opacity: 0; */ +/* } */ +/* .node text:not(:first-of-type){ */ +/* opacity: 0; */ +/* } */ + +.text-highlight-nodes text{ + opacity: 1 !important; + stroke-width: 5; + font-size: 20px; + font-weight: bold; + fill: black; +} +.text-highlight-edges text{ + opacity: 1 !important; + stroke-width: 5; + font-size: 20px; + font-weight: bold; + fill: Indigo; +} +.edge-highlight path{ + opacity: 1; + stroke-width: 5; + stroke: crimson; +} +.edge-highlight polygon{ + opacity: 1; + stroke-width: 5; + stroke: crimson; +} +.node-highlight polygon{ + opacity: 1; + stroke-width: 5; + stroke: crimson; + z-index:99999; +} +.node-highlight ellipse{ + opacity: 1; + stroke-width: 5; + stroke: crimson; + z-index:99999; +} +.node-highlight path{ + opacity: 1; + stroke-width: 5; + stroke: crimson; + z-index:99999; +} +.compass { + fill: #fff; + stroke: #000; + stroke-width: 1; +} +.plus-minus { + fill: #fff; + pointer-events: none; +} diff --git a/src/sirocco/vizgraph.py b/src/sirocco/vizgraph.py new file mode 100644 index 0000000..b696f7f --- /dev/null +++ b/src/sirocco/vizgraph.py @@ -0,0 +1,113 @@ +from __future__ import annotations + +from colorsys import hsv_to_rgb +from pathlib import Path +from typing import TYPE_CHECKING, Any, ClassVar + +from lxml import etree +from pygraphviz import AGraph + +if TYPE_CHECKING: + from sirocco.core import Store +from sirocco.core import Workflow + + +def hsv_to_hex(h: float, s: float, v: float) -> str: + r, g, b = hsv_to_rgb(h, s, v) + return "#{:02x}{:02x}{:02x}".format(*map(round, (255 * r, 255 * g, 255 * b))) + + +def node_colors(h: float) -> dict[str:str]: + fill = hsv_to_hex(h / 365, 0.15, 1) + border = hsv_to_hex(h / 365, 1, 0.20) + font = hsv_to_hex(h / 365, 1, 0.15) + return {"fillcolor": fill, "color": border, "fontcolor": font} + + +class VizGraph: + """Class for visualizing a Sirocco workflow""" + + node_base_kw: ClassVar[dict[str:Any]] = {"style": "filled", "fontname": "Fira Sans", "fontsize": 14, "penwidth": 2} + edge_base_kw: ClassVar[dict[str:Any]] = {"color": "#77767B", "penwidth": 1.5} + data_node_base_kw: ClassVar[dict[str:Any]] = node_base_kw | {"shape": "ellipse"} + + data_av_node_kw: ClassVar[dict[str:Any]] = data_node_base_kw | node_colors(116) + data_gen_node_kw: ClassVar[dict[str:Any]] = data_node_base_kw | node_colors(214) + task_node_kw: ClassVar[dict[str:Any]] = node_base_kw | {"shape": "box"} | node_colors(354) + io_edge_kw: ClassVar[dict[str:Any]] = edge_base_kw + wait_on_edge_kw: ClassVar[dict[str:Any]] = edge_base_kw | {"style": "dashed"} + cluster_kw: ClassVar[dict[str:Any]] = {"bgcolor": "#F6F5F4", "color": None, "fontsize": 16} + + def __init__(self, name: str, cycles: Store, data: Store) -> None: + self.name = name + self.agraph = AGraph(name=name, fontname="Fira Sans", newrank=True) + for data_node in data.values(): + gv_kw = self.data_av_node_kw if data_node.available else self.data_gen_node_kw + tooltip = data_node.name if data_node.date is None else f"{data_node.name}\n {data_node.date}" + self.agraph.add_node(data_node, tooltip=tooltip, label=data_node.name, **gv_kw) + + k = 1 + for cycle in cycles.values(): + # NOTE: For some reason, clusters need to have a unique name that starts with 'cluster' + # otherwise they are not taken into account. Hence the k index. + cluster_nodes = [] + for task_node in cycle.tasks: + cluster_nodes.append(task_node) + tooltip = task_node.name if task_node.date is None else f"{task_node.name}\n {task_node.date}" + self.agraph.add_node(task_node, label=task_node.name, tooltip=tooltip, **self.task_node_kw) + for data_node in task_node.inputs: + self.agraph.add_edge(data_node, task_node, **self.io_edge_kw) + for data_node in task_node.outputs: + self.agraph.add_edge(task_node, data_node, **self.io_edge_kw) + cluster_nodes.append(data_node) + for wait_task_node in task_node.wait_on: + self.agraph.add_edge(wait_task_node, task_node, **self.wait_on_edge_kw) + cluster_label = cycle.name + if cycle.date is not None: + cluster_label += "\n" + cycle.date.isoformat() + self.agraph.add_subgraph( + cluster_nodes, + name=f"cluster_{cycle.name}_{k}", + clusterrank="global", + label=cluster_label, + tooltip=cluster_label, + **self.cluster_kw, + ) + k += 1 + + def draw(self, **kwargs): + # draw graphviz dot graph to svg file + self.agraph.layout(prog="dot") + file_path = Path(f"./{self.name}.svg") + self.agraph.draw(path=file_path, format="svg", **kwargs) + + # Add interactive capabilities to the svg graph thanks to + # https://github.com/BartBrood/dynamic-SVG-from-Graphviz + + # Parse svg + svg = etree.parse(file_path) # noqa: S320 this svg is safe as generated internaly + svg_root = svg.getroot() + # Add 'onload' tag + svg_root.set("onload", "addInteractivity(evt)") + # Add css style for interactivity + this_dir = Path(__file__).parent + style_file_path = this_dir / "svg-interactive-style.css" + node = etree.Element("style") + node.text = style_file_path.read_text() + svg_root.append(node) + # Add scripts + js_file_path = this_dir / "svg-interactive-script.js" + node = etree.Element("script") + node.text = etree.CDATA(js_file_path.read_text()) + svg_root.append(node) + + # write svg again + svg.write(file_path) + + @classmethod + def from_core_workflow(cls, workflow: Workflow): + return cls(workflow.name, workflow.cycles, workflow.data) + + @classmethod + def from_yaml(cls, config_path: str): + return cls.from_core_workflow(Workflow.from_yaml(config_path)) diff --git a/tests/test_wc_workflow.py b/tests/test_wc_workflow.py index 41e58ff..f6d7333 100644 --- a/tests/test_wc_workflow.py +++ b/tests/test_wc_workflow.py @@ -4,6 +4,7 @@ from sirocco.core import Workflow from sirocco.pretty_print import PrettyPrinter +from sirocco.vizgraph import VizGraph @pytest.fixture @@ -42,3 +43,8 @@ def test_parse_config_file(config_case, pprinter): def test_serialize_workflow(config_case): config_path, reference_path = config_case reference_path.write_text(pprinter.format(Workflow.from_yaml(config_path))) + + +def test_vizgraph(config_case): + config_path, _ = config_case + VizGraph.from_yaml(config_path).draw()