Synth, sample and param editors. Some UI cleanup.

--- sonic-jam/src/sonic_jam/core.cljs
+++ sonic-jam/src/sonic_jam/core.cljs
@@ -44,127 +44,207 @@
 (declare grid-view)

 (defn param-editor [{:keys [cursor id set-state-ch]} owner]
-  (reify
-    om/IInitState
-    (init-state [_]
-      {:state :init})
-    om/IRenderState
-    (render-state [this state]
-      (let [commit #(do
-                      (om/update-state! owner (fn [s] (merge s {:state :init})))
-                      (om/transact! cursor (fn [c] (assoc c (:field state) (:text state))))
-                      (go (>! set-state-ch id)))]
-        (condp = (:state state)
-          :init (dom/table nil
-                           (apply dom/tbody nil
-                                  (concat
-                                   (map (fn [k v] (dom/tr nil
-                                                          (dom/td nil k)
-                                                          (dom/td #js {:onClick #(om/update-state! owner
-                                                                                (fn [s] (merge s {:state :editing
-                                                                                :text v
-                                                                                :field k})))}
-                                                                  v)))
-                                        (keys cursor)
-                                        (vals cursor))
-                                   [(dom/td #js {:onClick #(om/update-state! owner
-                                                                             (fn [s] (merge s {:state :adding-field
-                                                                                :text nil
-                                                                                :field ""})))}
-                                            "{+}")])))
-          :editing (dom/table nil
-                              (apply dom/tbody nil
+  (let [cursor (get cursor "params")]
+    (reify
+      om/IInitState
+      (init-state [_]
+        {:state :init})
+      om/IRenderState
+      (render-state [this state]
+        (let [commit #(do
+                        (om/update-state! owner (fn [s] (merge s {:state :open})))
+                        (om/transact! cursor (fn [c] (assoc c (:field state) (:text state))))
+                        (go (>! set-state-ch id)))
+              closer #js {:onClick #(om/update-state! owner (fn [s] (merge s {:state :init})))}
+              canceller #js {:onClick #(om/update-state! owner (fn [s] (merge s {:state :open})))}]
+          (condp = (:state state)
+            :init (dom/span #js {:onClick #(om/update-state! owner (fn [s] (merge s {:state :open})))} "{..}")
+            :open (dom/table nil
+                             (apply dom/tbody nil
+                                    (concat
+                                     [(dom/tr nil
+                                              (dom/td closer "{")
+                                              (dom/td nil nil)
+                                              (dom/td nil nil))]
+                                     (map (fn [k v] (dom/tr nil
+                                                            (dom/td nil nil)
+                                                            (dom/td nil (str k ":"))
+                                                            (dom/td #js {:onClick #(om/update-state! owner
+                                                                                (fn [s] (merge s {:state :editing
+                                                                                :text v
+                                                                                :field k})))}
+                                                                    v)))
+                                          (keys cursor)
+                                          (vals cursor))
+                                     [(dom/tr nil
+                                              (dom/td nil nil)
+                                              (dom/td #js {:onClick #(om/update-state! owner
+                                                                                (fn [s] (merge s {:state :adding-field
+                                                                                :text nil
+                                                                                :field ""})))}
+                                                      "[+]")
+                                              (dom/td nil nil))
+                                      (dom/tr nil
+                                              (dom/td closer "}")
+                                              (dom/td nil nil)
+                                              (dom/td nil nil))])))
+            :editing (dom/table nil
+                                (apply dom/tbody nil
+                                       (concat
+                                        [(dom/tr closer
+                                                 (dom/td closer "{")
+                                                 (dom/td nil nil)
+                                                 (dom/td nil nil))]
+                                        (map (fn [k v] (dom/tr nil
+                                                               (dom/td nil (str k ":"))
+                                                               (if (= k (:field state))
+                                                                 (dom/td nil
+                                                                         (dom/input #js {:type "text" :value (:text state)
+                                                                                :onChange #(handle-change % owner state :text)
+                                                                                :onKeyDown #(when (= 13 (.-which %))
+                                                                                (commit))}))
+                                                                 (dom/td #js {:onClick #(om/update-state! owner
+                                                                                (fn [s] (merge s {:state :editing
+                                                                                :text v
+                                                                                :field k})))}
+                                                                         v))))
+                                             (keys cursor)
+                                             (vals cursor))
+                                        [(dom/tr nil
+                                                 (dom/td nil nil)
+                                                 (dom/td #js {:onClick #(om/update-state! owner
+                                                                                (fn [s] (merge s {:state :adding-field
+                                                                                :text nil
+                                                                                :field ""})))}
+                                                         "[+]")
+                                                 (dom/td nil nil))
+                                         (dom/tr nil
+                                                 (dom/td closer "}")
+                                                 (dom/td nil nil)
+                                                 (dom/td nil nil))])))
+            :adding-field (dom/table nil
                                      (concat
+                                      [(dom/tr nil
+                                               (dom/td closer "{")
+                                               (dom/td nil nil)
+                                               (dom/td nil nil))]
                                       (map (fn [k v] (dom/tr nil
-                                                             (dom/td nil k)
-                                                             (if (= k (:field state))
-                                                               (dom/td nil
-                                                                       (dom/input #js {:type "text" :value (:text state)
-                                                                                :onChange #(handle-change % owner state :text)
-                                                                                :onKeyDown #(when (= 13 (.-which %))
-                                                                                (commit))}))
-                                                               (dom/td #js {:onClick #(om/update-state! owner
-                                                                                (fn [s] (merge s {:state :editing
-                                                                                :text v
-                                                                                :field k})))}
-                                                                       v))))
+                                                             (dom/td nil nil)
+                                                             (dom/td nil (str k ":"))
+                                                             (dom/td #js {:onClick #(om/update-state! owner
+                                                                                (fn [s] (merge s {:state :editing
+                                                                                :text v
+                                                                                :field k})))}
+                                                                     v)))
                                            (keys cursor)
                                            (vals cursor))
