metasoarous / oz

Data visualizations in Clojure and ClojureScript using Vega and Vega-lite
Eclipse Public License 1.0
830 stars 75 forks source link

Unable to embed static rendering on Windows #194

Open LouDnl opened 2 years ago

LouDnl commented 2 years ago

Hi @metasoarous ,

As requested here is the info for this issue.

Description:

I can create a nice vega-lite bar graph, works fine. But for some reason I keep getting the following stacktrace in the background and I'm not even asking it to use CLI. And I am not using a static ambed afaik?

OS:

Os is Windows (development system), have not yet tried on linux deployment box.

Error:
 2022-10-03T17:17:17.465+02 MAIN - ERROR [oz.core:683] - Unable to execute static embed
 2022-10-03T17:17:17.501+02 MAIN - ERROR [oz.core:684] - 
    nrepl.middleware.interruptible-eval/evaluate/fn  interruptible_eval.clj:   87
                                               ...                              
                       clojure.core/with-bindings*                core.clj: 1990 (repeats 2 times)
                                clojure.core/apply                core.clj:  667
                                               ...                              
nrepl.middleware.interruptible-eval/evaluate/fn/fn  interruptible_eval.clj:   87
                                 clojure.core/eval                core.clj: 3215
                                               ...                              
                    aorta.service.graphs/eval34760               REPL Input     

    oz.core/embed-for-html                core.clj:  728
                             oz.core/embed-for-html                core.clj:  717
                              oz.core/compile-tags                core.clj:  523
                              clojure.walk/prewalk                walk.clj:   65
                           oz.core/compile-tags/fn                core.clj:  531
                             oz.core/compiled-form                core.clj:  513
                           clojure.core/partial/fn                core.clj: 2641
                           oz.core/embed-vega-form                core.clj:  672
                        oz.core/embed-vega-form/fn                core.clj:  678
                                   oz.core/compile                core.clj: 1096

                                                ...                              
                              oz.core/eval24056/fn                core.clj:  440
                                  oz.core/vega-cli                core.clj:  407
                       oz.core/vega-cli-installed?                core.clj:  215
                oz.core/-check-vega-cli-installed?                core.clj:  210
                                               ...                              
                             clojure.java.shell/sh               shell.clj:   79
                             clojure.java.shell/sh               shell.clj:  113
                            java.lang.Runtime.exec                              
                    java.lang.ProcessBuilder.start                              

                       java.lang.ProcessImpl.start                              
                       java.lang.ProcessImpl.<init>                              
                      java.lang.ProcessImpl.create                              
java.io.IOException: CreateProcess error=2, The system cannot find the file specified
java.io.IOException: Cannot run program "which": CreateProcess error=2, The system cannot find the file specified
Codeblock:

I can't share the whole codeblock due to code being private

(def plot [:vega-lite {;:$schema "https://vega.github.io/schema/vega-lite/v5.json",
               :description "Some description",
               :width 800,
               :height 200,
               :padding 5,
                :data {:values [{:instantie "SomeCompany",
                                 :melddatum "2022-02-03",
                                 :einddatum "2022-04-06"},
                                {:instantie "SomeOtherCompany",
                                 :melddatum "2022-03-01",
                                 :einddatum "2022-10-03"},
                                {:instantie "AnotherCompany",
                                 :melddatum "2022-04-01",
                                 :einddatum "2022-08-01"}]}
               :params [{:name "highlight",
                         :select {:type "point",
                                  :on "mouseover"}},
                        {:name "select",
                         :select "point"}],
               :mark {:type "bar",
                      ;; :fill "#4C78A8",
                      :stroke "black",
                      :cursor "pointer",
                      :tooltip true},
               :encoding {:x  {:timeUnit "yearmonthdate",
                               :type "temporal",
                               :field "melddatum"
                               :as "test"},
                          :x2 {:timeUnit "yearmonthdate",
                               :type "temporal",
                               :field "einddatum"},
                          :y  {:field "instantie",
                               :type "ordinal"},
                          :color {:field "instantie"},
                          :fillOpacity {:condition {:param "select",
                                                    :value 1},
                                        :value 0.3},
                          :strokeWidth {:condition [{:param "select",
                                                     :empty false,
                                                     :value 2},
                                                    {:param "highlight",
                                                     :empty false,
                                                     :value 1}],
                                        :value 0}},
               :config {:scale {:bandPaddingInner 0.2}}}])
