From 7735abb3893b977d839639dfeaa1c9f9fb8d217d Mon Sep 17 00:00:00 2001 From: David An Date: Tue, 6 Nov 2018 15:46:55 -0500 Subject: [PATCH 1/5] get-workflow-details endpoint now accepts includeKey/excludeKey/expandSubWorkflows args --- src/cljs/main/broadfcui/endpoints.cljs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/cljs/main/broadfcui/endpoints.cljs b/src/cljs/main/broadfcui/endpoints.cljs index c936e3a930..82c13eb7e7 100644 --- a/src/cljs/main/broadfcui/endpoints.cljs +++ b/src/cljs/main/broadfcui/endpoints.cljs @@ -1,5 +1,6 @@ (ns broadfcui.endpoints (:require + [clojure.string :as string] [broadfcui.config :as config] [broadfcui.utils :as utils] [broadfcui.utils.ajax :as ajax] @@ -140,7 +141,7 @@ (set (keys query-parameters))) (str "Malformed query parameters: " query-parameters)) {:path (str "/workspaces/" (id-path workspace-id) "/entityQuery/" type "?" - (clojure.string/join "&" (keep (fn [[k v]] + (string/join "&" (keep (fn [[k v]] (some->> v str not-empty (str k "="))) query-parameters))) :method :get}) @@ -212,10 +213,20 @@ {:path (str "/workspaces/" (id-path workspace-id) "/submissions/" submission-id) :method :get}) -(defn get-workflow-details [workspace-id submission-id workflow-id] - {:path (str "/workspaces/" (id-path workspace-id) "/submissions/" submission-id - "/workflows/" workflow-id) - :method :get}) +(defn get-workflow-details + ;; basic form: use defaults for all query params + ([workspace-id submission-id workflow-id] (get-workflow-details workspace-id submission-id workflow-id nil [] [])) + ;; includes form: specify which fields to include; defaults for everything else + ([workspace-id submission-id workflow-id include-keys] (get-workflow-details workspace-id submission-id workflow-id nil include-keys [])) + ;; flexible form: specify any/all of expand-subworkflows, include-keys, exclude-keys + ([workspace-id submission-id workflow-id expand-subworkflows include-keys exclude-keys] + (let [expand (when (some? expand-subworkflows) (str "&expandSubWorkflows=" expand-subworkflows)) + include (string/join (mapv #(str "&includeKey=" %) include-keys)) + exclude (string/join (mapv #(str "&excludeKey=" %) exclude-keys)) + querystring (str expand include exclude)] + {:path (str "/workspaces/" (id-path workspace-id) "/submissions/" submission-id + "/workflows/" workflow-id "?" querystring) + :method :get}))) (defn get-workflow-cost [workspace-id submission-id workflow-id] {:path (str "/workspaces/" (id-path workspace-id) "/submissions/" submission-id From a544ef5ebaa265bf3b5436dc981cd393677b3ad9 Mon Sep 17 00:00:00 2001 From: David An Date: Fri, 9 Nov 2018 12:16:32 -0500 Subject: [PATCH 2/5] specify which keys to return for workflow metadata --- .../page/workspace/monitor/workflow_details.cljs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cljs/main/broadfcui/page/workspace/monitor/workflow_details.cljs b/src/cljs/main/broadfcui/page/workspace/monitor/workflow_details.cljs index a5d70733a0..09ac1cee71 100644 --- a/src/cljs/main/broadfcui/page/workspace/monitor/workflow_details.cljs +++ b/src/cljs/main/broadfcui/page/workspace/monitor/workflow_details.cljs @@ -33,6 +33,10 @@ :link-label link-label)] (str gcs-uri)))) +(defonce default-metadata-includes ["backendLogs" "backendStatus" "end" "executionEvents" "executionStatus" "callCaching:hit" + "id" "inputs" "jobId" "outputs" "start" "status" "stderr" "stdout" "submission" + "submittedFiles:inputs" "workflowLog" "workflowName" "workflowRoot"]) + (defn getTimingDiagramHeight [chartContainer] (let [e (-> chartContainer (.-childNodes) (aget 0) (.-childNodes) @@ -224,7 +228,7 @@ (endpoints/call-ajax-orch {:endpoint (endpoints/get-workflow-details - (:workspace-id props) (:submission-id props) (:workflow-id props)) + (:workspace-id props) (:submission-id props) (:workflow-id props) default-metadata-includes) :on-done (fn [{:keys [success? get-parsed-response status-text raw-response]}] (swap! state assoc :server-response {:success? success? @@ -362,7 +366,7 @@ (endpoints/call-ajax-orch {:endpoint (endpoints/get-workflow-details - (:workspace-id props) (:submission-id props) (:workflow-id props)) + (:workspace-id props) (:submission-id props) (:workflow-id props) default-metadata-includes) :on-done (fn [{:keys [success? get-parsed-response status-text raw-response]}] (swap! state assoc :metadata-response {:success? success? From 653a2d18f813d5b893425bd13e3c0d1a810c40d4 Mon Sep 17 00:00:00 2001 From: David An Date: Tue, 13 Nov 2018 15:02:15 -0500 Subject: [PATCH 3/5] lazy-load the workflow timing diagram --- .../workspace/monitor/workflow_details.cljs | 90 +++++++++++++------ 1 file changed, 62 insertions(+), 28 deletions(-) diff --git a/src/cljs/main/broadfcui/page/workspace/monitor/workflow_details.cljs b/src/cljs/main/broadfcui/page/workspace/monitor/workflow_details.cljs index 09ac1cee71..ae2758c877 100644 --- a/src/cljs/main/broadfcui/page/workspace/monitor/workflow_details.cljs +++ b/src/cljs/main/broadfcui/page/workspace/monitor/workflow_details.cljs @@ -33,10 +33,13 @@ :link-label link-label)] (str gcs-uri)))) -(defonce default-metadata-includes ["backendLogs" "backendStatus" "end" "executionEvents" "executionStatus" "callCaching:hit" +(defonce default-metadata-includes ["backendLogs" "backendStatus" "end" "executionStatus" "callCaching:hit" "id" "inputs" "jobId" "outputs" "start" "status" "stderr" "stdout" "submission" "submittedFiles:inputs" "workflowLog" "workflowName" "workflowRoot"]) +(defonce metadata-timing-includes ["attempt" "description" "end" "endTime" "executionEvents" "executionStatus" + "shardIndex" "start" "startTime" "workflowName"]) + (defn getTimingDiagramHeight [chartContainer] (let [e (-> chartContainer (.-childNodes) (aget 0) (.-childNodes) @@ -86,34 +89,65 @@ :filterable? false} :columns (if (:call-detail? props) columns (cons task-column columns))}}])])])}) -(react/defc- WorkflowTiming +(react/defc- WorkflowTimingDiagram + {:render + (fn [{:keys [props state refs]}] + (let [{:keys [error? expanded? metadata-response]} @state + {:keys [workspace-id submission-id workflow]} props + workflow-name (workflow "workflowName") + workflow-id (workflow "id")] + (cond + (nil? metadata-response) + (spinner "Loading timing diagram...") + (not (:success? metadata-response)) + (style/create-inline-error-message (str "Error loading timing data: " (:response metadata-response))) + :else + (let [data (:raw-response metadata-response)] + [:div {} + (if error? + (style/create-inline-error-message "Error loading charts.") + [ScriptLoader + {:on-error #(swap! state assoc :error? true) + :on-load (fn [] + (js/google.charts.load "current" (clj->js {"packages" ["timeline"]})) + (js/google.charts.setOnLoadCallback + (fn [] + (let [chart-container (@refs "chart-container")] + (.timingDiagram js/window chart-container data workflow-name 100) + (let [height (getTimingDiagramHeight chart-container)] + (.timingDiagram js/window chart-container data workflow-name (+ 75 height))))))) + :path "https://www.gstatic.com/charts/loader.js"}]) + [:div {:ref "chart-container" :style {:paddingTop "0.5rem"}}]])))) + :component-did-mount + (fn [{:keys [props state]}] + (let [{:keys [workspace-id submission-id workflow]} props + workflow-name (workflow "name") + workflow-id (workflow "id")] + (endpoints/call-ajax-orch + {:endpoint + (endpoints/get-workflow-details workspace-id submission-id workflow-id metadata-timing-includes) + :on-done (fn [{:keys [success? get-parsed-response status-text raw-response]}] + (swap! state assoc :metadata-response + {:success? success? + :response (if success? (get-parsed-response false) status-text) + :raw-response raw-response}))})))}) + +(react/defc- WorkflowTimingContainer {:render (fn [{:keys [props state refs]}] - (let [{:keys [error? expanded?]} @state - {:keys [label data]} props] + (let [{:keys [expanded?]} @state + {:keys [workspace-id submission-id workflow label]} props] [:div {} - (create-field - label - (if (empty? data) - "Not Available" - (links/create-internal {:onClick #(swap! state update :expanded? not)} - (if expanded? "Hide" "Show")))) - (when expanded? - [:div {} - (if error? - (style/create-server-error-message "Error loading charts.") - [ScriptLoader - {:on-error #(swap! state assoc :error? true) - :on-load (fn [] - (js/google.charts.load "current" (clj->js {"packages" ["timeline"]})) - (js/google.charts.setOnLoadCallback - (fn [] - (let [chart-container (@refs "chart-container")] - (.timingDiagram js/window chart-container data workflow-name 100) - (let [height (getTimingDiagramHeight chart-container)] - (.timingDiagram js/window chart-container data workflow-name (+ 75 height))))))) - :path "https://www.gstatic.com/charts/loader.js"}]) - [:div {:ref "chart-container" :style {:paddingTop "0.5rem"}}]])]))}) + (create-field label + (links/create-internal {:onClick #(swap! state update :expanded? not)} + (if expanded? "Hide" "Show"))) + ;; use expanded? to show/hide the WorkflowTimingDiagram component via the DOM + [:div {:myattr "whereami" :style {:display (if expanded? "block" "none")}} + (when (some? expanded?) + ;; as long as the user has clicked to show the WorkflowTimingDiagram at least once, + ;; render it. After the first render, show/hide it via the DOM so we don't trigger + ;; its ajax call more than once. + [WorkflowTimingDiagram (utils/restructure workspace-id submission-id workflow)])]]))}) (defn- backend-logs [data workspace-id] (when-let [log-map (data "backendLogs")] @@ -200,7 +234,7 @@ :backgroundColor (:background-light style/colors)}} (create-field "Subworkflow ID" (links/create-external {:href (render-gcs-path subworkflow-path-components)} (workflow "id"))) (when (seq (workflow "calls")) - [WorkflowTiming {:label "Workflow Timing" :data raw-data :workflow-name workflow-name}]) + [WorkflowTimingContainer (merge {:label "Workflow Timing"} (utils/restructure workspace-id submission-id workflow))]) (when-let [failures (workflow "failures")] [Failures {:data failures}]) (when (seq (workflow "calls")) @@ -331,7 +365,7 @@ (when-let [workflowLog (workflow "workflowLog")] (create-field "Workflow Log" (display-value (:namespace workspace-id) workflowLog (str "workflow." (workflow "id") ".log")))) (when (seq (workflow "calls")) - [WorkflowTiming {:label "Workflow Timing" :data raw-data :workflow-name workflow-name}]) + [WorkflowTimingContainer (merge {:label "Workflow Timing"} (utils/restructure workspace-id submission-id workflow))]) (when-let [failures (workflow "failures")] [Failures {:data failures}]) (when (seq (workflow "calls")) From 73074508a1a7ff67cd64f0fedf05c6333d37f8de Mon Sep 17 00:00:00 2001 From: David An Date: Wed, 14 Nov 2018 10:34:32 -0500 Subject: [PATCH 4/5] remove debugging artifact --- .../main/broadfcui/page/workspace/monitor/workflow_details.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cljs/main/broadfcui/page/workspace/monitor/workflow_details.cljs b/src/cljs/main/broadfcui/page/workspace/monitor/workflow_details.cljs index ae2758c877..15b1e655b0 100644 --- a/src/cljs/main/broadfcui/page/workspace/monitor/workflow_details.cljs +++ b/src/cljs/main/broadfcui/page/workspace/monitor/workflow_details.cljs @@ -142,7 +142,7 @@ (links/create-internal {:onClick #(swap! state update :expanded? not)} (if expanded? "Hide" "Show"))) ;; use expanded? to show/hide the WorkflowTimingDiagram component via the DOM - [:div {:myattr "whereami" :style {:display (if expanded? "block" "none")}} + [:div {:style {:display (if expanded? "block" "none")}} (when (some? expanded?) ;; as long as the user has clicked to show the WorkflowTimingDiagram at least once, ;; render it. After the first render, show/hide it via the DOM so we don't trigger From 1fe4851a4138ccb835b272d1212a455797f2c348 Mon Sep 17 00:00:00 2001 From: David An Date: Wed, 14 Nov 2018 10:37:01 -0500 Subject: [PATCH 5/5] fix indentation --- src/cljs/main/broadfcui/endpoints.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cljs/main/broadfcui/endpoints.cljs b/src/cljs/main/broadfcui/endpoints.cljs index 82c13eb7e7..fe059720d4 100644 --- a/src/cljs/main/broadfcui/endpoints.cljs +++ b/src/cljs/main/broadfcui/endpoints.cljs @@ -142,8 +142,8 @@ (str "Malformed query parameters: " query-parameters)) {:path (str "/workspaces/" (id-path workspace-id) "/entityQuery/" type "?" (string/join "&" (keep (fn [[k v]] - (some->> v str not-empty (str k "="))) - query-parameters))) + (some->> v str not-empty (str k "="))) + query-parameters))) :method :get}) (defn get-entities-by-type [workspace-id]