You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by as...@apache.org on 2021/03/19 15:06:34 UTC

[airflow] 32/42: Remember expanded task groups in localStorage (#14661)

This is an automated email from the ASF dual-hosted git repository.

ash pushed a commit to branch v2-0-test
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 3a0fb37dd82724a4b0b602d436e6a0c464ba00d9
Author: yuqian90 <yu...@gmail.com>
AuthorDate: Fri Mar 12 23:11:58 2021 +0800

    Remember expanded task groups in localStorage (#14661)
    
    * Save expanded and focused state of TaskGroups in localStorage
    
    * Restore focus on refresh
    
    * Address style issues according to comments
    
    (cherry picked from commit 456a7ddfd1da797d493abd8a57d36d05424eaaa6)
---
 airflow/www/templates/airflow/graph.html | 117 +++++++++++++++++++++++++++++--
 1 file changed, 110 insertions(+), 7 deletions(-)

diff --git a/airflow/www/templates/airflow/graph.html b/airflow/www/templates/airflow/graph.html
index 844ce38..807cef1 100644
--- a/airflow/www/templates/airflow/graph.html
+++ b/airflow/www/templates/airflow/graph.html
@@ -235,7 +235,7 @@
         });
 
         d3.selectAll("g.node").on("mouseout", function (d) {
-          d3.select(this).selectAll("rect").style("stroke", null);
+          d3.select(this).selectAll("rect,circle").style("stroke", null);
           highlight_nodes(g.predecessors(d), null, initialStrokeWidth)
           highlight_nodes(g.successors(d), null, initialStrokeWidth)
           d3.selectAll("g.node")
@@ -244,6 +244,7 @@
             .style("stroke-width", initialStrokeWidth);
           d3.selectAll("g.edgePath")
             .style("opacity", 1);
+          localStorage.removeItem(focused_group_key(dag_id));
         });
         updateNodesStates(task_instances);
         setUpZoomSupport();
@@ -417,6 +418,8 @@
               .style("opacity", 1);
           d3.selectAll('.js-state-legend-item')
               .style("background-color", null);
+
+          localStorage.removeItem(focused_group_key(dag_id));
       }
 
       function focusState(state, node, color){
@@ -591,6 +594,22 @@
         return children
       }
 
+      // Return list of all task group ids in the given task group including the given group itself.
+      function get_all_group_ids(group) {
+        var children = [group.id];
+
+        for (const [key, val] of Object.entries(group.children)) {
+          if (val.children != undefined) {
+            // group
+            const sub_group_children = get_all_group_ids(val)
+            for (const id of sub_group_children) {
+              children.push(id);
+            }
+          }
+        }
+        return children;
+      }
+
 
       // Return the state for the node based on the state of its taskinstance or that of its
       // children if it's a group node
@@ -626,6 +645,16 @@
         return "no_status"
       }
 
+      // Returns the key used to store expanded task group ids in localStorage
+      function expanded_groups_key(dag_id) {
+          return `expanded_groups_${dag_id}`;
+      }
+
+      // Returns the key used to store the focused task group id in localStorage
+      function focused_group_key(dag_id) {
+          return `focused_group_${dag_id}`;
+      }
+
       // Focus the graph on the expanded/collapsed node
       function focus_group(node_id) {
         if(node_id != null && zoom != null) {
@@ -668,11 +697,13 @@
                       .style("opacity", 0.2).duration(duration)
               }
             });
+
+            localStorage.setItem(focused_group_key(dag_id), node_id);
         }
       }
 
       // Expands a group node
-      function expand_group(node_id, node) {
+      function expand_group(node_id, node, focus=true) {
         node.children.forEach(function (val) {
           // Set children nodes
           g.setNode(val.id, val.value)
@@ -706,17 +737,22 @@
         })
 
         draw()
-        focus_group(node_id)
+
+        if (focus) {
+          focus_group(node_id);
+        }
+
+        save_expanded_group(node_id)
     }
 
     // Remove the node with this node_id from g.
     function remove_node(node_id) {
-      if(g.hasNode(node_id)) {
+      if (g.hasNode(node_id)) {
           node = g.node(node_id)
           if(node.children != undefined) {
             // If the child is an expanded group node, remove children too.
             node.children.forEach(function (child) {
-              remove_node(child.id)
+              remove_node(child.id);
             })
           }
       }
@@ -745,10 +781,77 @@
 
         draw()
         focus_group(node_id)
+
+        remove_expanded_group(node_id, node);
       }
 
-      expand_group(null, nodes)
+    function get_saved_groups(dag_id) {
+        // expanded_groups is a Set
+        try {
+            var expanded_groups = new Set(JSON.parse(localStorage.getItem(expanded_groups_key(dag_id))));
+        } catch {
+            var expanded_groups = new Set();
+        }
+
+        return expanded_groups;
+    }
+
+    // Clean up invalid group_ids from saved_group_ids (e.g. due to DAG changes)
+    function prune_invalid_saved_group_ids() {
+        // All the group_ids in the whole DAG
+        const all_group_ids = new Set(get_all_group_ids(nodes));
+        var expanded_groups = get_saved_groups(dag_id);
+        expanded_groups = Array.from(expanded_groups).filter(group_id => all_group_ids.has(group_id));
+        localStorage.setItem(expanded_groups_key(dag_id), JSON.stringify(expanded_groups));
+    }
+
+    // Remember the expanded groups in local storage so that it can be used to restore the expanded state
+    // of task groups.
+    function save_expanded_group(node_id) {
+        // expanded_groups is a Set
+        var expanded_groups = get_saved_groups(dag_id);
+        expanded_groups.add(node_id)
+        localStorage.setItem(expanded_groups_key(dag_id), JSON.stringify(Array.from(expanded_groups)));
+    }
+
+    // Remove the node_id from the expanded state
+    function remove_expanded_group(node_id, node) {
+        var expanded_groups = get_saved_groups(dag_id);
+        const child_group_ids = get_all_group_ids(node);
+        child_group_ids.forEach(child_id => expanded_groups.delete(child_id));
+        localStorage.setItem(expanded_groups_key(dag_id), JSON.stringify(Array.from(expanded_groups)));
+    }
+
+    // Restore previously expanded task groups
+    function expand_saved_groups(expanded_groups, node) {
+        if (node.children == undefined) {
+            return;
+        }
+
+        node.children.forEach(function (child_node) {
+            if(expanded_groups.has(child_node.id)) {
+                expand_group(child_node.id, g.node(child_node.id), false);
+
+                expand_saved_groups(expanded_groups, child_node);
+            }
+        });
+    }
+
+    prune_invalid_saved_group_ids();
+    const focus_node_id = localStorage.getItem(focused_group_key(dag_id));
+    const expanded_groups = get_saved_groups(dag_id);
+
+    // Always expand the root node
+    expand_group(null, nodes);
+
+    // Expand the node that were previously expanded
+    expand_saved_groups(expanded_groups, nodes);
+
+    // Restore focus (if available)
+    if(g.hasNode(focus_node_id)) {
+      focus_group(focus_node_id);
+    }
 
-      initRefresh();
+    initRefresh();
   </script>
 {% endblock %}