-                                      [(dom/td #js {:onClick #(om/update-state! owner
-                                                                                (fn [s] (merge s {:state :adding-field
-                                                                                :text nil
-                                                                                :field ""})))}
-                                               "{+}")])))
-          :adding-field (dom/table nil
-                                   (concat
-                                    (map (fn [k v] (dom/tr nil
-                                                           (dom/td nil k)
-                                                           (dom/td #js {:onClick #(om/update-state! owner
-                                                                                (fn [s] (merge s {:state :editing
-                                                                                :text v
-                                                                                :field k})))}
-                                                                   v)))
-                                         (keys cursor)
-                                         (vals cursor))
-                                    [(dom/tr nil
-                                             (dom/td nil
-                                                     (dom/input #js {:type "text" :value (:field state)
-                                                                     :onChange #(handle-change % owner state :field)
-                                                                     :onKeyDown #(when (= 13 (.-which %))
-                                                                                (om/update-state! owner
-                                                                                (fn [s] (merge {:state :adding-value
-                                                                                :field (:field state)}))))}))
-                                             (dom/td nil nil))]))
-          :adding-value (dom/table nil
-                                   (concat
-                                    (map (fn [k v] (dom/tr nil
-                                                           (dom/td nil k)
-                                                           (dom/td #js {:onClick #(om/update-state! owner
-                                                                                (fn [s] (merge s {:state :editing
-                                                                                :text v
-                                                                                :field k})))}
-                                                                   v)))
-                                         (keys cursor)
-                                         (vals cursor))
-                                    [(dom/tr nil
-                                             (dom/td nil (:field state))
-                                             (dom/td nil
-                                                     (dom/input #js {:type "text" :value (:text state)
-                                                                     :onChange #(handle-change % owner state :text)
-                                                                     :onKeyDown #(when (= 13 (.-which %))
-                                                                                (commit))})))])))))))
+                                       [(dom/tr nil
+                                                (dom/td nil nil)
+                                                (dom/td nil
+                                                        (dom/span canceller "[- ")
+                                                        (dom/input #js {:type "text" :value (:field state)
+                                                                        :onChange #(handle-change % owner state :field)
+                                                                        :onKeyDown #(when (= 13 (.-which %))
+                                                                                (om/update-state! owner
+                                                                                (fn [s] (merge {:state :adding-value
+                                                                                :field (:field state)}))))}))
+                                                (dom/td nil " ]"))
+                                        (dom/tr nil
+                                                (dom/td closer "}")
+                                                (dom/td nil nil)
+                                                (dom/td nil nil))]))
+            :adding-value (dom/table nil
+                                     (concat
+                                      [(dom/tr nil
+                                               (dom/td closer "}")
+                                               (dom/td nil nil)
+                                               (dom/td nil nil))]
+                                      (map (fn [k v] (dom/tr nil
+                                                             (dom/td nil nil)
+                                                             (dom/td nil (str k ":"))
+                                                             (dom/td #js {:onClick #(om/update-state! owner
+                                                                                (fn [s] (merge s {:state :editing
+                                                                                :text v
+                                                                                :field k})))}
+                                                                     v)))
+                                           (keys cursor)
+                                           (vals cursor))
+                                      [(dom/tr nil
+                                               (dom/td nil nil)
+                                               (dom/td nil
+                                                       (dom/span canceller "[- ")
+                                                       (dom/span nil (str (:field state) ":")))
+                                               (dom/td nil
+                                                       (dom/input #js {:type "text" :value (:text state)
+                                                                       :onChange #(handle-change % owner state :text)
+                                                                       :onKeyDown #(when (= 13 (.-which %))
+                                                                                (commit))})
+                                                       (dom/span nil " ]")))
+                                       (dom/tr nil
+                                               (dom/td closer "}")
+                                               (dom/td nil nil)
+                                               (dom/td nil nil))]))))))))

 (defn type-editor [{:keys [cursor id set-state-ch]} owner]
   (reify
     om/IInitState
     (init-state [_]
-      {:state :init
+      {:state (if (= "none" (get cursor "type"))
+                :selecting
+                :immutable)
        :type (get cursor "type")})
     om/IRenderState
     (render-state [this state]
       (condp = (:state state)
-        :init (dom/span #js {:onClick #(om/update-state! owner (fn [s] (merge s {:state :selecting})))}
-                        (get cursor "type"))
         :selecting (dom/select #js {:value (:type state)
                                     :onChange #(handle-change % owner state :type)
                                     :onClick #(handle-change % owner state :type)
                                     :onKeyDown #(when (= 13 (.-which %))
-                                                  (om/update-state! owner (fn [s] (merge s {:state :init})))
-                                                  (om/transact! cursor (fn [c] (assoc c "type" (:type state))))
-                                                  (when-not (contains? cursor "id")
+                                                  (let [grid-id (new-id)]
+                                                    (om/update-state! owner (fn [s] (merge s {:state :immutable})))
+                                                    (om/transact! cursor (fn [c] (assoc c
+                                                                                "type" (:type state)
+                                                                                "id" grid-id)))
                                                     (let [grid {"name" "sub-grid"
-                                                                "id" (new-id)
+                                                                "id" grid-id
                                                                 "bpc" 1
                                                                 "tracks" []}]
                                                       (om/transact! (om/observe owner (grids))
                                                                     (fn [c] (assoc c (get grid "id") grid)))
-                                                      (go (>! set-state-ch (get grid "id")))))
-                                                  (go (>! set-state-ch id)))}
+                                                      (go (>! set-state-ch (get grid "id"))))
+                                                    (go (>! set-state-ch id))))}
                                (dom/option nil "synth")
                                (dom/option nil "sample")
                                (dom/option nil "play")
-                               (dom/option nil "grid"))))))
+                               (dom/option nil "grid"))
+        :immutable (dom/span nil (get cursor "type"))))))
+
+(defn select-editor-builder [key options-ref]
+  (fn [{:keys [cursor id set-state-ch]} owner]
+    (reify
+      om/IInitState
+      (init-state [_]
+        {:state :init})
+      om/IRenderState
+      (render-state [_ state]
+        (if (and (= :init (:state state))
+                 (contains? cursor key))
+          (dom/span #js {:onClick #(om/update-state! owner (fn [s] (merge s {:state :selecting})))} (get cursor key))
+          (apply dom/select #js {:value ((keyword key) state)
+                                 :onChange #(handle-change % owner state (keyword key))
+                                 :onClick #(handle-change % owner state (keyword key))
+                                 :onKeyDown #(when (= 13 (.-which %))
+                                               (om/update-state! owner (fn [s] (merge s {:state :init})))
+                                               (om/transact! cursor (fn [c] (assoc c key ((keyword key) state))))
+                                               (go (>! set-state-ch id)))}
+                 (map #(dom/option nil %) (om/observe owner (options-ref)))))))))
+
+(def synth-editor
+  (select-editor-builder "synth" synths))
+
+(def sample-editor
+  (select-editor-builder "sample" samples))

 (defn track-editor [{:keys [cursor id delete-ch set-state-ch] :as input} owner]
   (reify
@@ -193,18 +273,23 @@
                                                (dom/tr nil
                                                        (dom/td nil nil)
                                                        (dom/td nil "synth:")
-                                                       (dom/td nil (get cursor "synth")))
+                                                       (dom/td nil (om/build synth-editor input)))
                                                "sample"
                                                (dom/tr nil
                                                        (dom/td nil nil)
                                                        (dom/td nil "sample:")
-                                                       (dom/td nil (get cursor "sample")))
+                                                       (dom/td nil (om/build sample-editor input)))
                                                "play"
                                                (dom/tr nil
                                                        (dom/td nil nil)
                                                        (dom/td nil "note:")
                                                        (dom/td nil (get cursor "note")))
-                                               (print "Unable to show open type. This is a bug."))
+                                               nil)
+                                             (when-not (= "none" (get cursor "type"))
+                                               (dom/tr nil
+                                                       (dom/td nil nil)
+                                                       (dom/td nil "params:")
+                                                       (dom/td nil (om/build param-editor input))))
                                              (dom/tr nil
                                                      (dom/td closer "}")
                                                      (dom/td nil nil)
@@ -222,7 +307,6 @@
                         cursor (fn [s]
                                  (clj->js
                                   (conj s {"type" "none"
-                                           "id" (new-id)
                                            "beats" (repeat % [0])
                                            "params" {}}))))
             width-selection (fn [w s]
@@ -235,19 +319,19 @@
           :init (let [width (apply max (map #(count (get % "beats")) cursor))]
                   (if (= 0 (count cursor))
                     (dom/p style-grey
-                           (dom/span #js {:onClick #(transition {:state :open})} "{+}"))
+                           (dom/span #js {:onClick #(transition {:state :open})} "[+]"))
                     (dom/p style-grey
-                           (dom/span #js {:onClick #(transition {:state :open})} "{+")
+                           (dom/span #js {:onClick #(transition {:state :open})} "[+")
                            (width-selection width (str " " width))
-                           (dom/span nil "}"))))
+                           (dom/span nil "]"))))
           :open (dom/p style-grey
-                       (dom/span #js {:onClick #(transition {:state :init})} "{- ")
+                       (dom/span #js {:onClick #(transition {:state :init})} "[- ")
                        (width-selection 1 " 1")
                        (width-selection 2 " 2")
                        (width-selection 4 " 4")
                        (width-selection 8 " 8")
                        (width-selection 16 " 16")
-                       (dom/span nil "}")))))))
+                       (dom/span nil "]")))))))

 (defn track-view [{:keys [cursor id delete-ch]} owner]
   (reify
@@ -264,8 +348,6 @@
         (dom/tr nil (dom/td #js {:onClick #(om/set-state! owner :track-expanded true)
                                  :style #js {:color "#999"}} ">"))
         (dom/tr nil
-                (dom/td #js {:onClick #(om/set-state! owner :track-expanded false)
-                             :style #js {:color "#999"}} "<")
                 (dom/td nil
                  (dom/table nil
                   (apply dom/tr nil
@@ -302,18 +384,20 @@
             (recur)))))
     om/IRenderState
     (render-state [_ state]
-      (let [cursor (get (om/observe owner (grids)) id)]
+      (let [cursor (get (om/observe owner (grids)) id)
+            name (get cursor "name")]
+        (print id " : " cursor)
         (dom/div #js {:style #js {:borderLeft "solid 3px #ccc"
                                   :paddingLeft "10px"}}
                  (if-not (:grid-expanded state)
                    (dom/div nil
                             (dom/p #js {:onClick #(om/set-state! owner :grid-expanded true)
                                         :style #js {:color "#999"}}
-                                   (str "[+] " id " (" (get cursor "bpc") ")")))
+                                   (str name " (" (get cursor "bpc") ")")))
                    (dom/div nil
                             (dom/p #js {:onClick #(om/set-state! owner :grid-expanded false)
                                         :style #js {:color "#999"}}
-                                   (str "[-] " id " (" (get cursor "bpc") ")"))
+                                   (str name " (" (get cursor "bpc") ")"))
                             (dom/table nil
                                        (apply dom/tbody nil
                                               (let [cursors (map #(hash-map :cursor %1 :id %2 :delete-ch %3)


Add test for invoke-private

Add test for invoke-private

--- test/useful/java_test.clj
+++ test/useful/java_test.clj
@@ -6,4 +6,13 @@
   (is (= 3   (rescue (/ 9 3) nil))))

 (deftest test-construct
-  (is (= "test" (construct String "test"))))+  (is (= "test" (construct String "test"))))
+
+(deftest test-invoke-private
+  (let [hash (doto (java.util.Hashtable.)
+               (.put 1 2)
+               (.put 3 4))]
+    (is (thrown? Throwable (.rehash hash)))
+    (is (= {1 2 3 4}
+           (doto hash (invoke-private "rehash"))))
+    (is (thrown? Throwable (.rehash hash)))))


updating parties to add titles, double checked names

--- src/app/parties.cljs
+++ src/app/parties.cljs
@@ -1,317 +1,427 @@
 (ns app.parties)

 (def parties
-  '[{:names ["Alexis Becker"],
+  '[{:title "Alexis Becker & Guest",
+     :names ["Alexis Becker"],
      :contact "alexiskb@gmail.com",
      :has-plus-one true}
-    {:names ["Evan Chung" "Karin Fjellman"],
+    {:title "Evan Chung & Karin Fjellman",
+     :names ["Evan Chung" "Karin Fjellman"],
      :contact "rlsteinbeck@gmail.com",
      :has-plus-one false}
-    {:names ["Steve Coats" "James Damron"],
+    {:title "Steve Coats & James Damron",
+     :names ["Steve Coats" "James Damron"],
      :contact "stevec@uchicago.edu",
      :has-plus-one false}
-    {:names ["Michael Joseph D'Arcy V"],
+    {:title "Michael Joseph D'Arcy V",
+     :names ["Michael Joseph D'Arcy V"],
      :contact "mjdarcy@gmail.com",
      :has-plus-one false}
-    {:names ["Karan Dhadialla"],
+    {:title "Karan Dhadialla",
+     :names ["Karan Dhadialla"],
      :contact "karan.dhadialla@gmail.com",
      :has-plus-one false}
-    {:names ["Tom Discepola" "Trudy Chan"],
+    {:title "Tom Discepola & Trudy Chan",
+     :names ["Tom Discepola" "Trudy Chan"],
      :contact "t.discepola@gmail.com",
      :has-plus-one false}
-    {:names ["Kim Eggert" "Dan Corash"],
+    {:title "Kim Eggert & Dan Corash",
+     :names ["Kim Eggert" "Dan Corash"],
      :contact "kreggert@gmail.com",
      :has-plus-one false}
-    {:names ["Sam Finn Cate-Gumpert" "Michelle Ruby Ray"],
+    {:title "Sam Finn Cate-Gumpert & Michelle Ruby Ray",
+     :names ["Sam Finn Cate-Gumpert" "Michelle Ruby Ray"],
      :contact "sfinncg@gmail.com",
      :has-plus-one false}
-    {:names ["Chris Freeberg"],
+    {:title "Chris Freeberg",
+     :names ["Chris Freeberg"],
      :contact "cfreeberg@gmail.com",
      :has-plus-one false}
-    {:names ["Conor Gaffney" "Heather Gold"],
+    {:title "Conor Gaffney & Heather Gold",
+     :names ["Conor Gaffney" "Heather Gold"],
      :contact "conorsg@gmail.com",
      :has-plus-one false}
-    {:names ["Emerald Gao"],
+    {:title "Emerald Gao",
+     :names ["Emerald Gao"],
      :contact "chaplinesque@gmail.com",
      :has-plus-one false}
-    {:names ["Matt Giles"],
+    {:title "Matt Giles",
+     :names ["Matt Giles"],
      :contact "matt.s.giles@gmail.com",
      :has-plus-one false}
-    {:names ["Matt Gipp"],
+    {:title "Matt Gipp & Guest",
+     :names ["Matt Gipp"],
      :contact "m.r.gipp@gmail.com",
      :has-plus-one true}
-    {:names ["Lauren Henry"],
+    {:title "Lauren Henry & Guest",
+     :names ["Lauren Henry"],
      :contact "lauren.henry7@gmail.com",
      :has-plus-one true}
-    {:names ["Zach Herz"],
+    {:title "Zach Herz",
+     :names ["Zach Herz"],
      :contact "zacharyherz@gmail.com",
      :has-plus-one false}
-    {:names ["Viet Huynh" "Li Kim Lee" "Vigo Huynh"],
+    {:title "Viet Huynh & Li Kim Lee, Vigo Huynh",
+     :names ["Viet Huynh" "Li Kim Lee" "Vigo Huynh"],
      :contact "schema@gmail.com",
      :has-plus-one false}
-    {:names ["The Huynh Family"],
+    {:title "The Huynh Family",
+     :names ["Phuoc Huynh", "Dao Nguyen"],
      :contact "siamping1@gmail.com",
      :has-plus-one false}
-    {:names ["Samantha Kumar"],
+    {:title "Samantha Kumar & Guest",
+     :names ["Samantha Kumar"],
      :contact "samantha.kumar3@gmail.com",
      :has-plus-one true}
-    {:names ["Tiffany Kwak"],
+    {:title "Tiffany Kwak & Guest",
+     :names ["Tiffany Kwak"],
      :contact "soyoung.kwak@gmail.com",
      :has-plus-one true}
-    {:names ["Bourree Lam" "David Iseri"],
+    {:title "Bourree Lam & David Iseri",
+     :names ["Bourree Lam" "David Iseri"],
      :contact "bourree.lam@gmail.com",
      :has-plus-one false}
-    {:names ["Hilary Lee"],
+    {:title "Hilary Lee",
+     :names ["Hilary Lee"],
      :contact "hmlee2008@gmail.com",
      :has-plus-one false}
-    {:names ["Jeffrey Louden"],
+    {:title "Jeffrey Louden",
+     :names ["Jeffrey Louden"],
      :contact "heph502@gmail.com",
      :has-plus-one false}
-    {:names ["Matt Malinowski" "Caitlin Rubin"],
+    {:title "Matt Malinowski & Caitlin Rubin",
+     :names ["Matt Malinowski" "Caitlin Rubin"],
      :contact "mdmalinowski@gmail.com",
      :has-plus-one false}
-    {:names ["Amber Meriwether" "Jared Davis" "Clark Davis"],
+    {:title "Amber Meriwether & Jared Davis, Clark Davis",
+     :names ["Amber Meriwether" "Jared Davis" "Clark Davis"],
      :contact "amber.meriwether@gmail.com",
      :has-plus-one false}
-    {:names ["The Nguyen Family"],
+    {:title "The Nguyen Family",
+     :names  ["Joel Hanh Nguyen", "Yvette Lieu Nguyen", "Alex Nguyen", "Julie Nguyen"],
      :contact "joel_hanh@yahoo.com",
      :has-plus-one false}
-    {:names ["Juliana Pino Alcaraz" "Erin Tinnon"],
+    {:title "Juliana Pino Alcaraz & Erin Tinnon",
+     :names  ["Juliana Pino Alcaraz" "Erin Tinnon"],
      :contact "julis.pino@gmail.com",
      :has-plus-one false}
-    {:names ["Matt Ranalletta"],
+    {:title "Matt Ranalletta",
+     :names  ["Matt Ranalletta"],
      :contact "matthew.ranalletta@gmail.com",
      :has-plus-one false}
-    {:names ["Michael Renehan" "Weston Walker"],
+    {:title "Michael Renehan & Weston Walker",
+     :names  ["Michael Renehan" "Weston Walker"],
      :contact "kmrenehan@gmail.com",
      :has-plus-one false}
-    {:names ["Michael Schlegelmilch"],
+    {:title "Michael Schlegelmilch",
+     :names  ["Michael Schlegelmilch"],
      :contact "mikeschlegelmilch@gmail.com",
      :has-plus-one false}
-    {:names ["Gwen Smuda"],
+    {:title "Gwen Smuda",
+     :names  ["Gwen Smuda"],
      :contact "gwen.smuda@gmail.com",
      :has-plus-one false}
-    {:names ["Amanda Strick"],
+    {:title "Amanda Strick",
+     :names  ["Amanda Strick"],
      :contact "amandastrick@gmail.com",
      :has-plus-one false}
-    {:names ["Danielle Tcholakian"],
+    {:title "Danielle Tcholakian & Guest",
+     :names  ["Danielle Tcholakian"],
      :contact "danielleingridanais@gmail.com",
      :has-plus-one true}
-    {:names ["Karen Tye" "David Palm"],
+    {:title "Karen Tye & David Palm",
+     :names  ["Karen Tye" "David Palm"],
      :contact "ktye07@gmail.com",
      :has-plus-one false}
-    {:names ["Kyle Westphal" "Katherine Greenleaf"],
+    {:title "Kyle Westphal & Katherine Greenleaf",
+     :names  ["Kyle Westphal" "Katherine Greenleaf"],
      :contact "kawest@gmail.com",
      :has-plus-one false}
-    {:names ["Chris Wolff" "Abby Terese"],
+    {:title "Chris Wolff & Abby Terese",
+     :names  ["Chris Wolff" "Abby Terese"],
      :contact "anziecorgan@gmail.com",
      :has-plus-one false}
-    {:names ["Linda Wu"], :contact "lyue.wu@gmail.com", :has-plus-one false}
-    {:names ["Steve Young"],
+    {:title "Linda Wu",
+     :names  ["Linda Wu"],
+     :contact "lyue.wu@gmail.com",
+     :has-plus-one false}
+    {:title "Steve Young",
+     :names  ["Steve Young"],
      :contact "slyoung765@gmail.com",
      :has-plus-one false}
-    {:names ["The Zvosec Family"],
+    {:title "The Zvosec Family",
+     :names  ["Colleen Zvosec", "Richard Zvosec", "Bea Zvosec", "Adeline Zvosec"],
      :contact "ckzvosec@comcast.net",
      :has-plus-one false}
-    {:names ["Adam Alix" "Elise Murray"],
+    {:title "Adam Alix & Elise Murray",
+     :names  ["Adam Alix" "Elise Murray"],
      :contact "adam.alix@gmail.com",
      :has-plus-one false}
-    {:names ["Zack Boka"],
+    {:title "Zack Boka",
+     :names  ["Zack Boka"],
      :contact "zackboka@gmail.com",
      :has-plus-one false}
-    {:names ["Aki Rose Braun" "Mark Smirniotis"],
+    {:title "Aki Rose Braun & Mark Smirniotis",
+     :names  ["Aki Rose Braun" "Mark Smirniotis"],
      :contact "aki.kyozoku@gmail.com",
      :has-plus-one false}
-    {:names ["Eryan Cobham" "Nneka Onyezia" "Dania Cobham"],
+    {:title "Eryan Cobham & Nneka Onyezia, Dania",
+     :names  ["Eryan Cobham" "Nneka Onyezia" "Dania"],
      :contact "eryan@eryancobham.com",
      :has-plus-one false}
-    {:names ["Raf Kuhn"],
+    {:title "Raf Kuhn",
+     :names  ["Raf Kuhn"],
      :contact "raf.kuhn@gmail.com",
      :has-plus-one false}
-    {:names ["Nathan Kuras"],
+    {:title "Nathan Kuras",
+     :names  ["Nathan Kuras"],
      :contact "nathankuras@gmail.com",
      :has-plus-one false}
-    {:names ["Nathan Pinsley" "Peter Kriss"],
+    {:title "Nathan Pinsley & Peter Kriss",
+     :names  ["Nathan Pinsley" "Peter Kriss"],
      :contact "nathan.pinsley@gmail.com",
      :has-plus-one false}
-    {:names ["Kyle Rush" "Gregory Goodwin"],
+    {:title "Kyle Rush & Gregory Goodwin",
+     :names  ["Kyle Rush" "Gregory Goodwin"],
      :contact "kylerrush@gmail.com",
      :has-plus-one false}
-    {:names ["Nancy Shelby"],
+    {:title "Nancy Shelby",
+     :names  ["Nancy Shelby"],
      :contact "nancyshelby@gmail.com",
      :has-plus-one false}
-    {:names ["Will St. Clair"],
+    {:title "Will St. Clair",
+     :names  ["Will St. Clair"],
      :contact "will@willstclair.com",
      :has-plus-one false}
-    {:names ["Gabe Stein"],
+    {:title "Gabe Stein",
+     :names  ["Gabe Stein"],
      :contact "gabe@ghscommunications.com",
      :has-plus-one false}
-    {:names ["Cecilia Zvosec"],
+    {:title "Cecilia Zvosec & Guest",
+     :names  ["Cecilia Zvosec"],
      :contact "cezvosec@gmail.com",
-     :has-plus-one false}
-    {:names ["Denny Abrams"],
+     :has-plus-one true}
+    {:title "Denny Abrams",
+     :names  ["Denny Abrams"],
      :contact "edgeguy@aol.com",
      :has-plus-one false}
-    {:names ["Sheila Balter"],
+    {:title "Sheila Balter",
+     :names  ["Sheila Balter"],
      :contact "beeshebalter@gmail.com",
      :has-plus-one false}
-    {:names ["Summer Brenner" "Michael Weber"],
+    {:title "Summer Brenner & Michael Weber",
+     :names  ["Summer Brenner" "Michael Weber"],
      :contact "summerbrenner@gmail.com",
      :has-plus-one false}
-    {:names ["Chloe Byruck" "Karen Jackson"],
+    {:title "Chloe Byruck & Karen Jackson",
+     :names  ["Chloe Byruck" "Karen Jackson"],
      :contact "chloebyruck@gmail.com",
      :has-plus-one false}
-    {:names ["Sara" "Marcus Byruck"],
+    {:title "Sara & Marcus Byruck",
+     :names  ["Sara Byruck" "Marcus Byruck"],
      :contact "sbyruck@gmail.com",
      :has-plus-one false}
-    {:names ["Karima Cammell" "Duncan Brown" "Raphaella" "Valentina"],
+    {:title "Karima Cammell & Duncan Brown, Raphaella and Valentina",
+     :names  ["Karima Cammell" "Duncan Brown" "Raphaella" "Valentina"],
      :contact "karima@castleintheair.biz",
      :has-plus-one false}
-    {:names ["Denise" "Barney Cassidy"],
+    {:title "Denise & Barney Cassidy",
+     :names  ["Denise Cassidy" "Barney Cassidy"],
      :contact "cassidycentral@gmail.com",
      :has-plus-one false}
-    {:names ["Jeri Lynn Cohen" "Julia Rose Kibben"],
+    {:title "Jeri Lynn Cohen & Julia Rose Kibben",
+     :names  ["Jeri Lynn Cohen" "Julia Rose Kibben"],
      :contact "cohenjerilynn@gmail.com",
      :has-plus-one false}
-    {:names ["David Cramer" "Marlene Tobias"],
+    {:title "David Cramer & Marlene Tobias",
+     :names  ["David Cramer" "Marlene Tobias"],
      :contact "dcramera@sbcglobal.net",
      :has-plus-one false}
-    {:names ["Carol Edgarian" "Tom Jenks" "Lucy" "Olivia"],
+    {:title "Carol Edgarian & Tom Jenks, Lucy and Olivia",
+     :names  ["Carol Edgarian" "Tom Jenks" "Lucy" "Olivia"],
      :contact "ce@narrativemagazine.com",
      :has-plus-one false}
-    {:names ["Katherine Esmay" "Lennart Ohlsson"],
+    {:title "Katherine Esmay & Lennart Ohlsson",
+     :names  ["Katherine Esmay" "Lennart Ohlsson"],
      :contact "klesmay@aol.com",
      :has-plus-one false}
-    {:names ["Leah" "Jerry Garchik"],
+    {:title "Leah and Jerry Garchik",
+     :names  ["Leah Garchik" "Jerry Garchik"],
      :contact "lgarchik@sfchronicle.com",
      :has-plus-one false}
-    {:names ["Susan Harloe"],
+    {:title "Susan Harloe",
+     :names  ["Susan Harloe"],
      :contact "susanharloe@hotmail.com",
      :has-plus-one false}
-    {:names ["Amanda Houston Hamilton"],
+    {:title "Amanda Houston Hamilton",
+     :names  ["Amanda Houston Hamilton"],
      :contact "docdmh@aol.com",
      :has-plus-one false}
-    {:names ["Stephanie Hunt" "Josh Ende"],
+    {:title "Stephanie Hunt and Josh Ende",
+     :names  ["Stephanie Hunt" "Josh Ende"],
      :contact "stephjanehunt@hotmail.com",
      :has-plus-one false}
-    {:names ["Daryn Kenny"],
+    {:title "Daryn Kenny",
+     :names  ["Daryn Kenny"],
      :contact "californiasunshine@comcast.net",
      :has-plus-one false}
-    {:names ["Amy Kossow" "Peter Oubourg"],
+    {:title "Amy Kossow & Peter Oubourg",
+     :names  ["Amy Kossow" "Peter Oubourg"],
      :contact "amykossowemail@gmail.com",
      :has-plus-one false}
-    {:names ["Kathryn Lee" "Gary Pursell" "Chase Jackson"],
+    {:title "Kathryn Lee, Gary Pursell & Chase Jackson",
+     :names  ["Kathryn Lee" "Gary Pursell" "Chase Jackson"],
      :contact "kathrynleelee123@gmail.com",
      :has-plus-one false}
-    {:names ["Delia MacDougall"],
+    {:title "Delia MacDougall",
+     :names  ["Delia MacDougall"],
      :contact "deliamacdougall@gmail.com",
      :has-plus-one false}
-    {:names ["Brent" "Beckie Newell"],
+    {:title "Brent & Beckie Newell",
+     :names  ["Brent Newell" "Beckie Newell"],
      :contact "bnewell@crpe-ej.org",
      :has-plus-one false}
-    {:names ["Patty Rose"],
+    {:title "Patty Rose",
+     :names  ["Patty Rose"],
      :contact "pattyrose@greenspacencr.org",
      :has-plus-one false}
-    {:names ["Alice Sealey" "Marc Stess"],
+    {:title "Alice Sealey & Marc Stess",
+     :names  ["Alice Sealey" "Marc Stess"],
      :contact "alicesealey@gmail.com",
      :has-plus-one false}
-    {:names ["Patricia Silver"],
+    {:title "Patricia Silver",
+     :names  ["Patricia Silver"],
      :contact "patricia.silver@gmail.com",
      :has-plus-one false}
-    {:names ["Gretchen" "Rocky Stone"],
+    {:title "Gretchen & Rocky Stone",
+     :names  ["Gretchen Stone" "Rocky Stone"],
      :contact "gretchenjcstone@gmail.com",
      :has-plus-one false}
-    {:names ["Ellen Widess" "Rick Warren"],
+    {:title "Ellen Widess & Rick Warren",
+     :names  ["Ellen Widess" "Rick Warren"],
      :contact "ewidess@gmail.com",
      :has-plus-one false}
-    {:names ["JoAnne Winter" "Adrian Elfenbaum"],
+    {:title "JoAnne Winter & Adrian Elfenbaum",
+     :names  ["JoAnne Winter" "Adrian Elfenbaum"],
      :contact "jwinter@zspace.org",
      :has-plus-one false}
-    {:names ["Michelle Eliezer"],
+    {:title "Michelle Eliezer",
+     :names  ["Michelle Eliezer"],
      :contact "tangofeet@gmail.com",
      :has-plus-one false}
-    {:names ["Dan Brown" "Alex Smolyanskaya"],
+    {:title "Dan Brown & Alex Smolyanskaya",
+     :names  ["Dan Brown" "Alex Smolyanskaya"],
      :contact "jdanbrown@gmail.com",
      :has-plus-one false}
-    {:names ["Mischa Byruck"],
+    {:title "Mischa Byruck",
+     :names  ["Mischa Byruck"],
      :contact "mbyruck@gmail.com",
      :has-plus-one false}
-    {:names ["Emily Chen"],
+    {:title "Emily Chen",
+     :names  ["Emily Chen"],
      :contact "tenshiemi@gmail.com",
      :has-plus-one false}
-    {:names ["Peter" "Jarka Cole" "Charlie"],
+    {:title "Peter & Jarka Cole, Charlie",
+     :names  ["Peter Cole" "Jarka Cole" "Charlie"],
      :contact "pedrolandia@gmail.com",
      :has-plus-one false}
-    {:names ["Alexandra Cole" "Frank Dobyns"],
+    {:title "Alexandra Cole & Frank Dobyns",
+     :names  ["Alexandra Cole" "Frank Dobyns"],
      :contact "accole5@yahoo.com",
      :has-plus-one false}
-    {:names ["Skip" "Shelley Cole"],
+    {:title "Skip & Shelley Cole",
+     :names  ["Skip Cole" "Shelley Cole"],
      :contact "scole@arthistory.ucsb.edu",
      :has-plus-one false}
-    {:names ["Sarah Cole"], :contact nil, :has-plus-one false}
-    {:names ["Thomas" "Linda Cole" "Allie" "Tobias"],
+    {:title "Sarah Cole",
+     :names  ["Sarah Cole"],
+     :contact nil,
+     :has-plus-one false}
+    {:title "Thomas & Linda Cole, Allie and Tobias",
+     :names  ["Thomas Cole" "Linda Cole" "Allie" "Tobias"],
      :contact "eckerbomcole@hotmail.com",
      :has-plus-one false}
-    {:names ["Anders Conbere" "Mollie Caka" "Mae" "Hambone"],
+    {:title "Anders Conbere & Mollie Caka, Mae and Hambone",
+     :names  ["Anders Conbere" "Mollie Caka" "Mae" "Hambone"],
      :contact "anders@conbere.org",
      :has-plus-one false}
-    {:names ["Dorian Jamal Campbell Cool" "Tiffany Lee"],
+    {:title "Dorian Jamal Campbell Cool & Tiffany Lee",
+     :names  ["Dorian Jamal Campbell Cool" "Tiffany Lee"],
      :contact "jamal.cool@gmail.com",
      :has-plus-one false}
-    {:names ["Gina DeVries"],
+    {:title "Gina DeVries",
+     :names  ["Gina DeVries"],
      :contact "queershoulder@gmail.com",
      :has-plus-one false}
-    {:names ["Serge" "Winnie Doubinski"],
+    {:title "Serge & Winnie Doubinski",
+     :names  ["Serge Doubinski" "Winnie Doubinski"],
      :contact "v01and04@gmail.com",
      :has-plus-one false}
-    {:names ["Theo Eliezer"],
+    {:title "Theo Eliezer & Guest",
+     :names  ["Theo Eliezer"],
      :contact "theo.eliezer@gmail.com",
      :has-plus-one true}
-    {:names ["Jon Garrett" "Danielle Peña"],
+    {:title "Jon Garrett & Danielle Peña",
+     :names  ["Jon Garrett" "Danielle Peña"],
      :contact "jonathan.garrett@gmail.com",
      :has-plus-one false}
-    {:names ["April Goldman" "Cody Sims"],
+    {:title "April Goldman & Cody Sims",
+     :names  ["April Goldman" "Cody Sims"],
      :contact "aprileliza@gmail.com",
      :has-plus-one false}
-    {:names ["Isabel Gomez"],
+    {:title "Isabel Gomez",
+     :names  ["Isabel Gomez"],
      :contact "isabelcgomez@gmail.com",
      :has-plus-one false}
-    {:names ["Molly Hirschfeld" "Chris Rogers"],
+    {:title "Molly Hirschfeld & Chris Rogers",
+     :names  ["Molly Hirschfeld" "Chris Rogers"],
      :contact "molly.hirschfeld@gmail.com",
      :has-plus-one false}
-    {:names ["Alexis Hyatt" "Jake Hirschfeld"],
+    {:title "Alexis Hyatt & Jake Hirschfeld",
+     :names  ["Alexis Hyatt" "Jake Hirschfeld"],
      :contact "countslathyl@hotmail.com",
      :has-plus-one false}
-    {:names ["Erin Ivie" "Sean Maher"],
+    {:title "Erin Ivie & Sean Maher",
+     :names  ["Erin Ivie" "Sean Maher"],
      :contact "erin.j.ivie@gmail.com",
      :has-plus-one false}
-    {:names ["Anna Comerford" "Vikash Mansinghka"],
+    {:title "Anna Comerford & Vikash Mansinghka",
+     :names  ["Anna Comerford" "Vikash Mansinghka"],
      :contact "vkmvkmvkmvkm@gmail.com",
      :has-plus-one false}
-    {:names ["Denise" "Manuel Martinez"],
+    {:title "Denise & Manuel Martinez",
+     :names  ["Denise Martinez" "Manuel Martinez"],
      :contact "manmarti@sbcglobal.net",
      :has-plus-one false}
-    {:names ["Daniel Parker"],
+    {:title "Daniel Parker & Guest",
+     :names  ["Daniel Parker"],
      :contact "danielpfocht@gmail.com",
-     :has-plus-one false}
-    {:names ["Susan Putnins" "Clinton Degan"],
+     :has-plus-one true}
+    {:title "Susan Putnins & Clinton Degan",
+     :names  ["Susan Putnins" "Clinton Degan"],
      :contact "susanputnins@gmail.com",
      :has-plus-one false}
-    {:names ["Pavel" "Linna Repin" "Nikolai" "Daniel"],
+    {:title "Pavel & Linna Repin, Nikolai and Daniel",
+     :names  ["Pavel Repin" "Linna Repin" "Nikolai" "Daniel"],
      :contact "prepin@gmail.com",
      :has-plus-one false}
-    {:names ["Ivy Roth"],
+    {:title "Ivy Roth",
+     :names  ["Ivy Roth"],
      :contact "roth.ivy@gmail.com",
      :has-plus-one false}
-    {:names ["Ashton Saylor"],
+    {:title "Ashton Saylor",
+     :names  ["Ashton Saylor"],
      :contact "ashtonsaylor@gmail.com",
      :has-plus-one false}
-    {:names ["Joshua Thayer" "Martha Montgomery"],
+    {:title "Joshua Thayer & Martha Montgomery ",
+     :names  ["Joshua Thayer" "Martha Montgomery"],
      :contact "joshuamilesthayer@gmail.com",
      :has-plus-one false}
-    {:names ["Tim Trautman"],
+    {:title "Tim Trautman",
+     :names  ["Tim Trautman"],
      :contact "tctrautman@gmail.com",
      :has-plus-one false}
-    {:names ["Chris Wybenga" "Ana Cristina Alanis"],
+    {:title "Chris Wybenga & Ana Cristina Alanis",
+     :names  ["Chris Wybenga" "Ana Cristina Alanis"],
      :contact "c.wybenga@gmail.com",
      :has-plus-one false}])


fix exercise and more hints

fix exercise and more hints

--- src/cljs/daveconservatoire/site/ui/exercises.cljs
+++ src/cljs/daveconservatoire/site/ui/exercises.cljs
@@ -64,9 +64,12 @@

 (def progress-bar (om/factory ProgressBar))

+(defn answer-label [{::keys [correct-answer options]}]
+  (-> (into {} options) (get correct-answer)))
+
 (s/fdef progress-bar
-  :args (s/cat :props (s/keys :opt [::progress-total ::progress-value]))
-  :ret ::react-component)
+        :args (s/cat :props (s/keys :opt [::progress-total ::progress-value]))
+        :ret ::react-component)

 (defn note->node [note]
   {::audio/node-gen (->> note audio/semitone->note (get @audio/*sound-library*))})
@@ -143,8 +146,8 @@
                  (int-in (audio/note->semitone a) (audio/note->semitone b)))))))

 (s/fdef descriptor->value
-  :args (s/cat :desc ::value-descriptor)
-  :ret ::audio/semitone)
+        :args (s/cat :desc ::value-descriptor)
+        :ret ::audio/semitone)

 (defn completed? [{:keys [::ex-total-questions ::streak-count]}]
   (>= streak-count ex-total-questions))
@@ -165,7 +168,8 @@
     (let [{::keys [options ex-answer ex-total-questions streak-count notes hints hints-used]
            :as    props} (om/props this)
           [opt-type _] (s/conform ::options options)
-          parent       (om/parent this)
+          parent (om/parent this)
+          hints (hints props)
           check-answer #(do
                           (om/transact! parent `[(dcex/check-answer)
                                                  (untangled/load {:query [{:app/me ~(om/get-query UserScore)}] :marker false})]))]
@@ -199,7 +203,7 @@
                                     "Play Again")))))
                           (dom/div #js {:id "hintsarea"}
                             (for [[hint i] (map vector (take hints-used hints) (range))]
-                              (dom/p #js {:key i} (template/translate hint props)))))
+                              (dom/p #js {:key i} hint))))
                         (dom/div #js {:id "answer_area_wrap"}
                           (dom/div #js {:id "answer_area"}
                             (dom/form #js {:id "answerform" :name "answerform" :onSubmit #(do
@@ -259,19 +263,19 @@
 (defn rand-direction [] (rand-nth [1 -1]))

 (s/fdef rand-direction
-  :args (s/cat)
-  :ret #{-1 1})
+        :args (s/cat)
+        :ret #{-1 1})

 (defn vary-pitch [{:keys [::pitch ::variation ::direction]}]
   (let [direction (or direction [-1 1])
-        a         (descriptor->value pitch)
-        b         (+ a (* (descriptor->value variation)
-                          (descriptor->value direction)))]
+        a (descriptor->value pitch)
+        b (+ a (* (descriptor->value variation)
+                  (descriptor->value direction)))]
     [a b]))

 (s/fdef vary-pitch
-  :args (s/cat :data (s/keys :req [::pitch ::variation]))
-  :ret (s/tuple ::audio/semitone ::audio/semitone))
+        :args (s/cat :data (s/keys :req [::pitch ::variation]))
+        :ret (s/tuple ::audio/semitone ::audio/semitone))

 (om/defui ^:once PitchDetection
   static uc/InitialAppState
@@ -312,7 +316,11 @@
         {::name      "identify-octaves"
          ::options   [["yes" "Yes"] ["no" "No"]]
          ::pitch     ["C3" ".." "B5"]
-         ::variation [3 5 6 7 8 9 12 15 16]}
+         ::variation [3 5 6 7 8 9 15 16 12 12 12 12 12 12 12]
+         ::hints     (fn [props]
+                       ["Does this sound like two flavours of the same thing?"
+                        "Can you sing the notes between the two pitches?  Do they make a complete scale?"
+                        (str "The answer is " (get props ::correct-answer))])}
         props)))

   static om/Ident
@@ -325,7 +333,7 @@
   (new-round [_ props]
     (let [[a b :as notes] (vary-pitch props)
           distance (- b a)
-          octave?  (zero? (mod distance 8))]
+          octave? (= (js/Math.abs distance) 12)]
       (assoc props
         ::notes notes
         ::correct-answer (if octave? "yes" "no"))))
@@ -338,20 +346,26 @@
 (def notes-treble
   {::read-note-order  ["E" "F" "G" "A" "B" "C" "D" "E" "F"]
    ::read-note-prefix "trebleclefimages"
-   ::hints            ["Does this note sit on a line or in a space?"
-                       "\"FACE in the space\" and \"Every Green Bus Drives Fast\""
-                       ["This note is " ::correct-answer]]})
+   ::hints            (fn [props]
+                        ["Does this note sit on a line or in a space?"
+                         "\"FACE in the space\" and \"Every Green Bus Drives Fast\""
+                         (str "This note is " (get props ::correct-answer))])})

 (def notes-bass
   {::read-note-order  ["G" "A" "B" "C" "D" "E" "F" "G" "A"]
    ::read-note-prefix "bassclefimages"
-   ::hints            ["Does this note sit on a line or in a space?"
-                       "\"All Cows Eat Grass\" and \"Good Boys Deserve Fresh Apples\""
-                       ["This note is " ::correct-answer]]})
+   ::hints            (fn [props]
+                        ["Does this note sit on a line or in a space?"
+                         "\"All Cows Eat Grass\" and \"Good Boys Deserve Fresh Apples\""
+                         (str "This note is " (get props ::correct-answer))])})

 (def notes-grand-staff
   {::read-note-order  ["G" "A" "B" "C" "D" "E" "F" "G" "A" "B" #_"TREBLE" "D" "E" "F" "G" "A" "B" "C" "D" "E" "F" "C"]
-   ::read-note-prefix "grandstaffimages"})
+   ::read-note-prefix "grandstaffimages"
+   ::hints            (fn [props]
+                        ["Identify if the note belongs to treble clef or the bass clef."
+                         "The treble clef is the upper staff, the bass clef is the lower staff."
+                         (str "This note is " (get props ::correct-answer))])})

 (defn rand-int-new [n old]
   "Generate a new random number like rand-int, if number is equals to `old` a new
@@ -369,8 +383,7 @@
       (merge
         (uc/initial-state Exercise nil)
         {::name    "reading-music"
-         ::options ::option-type-text}
-        props)))
+         ::options ::option-type-text} props)))

   static om/Ident
   (ident [_ props] [:exercise/by-name (::name props)])
@@ -381,10 +394,10 @@
   static IExercise
   (new-round [_ props]
     (let [{::keys [read-note-order read-note]} props
-          pos    (rand-int-new (count read-note-order) read-note)
-          note   (get read-note-order pos)
+          pos (rand-int-new (count read-note-order) read-note)
+          note (get read-note-order pos)
           octave (if (> pos 4) 4 3)
-          notes  [(str note octave)]]
+          notes [(str note octave)]]
       (assoc props
         ::read-note pos
         ::notes notes
@@ -509,7 +522,7 @@

   static IExercise
   (new-round [_ props]
-    (let [type      (rand-nth ["major" "minor"])
+    (let [type (rand-nth ["major" "minor"])
           base-note (descriptor->value ["C3" ".." "F3"])]
       (assoc props
         ::notes (audio/chord base-note (type->arrengement type))
@@ -548,6 +561,8 @@

 (def rhytm-metronome (flatten1 (repeat 5 [["high" 1] ["low" 1] ["low" 1] ["low" 1]])))

+
+
 (om/defui ^:once RhythmReading
   static uc/InitialAppState
   (initial-state [this props]
@@ -556,15 +571,19 @@
         (uc/initial-state Exercise nil)
         {::name       "rhythm-reading"
          ::options    [["0" "A"] ["1" "B"] ["2" "C"] ["3" "D"]]
-         ::play-notes (let [last-play       (atom [])
+         ::play-notes (let [last-play (atom [])
                             time-multiplier 0.7]
                         (fn [notes]
-                          (let [time  (audio/current-time)
+                          (let [time (audio/current-time)
                                 nodes (map #(update % ::audio/duration (partial * time-multiplier)) (prepare-notes notes))
                                 metro (map #(update % ::audio/duration (partial * time-multiplier)) (prepare-notes rhytm-metronome))]
                             (run! audio/stop @last-play)
                             (reset! last-play (concat (audio/play-sequence metro {::audio/time time})
-                                                      (audio/play-sequence nodes {::audio/time (+ time (* 4 time-multiplier))}))))))}
+                                                      (audio/play-sequence nodes {::audio/time (+ time (* 4 time-multiplier))}))))))
+         ::hints      (fn [props]
+                        ["Choose one bar and really focus on listening to that when the music plays."
+                         "Clap along with the version you chose as the music plays. "
+                         (str "The correct version is " (answer-label props))])}
         props)))

   static om/Ident
@@ -576,13 +595,13 @@
   static IExercise
   (new-round [_ props]
     (let [rhytms (partition 4 (take 16 (random-bars)))
-          idx    (rand-int 4)
-          notes  (->> (nth rhytms idx)
-                      (map ::vf/notes)
-                      (flatten)
-                      (map (comp #(vector "B4" %)
-                                 duration->seconds
-                                 ::vf/duration)))]
+          idx (rand-int 4)
+          notes (->> (nth rhytms idx)
+                     (map ::vf/notes)
+                     (flatten)
+                     (map (comp #(vector "B4" %)
+                                duration->seconds
+                                ::vf/duration)))]
       (assoc props
         ::rhytms rhytms
         ::notes notes


changes

changes

--- src/cljs/main/broadfcui/common/notifications.cljs
+++ src/cljs/main/broadfcui/common/notifications.cljs
@@ -296,8 +296,8 @@
                            :margin-left "112px"
                            :text-align "center"
                            :font-weight "500"}}
-            (if (common/has-terra-return?)
-              "This page is displaying in our legacy application. Please bear with us as we migrate these features fully into Terra"
+            (if (and (common/has-terra-return?) (config/terra-redirects-enabled))
+              "This page is displaying in our legacy application. Please bear with us as we migrate these features fully into Terra."
               [:span {}
                "On May 1st FireCloud will get a new look as it becomes "
                [:a {:href "https://software.broadinstitute.org/firecloud/blog?id=23627"


Fixing merge mistake.

Fixing merge mistake.

--- src/cmr/search/services/parameters/conversion.clj
+++ src/cmr/search/services/parameters/conversion.clj
@@ -38,7 +38,7 @@
                 :polygon :polygon
                 :bounding-box :bounding-box
                 :point :point
-                :keyword :keyword}
+                :keyword :keyword
                 :line :line}
    :granule {:granule-ur :string
              :concept-id :granule-concept-id


Implement reset for Infinirator

Implement reset for Infinirator

--- src/aurora/join.cljs
+++ src/aurora/join.cljs
@@ -226,34 +226,36 @@
       (.next join-itr))
     results))

-(deftype Infinirator [^:mutable end? func max-func ^:mutable cur-key map]
+(deftype Infinirator [func initial-func max-func size ^:mutable cur-key ^:mutable end?]
   Object
-  (reset [this])
+  (reset [this]
+         (set! cur-key (array))
+         (dotimes [_ size]
+           (.push cur-key least))
+         (initial-func cur-key)
+         (set! end? false))

   (key [this]
-       (when (identical? end? false)
+       (when (false? end?)
          cur-key))

   (val [this]
        nil)

   (next [this]
-        (set! end? true)
-        )
+        (set! end? true))
+
   (seek [this key]
         (set! end? false)
         (ainto cur-key key)
         (func cur-key key)
         (when (key-gt key cur-key)
-          (max-func cur-key))
-        ))
+          (max-func cur-key))))

 (defn infinirator [size func initial-func max-func]
-  (let [cur-key (array)]
-    (dotimes [x size]
-      (.push cur-key least))
-    (initial-func cur-key)
-    (Infinirator. false func max-func cur-key size)))
+  (let [itr (Infinirator. func initital max-func size)]
+    (.reset itr)
+    itr))

 (defn constant-filter [size i v]
   (infinirator size


fixing merge

fixing merge

--- src/aws/provider.clj
+++ src/aws/provider.clj
@@ -136,10 +136,10 @@
             (instance-desc endpoint uuid :state :name)
             (catch [:type ::aws:missing-id] e false))))

-(def defaults {:min-count 1 :max-count 1})
+(def defaults {:aws {:min-count 1 :max-count 1}})

 (defmethod translate :aws [{:keys [aws machine] :as spec}]
-  [(aws :endpoint) (merge (dissoc-in* spec [:aws :endpoint]) defaults) (str (UUID/randomUUID)) (or (machine :user) "root")])
+  [(aws :endpoint) (merge-with merge (dissoc-in* spec [:aws :endpoint]) defaults) (str (UUID/randomUUID)) (or (machine :user) "root")])

 (defmethod vconstruct :aws [spec]
   (apply ->Instance (translate spec)))


cleanup: remove unneeded variable

cleanup: remove unneeded variable

--- src/babel/engine.clj
+++ src/babel/engine.clj
@@ -32,9 +32,6 @@
         language-model (if (future? language-model)
                          @language-model
                          language-model)
-        morph (if (future? language-model)
-                (:morph @language-model)
-                (:morph language-model))

         debug (log/debug (str "pre-enriched spec: " spec))

@@ -50,7 +47,7 @@
                      (:grammar language-model)
                      (:lexicon language-model)
                      (:index language-model)
-                     morph)))
+                     (:morph language-model))))

 (defn generate-from-request [request]
   "respond to an HTTP client's request with a generated sentence, given the client's desired spec, language name, and language model name."


158/161/162

--- stuf/src/sandbox/4clojure.clj
+++ stuf/src/sandbox/4clojure.clj
@@ -1415,3 +1415,25 @@
 ;;157 Indexing Sequences
 #(map vector % (range))

+;;158 Decurry
+(defn f [g]
+  (let [decurry-rec
+        (fn [g [fst & rst]]
+          (if (number? g)
+            g
+            (recur (g fst) rst)))]
+    (fn decurry [& args]
+      (decurry-rec g args))))
+;;wow solution
+(fn [f] (fn [& xs] (reduce #(%1 %2) f xs)))
+;;wowow solution
+(fn decurry [f]
+  (fn [& args]
+    (loop [f f [x & r] args]
+      (if (fn? f) (recur (f x) r) f) )))
+
+;;161 Subset and Superset
+#{1 2}
+
+;;162 Logical falsity and truth
+1


Add new converter: `to-flowable`.

Add new converter: `to-flowable`.

--- src/beicon/core.cljc
+++ src/beicon/core.cljc
@@ -777,6 +777,26 @@
    (let [disposable (on-value ob #(swap! a f %))]
      (disposable-atom a disposable))))

+#?(:clj
+   (defn- as-backpressure-strategy
+     [strategy]
+     (if (instance? BackpressureStrategy strategy)
+       strategy
+       (case strategy
+         :buffer BackpressureStrategy/BUFFER
+         :drop BackpressureStrategy/DROP
+         :missing BackpressureStrategy/MISSING
+         :error BackpressureStrategy/ERROR
+         (throw (ex-info "Unexpected option" {:strategy strategy}))))))
+
+#?(:clj
+   (defn to-flowable
+     "Convert an observable into backpressure-aware Flowable instance."
+     ([ob]
+      (to-flowable :buffer ob))
+     ([strategy ^Observable ob]
+      (.toFlowable ob ^BackpressureStrategy (as-backpressure-strategy strategy)))))
+
 ;; --- Observable Transformations

 (defn race


prevent ff from not picking up that mask changed

--- src-cljs/frontend/components/canvas.cljs
+++ src-cljs/frontend/components/canvas.cljs
@@ -1001,7 +1001,9 @@
                       :id "svg-canvas"
                       :xmlns "http://www.w3.org/2000/svg"
                       :key "svg-canvas"
-                      :mask (when (ua-browser/isFirefox) (utils/absolute-css-hash "canvas-mask"))
+                      :mask (when (and (ua-browser/isFirefox)
+                                       (get-in app state/chat-opened-path))
+                              (utils/absolute-css-hash "canvas-mask"))
                       :className (str "canvas-frame "
                                       (cond (keyboard/arrow-shortcut-active? app) " arrow-tool "
                                             (keyboard/pan-shortcut-active? app) " pan-tool "


Use "" when pressing down on the first input

--- src-frontend/cljs/cljs_webrepl/core.cljs
+++ src-frontend/cljs/cljs_webrepl/core.cljs
@@ -179,8 +179,8 @@
     (if (> new-cursor 0)
       (assoc state
              :cursor new-cursor
-             :input (:expression (get history (- c new-cursor))))
-      state)))
+             :input (:expression (get history (- c new-cursor) "")))
+      (assoc state :input ""))))

 (defn clear-input [state]
   (assoc state


minor - comments

minor - comments

--- src-cljc/tourney_nerd/events.cljc
+++ src-cljc/tourney_nerd/events.cljc
@@ -10,6 +10,13 @@
     [tourney-nerd.groups :as groups]
     [tourney-nerd.schedule :as schedule]
     [tourney-nerd.teams :as teams]))
+
+;; TODO: we need an "event integrity" function
+;; - all games timeslots are on the schedule
+;; - all game team IDs are valid
+;; - all games divisions match the teams playing
+;; - all fields + timeslots are unique (no double-booked fields)
+;; - scheduled games cannot have scores

 (defn new-random-id [id]
   (let [[head _tail] (str/split id #"-")]


Programmes, Projects and Properities now display on Programmes tab

--- src-cljs/kixi/hecuba/main_om.cljs
+++ src-cljs/kixi/hecuba/main_om.cljs
@@ -9,45 +9,55 @@

 (enable-console-print!)

-(defn table-row [fields]
-  (fn [data owner]
-    (om/component
-        (apply dom/tr #js {:onClick (fn [e] (.log js/console "ooh!"))}
-               (for [field fields]
-                 (dom/td nil (str (get data field))))
-               ))))
+(defn table-row [data owner cols]
+  (om/component
+      (apply dom/tr nil
+             (for [col cols]
+               (let [d (get data col)
+                     sd (if (keyword? d) (name d) (str d))]
+                 (dom/td nil (if (#{:hecuba/href :hecuba/parent-href} col) (dom/a #js {:href sd} sd) sd)))))))

-(defn create-table [model-path fields row]
-  (fn [data owner]
-    (reify
-      om/IRender
-      (render [_]
-        (dom/div #js {:className "table-responsive"}
-             (dom/table #js {:className "table table-bordered table-hover table-striped"}
-                  (dom/thead nil
-                       (apply dom/tr nil
-                            (for [field fields]
-                              (dom/th nil field))))
-                  (dom/tbody nil
-                       (om/build-all row
-                           (get-in data model-path)
-                           {:key :hecuba/name}))))))))
-
-(defn about-card [data owner]
+(defn table [data owner]
   (om/component
-      (dom/p nil "I'm the About card")))
+      (dom/div #js {:className "table-responsive"}
+           (dom/table #js {:className "table table-bordered table-hover table-striped"}
+                (dom/thead nil
+                     (apply dom/tr nil
+                            (for [col (:cols data)]
+                              (dom/th nil (name col)))))
+                (dom/tbody nil
+                     (om/build-all table-row (:rows data)
+                         {:key :hecuba/id :opts (:cols data)}))))))

 (defn blank-card [data owner]
   (om/component
       (dom/p nil "This page is unintentionally left blank")))

+(defn about-card [data owner card]
+  (om/component
+      (dom/p nil "I'm the About card")))
+
+(defn programmes-card [data owner card]
+  (om/component
+      (dom/div nil
+           (dom/h1 nil (:title card))
+           (when-let [tabdata (get-in data [:programmes :table])]
+             (om/build table tabdata))
+           (dom/h1 nil "Projects")
+           (when-let [tabdata (get-in data [:projects :table])]
+             (om/build table tabdata))
+           (dom/h1 nil "Properties")
+           (when-let [tabdata (get-in data [:properties :table])]
+             (om/build table tabdata)))))
+
 (defn card-container [cards]
   (fn [data owner]
     (om/component
         (dom/div nil
-             (if-let [card (get cards (-> data :card-container :selected))]
-               (om/build card data)
-               (om/build blank-card data))))))
+             (let [selected (-> data :card-container :selected)]
+               (if-let [card (get cards selected)]
+                 (om/build card data {:opts (->> data :card-container :cards (filter #(= (:name %) selected)) first)})
+                 (om/build blank-card data)))))))

 (def app-model
@@ -63,14 +73,15 @@
                            {:name :documentation :label "Documentation"}
                            {:name :api_users :label "API users"}
                            ]}
-         :card-container {:selected "about"
+         :card-container {:selected :programmes
                           :cards [{:name :about :title "About"}
                                   {:name :documentation :title "Documentation"}
                                   {:name :users :title "Users"}
                                   {:name :programmes :title "Programmes"}
                                   ]}
-         :projects []
-         :properties []}))
+         :programmes {}
+         :projects {}
+         :properties {}}))

 (defn ^:export handle-left-nav [menu-item]
   ;; Currently we implement a one-to-one correspondence between the
@@ -90,24 +101,30 @@
 ;; Attach projects to a table component at hecuba-projects
 #_(om/root app-model (create-table [:projects]
                                  ["Name" "Project code" "Leaders"]
-                                 (table-row [:hecuba/name :project-code :leaders])) (.getElementById js/document "hecuba-projects"))
+                                 (make-table-row [:hecuba/name :project-code :leaders])) (.getElementById js/document "hecuba-projects"))

 #_(om/root app-model (create-table [:properties]
                                  ["Name" "Address" "Rooms" "Construction date"]
-                                 (table-row [:hecuba/name :address :rooms :date-of-construction])) (.getElementById js/document "hecuba-properties"))
+                                 (make-table-row [:hecuba/name :address :rooms :date-of-construction])) (.getElementById js/document "hecuba-properties"))

-(om/root app-model (card-container {:about about-card}) (.getElementById js/document "hecuba-cards"))
+(om/root app-model (card-container {:about about-card
+                                    :programmes programmes-card}) (.getElementById js/document "hecuba-cards"))

-#_(GET "/messages/" {:handler (fn [x]
+(GET "/messages/" {:handler (fn [x]
                               (println "Messages: " x)
                               (swap! app-model assoc-in [:messages] x))
                    :headers {"Accept" "application/edn"}})

+(defn handle-get [k]
+  (fn [data]
+    (let [cols (distinct (mapcat keys data))]
+      (swap! app-model assoc-in [k]
+             {:raw data
+              :table {:cols cols
+                      :rows data}
+              }))))
+
 ;; Get the real project data
-#_(GET "/projects/" {:handler #(swap! app-model assoc-in [:projects] %)
-                   :headers {"Accept" "application/edn"}})
-
-#_(GET "/propertiesX/" {:handler #(swap! app-model assoc-in [:properties] %)
-                      :headers {"Accept" "application/edn"}})
-
-;; Observation: Projects and Messages appear to be competing - only one of them seems to 'win'
+(GET "/programmes/" {:handler (handle-get :programmes) :headers {"Accept" "application/edn"}})
+(GET "/projects/" {:handler (handle-get :projects) :headers {"Accept" "application/edn"}})
+(GET "/propertiesX/" {:handler (handle-get :properties) :headers {"Accept" "application/edn"}})


reduce timeout from 1 minute to 5 seconds

--- src/ads_txt_crawler/httpkit.clj
+++ src/ads_txt_crawler/httpkit.clj
@@ -29,7 +29,7 @@
 ;; Pass the :client above in
 ;; So this routine tries normally and if there is an error tries the SNI supported call
 (defn get-url [url]
-  (let [opts {:follow-redirects true :insecure? true :max-redirects 10}]
+  (let [opts {:follow-redirects true :insecure? true :max-redirects 10 :timeout 5000}]
     (let [{:keys [status headers body error] :as resp} @(http/get url opts)]
       (if error
         @(http/get url (assoc opts :client client))


No fast path stuff

No fast path stuff

--- src/cljs/go/core.clj
+++ src/cljs/go/core.clj
@@ -182,31 +182,6 @@
              (loop* ~(vec (interleave gs gs))
                (let ~(vec (interleave bs gs))
                  ~@body)))))))
-
-(def fast-path-protocols
-  "protocol fqn -> [partition number, bit]"
-  (zipmap (map #(symbol "cljs.core" (core/str %))
-               '[IFn ICounted IEmptyableCollection ICollection IIndexed ASeq ISeq INext
-                 ILookup IAssociative IMap IMapEntry ISet IStack IVector IDeref
-                 IDerefWithTimeout IMeta IWithMeta IReduce IKVReduce IEquiv IHash
-                 ISeqable ISequential IList IRecord IReversible ISorted IPrintWithWriter IWriter
-                 IPrintWithWriter IPending IWatchable IEditableCollection ITransientCollection
-                 ITransientAssociative ITransientMap ITransientVector ITransientSet
-                 IMultiFn IChunkedSeq IChunkedNext IComparable INamed ICloneable IAtom
-                 IReset ISwap])
-          (iterate (fn [[p b]]
-                     (if (core/== 2147483648 b)
-                       [(core/inc p) 1]
-                       [p (core/bit-shift-left b 1)]))
-                   [0 1])))
-
-(def fast-path-protocol-partitions-count
-  "total number of partitions"
-  (let [c (count fast-path-protocols)
-        m (core/mod c 32)]
-    (if (core/zero? m)
-      (core/quot c 32)
-      (core/inc (core/quot c 32)))))

 (defmacro str [& xs]
   ;; Eagerly stringify any string or char literals.


Add test case for chr X

Add test case for chr X


remove unused import

remove unused import

--- test/metabase/driver/generic_sql/query_processor_test.clj
+++ test/metabase/driver/generic_sql/query_processor_test.clj
@@ -5,7 +5,6 @@
             [metabase.driver :as driver]
             (metabase.driver [query-processor :refer [max-result-bare-rows]]
                              [query-processor-test :as qp-test])
-            [metabase.test.data :refer [db-id table->id field->id]]
             [metabase.test.data.datasets :as datasets]))

 ;; # ERROR RESPONSES


make *server-instance* dynamic

make *server-instance* dynamic

--- src/app/clj/one/sample/repl.clj
+++ src/app/clj/one/sample/repl.clj
@@ -7,7 +7,7 @@
             [one.sample.dev-server :as dev]
             [clojure.java.browse :as browse]))

-(def *server-instance* (atom nil))
+(def ^:dynamic *server-instance* (atom nil))

 (defn- run-server []
   (if (nil? @*server-instance*)


(maint) Remove redundant ring.mock.request

This patch removes a redundant requirement on ring.mock.request from the
puppetlabs.services.master.master-core-test test suite.

--- test/unit/puppetlabs/services/master/master_core_test.clj
+++ test/unit/puppetlabs/services/master/master_core_test.clj
@@ -3,7 +3,6 @@
             [clojure.string :refer [split]]
             [cheshire.core :as json]
             [puppetlabs.services.master.master-core :refer :all]
-            [ring.mock.request :as mock]
             [schema.test :as schema-test]
             [puppetlabs.comidi :as comidi]
             [puppetlabs.services.protocols.jruby-puppet :as jruby]
@@ -41,7 +40,7 @@

 (defn app-request
   ([app path] (app-request app :get path))
-  ([app method path] (app (mock/request method path))))
+  ([app method path] (app (ring-mock/request method path))))

 (deftest test-master-routes
   (let [handler     (fn ([req] {:request req}))
@@ -52,7 +51,7 @@
     (is (= 200 (:status (app (-> {:request-method :post
                                   :uri "/v3/catalog/bar"
                                   :content-type "application/x-www-form-urlencoded"}
-                                 (mock/body "environment=environment1234"))))))
+                                 (ring-mock/body "environment=environment1234"))))))
     (is (nil? (request "/foo")))
     (is (nil? (request "/foo/bar")))
     (doseq [[method paths]
@@ -279,7 +278,7 @@
           resp        (app (-> {:request-method :put
                                 :content-type "application/octet-stream"
                                 :uri "/v3/file_bucket_file/bar"}
-                               (mock/body "foo")))]
+                               (ring-mock/body "foo")))]
       (is (= "application/octet-stream"
              (get-in resp [:request :content-type])))

@@ -288,7 +287,7 @@
         (let [resp (app (-> {:request-method :put
                           :content-type "something-crazy/for-content-type"
                           :uri "/v3/file_bucket_file/bar"}
-                            (mock/body "foo")))]
+                            (ring-mock/body "foo")))]
           (is (= "something-crazy/for-content-type"
                  (get-in resp [:request :content-type]))))))))


fix namespace ref in tests

fix namespace ref in tests

--- test/doctopus/db_test.clj
+++ test/doctopus/db_test.clj
@@ -1,25 +1,25 @@
-(ns doctopus.db.core-test
+(ns doctopus.db-test
   (:require [doctopus.db.core :refer :all]
             [clojure.test :refer :all]))

 (deftest db
   (testing "->kebab-keys transforms all keys to kebab-case"
-    (is (= (#'doctopus.db/->kebab-keys {:key_name "lol"}) {:key-name "lol"}))
-    (is (= (#'doctopus.db/->kebab-keys {:key_name "lol" :another_key "heh"})
+    (is (= (#'doctopus.db.core/->kebab-keys {:key_name "lol"}) {:key-name "lol"}))
+    (is (= (#'doctopus.db.core/->kebab-keys {:key_name "lol" :another_key "heh"})
            {:key-name "lol" :another-key "heh"}))
-    (is (= (#'doctopus.db/->kebab-keys
+    (is (= (#'doctopus.db.core/->kebab-keys
             {:key_name "lol" :another_key "heh" :untouched-key "laffo"})
            {:key-name "lol" :another-key "heh" :untouched-key "laffo"})))
   (testing "->snake-keys transforms all keys to snake-case"
-    (is (= (#'doctopus.db/->snake-keys {:key-name "lol"}) {:key_name "lol"}))
-    (is (= (#'doctopus.db/->snake-keys {:key-name "lol" :another-key "heh"})
+    (is (= (#'doctopus.db.core/->snake-keys {:key-name "lol"}) {:key_name "lol"}))
+    (is (= (#'doctopus.db.core/->snake-keys {:key-name "lol" :another-key "heh"})
            {:key_name "lol" :another_key "heh"}))
-    (is (= (#'doctopus.db/->snake-keys
+    (is (= (#'doctopus.db.core/->snake-keys
             {:key-name "lol" :another-key "heh" :untouched_key "laffo"})
            {:key_name "lol" :another_key "heh" :untouched_key "laffo"})))
   (testing "add-date-fields adds updated and created fields"
-    (is (= (#'doctopus.db/add-date-fields {} "right-now")
+    (is (= (#'doctopus.db.core/add-date-fields {} "right-now")
            {:updated "right-now" :created "right-now"})))
   (testing "add-date-fields does not updated created if present"
-    (is (= (#'doctopus.db/add-date-fields {:created "earlier"} "right-now")
+    (is (= (#'doctopus.db.core/add-date-fields {:created "earlier"} "right-now")
            {:updated "right-now" :created "earlier"}))))


Limit description size in aggregate stream

Thanks 行者 for the patch

--- satori-rules/rules/alarm.clj
+++ satori-rules/rules/alarm.clj
@@ -131,10 +131,11 @@
       (throw (Exception. "aggregate must accept list of events")))

     (when (> (count evts) 0)
-      (let [m (mapv :metric evts), v (f m)]
-        (call-rescue
-          (into (last evts) {:metric v, :description (string/join "\n" (map #(str (:host %) "=" (:metric %)) evts))})
-          children)))))
+      (let [m (mapv :metric evts), v (f m)
+            s (string/join "\n" (map #(str (:host %) "=" (:metric %)) evts))
+            s (if (> (count s) 3000) (subs s 0 3000) s)
+            aggregated (into (last evts) {:metric v, :description s})]
+        (call-rescue aggregated children)))))

 (defn aggregate [f & children]
   (apply aggregate* (fn [m] (apply f m)) children))
@@ -365,4 +366,10 @@
           rst (inject! [s] [{:host "meh" :service "metric.ev1" :metric 10},
                             {:host "meh" :service "metric.ev2" :metric 20}])]
       (is (= [{:host "meh" :service "metric.final" :metric [10 20 10 20 10 20]}]
-             (:slot-coalesce-test rst))))))
+             (:slot-coalesce-test rst)))))
+
+  (deftest aggregate-test
+    (let [s (alarm/aggregate + (tap :aggregate-test))
+          rst (inject! [s] [[{:host "meh" :service "bar" :metric 10},
+                            {:host "meh" :service "bar" :metric 80}]])]
+      (is (= 90 (get-in rst [:aggregate-test 0 :metric]))))))


Notes on managing state.

Notes on managing state.

--- test/com/nomistech/clojure_the_language/intro_to_state.clj
+++ test/com/nomistech/clojure_the_language/intro_to_state.clj
@@ -51,6 +51,7 @@
                      :address {:line-1 "78 Green Lane"
                                :line-2 "New Town"}})

+
 ;;;; ___________________________________________________________________________
 ;;;; Atoms and concurrency

@@ -59,10 +60,11 @@
 ;;;; - Each thread uses `swap!` with a function that runs for a long time.
 ;;;;
 ;;;; - Clojure allows each thread to be optimistic.
+;;;;   - So multiple swap operations run concurrently.
 ;;;;
-;;;; - When a thread finishes:
-;;;;   - If the current value is the same as the value when the operation
-;;;;     started
+;;;; - When a swap operation finishes:
+;;;;   - If the current value of the atom is the same as the value when the
+;;;;     operation started
 ;;;;     then
 ;;;;         the new value is swapped in
 ;;;;     else


Not sure how this got in here

Not sure how this got in here

--- integration-tests/apps/tx/.descriptors/tx.clj
+++ integration-tests/apps/tx/.descriptors/tx.clj
@@ -1 +0,0 @@
-{:root "/Users/jim/src/immutant/integration-tests/apps/tx", :context-path "tx-c34beae8-f8d0-4b48-b48f-e845784e5b4f", :lein-profiles [:default :test], :swank-port nil, :nrepl-port 7888}


fixup

fixup

--- src/clj/freestuffly/scraper.clj
+++ src/clj/freestuffly/scraper.clj
@@ -15,7 +15,9 @@
   [results]
   (cset/select
     (fn [result]
-      (re-find (re-matcher interesting-keywords (first (:content (into {} result))))))
+      (re-find
+        (re-matcher interesting-keywords
+                    (first (:content (into {} result))))))
     (set results)))

 (def ^:private my-group-urls
@@ -38,13 +40,13 @@
           (s/and (s/tag :a) (s/nth-child 1)))
       html)))

-(defn- rubbish-value?
+(defn- interesting-value?
   [content-values]
   (not= (content-values :content) ["See details"]))

 (defn- content-for
   [html-vector]
-  (filter rubbish-value? (map #(select-keys % [:attrs :content]) html-vector)))
+  (filter interesting-value? (map #(select-keys % [:attrs :content]) html-vector)))

 (defn- presentable
   [results]


allow choosing number of accesses with Medium/Nerve Agent

--- src/clj/game/cards-programs.clj
+++ src/clj/game/cards-programs.clj
@@ -295,21 +295,47 @@
    {:abilities [{:cost [:click 1] :effect (effect (gain :credit 2)) :msg "gain 2 [Credits]"}]}

    "Medium"
-   {:events
-    {:successful-run {:req (req (= target :rd))
-                      :effect (effect (add-prop card :counter 1))}
-     :pre-access {:req (req (= target :rd))
-                  :effect (effect (access-bonus (max 0 (dec (get-virus-counters state side (get-card state card))))))}}}
+   {:events {:no-action {:req (req (and run (= (:server run) [:rd]) (not current-ice))
+                                        (and (:counter card) (> (:counter card) 0)))
+                         :effect (req (system-msg state :runner (str "may choose fewer than all additional R&D accesses"
+                                                                     " by clicking on Medium"))
+                                      (update! state side (assoc card :medium-active true)))}
+             :successful-run {:req (req (and (= target :rd)
+                                             (or (:medium-active card) (nil? (:counter card)))))
+                              :effect (effect (add-prop card :counter 1))}
+             :pre-access {:req (req (and (= target :rd) (:medium-active card)))
+                          :effect (effect (access-bonus (max 0 (dec (get-virus-counters state side (get-card state card))))))}}
+    :abilities [{:req (req (:medium-active card))
+                 :effect (effect (add-prop card :counter 1)
+                                 (resolve-ability
+                                   {:prompt "Choose how many additional R&D accesses to make"
+                                    :choices {:number (req (get-virus-counters state side card))}
+                                    :msg (msg "do " target " additional accesses from R&D")
+                                    :effect (effect (access-bonus (max 0 target))
+                                                    (update! (dissoc (get-card state card) :medium-active)))} card nil))}]}

    "Multithreader"
    {:recurring 2}

    "Nerve Agent"
-   {:events
-    {:successful-run {:req (req (= target :hq))
-                      :effect (effect (add-prop card :counter 1))}
-     :pre-access {:req (req (= target :hq))
-                  :effect (effect (access-bonus (max 0 (dec (get-virus-counters state side (get-card state card))))))}}}
+   {:events {:no-action {:req (req (and run (= (:server run) [:hq]) (not current-ice))
+                                        (and (:counter card) (> (:counter card) 0)))
+                         :effect (req (system-msg state :runner (str "may choose fewer than all additional HQ accesses"
+                                                                     " by clicking on Nerve Agent"))
+                                      (update! state side (assoc card :nerve-active true)))}
+             :successful-run {:req (req (and (= target :rd)
+                                             (or (:nerve-active card) (nil? (:counter card)))))
+                              :effect (effect (add-prop card :counter 1))}
+             :pre-access {:req (req (and (= target :hq) (:nerve-active card)))
+                          :effect (effect (access-bonus (max 0 (dec (get-virus-counters state side (get-card state card))))))}}
+    :abilities [{:req (req (:nerve-active card))
+                 :effect (effect (add-prop card :counter 1)
+                                 (resolve-ability
+                                   {:prompt "Choose how many additional HQ accesses to make"
+                                    :choices {:number (req (get-virus-counters state side card))}
+                                    :msg (msg "do " target " additional accesses from HQ")
+                                    :effect (effect (access-bonus (max 0 target))
+                                                    (update! (dissoc (get-card state card) :nerve-active)))} card nil))}]}

    "Net Shield"
    {:prevent {:damage [:net]}


the wrong key was being used to access the workflow id in the cache

--- src/clj/jsk/conductor/cache.clj
+++ src/clj/jsk/conductor/cache.clj
@@ -196,7 +196,7 @@
         id-fn (fn [node-fn node-id-kw]
                 (->> c node-fn (filter filter-fn) (map node-id-kw)))
         job-ids (id-fn jobs :job-id)
-        wf-ids (id-fn workflows :wf-id)]
+        wf-ids (id-fn workflows :workflow-id)]
     (set (concat job-ids wf-ids))))


Provide JSON header

Provide JSON header

--- src/clj/sparqlab/routes/api.clj
+++ src/clj/sparqlab/routes/api.clj
@@ -12,6 +12,9 @@
             [cheshire.core :refer [generate-string]]
             [clojure.set :as set]))

+(def ^:private json-header
+  {"Content-Type" "application/json"})
+
 (defn sparql-query
   "Execute a SPARQL query in an HTTP GET request using `params` and HTTP `headers`."
   [{:keys [query]
@@ -24,7 +27,7 @@
                                                  :query-params params
                                                  :throw-entire-message? true}))
       {:status 400
-       :headers {"Content-Type" "application/json"}
+       :headers json-header
        :body (generate-string validation-results)})))

 (defn get-exercise-solution
@@ -49,7 +52,9 @@
                            (-> construct
                                (dissoc :lang)
                                (set/rename-keys {:construct :value})))]
-    (generate-string (map format-construct sparql-constructs))))
+    {:status 200
+     :headers json-header
+     :body (generate-string (map format-construct sparql-constructs))}))

 (defroutes api-routes
   (context "/api" []


Functions to make temporary directory and generate name for this directory

--- src/clj_fileutils/fileutils.clj
+++ src/clj_fileutils/fileutils.clj
@@ -11,6 +11,9 @@
 ;

 (ns clj-fileutils.fileutils)
+
+(def temporary-name-prefix
+    "clojure-fileutils-")

 (defn print-slurp-exception
     "Print warning message to the standard output."
@@ -49,3 +52,19 @@
     [filename1 filename2]
     (mv-file filename1 filename2))

+(defn make-temporary-log-file-name
+    []
+    (let [basedir-name (new java.io.File (System/getProperty "java.io.tmpdir"))
+          base-name    temporary-name-prefix
+          temp-file    (java.io.File/createTempFile base-name ".log" basedir-name)]
+          (.getAbsolutePath temp-file)))
+
+(defn make-temporary-directory
+    "Make temporary directory that would reside in /tmp (at least on Linux)."
+    []
+    (let [basedir-name (System/getProperty "java.io.tmpdir")
+          base-name    (str temporary-name-prefix (System/currentTimeMillis))
+          temp-dir     (new java.io.File basedir-name base-name)]
+        (.mkdir temp-dir)
+        temp-dir))
+


LocalJobRunner may not cache, causing mixed-mode to lack extant locals.

--- src/clojure/parkour/io/dval.clj
+++ src/clojure/parkour/io/dval.clj
@@ -1,6 +1,7 @@
 (ns parkour.io.dval
   (:require [clojure.java.io :as io]
-            [parkour (fs :as fs) (cser :as cser) (reducers :as pr)]
+            [parkour (fs :as fs) (cser :as cser) (mapreduce :as mr)
+             ,       (reducers :as pr)]
             [parkour.io.transient :refer [transient-path]]
             [parkour.util :as util :refer [doto-let]])
   (:import [java.io Writer]
@@ -28,17 +29,18 @@
         (fs/uri (subs uri-s 0 n))))))

 (defn ^:private ->entry
-  "Parse `remote` and `local` into mapping tuple of (fragment, local file)."
+  "Parse `remote` and `local` into mapping tuple of (fragment, cache path)."
   [^URI remote local]
   (if-let [fragment (.getFragment remote)]
     (let [symlink (io/file fragment), symlink? (.exists symlink)
           local (io/file (str local)), local? (.exists local)
           remote (unfragment remote), remote? (= "file" (.getScheme remote))
           source (cond symlink? symlink, local? local, remote? remote
+                       (mr/local-runner? cser/*conf*) remote
                        :else (throw (ex-info
                                      (str remote ": cannot locate local file")
                                      {:remote remote, :local local})))]
-      [fragment source])))
+      [fragment (fs/path source)])))

 (defn ^:private distcache-dval
   "Remote-side dval data reader, reconstituting as a delay."


Add header to welcome

Add header to welcome

--- src/clojure/staircase/views.clj
+++ src/clojure/staircase/views.clj
@@ -140,12 +140,23 @@
   (common "Page Not Found"
           [:div#four-oh-four "The page you requested could not be found"]))

+(def about-header
+     [:div.about-header
+      [:div.container
+       [:h1 "InterMine Steps"]
+       [:p "The data-flow interface to InterMine data-warehouses,
+           providing an extensible, programmable tool-box for
+           scientists."]]])
+
 (def welcome
    [:div.row {:ng-controller "WelcomeCtrl" :ng-show "showWelcome"}
     [:div.panel.panel-default
      [:div.panel-heading
       "Welcome to " [:strong "Steps"]]
      [:div.panel-body
+
+      about-header
+
       [:p
        "This is the data-flow interface for intermine data-warehouses.
        If this is your first time here, maybe you might like to read more about
@@ -335,12 +346,9 @@
   [:div.container-fluid
    [:div.row
     [:div.about.col-xs-10.col-xs-offset-1
-     [:div.about-header
-      [:div.container
-       [:h1 "InterMine Steps"]
-       [:p "The data-flow interface to InterMine data-warehouses,
-           providing an extensible, programmable tool-box for
-           scientists."]]]
+
+     about-header
+
      [:section
       [:h2 "Using the home-page"]
       [:p.lead


Remove nml-parse debugging code

Remove nml-parse debugging code

--- src/nml/core.clj
+++ src/nml/core.clj
@@ -101,12 +101,6 @@

 (defn- nml-parse [s start src]
   (let [result (parse s :start start :optimize :memory)]
-;;   (let [parses (insta/parses parse s)]
-;;     (binding [*out* *err*]
-;;       (doseq [parse parses]
-;;         (println parse)
-;;         (println)))
-;;       (println (str "### " (count parses))))
     (if (insta/failure? result)
       (let [{t :text l :line c :column} result]
         (fail (str "Error parsing " src " at line " l " column " c ":")


fix comment

fix comment

--- src/nml/core.clj
+++ src/nml/core.clj
@@ -274,7 +274,7 @@
     (if (and sets (:no-prefix options)) (fail (msgs :set+no-prefix)))
     (if (and (:create options) (:in options)) (fail (msgs :create+in)))

-    ;; read -> parse -> [modify] -> output
+    ;; read -> parse -> lookup or modify -> output

     (let [fmt (let [f (:format options)] (if f (formats f) fmt-namelist))
           m   (nml-map (if (:create options) "" (read-file in)) src)]


fix typos

fix typos


Anotations: added middle-style

Anotations: added middle-style

--- src/main/clojure/conexp/gui/draw/nodes_and_connections.clj
+++ src/main/clojure/conexp/gui/draw/nodes_and_connections.clj
@@ -89,6 +89,13 @@
   "Default style for nodes being an attribute concept."
   (doto (GStyle.)
     (.setBackgroundColor Color/BLUE)))
+
+(def- default-middle-style
+  "Default style for the valuation part of the concept."
+  (doto (GStyle.)
+    (.setBackgroundColor Color/WHITE)
+    (.setForegroundColor Color/RED)))
+

 (def- default-node-label-style
   "Default style for node labels."
@@ -135,13 +142,18 @@
                          lower-style (if (= 1 (some-> this lower-neighbors count))
                                        default-object-concept-style
                                        nil),
+                         middle-style default-middle-style
                          [x y] (position this),
                          [l u] (create-two-halfcircles x y (radius this))]
                      (.setGeometryXy lower-segment l)
                      (.setGeometryXy upper-segment u)
-                     (.setGeometry middle-segment  (double x) (double y))
+                     (println (double x) (double y))
+                     (.setGeometry middle-segment
+                                   (double (+ x (/ (radius this) 1.5)))
+                                   (double y))
                      (.setStyle lower-segment lower-style)
-                     (.setStyle upper-segment upper-style))))
+                     (.setStyle upper-segment upper-style)
+                     (.setStyle middle-segment middle-style))))
         style (GStyle.)]
     (doto object
       (.setStyle default-node-style)


Also parse int

Also parse int

--- src/wishlistd/models/sql.clj
+++ src/wishlistd/models/sql.clj
@@ -71,4 +71,4 @@

 (defn delete-wish [id]
   (delete wish
-    (where {:id id})))
+    (where {:id (Integer/parseInt id)})))


code to build char location definitions from image

--- src/clj/infinitelives/pixi/pixelfont.clj
+++ src/clj/infinitelives/pixi/pixelfont.clj
@@ -35,16 +35,40 @@
 ;;   [& args]
 ;;   (println "Hello, World!"))

+(defn strips [strip-fn dim image x1 x2 y1 y2]
+  (let [strip-sizes
+        (-> image
+            (strip-fn x1 x2 y1 y2)
+            (->> (map-indexed vector)
+                 (partition-by second)
+                 (map count))
+            )]
+    ;strip-sizes
+    (partition 2 (for [n (range (count strip-sizes))]
+                   (apply + (case dim :y y1 :x x1) (take (inc n) strip-sizes))))))
+
+(def horizontal-strips (partial strips transparent-hlines :y))
+(def vertical-strips (partial strips transparent-vlines :x))
+
+(defn char-dimensions [image xi1 xi2 yi1 yi2 chars]
+  (let [hstrips (horizontal-strips image xi1 xi2 yi1 yi2)
+        vsize (apply max (map #(Math/abs (apply - %)) hstrips))]
+    (map
+     conj
+     (for [[y1 y2] hstrips
+           [x1 x2] (vertical-strips image xi1 xi2 y1 y2)]
+       [x1 x2 y1 (+ y1 vsize)])
+     chars)))
+
+
+
 (comment
   (def a (-> "test.png"
              io/file
              ImageIO/read
-             (transparent-hlines 0 200 0 200)
-             (->> (map-indexed vector)
-                  (partition-by second)
-                  (map count))
+                                        ;(horizontal-strips 0 200 0 200)
+                                        ;(process-line 0 1000 89 96)
+             (char-dimensions 127 350 84 128 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
+    )

-             ))
-
-
-  (for [n (range (count a))] (apply + (take (inc n) a))))
+  )