Hiccup part embedded in clojure file
[:div.card.box-shadow
      [:script {:src "https://cdn.jsdelivr.net/npm/vega@5"}]
      [:script {:src "https://cdn.jsdelivr.net/npm/vega-lite@5"}]
      [:script {:src "https://cdn.jsdelivr.net/npm/vega-embed@6"}]
      [:h1 "Testpage"]
     (oz/embed-for-html plot)]
metasoarous commented 2 years ago

Hi @LouDnl. Thanks for posting here.

The problem here is that oz is trying to generate a static image to embed in the page, which then gets replaced by the live/interactive vega visualization once the dom loads up. To do this, it's incidentally calling out to the which command, which is a linux command.

I'm happy to accept a PR to make this code Windows compatible, but I don't use Windows, so I'm probably not the best person to work on this.

If you're fine not having the statically embedded image (which, to be honest, is a pretty minor convenience in most scenarios; it's mostly there so that if js is turned off, you can still see the plots, such as in Dropbox previews, or for converting to PDF), you can just keep using the existing code unaltered. If it's important to you though, you could try using the WSL for Windows, which was getting pretty good last time I tried it, and to my understanding has only gotten better.

Please let me know if I can be of further help with this.

Thanks again!

metasoarous commented 2 years ago

PS I think the correct behavior here would be for Oz to try to use darkstar to compile a static svg instead of png via the vega npm cli(s) if it's not able to find the latter. The default/fallback behavior here could probably use a review and revamp, as it doesn't always do what you'd want at the moment.

LouDnl commented 2 years ago

Hi @metasoarous , thanks for your reply. Is it possible to disable the local generation of a static image by supplying an option? I have tried to find it but failed. For security reasons I'm not really into code accessing local filesystems in a production environment.

LouDnl commented 2 years ago

I think I found a workaround, will use this:

(embed-vega-form {:vega-embed-opts {:live-embed? true :static-embed false}}
                   {:mode :hiccup
                    :doc [:vega-lite {:description "Bar chart with text labels. Set domain to make the frame cover the labels.",
                                      :data {:values [{:a "A", :b 28},
                                                      {:a "B", :b 55},
                                                      {:a "C", :b 43}]},
                                      :encoding {:y {:field "a", :type "nominal"},
                                                 :x {:field "b", :type "quantitative", :scale {:domain [0, 60]}}},
                                      :layer [{:mark "bar"}, {:mark {:type "text",
                                                                     :align "left",
                                                                     :baseline "middle",
                                                                     :dx 3},
                                                              :encoding {:text {:field "b", :type "quantitative"}}}]}]})
LouDnl commented 2 years ago

Almost, this works:

(oz/embed-vega-form {:vega-embed-opts {:live-embed? true :static-embed false}}
[:vega-lite {:description "Bar chart with text labels. Set domain to make the frame cover the labels.",
                                      :data {:values [{:a "A", :b 28},
                                                      {:a "B", :b 55},
                                                      {:a "C", :b 43}]},
                                      :encoding {:y {:field "a", :type "nominal"},
                                                 :x {:field "b", :type "quantitative", :scale {:domain [0, 60]}}},
                                      :layer [{:mark "bar"}, {:mark {:type "text",
                                                                     :align "left",
                                                                     :baseline "middle",
                                                                     :dx 3},
                                                              :encoding {:text {:field "b", :type "quantitative"}}}]}])
metasoarous commented 2 years ago

If you set :static-embed :svg it will use darkstar instead of npm, so won't touch the filesystem.