SlideShare una empresa de Scribd logo
1 de 34
Descargar para leer sin conexión
Om Nom Nom Nom 
Anna Pawlicka 
Data Engineer 
@AnnaPawlicka
Dashboard 
• Communication tool 
• See progress and compare values 
• Show danger and success 
• Share results 
• Scan quickly
Why build your own? 
• We can never make a good dashboard that 
everyone will like 
• Backend and front-end written in Clojure 
• Clojurians like to build out of small components 
• Mix and match & reuse 
• It’s a good UI programming exercise
A few principles
Tables & Charts 
• Tables for values: 
Month Additons Deletions 
1 29 14 
2 68 34 
Month Additions Deletions 
• Charts for trends and overall comparison: 
1 29 14 
2 68 34 
vs. 
100 
75 
50 
25 
0 
April May June July 
200 
150 
100 
50 
0 
April May June July 
20 
15 
10 
5 
0 
0 3 6 9 12
Don’t visualise! 
14 
10.5 
7 
3.5 
Last week This week Variance 
14 8 - 57% 
14 
10.5 
7 
3.5 
0 
8 
14 
2014-10-27 2014-11-03 
0 
2014-10-27 2014-11-03 
vs.
Avoid pie charts 
Sci-Fi Drama Romance Action Comedy Horror 
Favourite Films 
18 
21 
26 
28 
76 
91 
Sci-Fi 
Drama 
Romance 
Action 
Comedy 
Horror 
21 
18 
28 
26 
76 
91 
0 25 50 75 100 
vs.
3D - why?? 
Region 1 Region 2 Region 3
Data-Ink Ratio 
100 
75 
50 
25 
0 
April May June July 
100 
75 
50 
25 
0 
April May June July
Step 1: Components
Facebook’s React 
• Solves complex UI rendering 
• Declarative framework 
• One way data binding 
• Maintains virtual DOM 
• Diffs between previous and next renders of a UI 
• Less code 
• Shorter time to update
Om 
• Entire state of the UI in a single piece of data 
• Immutable data structures = Reference equality check 
• No need to worry about optimisation 
• Snapshot table & free undo
Global state 
• Application state 
• Global state stored in an atom 
• Accessed and modified through cursors: 
(def app-state (atom {:films {:data [{:v 1} {:v 2}]}})) 
(-> cursor :films :data first) 
:value {:v 1} 
:path [:films :data 0] 
:state #<Atom: {:films ..}> 
Cursor 
• Updated using: 
(o m/update! cursor [:films :data] data) 
(om/transact! cursor [:films :data] #(conj % data))
Local & shared state 
• Local state 
• Local to single component 
• Best for transient state, flags 
• Updated by using: 
• Shared state 
• Accessible by the entire tree 
• Does not trigger renders 
(om/set-state! owner :value value)
Components <-> Widgets 
• D3.js 
• Data bound to DOM 
• Interactive - transformations driven by data 
• Huge community 
• Higher level libraries available 
• Dimple.js 
• Higher level library powered by d3 
• Interactive 
• Simple API 
• Access to underlying D3 functions
Step 2: Data
How fresh is fresh data? 
• You could refresh for new data 
• You could schedule to pull data at some intervals 
• Or you could use: 
• Long polling / HTTP streaming 
• WebSockets
WebSockets / Long polling 
• Http-kit: 
• Clojure HTTP server 
• Great out-of-the box support for WebSockets 
• HTTP streaming / Long polling 
• Sente: 
• Clojure + ClojureScript + core.async + ajax + WebSockets 
• Simple API 
• Client & server 
• There are also: Timbre, Jetty 7 websockets async
Step 3: Combine them into 
dashboard 
a.k.a. demo
Dev setup 
• Chestnut: 
• Figwheel - instant reloading ClojureScript, and CSS 
• Weasel - browser-connected REPL is also included 
• Heroku support
Communication between server and client 
• Server: 
(let [{:keys [ch-recv send-fn ajax-get-or-ws-handshake-fn]} (s/make-channel-socket {})] 
(def ring-ajax-get-ws ajax-get-or-ws-handshake-fn) 
(def ch-chsk ch-recv) ; ChannelSocket's receive channel 
(def chsk-send! send-fn)) ; ChannelSocket's send API fn 
• Client: 
(let [{:keys [chsk ch-recv send-fn]} (s/make-channel-socket! "/ws" {} {:type :auto})] 
(def chsk chsk) 
(def ch-chsk ch-recv) ; ChannelSocket's receive channel 
(def chsk-send! send-fn)) ; ChannelSocket's send API fn
• Server: 
(defmethod handle-event :dashboard/github-issues [{:keys [?data ring-req]}] 
(let [{:keys [url refresh-rate]} ?data] 
(while true 
(http/get url {} (fn [{:keys [status headers body error]}] 
(chsk-send! uid [:dashboard/github-issues body]))) 
(Thread/sleep refresh-rate)))) 
(defn event-loop [] 
(go (loop [ev-msg (<! ch-chsk)] 
(thread (handle-event ev-msg)) 
(recur (<! ch-chsk))))) 
• Client: 
(defmethod handle-event :dashboard/github-issues [[_ msg] app] 
(om/update! app [:repository :issues :updated-at] (new js/Date)) 
(om/update! app [:repository :issues :value] (count msg))) 
(defn event-loop [app] 
(go (loop [{:keys [event]} (<! ch-chsk)] 
… 
(handle-event payload app)))) 
(recur (<! ch-chsk)))))
(defonce app-state 
(atom {:repository {:header {:selected-week nil} 
:code-frequency {:div {} 
App model 
:event-toggle {:current :hover} 
:data [] 
:updated-at nil} 
:contributors {:view {:current :charts} 
:data [] 
:updated-at nil}}})) 
(defn main [] 
(let [event-chan (chan (sliding-buffer 100))] 
(om/root 
application 
app-state 
{:target (. js/document (getElementById "app")) 
Shared channels 
:shared {:event-chan event-chan :mult-chan (mult event-chan)}})))
(defn dashboard [app owner] 
(reify 
om/IRender 
(render [_] 
(html 
[:div (r/jumbotron {} (html [:h1 "Dashboard" [:small (om/build clock (:clock app))]])) 
(om/build repository-view (-> app :repository) 
{:opts {:url “https://api.github.com/repos/mastodonc/kixi.hecuba"}})])))) 
(defn application [app owner] 
(reify 
om/IWillMount 
(will-mount [_] 
(event-loop app owner)) 
om/IRender 
(render [_] 
(html 
[:div 
(case (:state app) 
Event loop 
:open (om/build dashboard app) 
:unknown [:div "Loading dashboard…"])]))))
(defn repository-view [cursor owner {:keys [url]}] 
(reify 
om/IDidMount 
(did-mount [_] 
(send-messages [{:k :dashboard/github-issues 
:url url}])) 
om/IRender 
(render [_] 
(let [{:keys [code-frequency contributors 
issues pulls]} cursor] 
(html 
[:div 
(p/panel {:header (om/build toggles (-> code-frequency 
:event-toggle))} 
(om/build charts/stacked-bar-chart code-frequency)) 
(p/panel {:header (om/build toggles (-> contributors :view)} 
(om/build code-frequency-stats contributors)) 
(om/build numbers/simple-stats-card issues) 
(om/build numbers/simple-stats-card pulls)])))))
(defmulti code-frequency-stats 
(fn [cursor owner] (-> cursor :view :current))) 
(defmethod code-frequency-stats :charts [cursor owner] 
(om/component 
(html 
[:div 
(when (seq (:data cursor)) 
(om/build chart-stats-view {:data (:data cursor) 
:div (:div cursor)} 
{:fn (parse-contributors :charts)}))]))) 
(defmethod code-frequency-stats :table [cursor owner] 
(om/component 
(html 
[:div 
(om/build table-stats-view (:data cursor) 
{:fn (parse-contributors :table)})]))) 
(defmethod code-frequency-stats :cards [cursor owner] 
(om/component 
(html 
[:div 
(om/build team-members-stats-view (:data cursor) 
{:fn (parse-contributors :table)})]))) 
Transform data before rendering 
it, without losing benefits of a 
cursor
(defn chart-stats [cursor owner {:keys [y-axis color]}] 
(reify 
om/IInitState 
(init-state [_] 
{:c (chan (sliding-buffer 10)) 
:value (-> cursor :data first :week)}) 
om/IWillMount 
(will-mount [_] 
(let [c (om/get-state owner :c) 
mult-chan (om/get-shared owner :mult-chan) 
week (header)] 
(tap mult-chan c) 
(go-loop [] 
(let [event-chan (om/get-state owner :c) 
{:keys [event v]} (<! event-chan)] 
(om/set-state! owner :value v) 
(om/update! week :selected-week (common/unparse-date v "yyyy-MM-dd"))) 
(recur)))) 
om/IRenderState 
… 
om/IWillUnmount 
(will-unmount [_] 
(untap (om/get-shared owner :mult-chan) (om/get-state owner :c))) 
…)) 
New channel that we tap to to 
our shared channel
(defn chart-stats [cursor owner {:keys [y-axis color]}] 
(reify 
om/IInitState 
… 
om/IWillMount 
… 
om/IRenderState 
(render-state [_ state] 
(html 
[:div 
(let [value (common/timestamp->value (:value state) 
"week" (:data cursor)) 
selected-event (om/observe owner (event-type))] 
(om/build charts/bar-chart {:data value :div (:div cursor)} 
{:opts {:y-axis y-axis 
:event-type (:current selected-event)}}))])) 
om/IWillUnmount 
…)) 
Reference cursor 
(defn event-type [] 
(om/ref-cursor (-> (om/root-cursor app-state) 
:repository 
:code-frequency 
:event-toggle)))
dimple chart 
(defn bar-chart 
[{:keys [data div]} owner {:keys [id] :as opts}] 
(reify 
om/IWillMount 
(will-mount [_] 
(.addEventListener js/window "resize" 
(fn [] 
(let [e (.getElementById js/document id) 
x (.-clientWidth e) 
y (.-clientHeight e)] 
(om/update! div :size {:width x :height y}))))) 
om/IRender 
(render [_] 
(html [:div {:id id}])) 
om/IDidMount 
(did-mount [_] 
(let [n (.getElementById js/document id)] 
(while (.hasChildNodes n) 
(.removeChild n (.-lastChild n)))) 
(draw-chart data div opts)) 
om/IDidUpdate 
(did-update [_ _ _] 
(let [n (.getElementById js/document id)] 
(while (.hasChildNodes n) 
(.removeChild n (.-lastChild n)))) 
(draw-chart data div opts))))
dimple chart 
(defn- draw-chart [data div {:keys [id bounds x-axis y-axis 
plot series event-type]}] 
(let [width (:width div) 
height (:height div) 
Chart (.-chart js/dimple) 
svg (.newSvg js/dimple (str "#" id) 
width height) 
dimple-chart (.setBounds (Chart. svg) (:x bounds) 
(:y bounds) (:width bounds) 
(:height bounds)) 
x (.addCategoryAxis dimple-chart "x" x-axis) 
y (.addMeasureAxis dimple-chart "y" y-axis) 
s (.addSeries dimple-chart series 
plot (clj->js [x y]))] 
(aset s "data" (clj->js data)) 
(.draw dimple-chart (when (= event-type :click) 1000))))
stacked bar chart 
(defn- draw [data hover-chan size id event-type] 
... 
(doto (.selectAll svg ".frequency") 
(-> (.data stacked) 
(.enter) 
… 
;; Event listeners 
(cond-> (= event-type :click) 
(.on "click" (fn [d] 
(put! hover-chan 
{:event :click 
:v (.-x d) 
:d d})))) 
(cond-> (= event-type :hover) 
(.on "mouseover" (fn [_] 
(put! hover-chan 
{:event :mouseover 
:v (.-x d) 
:d d})))))))
That’s all folks! 
• Try out all the amazing libraries out there 
• Don’t be afraid of JavaScript interop 
• Use Om with data visualisation libraries - it’s easy 
• Share your components 
• Share your tips and tricks 
• Don’t create 3D pie charts
Thank you!
Useful links 
• Om: https://github.com/swannodette/om 
• Sente: https://github.com/ptaoussanis/sente 
• Chestnut: https://github.com/plexus/chestnut 
• Demo: https://github.com/annapawlicka/pumpkin

Más contenido relacionado

La actualidad más candente

SparkSQL and Dataframe
SparkSQL and DataframeSparkSQL and Dataframe
SparkSQL and DataframeNamgee Lee
 
An Introduction to Higher Order Functions in Spark SQL with Herman van Hovell
An Introduction to Higher Order Functions in Spark SQL with Herman van HovellAn Introduction to Higher Order Functions in Spark SQL with Herman van Hovell
An Introduction to Higher Order Functions in Spark SQL with Herman van HovellDatabricks
 
Hive Functions Cheat Sheet
Hive Functions Cheat SheetHive Functions Cheat Sheet
Hive Functions Cheat SheetHortonworks
 
Time Series Meetup: Virtual Edition | July 2020
Time Series Meetup: Virtual Edition | July 2020Time Series Meetup: Virtual Edition | July 2020
Time Series Meetup: Virtual Edition | July 2020InfluxData
 
Monitoring Your ISP Using InfluxDB Cloud and Raspberry Pi
Monitoring Your ISP Using InfluxDB Cloud and Raspberry PiMonitoring Your ISP Using InfluxDB Cloud and Raspberry Pi
Monitoring Your ISP Using InfluxDB Cloud and Raspberry PiInfluxData
 
Scalding - the not-so-basics @ ScalaDays 2014
Scalding - the not-so-basics @ ScalaDays 2014Scalding - the not-so-basics @ ScalaDays 2014
Scalding - the not-so-basics @ ScalaDays 2014Konrad Malawski
 
Scaling up data science applications
Scaling up data science applicationsScaling up data science applications
Scaling up data science applicationsKexin Xie
 
Patterns and Operational Insights from the First Users of Delta Lake
Patterns and Operational Insights from the First Users of Delta LakePatterns and Operational Insights from the First Users of Delta Lake
Patterns and Operational Insights from the First Users of Delta LakeDatabricks
 
Anomaly Detection with Apache Spark
Anomaly Detection with Apache SparkAnomaly Detection with Apache Spark
Anomaly Detection with Apache SparkCloudera, Inc.
 
Horizontally Scalable Relational Databases with Spark: Spark Summit East talk...
Horizontally Scalable Relational Databases with Spark: Spark Summit East talk...Horizontally Scalable Relational Databases with Spark: Spark Summit East talk...
Horizontally Scalable Relational Databases with Spark: Spark Summit East talk...Spark Summit
 
User Defined Aggregation in Apache Spark: A Love Story
User Defined Aggregation in Apache Spark: A Love StoryUser Defined Aggregation in Apache Spark: A Love Story
User Defined Aggregation in Apache Spark: A Love StoryDatabricks
 
Unsupervised Learning with Apache Spark
Unsupervised Learning with Apache SparkUnsupervised Learning with Apache Spark
Unsupervised Learning with Apache SparkDB Tsai
 
A Rusty introduction to Apache Arrow and how it applies to a time series dat...
A Rusty introduction to Apache Arrow and how it applies to a  time series dat...A Rusty introduction to Apache Arrow and how it applies to a  time series dat...
A Rusty introduction to Apache Arrow and how it applies to a time series dat...Andrew Lamb
 
Map reduce: beyond word count
Map reduce: beyond word countMap reduce: beyond word count
Map reduce: beyond word countJeff Patti
 
Spark meetup v2.0.5
Spark meetup v2.0.5Spark meetup v2.0.5
Spark meetup v2.0.5Yan Zhou
 
Scio - Moving to Google Cloud, A Spotify Story
 Scio - Moving to Google Cloud, A Spotify Story Scio - Moving to Google Cloud, A Spotify Story
Scio - Moving to Google Cloud, A Spotify StoryNeville Li
 
Easy, scalable, fault tolerant stream processing with structured streaming - ...
Easy, scalable, fault tolerant stream processing with structured streaming - ...Easy, scalable, fault tolerant stream processing with structured streaming - ...
Easy, scalable, fault tolerant stream processing with structured streaming - ...Databricks
 

La actualidad más candente (20)

SparkSQL and Dataframe
SparkSQL and DataframeSparkSQL and Dataframe
SparkSQL and Dataframe
 
An Introduction to Higher Order Functions in Spark SQL with Herman van Hovell
An Introduction to Higher Order Functions in Spark SQL with Herman van HovellAn Introduction to Higher Order Functions in Spark SQL with Herman van Hovell
An Introduction to Higher Order Functions in Spark SQL with Herman van Hovell
 
Hive Functions Cheat Sheet
Hive Functions Cheat SheetHive Functions Cheat Sheet
Hive Functions Cheat Sheet
 
Spark etl
Spark etlSpark etl
Spark etl
 
Time Series Meetup: Virtual Edition | July 2020
Time Series Meetup: Virtual Edition | July 2020Time Series Meetup: Virtual Edition | July 2020
Time Series Meetup: Virtual Edition | July 2020
 
Monitoring Your ISP Using InfluxDB Cloud and Raspberry Pi
Monitoring Your ISP Using InfluxDB Cloud and Raspberry PiMonitoring Your ISP Using InfluxDB Cloud and Raspberry Pi
Monitoring Your ISP Using InfluxDB Cloud and Raspberry Pi
 
Scalding - the not-so-basics @ ScalaDays 2014
Scalding - the not-so-basics @ ScalaDays 2014Scalding - the not-so-basics @ ScalaDays 2014
Scalding - the not-so-basics @ ScalaDays 2014
 
Scaling up data science applications
Scaling up data science applicationsScaling up data science applications
Scaling up data science applications
 
Patterns and Operational Insights from the First Users of Delta Lake
Patterns and Operational Insights from the First Users of Delta LakePatterns and Operational Insights from the First Users of Delta Lake
Patterns and Operational Insights from the First Users of Delta Lake
 
Anomaly Detection with Apache Spark
Anomaly Detection with Apache SparkAnomaly Detection with Apache Spark
Anomaly Detection with Apache Spark
 
Horizontally Scalable Relational Databases with Spark: Spark Summit East talk...
Horizontally Scalable Relational Databases with Spark: Spark Summit East talk...Horizontally Scalable Relational Databases with Spark: Spark Summit East talk...
Horizontally Scalable Relational Databases with Spark: Spark Summit East talk...
 
User Defined Aggregation in Apache Spark: A Love Story
User Defined Aggregation in Apache Spark: A Love StoryUser Defined Aggregation in Apache Spark: A Love Story
User Defined Aggregation in Apache Spark: A Love Story
 
Unsupervised Learning with Apache Spark
Unsupervised Learning with Apache SparkUnsupervised Learning with Apache Spark
Unsupervised Learning with Apache Spark
 
Scio
ScioScio
Scio
 
A Rusty introduction to Apache Arrow and how it applies to a time series dat...
A Rusty introduction to Apache Arrow and how it applies to a  time series dat...A Rusty introduction to Apache Arrow and how it applies to a  time series dat...
A Rusty introduction to Apache Arrow and how it applies to a time series dat...
 
Map reduce: beyond word count
Map reduce: beyond word countMap reduce: beyond word count
Map reduce: beyond word count
 
Spark meetup v2.0.5
Spark meetup v2.0.5Spark meetup v2.0.5
Spark meetup v2.0.5
 
Scio - Moving to Google Cloud, A Spotify Story
 Scio - Moving to Google Cloud, A Spotify Story Scio - Moving to Google Cloud, A Spotify Story
Scio - Moving to Google Cloud, A Spotify Story
 
Streaming SQL
Streaming SQLStreaming SQL
Streaming SQL
 
Easy, scalable, fault tolerant stream processing with structured streaming - ...
Easy, scalable, fault tolerant stream processing with structured streaming - ...Easy, scalable, fault tolerant stream processing with structured streaming - ...
Easy, scalable, fault tolerant stream processing with structured streaming - ...
 

Destacado

Reactive data visualisations with Om
Reactive data visualisations with OmReactive data visualisations with Om
Reactive data visualisations with OmAnna Pawlicka
 
Visualising Data on Interactive Maps
Visualising Data on Interactive MapsVisualising Data on Interactive Maps
Visualising Data on Interactive MapsAnna Pawlicka
 
Cassandra and Clojure
Cassandra and ClojureCassandra and Clojure
Cassandra and Clojurenickmbailey
 
ClojureBridge In Practice 2016
ClojureBridge In Practice 2016ClojureBridge In Practice 2016
ClojureBridge In Practice 2016Anna Pawlicka
 
Clojure for Data Science
Clojure for Data ScienceClojure for Data Science
Clojure for Data Sciencehenrygarner
 
Study: The Future of VR, AR and Self-Driving Cars
Study: The Future of VR, AR and Self-Driving CarsStudy: The Future of VR, AR and Self-Driving Cars
Study: The Future of VR, AR and Self-Driving CarsLinkedIn
 
Hype vs. Reality: The AI Explainer
Hype vs. Reality: The AI ExplainerHype vs. Reality: The AI Explainer
Hype vs. Reality: The AI ExplainerLuminary Labs
 

Destacado (7)

Reactive data visualisations with Om
Reactive data visualisations with OmReactive data visualisations with Om
Reactive data visualisations with Om
 
Visualising Data on Interactive Maps
Visualising Data on Interactive MapsVisualising Data on Interactive Maps
Visualising Data on Interactive Maps
 
Cassandra and Clojure
Cassandra and ClojureCassandra and Clojure
Cassandra and Clojure
 
ClojureBridge In Practice 2016
ClojureBridge In Practice 2016ClojureBridge In Practice 2016
ClojureBridge In Practice 2016
 
Clojure for Data Science
Clojure for Data ScienceClojure for Data Science
Clojure for Data Science
 
Study: The Future of VR, AR and Self-Driving Cars
Study: The Future of VR, AR and Self-Driving CarsStudy: The Future of VR, AR and Self-Driving Cars
Study: The Future of VR, AR and Self-Driving Cars
 
Hype vs. Reality: The AI Explainer
Hype vs. Reality: The AI ExplainerHype vs. Reality: The AI Explainer
Hype vs. Reality: The AI Explainer
 

Similar a Build Interactive Dashboards with Clojure and Om

5 x HTML5 worth using in APEX (5)
5 x HTML5 worth using in APEX (5)5 x HTML5 worth using in APEX (5)
5 x HTML5 worth using in APEX (5)Christian Rokitta
 
SVCC 2013 D3.js Presentation (10/05/2013)
SVCC 2013 D3.js Presentation (10/05/2013)SVCC 2013 D3.js Presentation (10/05/2013)
SVCC 2013 D3.js Presentation (10/05/2013)Oswald Campesato
 
Building a Sustainable Data Platform on AWS
Building a Sustainable Data Platform on AWSBuilding a Sustainable Data Platform on AWS
Building a Sustainable Data Platform on AWSSmartNews, Inc.
 
Scalding big ADta
Scalding big ADtaScalding big ADta
Scalding big ADtab0ris_1
 
Data visualization in python/Django
Data visualization in python/DjangoData visualization in python/Django
Data visualization in python/Djangokenluck2001
 
Architecture for scalable Angular applications
Architecture for scalable Angular applicationsArchitecture for scalable Angular applications
Architecture for scalable Angular applicationsPaweł Żurowski
 
Getting Reactive with Cycle.js and xstream
Getting Reactive with Cycle.js and xstreamGetting Reactive with Cycle.js and xstream
Getting Reactive with Cycle.js and xstreamSteve Lee
 
PyCon SG x Jublia - Building a simple-to-use Database Management tool
PyCon SG x Jublia - Building a simple-to-use Database Management toolPyCon SG x Jublia - Building a simple-to-use Database Management tool
PyCon SG x Jublia - Building a simple-to-use Database Management toolCrea Very
 
Big Data for each one of us
Big Data for each one of usBig Data for each one of us
Big Data for each one of usOSCON Byrum
 
ClojureScript for the web
ClojureScript for the webClojureScript for the web
ClojureScript for the webMichiel Borkent
 
Reactive programming every day
Reactive programming every dayReactive programming every day
Reactive programming every dayVadym Khondar
 
Graph computation
Graph computationGraph computation
Graph computationSigmoid
 
Art & music vs Google App Engine
Art & music vs Google App EngineArt & music vs Google App Engine
Art & music vs Google App Enginethomas alisi
 
Cross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineCross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineAndy McKay
 
FP - Découverte de Play Framework Scala
FP - Découverte de Play Framework ScalaFP - Découverte de Play Framework Scala
FP - Découverte de Play Framework ScalaKévin Margueritte
 

Similar a Build Interactive Dashboards with Clojure and Om (20)

5 x HTML5 worth using in APEX (5)
5 x HTML5 worth using in APEX (5)5 x HTML5 worth using in APEX (5)
5 x HTML5 worth using in APEX (5)
 
Svcc 2013-d3
Svcc 2013-d3Svcc 2013-d3
Svcc 2013-d3
 
SVCC 2013 D3.js Presentation (10/05/2013)
SVCC 2013 D3.js Presentation (10/05/2013)SVCC 2013 D3.js Presentation (10/05/2013)
SVCC 2013 D3.js Presentation (10/05/2013)
 
Building a Sustainable Data Platform on AWS
Building a Sustainable Data Platform on AWSBuilding a Sustainable Data Platform on AWS
Building a Sustainable Data Platform on AWS
 
Scalding big ADta
Scalding big ADtaScalding big ADta
Scalding big ADta
 
Data visualization in python/Django
Data visualization in python/DjangoData visualization in python/Django
Data visualization in python/Django
 
Architecture for scalable Angular applications
Architecture for scalable Angular applicationsArchitecture for scalable Angular applications
Architecture for scalable Angular applications
 
Couchbas for dummies
Couchbas for dummiesCouchbas for dummies
Couchbas for dummies
 
Getting Reactive with Cycle.js and xstream
Getting Reactive with Cycle.js and xstreamGetting Reactive with Cycle.js and xstream
Getting Reactive with Cycle.js and xstream
 
PyCon SG x Jublia - Building a simple-to-use Database Management tool
PyCon SG x Jublia - Building a simple-to-use Database Management toolPyCon SG x Jublia - Building a simple-to-use Database Management tool
PyCon SG x Jublia - Building a simple-to-use Database Management tool
 
Big Data for each one of us
Big Data for each one of usBig Data for each one of us
Big Data for each one of us
 
Yavorsky
YavorskyYavorsky
Yavorsky
 
ClojureScript for the web
ClojureScript for the webClojureScript for the web
ClojureScript for the web
 
huhu
huhuhuhu
huhu
 
Learning with F#
Learning with F#Learning with F#
Learning with F#
 
Reactive programming every day
Reactive programming every dayReactive programming every day
Reactive programming every day
 
Graph computation
Graph computationGraph computation
Graph computation
 
Art & music vs Google App Engine
Art & music vs Google App EngineArt & music vs Google App Engine
Art & music vs Google App Engine
 
Cross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineCross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App Engine
 
FP - Découverte de Play Framework Scala
FP - Découverte de Play Framework ScalaFP - Découverte de Play Framework Scala
FP - Découverte de Play Framework Scala
 

Último

IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhisoniya singh
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?XfilesPro
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxOnBoard
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 

Último (20)

IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptx
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 

Build Interactive Dashboards with Clojure and Om

  • 1. Om Nom Nom Nom Anna Pawlicka Data Engineer @AnnaPawlicka
  • 2. Dashboard • Communication tool • See progress and compare values • Show danger and success • Share results • Scan quickly
  • 3. Why build your own? • We can never make a good dashboard that everyone will like • Backend and front-end written in Clojure • Clojurians like to build out of small components • Mix and match & reuse • It’s a good UI programming exercise
  • 5. Tables & Charts • Tables for values: Month Additons Deletions 1 29 14 2 68 34 Month Additions Deletions • Charts for trends and overall comparison: 1 29 14 2 68 34 vs. 100 75 50 25 0 April May June July 200 150 100 50 0 April May June July 20 15 10 5 0 0 3 6 9 12
  • 6. Don’t visualise! 14 10.5 7 3.5 Last week This week Variance 14 8 - 57% 14 10.5 7 3.5 0 8 14 2014-10-27 2014-11-03 0 2014-10-27 2014-11-03 vs.
  • 7. Avoid pie charts Sci-Fi Drama Romance Action Comedy Horror Favourite Films 18 21 26 28 76 91 Sci-Fi Drama Romance Action Comedy Horror 21 18 28 26 76 91 0 25 50 75 100 vs.
  • 8. 3D - why?? Region 1 Region 2 Region 3
  • 9. Data-Ink Ratio 100 75 50 25 0 April May June July 100 75 50 25 0 April May June July
  • 11. Facebook’s React • Solves complex UI rendering • Declarative framework • One way data binding • Maintains virtual DOM • Diffs between previous and next renders of a UI • Less code • Shorter time to update
  • 12. Om • Entire state of the UI in a single piece of data • Immutable data structures = Reference equality check • No need to worry about optimisation • Snapshot table & free undo
  • 13. Global state • Application state • Global state stored in an atom • Accessed and modified through cursors: (def app-state (atom {:films {:data [{:v 1} {:v 2}]}})) (-> cursor :films :data first) :value {:v 1} :path [:films :data 0] :state #<Atom: {:films ..}> Cursor • Updated using: (o m/update! cursor [:films :data] data) (om/transact! cursor [:films :data] #(conj % data))
  • 14. Local & shared state • Local state • Local to single component • Best for transient state, flags • Updated by using: • Shared state • Accessible by the entire tree • Does not trigger renders (om/set-state! owner :value value)
  • 15. Components <-> Widgets • D3.js • Data bound to DOM • Interactive - transformations driven by data • Huge community • Higher level libraries available • Dimple.js • Higher level library powered by d3 • Interactive • Simple API • Access to underlying D3 functions
  • 17. How fresh is fresh data? • You could refresh for new data • You could schedule to pull data at some intervals • Or you could use: • Long polling / HTTP streaming • WebSockets
  • 18. WebSockets / Long polling • Http-kit: • Clojure HTTP server • Great out-of-the box support for WebSockets • HTTP streaming / Long polling • Sente: • Clojure + ClojureScript + core.async + ajax + WebSockets • Simple API • Client & server • There are also: Timbre, Jetty 7 websockets async
  • 19. Step 3: Combine them into dashboard a.k.a. demo
  • 20. Dev setup • Chestnut: • Figwheel - instant reloading ClojureScript, and CSS • Weasel - browser-connected REPL is also included • Heroku support
  • 21. Communication between server and client • Server: (let [{:keys [ch-recv send-fn ajax-get-or-ws-handshake-fn]} (s/make-channel-socket {})] (def ring-ajax-get-ws ajax-get-or-ws-handshake-fn) (def ch-chsk ch-recv) ; ChannelSocket's receive channel (def chsk-send! send-fn)) ; ChannelSocket's send API fn • Client: (let [{:keys [chsk ch-recv send-fn]} (s/make-channel-socket! "/ws" {} {:type :auto})] (def chsk chsk) (def ch-chsk ch-recv) ; ChannelSocket's receive channel (def chsk-send! send-fn)) ; ChannelSocket's send API fn
  • 22. • Server: (defmethod handle-event :dashboard/github-issues [{:keys [?data ring-req]}] (let [{:keys [url refresh-rate]} ?data] (while true (http/get url {} (fn [{:keys [status headers body error]}] (chsk-send! uid [:dashboard/github-issues body]))) (Thread/sleep refresh-rate)))) (defn event-loop [] (go (loop [ev-msg (<! ch-chsk)] (thread (handle-event ev-msg)) (recur (<! ch-chsk))))) • Client: (defmethod handle-event :dashboard/github-issues [[_ msg] app] (om/update! app [:repository :issues :updated-at] (new js/Date)) (om/update! app [:repository :issues :value] (count msg))) (defn event-loop [app] (go (loop [{:keys [event]} (<! ch-chsk)] … (handle-event payload app)))) (recur (<! ch-chsk)))))
  • 23. (defonce app-state (atom {:repository {:header {:selected-week nil} :code-frequency {:div {} App model :event-toggle {:current :hover} :data [] :updated-at nil} :contributors {:view {:current :charts} :data [] :updated-at nil}}})) (defn main [] (let [event-chan (chan (sliding-buffer 100))] (om/root application app-state {:target (. js/document (getElementById "app")) Shared channels :shared {:event-chan event-chan :mult-chan (mult event-chan)}})))
  • 24. (defn dashboard [app owner] (reify om/IRender (render [_] (html [:div (r/jumbotron {} (html [:h1 "Dashboard" [:small (om/build clock (:clock app))]])) (om/build repository-view (-> app :repository) {:opts {:url “https://api.github.com/repos/mastodonc/kixi.hecuba"}})])))) (defn application [app owner] (reify om/IWillMount (will-mount [_] (event-loop app owner)) om/IRender (render [_] (html [:div (case (:state app) Event loop :open (om/build dashboard app) :unknown [:div "Loading dashboard…"])]))))
  • 25. (defn repository-view [cursor owner {:keys [url]}] (reify om/IDidMount (did-mount [_] (send-messages [{:k :dashboard/github-issues :url url}])) om/IRender (render [_] (let [{:keys [code-frequency contributors issues pulls]} cursor] (html [:div (p/panel {:header (om/build toggles (-> code-frequency :event-toggle))} (om/build charts/stacked-bar-chart code-frequency)) (p/panel {:header (om/build toggles (-> contributors :view)} (om/build code-frequency-stats contributors)) (om/build numbers/simple-stats-card issues) (om/build numbers/simple-stats-card pulls)])))))
  • 26. (defmulti code-frequency-stats (fn [cursor owner] (-> cursor :view :current))) (defmethod code-frequency-stats :charts [cursor owner] (om/component (html [:div (when (seq (:data cursor)) (om/build chart-stats-view {:data (:data cursor) :div (:div cursor)} {:fn (parse-contributors :charts)}))]))) (defmethod code-frequency-stats :table [cursor owner] (om/component (html [:div (om/build table-stats-view (:data cursor) {:fn (parse-contributors :table)})]))) (defmethod code-frequency-stats :cards [cursor owner] (om/component (html [:div (om/build team-members-stats-view (:data cursor) {:fn (parse-contributors :table)})]))) Transform data before rendering it, without losing benefits of a cursor
  • 27. (defn chart-stats [cursor owner {:keys [y-axis color]}] (reify om/IInitState (init-state [_] {:c (chan (sliding-buffer 10)) :value (-> cursor :data first :week)}) om/IWillMount (will-mount [_] (let [c (om/get-state owner :c) mult-chan (om/get-shared owner :mult-chan) week (header)] (tap mult-chan c) (go-loop [] (let [event-chan (om/get-state owner :c) {:keys [event v]} (<! event-chan)] (om/set-state! owner :value v) (om/update! week :selected-week (common/unparse-date v "yyyy-MM-dd"))) (recur)))) om/IRenderState … om/IWillUnmount (will-unmount [_] (untap (om/get-shared owner :mult-chan) (om/get-state owner :c))) …)) New channel that we tap to to our shared channel
  • 28. (defn chart-stats [cursor owner {:keys [y-axis color]}] (reify om/IInitState … om/IWillMount … om/IRenderState (render-state [_ state] (html [:div (let [value (common/timestamp->value (:value state) "week" (:data cursor)) selected-event (om/observe owner (event-type))] (om/build charts/bar-chart {:data value :div (:div cursor)} {:opts {:y-axis y-axis :event-type (:current selected-event)}}))])) om/IWillUnmount …)) Reference cursor (defn event-type [] (om/ref-cursor (-> (om/root-cursor app-state) :repository :code-frequency :event-toggle)))
  • 29. dimple chart (defn bar-chart [{:keys [data div]} owner {:keys [id] :as opts}] (reify om/IWillMount (will-mount [_] (.addEventListener js/window "resize" (fn [] (let [e (.getElementById js/document id) x (.-clientWidth e) y (.-clientHeight e)] (om/update! div :size {:width x :height y}))))) om/IRender (render [_] (html [:div {:id id}])) om/IDidMount (did-mount [_] (let [n (.getElementById js/document id)] (while (.hasChildNodes n) (.removeChild n (.-lastChild n)))) (draw-chart data div opts)) om/IDidUpdate (did-update [_ _ _] (let [n (.getElementById js/document id)] (while (.hasChildNodes n) (.removeChild n (.-lastChild n)))) (draw-chart data div opts))))
  • 30. dimple chart (defn- draw-chart [data div {:keys [id bounds x-axis y-axis plot series event-type]}] (let [width (:width div) height (:height div) Chart (.-chart js/dimple) svg (.newSvg js/dimple (str "#" id) width height) dimple-chart (.setBounds (Chart. svg) (:x bounds) (:y bounds) (:width bounds) (:height bounds)) x (.addCategoryAxis dimple-chart "x" x-axis) y (.addMeasureAxis dimple-chart "y" y-axis) s (.addSeries dimple-chart series plot (clj->js [x y]))] (aset s "data" (clj->js data)) (.draw dimple-chart (when (= event-type :click) 1000))))
  • 31. stacked bar chart (defn- draw [data hover-chan size id event-type] ... (doto (.selectAll svg ".frequency") (-> (.data stacked) (.enter) … ;; Event listeners (cond-> (= event-type :click) (.on "click" (fn [d] (put! hover-chan {:event :click :v (.-x d) :d d})))) (cond-> (= event-type :hover) (.on "mouseover" (fn [_] (put! hover-chan {:event :mouseover :v (.-x d) :d d})))))))
  • 32. That’s all folks! • Try out all the amazing libraries out there • Don’t be afraid of JavaScript interop • Use Om with data visualisation libraries - it’s easy • Share your components • Share your tips and tricks • Don’t create 3D pie charts
  • 34. Useful links • Om: https://github.com/swannodette/om • Sente: https://github.com/ptaoussanis/sente • Chestnut: https://github.com/plexus/chestnut • Demo: https://github.com/annapawlicka/pumpkin