Start support for Novation Launchpad Pro.

error: pathspec 'src/afterglow/controllers/launchpad_pro.clj' did not match any file(s) known to git

--- src/afterglow/controllers/launchpad_pro.clj
+++ src/afterglow/controllers/launchpad_pro.clj
@@ -0,0 +1,551 @@
+(ns afterglow.controllers.launchpad-pro
+  "Allows the Novation Launchpad Pro to be used as a control surface
+  for Afterglow."
+  {:author "James Elliott"}
+  (:require [afterglow.controllers :as controllers]
+            [afterglow.effects.cues :as cues]
+            [afterglow.effects.dimmer :refer [master-get-level master-set-level]]
+            [afterglow.midi :as amidi]
+            [afterglow.rhythm :as rhythm]
+            [afterglow.show :as show]
+            [afterglow.show-context :refer [with-show]]
+            [afterglow.util :as util]
+            [clojure.math.numeric-tower :as math]
+            [com.evocomputing.colors :as colors]
+            [overtone.at-at :as at-at]
+            [overtone.midi :as midi]
+            [taoensso.timbre :as timbre :refer [warn]]
+            [taoensso.truss :as truss :refer [have have! have?]]))
+
+
+(defonce ^{:doc "Counts the controller bindings which have been made,
+  so each can be assigned a unique ID."}
+  controller-counter (atom 0))
+
+(defonce ^{:doc "Controllers which are currently bound to shows,
+  indexed by the controller binding ID."}
+  active-bindings (atom {}))
+
+(def control-buttons
+  "The labeled buttons which send and respond to Control Change
+  events."
+  {
+   :record-arm 1
+   :track-select 2
+   :mute 3
+   :solo 4
+   :volume 5
+   :pan 6
+   :sends 7
+   :stop 8
+
+   :circle 10
+   :double 20
+   :duplicate 30
+   :quantize 40
+   :delete 50
+   :undo 60
+   :tap-tempo 70
+   :shift 80
+
+   :row-0 19
+   :row-1 29
+   :row-2 39
+   :row-3 49
+   :row-4 59
+   :row-5 69
+   :row-6 79
+   :row-8 89
+
+   :up-arrow    91
+   :down-arrow  92
+   :left-arrow  93
+   :right-arrow 94
+   :session-mode 95
+   :note-mode 96
+   :device-mode 97
+   :user-mode 98})
+
+(def button-off-color
+  "The color of buttons that are completely off."
+  (colors/create-color :black))
+
+(def button-available-color
+  "The color of buttons that can be pressed but haven't yet been."
+  (colors/darken (colors/create-color :orange) 45))
+
+(def button-pressed-color
+  "The color of an available button that is currently being pressed."
+  (colors/create-color :yellow))
+
+(def stop-available-color
+  "The color of the Stop button when not active."
+  (colors/darken (colors/create-color :red) 45))
+
+(def stop-active-color
+  "The color of the stop button when active."
+  (colors/create-color :red))
+
+(defn led-color-values
+  "Given a color, return the values that should be sent in a Sysex
+  message to set an LED to that color."
+  [color]
+  (let [r (colors/red color)
+        g (colors/green color)
+        b (colors/blue color)]
+    (list (quot r 4) (quot g 4) (quot b 4))))
+
+(defn set-led-color
+  "Set one of the LEDs, given its control or note number, to a
+  specific RGB color."
+  [controller led color]
+  (let [r (colors/red color)
+        g (colors/green color)
+        b (colors/blue color)]
+    (midi/midi-sysex (:port-out controller) [240 0 32 41 2 16 11 led (quot r 4) (quot g 4) (quot b 4) 247])))
+
+(defn set-pad-color
+  "Set the color of one of the 64 touch pads to a specific RGB color."
+  [controller x y color]
+  (set-led-color controller (+ 11 x (* y 10)) color))
+
+(defn- move-origin
+  "Changes the origin of the controller, notifying any registered
+  listeners."
+  [controller origin]
+  (when (not= origin @(:origin controller))
+    (reset! (:origin controller) origin)
+    (doseq [f @(:move-listeners controller)] (f @(:grid-controller-impl controller) :moved))))
+
+(defn show-labels
+  "Illuminates all buttons with text labels, for development assistance."
+  ([controller]
+   (show-labels controller button-available-color))
+  ([controller color]
+   (doseq [[_ led] control-buttons]
+     (set-led-color controller led color))))
+
+(defn clear-interface
+  "Clears all illuminated buttons and pads."
+  [controller]
+  (midi/midi-sysex (:port-out controller) [240 0 32 41 2 16 14 0 247])  ; Sets all LEDs to blackout
+  (reset! (:last-text-buttons controller) {}))  ; Note that no buttons are lit
+
+(defn- update-text-buttons
+  "Sees if any labeled buttons have changed state since the last time
+  the interface was updated, and if so, sends the necessary MIDI
+  commands to update them on the Launchpad Pro."
+  [controller]
+  ;; First turn off any which were on before but no longer are
+  (doseq [[button old-color] @(:last-text-buttons controller)]
+    (when-not (get @(:next-text-buttons controller) button)
+      (set-led-color controller button button-off-color)))
+
+  ;; Then, light any currently active buttons
+  (doseq [[button color] @(:next-text-buttons controller)]
+    (when-not (= (get @(:last-text-buttons controller)) color)
+      (set-led-color controller button color)))
+
+  ;; And record the new state for next time
+  (reset! (:last-text-buttons controller) @(:next-text-buttons controller)))
+
+(defn- render-cue-grid
+  "Figure out how the cue grid pads should be illuminated, based on
+  the currently active cues. Returns a vector of the rows, from bottom
+  to top. Each row is a vector of the colors in that row, from left to
+  right."
+  [controller]
+  (let [[origin-x origin-y] @(:origin controller)
+        active-keys (show/active-effect-keys (:show controller))]
+    (vec (for [y (range 8)]
+           (vec (for [x (range 8)]
+                  (let [[cue active] (show/find-cue-grid-active-effect (:show controller) (+ x origin-x) (+ y origin-y))
+                        ending (and active (:ending active))
+                        l-boost (when cue (if (zero? (colors/saturation (:color cue))) 20.0 0.0))
+                        color (when cue
+                                (colors/create-color
+                                 :h (colors/hue (:color cue))
+                                 :s (colors/saturation (:color cue))
+                                 ;; Figure the brightness. Active, non-ending cues are full brightness;
+                                 ;; when ending, they blink between middle and low. If they are not active,
+                                 ;; they are at middle brightness unless there is another active effect with
+                                 ;; the same keyword, in which case they are dim.
+                                 :l (+ (if active
+                                         (if ending
+                                           (if (> (rhythm/metro-beat-phase (:metronome (:show controller))) 0.4) 10 20)
+                                           50)
+                                         (if (active-keys (:key cue)) 10 20))
+                                       l-boost)))]
+                    (or color button-off-color))))))))
+
+(defn- count-grid-changes
+  "See how many changes we can find in the grid, with an upper limit
+  Returns the number of changes we see, or the upper bound if that is
+  reached before the end of the grid."
+  [controller limit]
+  (loop [found 0
+         old (flatten @(:last-grid-pads controller))
+         new (flatten @(:next-grid-pads controller))]
+    (if (or (>= found limit) (empty? new))
+      found
+      (recur
+       (if (= (first old) (first new)) found (inc found))
+       (rest old)
+       (rest new)))))
+
+(defn- update-cue-grid
+  "Set the colors of all the cue grid pads, based on the currently
+  active cues and overlays. On the Launchpad Pro this can be done with
+  a single Sysex message, but doing that when there are few changes
+  results in a visible flicker. So as a compromise, we update the
+  whole grid when six or more cells have changed, otherwise we send
+  the differences individually."
+  [controller]
+  (let [limit 6
+        changes (count-grid-changes controller limit)]
+    (timbre/info "changes" changes)
+    (if (>= changes limit)
+      (midi/midi-sysex (:port-out controller)  ; Send the entire grid as one Sysex message
+                       (concat [240 0 32 41 2 16 15 1]
+                               (flatten (map led-color-values (flatten @(:next-grid-pads controller))))
+                               [247]))
+      (doseq [x (range 8)  ; Send the changes only, individually
+              y (range 8)]
+        (let [color (get-in @(:next-grid-pads controller) [y x])]
+          (when-not (= (get-in @(:last-grid-pads controller) [y x]) color)
+            (set-pad-color controller x y color)))))
+    (reset! (:last-grid-pads controller) @(:next-grid-pads controller))))
+
+
+(defn- update-interface
+  "Determine the desired current state of the interface, and send any
+  changes needed to get it to that state."
+  [controller]
+  (try
+    ;; Assume we are starting out with a blank interface.
+    (reset! (:next-text-buttons controller) {})
+
+    ;; If the show has stopped without us noticing, enter stop mode
+    (with-show (:show controller)
+      (when-not (or (show/running?) @(:stop-mode controller))
+        #_(enter-stop-mode controller)))
+
+    ;; Reflect the shift button state
+    (swap! (:next-text-buttons controller)
+           assoc (:shift control-buttons)
+           (if @(:shift-mode controller) button-pressed-color button-available-color))
+
+    #_(update-scroll-arrows controller)
+
+    ;; Make the User button bright, since we live in User mode
+    (swap! (:next-text-buttons controller)
+           assoc (:user-mode control-buttons) button-pressed-color)
+
+    ;; Make the stop button visible, since we support it
+    (swap! (:next-text-buttons controller)
+           assoc (:stop control-buttons)
+           stop-available-color)
+
+    (reset! (:next-grid-pads controller) (render-cue-grid controller))
+
+    ;; Add any contributions from interface overlays, removing them
+    ;; if they report being finished.
+    (controllers/run-overlays (:overlays controller))
+
+    (update-cue-grid controller)
+    (update-text-buttons controller)
+
+    (catch Throwable t
+      (warn t "Problem updating Ableton Push Interface"))))
+
+(defn- midi-received
+  "Called whenever a MIDI message is received while the controller is
+  active; checks if it came in on the right port, and if so, decides
+  what should be done."
+  [controller message]
+  (when (amidi/same-device? (:device message) (:port-in controller))
+    ;(timbre/info message)
+    (when-not (controllers/overlay-handled? (:overlays controller) message)
+      (when (= (:command message) :control-change)
+        #_(control-change-received controller message))
+      (when (= (:command message) :note-on)
+        #_(note-on-received controller message))
+      (when (= (:command message) :note-off)
+        #_(note-off-received controller message)))))
+
+(defn- welcome-frame
+  "Render a frame of the welcome animation, or if it is done, start
+  the main interface update thread, and terminate the task running the
+  animation."
+  [controller counter task]
+  (try
+    (cond
+      (< @counter 8)
+      (doseq [y (range 0 (inc @counter))]
+        (let [color (colors/create-color
+                     :h 0 :s 0 :l (max 10 (- 75 (/ (* 50 (- @counter y)) 6))))]
+          (set-pad-color controller 3 y color)
+          (set-pad-color controller 4 y color)))
+
+      (< @counter 12)
+      (doseq [x (range 0 (- @counter 7))
+              y (range 0 8)]
+        (let [color (colors/create-color
+                     :h 340 :s 100 :l (- 75 (* (- @counter 8 x) 20)))]
+          (set-pad-color controller (- 3 x) y color)
+          (set-pad-color controller (+ 4 x) y color)))
+
+      (< @counter 15)
+      (doseq [y (range 0 8)]
+        (let [color (colors/create-color
+                     :h (* 13 (- @counter 11)) :s 100 :l 50)]
+          (set-pad-color controller (- @counter 7) y color)
+          (set-pad-color controller (- 14 @counter) y color)))
+
+      (= @counter 15)
+      (show-labels controller (colors/create-color :cyan))
+
+      (< @counter 24)
+      (doseq [x (range 0 8)]
+        (let [lightness-index (if (> x 3) (- 7 x) x)
+              lightness ([10 30 50 70] lightness-index)
+              color (colors/create-color
+                     :h (+ 60 (* 40 (- @counter 18))) :s 100 :l lightness)]
+          (set-pad-color controller x (- 23 @counter) color)))
+
+      (= @counter 24)
+      (show-labels controller (colors/create-color :blue))
+
+      (< @counter 33)
+      (doseq [x (range 0 8)]
+        (set-pad-color controller x (- 32 @counter) button-off-color))
+
+      :else
+      (do
+        (clear-interface controller)
+        (amidi/add-global-handler! @(:midi-handler controller))
+        (reset! (:task controller) (at-at/every (:refresh-interval controller)
+                                                #(update-interface controller)
+                                                controllers/pool
+                                                :initial-delay 10
+                                                :desc "Launchpad Pro interface update"))
+        (at-at/kill @task)))
+    (catch Throwable t
+      (warn t "Animation frame failed")))
+
+  (swap! counter inc))
+
+(defn- welcome-animation
+  "Provide a fun animation to make it clear the Launchpad Pro is online."
+  [controller]
+  (let [counter (atom 0)
+        task (atom nil)]
+    (reset! task (at-at/every 50 #(welcome-frame controller counter task)
+                              controllers/pool))))
+
+(defn deactivate
+  "Deactivates a controller interface, killing its update thread and
+  removing its MIDI listeners. If `:disconnected` is passed with a
+  `true` value, it means that the controller has already been removed
+  from the MIDI environment, so no effort will be made to clear its
+  display or take it out of User mode.
+
+  You can also pass a watcher object created by [[auto-bind]] as
+  `controller`; this will both deactivate the controller being managed
+  by the watcher, if it is currently connected, and cancel the
+  watcher itself. In such cases, `:disconnected` is meaningless."
+  [controller & {:keys [disconnected] :or {disconnected false}}]
+  {:pre [(#{:launchpad-pro :launchpad-pro-watcher} (type controller))]}
+  (if (= (type controller) :launchpad-pro-watcher)
+    (do ((:cancel controller))  ; Shut down the watcher
+        (when-let [watched-controller @(:controller controller)]
+          (deactivate watched-controller)))  ; And deactivate the controller it was watching for
+    (do ;; We were passed an actual controller, not a watcher, so deactivate it.
+      (swap! (:task controller) (fn [task]
+                                  (when task (at-at/kill task))
+                                  nil))
+      (show/unregister-grid-controller @(:grid-controller-impl controller))
+      (doseq [f @(:move-listeners controller)] (f @(:grid-controller-impl controller) :deactivated))
+      (reset! (:move-listeners controller) #{})
+      (amidi/remove-global-handler! @(:midi-handler controller))
+
+      (when-not disconnected
+        (Thread/sleep 35) ; Give the UI update thread time to shut down
+        (clear-interface controller)
+        ;; Leave the User button bright, in case the user has Live
+        ;; running and wants to be able to see how to return to it.
+        (set-led-color controller (:user-mode control-buttons) (colors/create-color :white)))
+
+      ;; Cancel any UI overlays which were in effect
+      (reset! (:overlays controller) (controllers/create-overlay-state))
+
+      ;; And finally, note that we are no longer active.
+      (swap! active-bindings dissoc (:id controller)))))
+
+(defn deactivate-all
+  "Deactivates all controller bindings which are currently active.
+  This will be regustered as a shutdown hook to be called when the
+  Java environment is shutting down, to clean up gracefully."
+  []
+  (doseq [[_ controller] @active-bindings]
+    (deactivate controller)))
+
+(defonce ^{:doc "Deactivates any Launchpad Pro bindings when Java is shutting down."
+           :private true}
+  shutdown-hook
+  (let [hook (Thread. deactivate-all)]
+    (.addShutdownHook (Runtime/getRuntime) hook)
+    hook))
+
+(defn bind-to-show
+  "Establish a connection to the Novation Launchpad Pro, for managing
+  the given show.
+
+  Initializes the display, and starts the UI updater thread. Since
+  SysEx messages are required for updating the display, if you are on
+  a Mac, you must
+  install [CoreMIDI4J](https://github.com/DerekCook/CoreMidi4J) to
+  provide a working implementation. (If you need to work with Java
+  1.6, you can instead
+  use [mmj](http://www.humatic.de/htools/mmj.htm), but that is no
+  longer developed, and does not support connecting or disconnecting
+  MIDI devices after Java has started.)
+
+  If you have more than one Launchpad connected, or have renamed how
+  it appears in your list of MIDI devices, you need to supply a value
+  after `:device-filter` which identifies the ports to be used to
+  communicate with the Launchpad you want this function to use. The
+  values returned by [[afterglow.midi/open-inputs-if-needed!]]
+  and [[afterglow.midi/open-outputs-if-needed!]] will be searched, and
+  the first port that matches with [[filter-devices]] will be used.
+
+  The controller will be identified in the user interface (for the
+  purposes of linking it to the web cue grid) as \"Launchpad Pro\". If
+  you would like to use a different name (for example, if have more
+  than one Launchpad), you can pass in a custom value after
+  `:display-name`.
+
+  If you want the user interface to be refreshed at a different rate
+  than the default of fifteen times per second, pass your desired
+  number of milliseconds after `:refresh-interval`."
+  [show & {:keys [device-filter refresh-interval display-name]
+           :or {device-filter "Standalone Port"
+                refresh-interval (/ 1000 15)
+                display-name "Launchpad Pro"}}]
+  {:pre [(some? show)]}
+  (let [port-in (first (amidi/filter-devices device-filter (amidi/open-inputs-if-needed!)))
+        port-out (first (amidi/filter-devices device-filter (amidi/open-outputs-if-needed!)))]
+    (if (every? some? [port-in port-out])
+      (let [controller
+            (with-meta
+              {:id (swap! controller-counter inc)
+               :display-name display-name
+               :show show
+               :origin (atom [0 0])
+               :refresh-interval refresh-interval
+               :port-in port-in
+               :port-out port-out
+               :task (atom nil)
+               :last-text-buttons (atom {})
+               :next-text-buttons (atom {})
+               :last-grid-pads (atom nil)
+               :next-grid-pads (atom nil)
+               :shift-mode (atom false)
+               :stop-mode (atom false)
+               :midi-handler (atom nil)
+               :tap-tempo-handler (amidi/create-tempo-tap-handler (:metronome show))
+               :overlays (controllers/create-overlay-state)
+               :move-listeners (atom #{})
+               :grid-controller-impl (atom nil)}
+              {:type :launchpad-pro})]
+        (reset! (:midi-handler controller) (partial midi-received controller))
+        (reset! (:grid-controller-impl controller)
+                (reify controllers/IGridController
+                  (display-name [this] (:display-name controller))
+                  (physical-height [this] 8)
+                  (physical-width [this] 8)
+                  (current-bottom [this] (@(:origin controller) 1))
+                  (current-bottom [this y] (move-origin controller (assoc @(:origin controller) 1 y)))
+                  (current-left [this] (@(:origin controller) 0))
+                  (current-left [this x] (move-origin controller (assoc @(:origin controller) 0 x)))
+                  (add-move-listener [this f] (swap! (:move-listeners controller) conj f))
+                  (remove-move-listener [this f] (swap! (:move-listeners controller) disj f))))
+        ;; Set controller in Programmer mode
+        (midi/midi-sysex (:port-out controller) [240 0 32 41 2 16 44 3 247])
+        (clear-interface controller)
+        (welcome-animation controller)
+        (swap! active-bindings assoc (:id controller) controller)
+        (show/register-grid-controller @(:grid-controller-impl controller))
+        (amidi/add-disconnected-device-handler! port-in #(deactivate controller :disconnected true))
+        controller)
+      (timbre/error "Unable to find Launchpad Pro" (amidi/describe-device-filter device-filter)))))
+
+(defn auto-bind
+  "Watches for a Novation Launchpad Pro controller to be connected,
+  and as soon as it is, binds it to the specified show
+  using [[bind-to-show]]. If that controller ever gets disconnected,
+  it will be re-bound once it reappears. Returns a watcher structure
+  which can be passed to [[deactivate]] if you would like to
+  stop it watching for reconnections. The underlying controller
+  mapping, once bound, can be accessed through the watcher's
+  `:controller` key.
+
+  If you have more than one Launchpad Pro that might beconnected, or
+  have renamed how it appears in your list of MIDI devices, you need
+  to supply a value after `:device-filter` which identifies the ports
+  to be used to communicate with the Launchpad you want this function
+  to use. The values returned
+  by [[afterglow.midi/open-inputs-if-needed!]]
+  and [[afterglow.midi/open-outputs-if-needed!]] will be searched, and
+  the first port that matches using [[filter-devices]] will be used.
+
+  Once bound, the controller will be identified in the user
+  interface (for the purposes of linking it to the web cue grid) as
+  \"Launchpad Pro\". If you would like to use a different name (for
+  example, if you have more than one Launchpad), you can pass in a
+  custom value after `:display-name`.
+
+  If you want the user interface to be refreshed at a different rate
+  than the default of fifteen times per second, pass your desired
+  number of milliseconds after `:refresh-interval`."
+  [show & {:keys [device-filter refresh-interval display-name]
+           :or {device-filter "Standalone Port"
+                refresh-interval (/ 1000 15)
+                display-name "Launchpad Pro"}}]
+  {:pre [(some? show)]}
+  (let [idle (atom true)
+        controller (atom nil)]
+    (letfn [(disconnection-handler []
+              (reset! controller nil)
+              (reset! idle true))
+            (connection-handler [device]
+              (when (compare-and-set! idle true false)
+                (if (and (nil? @controller) (seq (amidi/filter-devices device-filter [device])))
+                    (let [port-in (first (amidi/filter-devices device-filter (amidi/open-inputs-if-needed!)))
+                          port-out (first (amidi/filter-devices device-filter (amidi/open-outputs-if-needed!)))]
+                      (when (every? some? [port-in port-out])  ; We found our Launchpad! Bind to it in the background.
+                        (timbre/info "Auto-binding to" device)
+                        (future
+                          (reset! controller (bind-to-show show :device-filter device-filter
+                                                           :refresh-interval refresh-interval
+                                                           :display-name display-name))
+                          (amidi/add-disconnected-device-handler! (:port-in @controller) disconnection-handler))))
+                    (reset! idle true))))
+            (cancel-handler []
+              (amidi/remove-new-device-handler! connection-handler)
+              (when-let [device (:port-in @controller)]
+                (amidi/remove-disconnected-device-handler! device disconnection-handler)))]
+
+      ;; See if our Launchpad seems to already be connected, and if so, bind to it right away.
+      (when-let [found (first (amidi/filter-devices device-filter (amidi/open-inputs-if-needed!)))]
+        (connection-handler found))
+
+      ;; Set up to bind when connected in future.
+      (amidi/add-new-device-handler! connection-handler)
+
+      ;; Return a watcher object which can provide access to the bound controller, and be canceled later.
+      (with-meta
+        {:controller controller
+         :device-filter device-filter
+         :cancel cancel-handler}
+        {:type :launchpad-pro-watcher}))))


Switch to roboto for base fonts

Switch to roboto for base fonts

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/zk/rx.git/'


switch order of items in footer

switch order of items in footer

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/clojurecup/cc2014-frontend.git/'


add icons to cat-item switches for visual reinforcement

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/starcity-properties/odin.git/'


macro

macro

fatal: unable to access 'https://github.com/rkj/7langs7weeks.git/': The requested URL returned error: 403


Include result in cover letter exception info

Trying to debug why this is breaking in new production cluster.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/democracyworks/election-mail-works.git/'


Include "Content-Type" header in error response from ovation.http/call-http

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/physion/ovation-webapi.git/'


Rule to attach DrugBank names to drugs

Rule to attach DrugBank names to drugs

error: pathspec 'kabob-build/src/main/resources/edu/ucdenver/ccp/kabob/build/rules/bio_labels/drugs.clj' did not match any file(s) known to git

--- kabob-build/src/main/resources/edu/ucdenver/ccp/kabob/build/rules/bio_labels/drugs.clj
+++ kabob-build/src/main/resources/edu/ucdenver/ccp/kabob/build/rules/bio_labels/drugs.clj
@@ -0,0 +1,38 @@
+{:name "drug-labels-DrugBank"
+
+ ;; The query pattern used to retrieve the data that will be used to construct
+ ;; the new triples.
+ :body
+ '(;; field value that contains the identifier
+   (_/drugbank_id_field kiao/hasTemplate iaodrugbank/DrugBankDrugRecord_drugBankIdDataField1)
+   (_/drugbank_id_field rdf/type kiao/FieldValue)
+   ;; the file record in which the field value appeared (record has-part
+   ;; field-value)
+   (_/record obo/BFO_0000051 _/drugbank_id_field)
+   (_/record kiao/hasTemplate iaodrugbank/DrugBankDrugRecordSchema1)
+   ;; the record will also have fields for the drug name
+   (_/record obo/BFO_0000051 _/drug_name_field)
+   (_/drug_name_field kiao/hasTemplate iaodrugbank/DrugBankDrugRecord_drugNameDataField1)
+   (_/drug_name_field obo/IAO_0000219 ?/drug_name)
+   ;; ICE that denotes a subclass of drug .
+   (_/drugbank_id_field obo/IAO_0000219 _/ice)
+   (_/ice kiao/denotesSubClassOf obo/CHEBI_23888) ;; CHEBI:drug
+   (_/ice obo/IAO_0000219 ?/bio))
+
+ ;; The form of the new triples that will be constructed.
+ :head
+ '((?/bio rdfs/label ?/drug_name))
+
+ ;; In this case no `:reify` entry is required, since all of the data required
+ ;; to form the new triples already exist.
+
+ :options
+ {:magic-prefixes
+  [;; AllegroGraph processing directives; http://franz.com/agraph/support/documentation/current/sparql-reference.html#header2-138
+
+   ;; AllegroGraph should use "chunk at a time" processing.
+   ;; This reduces memory consumption at the cost of query
+   ;; execution time.  Given that the BIO entity set is
+   ;; potentially quite large and that this is not a realtime
+   ;; operation, this is not an unreasonable trade-off.
+   ["franzOption_chunkProcessingAllowed" "franz:yes"]]}}


Extend JS types

Extend JS types

fatal: unable to access 'https://github.com/hhucn/discuss.git/': The requested URL returned error: 403


insert sort (clojure)

insert sort (clojure)

error: pathspec 'src/main/clojure/bsearch/insert_sort.clj' did not match any file(s) known to git

--- src/main/clojure/bsearch/insert_sort.clj
+++ src/main/clojure/bsearch/insert_sort.clj
@@ -0,0 +1,31 @@
+(ns bsearch.insert-sort
+  (:use clojure.test))
+
+(defn insert-sort [a-list]
+  (defn insert [value sorted-list]
+    (cond (empty? sorted-list) (list value)
+      (<= value (first sorted-list)) (cons value sorted-list)
+      :else (cons (first sorted-list) (insert value (rest sorted-list)))
+  ))
+
+  (if (< (count a-list) 2)
+    a-list
+    (insert (first a-list) (insert-sort (rest a-list)))
+))
+
+(deftest should-sort-list
+  (is (= (list) (insert-sort (list))))
+
+  (is (= (list 1) (insert-sort (list 1))))
+
+  (is (= (list 1 2) (insert-sort (list 1 2))))
+  (is (= (list 1 2) (insert-sort (list 2 1))))
+
+  (is (= (list 1 2 3) (insert-sort (list 1 2 3))))
+  (is (= (list 1 2 3) (insert-sort (list 1 3 2))))
+  (is (= (list 1 2 3) (insert-sort (list 2 1 3))))
+  (is (= (list 1 2 3) (insert-sort (list 2 3 1))))
+  (is (= (list 1 2 3) (insert-sort (list 3 1 2))))
+  (is (= (list 1 2 3) (insert-sort (list 3 2 1))))
+)
+(run-tests)


pretty printer for states

pretty printer for states

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/tiagoantao/turtle.git/'


Email is now an integration test.

Email is now an integration test.

--- test/riemann/test/email.clj
+++ test/riemann/test/email.clj
@@ -32,7 +32,7 @@
                 "foo and bar error and ok"
                 )))

-(deftest email-test
+(deftest ^:integration email-test
          (let [email (mailer {})
                stream (email "aphyr@aphyr.com")]
            (stream {:host "localhost"


Stub for new test module: git-data-fetcher-test

fatal: unable to access 'https://github.com/tisnik/arachnida.git/': The requested URL returned error: 403


Add reader spec tests.

Add reader spec tests.

error: pathspec 'spec/cats/monad/reader_spec.cljx' did not match any file(s) known to git

--- spec/cats/monad/reader_spec.cljx
+++ spec/cats/monad/reader_spec.cljx
@@ -0,0 +1,49 @@
+(ns cats.monad.reader-spec
+  #+cljs
+  (:require [speclj.core :as s :include-macros true]
+            [cats.builtin :as b]
+            [cats.protocols :as p]
+            [cats.monad.reader :as reader]
+            [cats.monad.maybe :as maybe]
+            [cats.core :as m :include-macros true])
+  #+clj
+  (:require [speclj.core :as s]
+            [cats.builtin :as b]
+            [cats.protocols :as p]
+            [cats.monad.reader :as reader]
+            [cats.monad.maybe :as maybe]
+            [cats.core :as m]))
+
+(s/describe "reader-monad"
+  (s/it "The `ask` reader gives you access to the environment"
+    (s/should= {:foo "bar"} (reader/run-reader reader/ask {:foo "bar"})))
+
+  (s/it "The `local` function allows you to run readers in a modified environment"
+    (s/should= 42 (reader/run-reader (reader/local inc reader/ask) 41)))
+
+  (s/it "The monadic values can be mapped over"
+    (s/should= 42 (reader/run-reader (m/fmap inc reader/ask) 41))))
+
+
+(def maybe-reader (reader/reader-transformer maybe/maybe-monad))
+
+(s/describe "reader-transformerformer"
+  (s/it "The `ask` reader gives you access to the environment"
+    (s/should= (maybe/just {:foo "bar"})
+               (m/with-monad maybe-reader
+                 (reader/run-reader reader/ask {:foo "bar"}))))
+
+  (s/it "The `local` function allows you to run readers in a modified environment"
+    (s/should= (maybe/just 42)
+               (m/with-monad maybe-reader
+                 (reader/run-reader (reader/local inc reader/ask) 41))))
+
+  (s/it "Monadic values can be lifted to the reader transformer"
+    (s/should= (maybe/just 42)
+               (m/with-monad maybe-reader
+                 (reader/run-reader (m/lift (maybe/just 42)) {}))))
+
+  (s/it "The monad transformer values can be mapped over"
+    (s/should= (maybe/just 42)
+               (m/with-monad maybe-reader
+                 (reader/run-reader (m/fmap inc reader/ask) 41)))))


day17

error: pathspec 'src/adventofcode/day17.clj' did not match any file(s) known to git

--- src/adventofcode/day17.clj
+++ src/adventofcode/day17.clj
@@ -0,0 +1,17 @@
+(ns adventofcode.day17
+  (:require [clojure.math.combinatorics :as combo]))
+
+(def input [33 14 18 20 45 35 16 35 1 13 18 13 50 44 48 6 24 41 30 42])
+
+(->> (map-indexed vector input)
+     combo/subsets
+     (filter #(= 150 (reduce + (map second %))))
+     count)
+
+(->> (map-indexed vector input)
+     combo/subsets
+     (filter #(= 150 (reduce + (map second %))))
+     (map count)
+     frequencies)
+
+


Add simplistic console reporting

Add simplistic console reporting

error: pathspec 'src/main/clojure/lazytest/report/console.clj' did not match any file(s) known to git

--- src/main/clojure/lazytest/report/console.clj
+++ src/main/clojure/lazytest/report/console.clj
@@ -0,0 +1,92 @@
+(ns lazytest.report.console
+  (:use [lazytest.test-result :only (success?)]
+	[clojure.stacktrace :only (print-cause-trace)]))
+
+(defprotocol ReportPrinter
+  (p [this]))
+
+(extend-protocol ReportPrinter
+  ;; Base TestResult types
+  lazytest.test-result.Pass
+  (p [this]
+     (print ".")
+     (flush))
+
+  lazytest.test-result.Fail
+  (p [this]
+     (println "\nFAIL")
+     (print "Metadata: ")
+     (pprint (meta this))
+     (print "Reason: ")
+     (p (:reason this)))
+
+  lazytest.test-result.Thrown
+  (p [this]
+     (println "\nERROR")
+     (print "Metadata: ")
+     (pprint (meta this))
+     (print-cause-trace (:throwable this)))
+
+  lazytest.test-result.Pending
+  (p [this]
+     (println "\nPENDING")
+     (print "Metadata: ")
+     (print "Reason:" (:reason this)))
+
+  lazytest.test-result.Skip
+  (p [this]
+     (println "\nSKIPPED")
+     (print "Metadata: ")
+     (print "Reason:" (:reason this)))
+
+  ;; Failure types
+  lazytest.failure.NotEqual
+  (p [this]
+     (println "Not equal:")
+     (doseq [o (:objects this)]
+       (print " -- ")
+       (pprint o)))
+
+  lazytest.failure.NotNotEqual
+  (p [this]
+     (println "Equal but should not be:")
+     (doseq [o (:objects this)]
+       (print " -- ")
+       (pprint o)))
+
+  lazytest.failure.NotInstanceOf
+  (p [this]
+     (println "Expected an instance of" (:expected-class this))
+     (println "But got an instance of" (:actual-class this)))
+
+  lazytest.failure.NotThrown
+  (p [this]
+     (println "Expected an instance of" (:class this) "to be thrown")
+     (println "But nothing was thrown"))
+
+  lazytest.failure.ThrownWithWrongMessage
+  (p [this]
+     (println "Expected an object to be thrown with a message matching" (:expected-re this))
+     (println "But the actual message was" (:actual-message this)))
+
+  lazytest.failure.NotLogicalTrue
+  (p [this]
+     (println "Not true:")
+     (print " -- ")
+     (pprint (:value this)))
+
+  lazytest.failure.PredicateFailed
+  (p [this]
+     (println "The function" (:pred this))
+     (println "Did not return true for these arguments:")
+     (doseq [arg (:args this)]
+       (print " -- ")
+       (pprint arg))))
+
+(defn report [results]
+  (doseq [r results]
+    (if (seq (:children r))
+      (report (:children r))
+      (if (success? r)
+	(do (print ".") (flush))
+	(p r)))))


make build a blocking loop for now

make build a blocking loop for now

--- sidecar/src/figwheel_sidecar/auto_builder.clj
+++ sidecar/src/figwheel_sidecar/auto_builder.clj
@@ -38,13 +38,15 @@
           (clj-stacktrace.repl/pst+ e)
           (fig/compile-error-occured figwheel-state e))))))

-(defn autobuild [src-dirs build-options figwheel-options]
-  (let [figwheel-options (merge figwheel-options
-                                (select-keys build-options [:output-dir :output-to]))
-        figwheel-state (fig/start-server figwheel-options)]
+(defn autobuild* [builds figwheel-options]
+  (let [figwheel-state (fig/start-server figwheel-options)]
     (reset! server-kill-switch (:http-server figwheel-state))
-    (auto/autobuild*
-     {:src-dirs src-dirs
-      :build-options build-options
+    (auto/autobuild-blocking*
+     {:builds  builds
       :builder (builder figwheel-state)
       :each-iteration-hook (fn [_] (fig/check-for-css-changes figwheel-state))})))
+
+(defn autobuild [src-dirs build-options figwheel-options]
+  (autobuild* [{:source-paths src-dirs
+                :build-options build-options}]
+              figwheel-options))


reorg helpers section

reorg helpers section

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/michaelsbradleyjr/transwarp.git/'


Gangway - add validation namespace

Gangway - add validation namespace

fatal: unable to access 'https://github.com/vlacs/galleon.git/': The requested URL returned error: 403


Add utils/sql.clj

Add utils/sql.clj

error: pathspec 'src/cider_ci/utils/sql.clj' did not match any file(s) known to git

--- src/cider_ci/utils/sql.clj
+++ src/cider_ci/utils/sql.clj
@@ -0,0 +1,11 @@
+(ns cider-ci.utils.sql
+  )
+
+(defn placeholders [col]
+  (->> col
+       (map (fn [_] "?"))
+       (clojure.string/join  ", ")))
+  ;(placeholders (range 1 5))
+
+
+


add-material

add-material

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/kenichirow/recipes.git/'


Basic geofire stuff

Basic geofire stuff

error: pathspec 'src/pani/cljs/geofire.cljs' did not match any file(s) known to git

--- src/pani/cljs/geofire.cljs
+++ src/pani/cljs/geofire.cljs
@@ -0,0 +1,45 @@
+(ns pani.cljs.geofire
+  (:require [cljs.core.async :refer (chan put!)]))
+
+(defn root
+  "Create and return a Geofire root"
+  [url]
+  (GeoFire. url))
+
+(defn set!
+  "Set the location for the given key"
+  [r k lat lng]
+  (.set r key [lat lng]))
+
+(defn get
+  "Get location associated with the given key, can accept a callback or returns a channel"
+  ([r k]
+   (let [c (chan)]
+     (get! r k #(put! c %))
+     c))
+  ([r k f]
+   (-> (.get r)
+       (.then f))))
+
+
+(def query
+  "Setup a geofire query"
+  ([r lat lng radius]
+   (let [c (chan)]
+     (query r lat lng #(put! c %))
+     c))
+
+  ([r lat lng radius f]
+   (let [q (.query r (js-obj "center" [lat lng]
+                             "radius" radius))]
+     (doto q
+       (.on "key_entered" (fn [k l d]
+                            (f {:type     :entered
+                                :key      k
+                                :location l
+                                :distance d})))
+       (.on "key_exited" (fn [k l d]
+                           (f {:type     :exited
+                               :key      k
+                               :location l
+                               :distance d})))))))


Add bootlaces to build.boot

Add bootlaces to build.boot

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/manenko/re-widgets.git/'


Add additional check for next button

Add additional check for next button

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/starcity-properties/odin.git/'


OPTIONS stub

OPTIONS stub

fatal: unable to access 'https://github.com/delitescere/yada.git/': The requested URL returned error: 403


WIP: generate robot names by incrementing a num.

error: pathspec 'clojure/robot-name/robot.clj' did not match any file(s) known to git

--- clojure/robot-name/robot.clj
+++ clojure/robot-name/robot.clj
@@ -0,0 +1,27 @@
+(ns robot)
+
+(def name-seq (atom (list "A" "A" 1)))
+
+(defn- format-name
+  [name-seq]
+  (apply format "%s%s%03d" @name-seq))
+
+(defn- inc-name-seq
+  [curr-seq]
+  (list "A" "A" (inc (last curr-seq))))
+
+(defn- generate-name
+  []
+  (swap! name-seq inc-name-seq)
+  (format-name name-seq))
+
+(defn robot
+  []
+  {:name (generate-name)})
+
+(defn robot-name
+  [robot]
+  (:name robot))
+
+;; (defn reset-name
+;;   [robot])


Add blob unpack hooks for h2.

Add blob unpack hooks for h2.

error: pathspec 'src/cavm/h2_unpack_rows.clj' did not match any file(s) known to git

--- src/cavm/h2_unpack_rows.clj
+++ src/cavm/h2_unpack_rows.clj
@@ -0,0 +1,35 @@
+(ns cavm.h2-unpack-rows)
+
+; The following a mechanism to avoid a direct dependency on other cavm code
+; that should not be aot compiled.
+; This file has to be aot compiled so h2 can find it (or so I'm lead to believe).
+; However a dependency on other files causes them to be affected by the aot. This
+; messes up dynamic reloading of those files, meaning we can't develop from
+; the repl reliably. The workaround is to provide a method for injecting the
+; dependency, by setting a var root.
+
+(def lookup-row nil)
+(defn set-lookup-row! [f]
+  (alter-var-root (var lookup-row) (fn [_] f)))
+
+(def lookup-value nil)
+(defn set-lookup-value! [f]
+  (alter-var-root (var lookup-value) (fn [_] f)))
+
+;
+; Blob unpack functions for h2.
+;
+
+(defn- -unpack [conn field-id row]
+  (lookup-row conn field-id row))
+
+; Somehow the return value will be cast automatically.
+(def ^:private -unpackCode -unpack)
+
+(defn- -unpackValue [conn field-id row]
+  (lookup-value conn field-id row))
+
+(gen-class :name unpack_rows
+           :methods [^:static [unpackCode [java.sql.Connection int int] int]
+                     ^:static [unpack [java.sql.Connection int int] float]
+                     ^:static [unpackValue [java.sql.Connection int int] String]])


Add a test for :put requests with :form-params

--- test/clj_http/test/client.clj
+++ test/clj_http/test/client.clj
@@ -291,6 +291,13 @@
                                             :param2 "value2"}})]
       (is (= "param1=value1&param2=value2" (:body resp)))
       (is (= "application/x-www-form-urlencoded" (:content-type resp)))
+      (is (not (contains? resp :form-params))))
+    (let [param-client (client/wrap-form-params identity)
+          resp (param-client {:request-method :put
+                              :form-params {:param1 "value1"
+                                            :param2 "value2"}})]
+      (is (= "param1=value1&param2=value2" (:body resp)))
+      (is (= "application/x-www-form-urlencoded" (:content-type resp)))
       (is (not (contains? resp :form-params)))))
   (testing "Ensure it does not affect GET requests"
     (let [param-client (client/wrap-form-params identity)


whitespace

whitespace

--- test/clj_http/test/client.clj
+++ test/clj_http/test/client.clj
@@ -59,7 +59,6 @@
     (is (= :get (:request-method (:req resp))))
     (is (= :http (:scheme (:req resp))))
     (is (= ["http://foo.com" "http://bar.com/bat"] (:trace-redirects resp)))
-
     (is (= "/bat" (:uri (:req resp))))))

 (deftest relative-redirect-on-get
@@ -75,7 +74,6 @@
     (is (= :get (:request-method (:req resp))))
     (is (= :http (:scheme (:req resp))))
     (is (= ["http://foo.com" "http://foo.com/bat"] (:trace-redirects resp)))
-
     (is (= "/bat" (:uri (:req resp))))))

 (deftest max-redirects


Increase subscription endpoint timeouts to 60 secs

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/democracyworks/election-notification-http-api.git/'


Remove `print-namespace-doc` from quckref

--- src/clj/clojuredocs/pages/quickref/static.clj
+++ src/clj/clojuredocs/pages/quickref/static.clj
@@ -424,8 +424,7 @@
            ns-map),
          :title "Inspect"}
         {:syms (in-ns ns-resolve ns-unalias ns-unmap alias), :title "Use"}
-        {:syms (namespace-munge print-namespace-doc
-                 ), :title "Misc"})}
+        {:syms (namespace-munge), :title "Misc"})}
       {:title "Hierarchies",
        :groups
        ({:syms


Get electorate db once before looping in verify

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/democracyworks/electorate-works.git/'


trampoline solution

trampoline solution

error: pathspec 'src/4clojure/trampoline.clj' did not match any file(s) known to git

--- src/4clojure/trampoline.clj
+++ src/4clojure/trampoline.clj
@@ -0,0 +1,21 @@
+;; Intro to Trampoline
+;; http://www.4clojure.com/problem/76
+;; Difficulty:	Medium
+;; Topics:	recursion
+
+
+;; The trampoline function takes a function f and a variable number of parameters. Trampoline calls f with any parameters that were supplied. If f returns a function, trampoline calls that function with no arguments. This is repeated, until the return value is not a function, and then trampoline returns that non-function value. This is useful for implementing mutually recursive algorithms in a way that won't consume the stack.
+;; test not run
+
+;; (def __ [1 3 5 7 9 11])
+
+(= __
+   (letfn
+     [(foo [x y] #(bar (conj x y) y))
+      (bar [x y] (if (> (last x) 10)
+                   x
+                   #(foo x (+ 2 y))))]
+     (trampoline foo [] 1)))
+
+;; copy solution
+[1 3 5 7 9 11]


mongo support for new nested compound filters

--- src/metabase/driver/mongo/query_processor.clj
+++ src/metabase/driver/mongo/query_processor.clj
@@ -315,14 +315,15 @@
       :<=          {field {$lte value}}
       :>=          {field {$gte value}})))

+(defn- parse-filter-clause [{:keys [compound-type subclauses], :as clause}]
+  (cond
+    (= compound-type :and) {$and (mapv parse-filter-clause subclauses)}
+    (= compound-type :or)  {$or  (mapv parse-filter-clause subclauses)}
+    :else                  (parse-filter-subclause clause)))
+

 (defclause :filter filter-clause
-  (let [{:keys [compound-type subclauses]} filter-clause
-        subclauses (mapv parse-filter-subclause subclauses)]
-    (case compound-type
-      :and    {$and subclauses}
-      :or     {$or  subclauses}
-      :simple (first subclauses))))
+  (parse-filter-clause filter-clause))

 ;; ### limit


Give 2 elections interceptors names

Give 2 elections interceptors names

This makes it much easier to debug things.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/democracyworks/turbovote-http-api.git/'


Add transmit lib error test

Add transmit lib error test

error: pathspec 'transmit-lib/test/cmr/transmit/test/echo/rest.clj' did not match any file(s) known to git

--- transmit-lib/test/cmr/transmit/test/echo/rest.clj
+++ transmit-lib/test/cmr/transmit/test/echo/rest.clj
@@ -0,0 +1,10 @@
+(ns cmr.transmit.test.echo.rest
+  "Tests for cmr.transmit.echo.rest namespace"
+  (:require [clojure.test :refer :all]
+            [cmr.transmit.echo.rest :as rest]))
+
+(deftest test-error-masked
+  (let [error-message "Unexpected error message: Token 123 does not exist."
+       status 500]
+    (is (= (format "Unexpected status %d from response. body: %s" status "Token does not exist")
+           (rest/unexpected-status-error! status error-message)))))


change ns

change ns

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/tokenmill/clj-gate.git/'


removed files

removed files

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/clojure-ml/cml.git/'


Add test case for issue #25.

Add test case for issue #25.

--- test/sablono/core_test.cljs
+++ test/sablono/core_test.cljs
@@ -429,4 +429,8 @@
     (is (= (html-str [:input.helloworld (style-it "dinosaurs")])
            "<input placeholder=\"dinosaurs\" type=\"text\" class=\"helloworld\">"))))

+(deftest test-issue-25-comma-separated-class
+  (is (= (html-str [:div.c1.c2 "text"])
+         "
text
")))
+
 (comment (run-tests))


Gogoko liburua ongi ezabatu

Gogoko liburua ongi ezabatu

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/lnmnd/magnet.zer.git/'


Add vector loading logic

Add vector loading logic

error: pathspec 'test/caesium/vectors.clj' did not match any file(s) known to git

--- test/caesium/vectors.clj
+++ test/caesium/vectors.clj
@@ -0,0 +1,10 @@
+(ns caesium.vectors
+  (:require
+   [clojure.java.io :as io]
+   [caesium.util :as u]
+   [clojure.string :as s]))
+
+(def hex-resource
+  "Gets a named resource in hex format; returns its contents as a byte
+  array."
+  (comp u/unhexify s/trim slurp io/resource))


Made some changes

Made some changes

error: pathspec 'src/clojush/problems/software/syllables_c.clj' did not match any file(s) known to git

--- src/clojush/problems/software/syllables_c.clj
+++ src/clojush/problems/software/syllables_c.clj
@@ -0,0 +1,190 @@
+;; syllables.clj
+;; Tom Helmuth, thelmuth@cs.umass.edu
+;;
+;; Problem Source:
+;;   C. Le Goues et al., "The ManyBugs and IntroClass Benchmarks for Automated Repair of C Programs,"
+;;   in IEEE Transactions on Software Engineering, vol. 41, no. 12, pp. 1236-1256, Dec. 1 2015.
+;;   doi: 10.1109/TSE.2015.2454513
+;;
+;; Given a string (max length 20, containing symbols, spaces, digits, and
+;; lowercase letters), count the number of occurrences of vowels (a,e,i,o,u,y)
+;; in the string and print that number as X in "The number of syllables is X"
+;;
+;; input stack has the input string
+
+(ns clojush.problems.software.syllables-c
+  (:use clojush.pushgp.pushgp
+        [clojush pushstate interpreter random util globals]
+        clojush.instructions.tag
+        clojure.math.numeric-tower)
+    (:require [clojure.string :as string]))
+
+;; Define test cases
+(defn syllables-input
+  "Makes a Syllables input of length len."
+  [len]
+  (apply str
+         (repeatedly len
+                     #(if (< (lrand) 0.2)
+                        (lrand-nth "aeiouy")
+                        (lrand-nth (map char (concat (range 32 65) (range 91 127))))))))
+
+; Atom generators
+(def syllables-atom-generators
+  (concat (list
+            "The number of syllables is "
+            \a
+            \e
+            \i
+            \o
+            \u
+            \y
+            "aeiouy"
+            ;;; end constants
+            (fn [] (lrand-nth (concat [\newline \tab] (map char (range 32 127))))) ;Visible character ERC
+            (fn [] (syllables-input (lrand-int 21))) ;String ERC
+            ;;; end ERCs
+            (tag-instruction-erc [:exec :integer :boolean :string :char] 1000)
+            (tagged-instruction-erc 1000)
+            (registered-for-type "return_")
+            ;;; end tag ERCs
+            'in1
+            ;;; end input instructions
+            )
+          (registered-for-stacks [:integer :boolean :string :char :exec :print])))
+
+
+;; A list of data domains for the problem. Each domain is a vector containing
+;; a "set" of inputs and two integers representing how many cases from the set
+;; should be used as training and testing cases respectively. Each "set" of
+;; inputs is either a list or a function that, when called, will create a
+;; random element of the set.
+(def syllables-data-domains
+  [[(list "", "a", "v", "4", "o", " ", "aei", "ouy", "chf", "quite", "a r e9j>"
+          "you are many yay yea"
+          (apply str (repeat 20 \s))
+          (apply str (repeat 20 \o))
+          (apply str (take 20 (cycle (list \w \i \space))))
+          (apply str (take 20 (cycle (list \x \space \y \space))))
+          (apply str (take 20 (cycle (list \e \i \o \y))))) 17 0] ;; Hand-chosen edge cases
+   [(fn [] (syllables-input (inc (lrand-int 20)))) 83 1000]
+   ])
+
+;;Can make syllables test data like this:
+;(test-and-train-data-from-domains syllables-data-domains)
+
+; Helper function for error function
+(defn syllables-test-cases
+  "Takes a sequence of inputs and gives IO test cases of the form
+   [input output]."
+  [inputs]
+  (map (fn [in]
+         (vector in
+                 (str "The number of syllables is " (count (filter #(some #{%} "aeiouy") in)))))
+       inputs))
+
+(defn make-syllables-error-function-from-cases
+  [train-cases test-cases]
+  (fn the-actual-syllables-error-function
+    ([individual]
+      (the-actual-syllables-error-function individual :train))
+    ([individual data-cases] ;; data-cases should be :train or :test
+     (the-actual-syllables-error-function individual data-cases false))
+    ([individual data-cases print-outputs]
+      (let [behavior (atom '())
+            errors (flatten
+                     (doall
+                       (for [[input correct-output] (case data-cases
+                                                      :train train-cases
+                                                      :test test-cases
+                                                      [])]
+                         (let [final-state (run-push (:program individual)
+                                                     (->> (make-push-state)
+                                                       (push-item input :input)
+                                                       (push-item "" :output)))
+                               printed-result (stack-ref :output 0 final-state)]
+                           (when print-outputs
+                             (println (format "\n| Correct output: %s\n| Program output: %s" (pr-str correct-output) (pr-str printed-result))))
+                           ; Record the behavior
+                           (swap! behavior conj printed-result)
+                           ; Error is Levenshtein distance and, if ends in an integer, distance from correct integer
+                           (vector
+                             (levenshtein-distance correct-output printed-result)
+                             (if-let [num-result (try (Integer/parseInt (last (string/split printed-result #"\s+")))
+                                                   (catch Exception e nil))]
+                               (abs (- (Integer/parseInt (last (string/split correct-output #"\s+")))
+                                       num-result)) ;distance from correct integer
+                               1000)
+                             )))))]
+        (if (= data-cases :train)
+          (assoc individual :behaviors @behavior :errors errors)
+          (assoc individual :test-errors errors))))))
+
+(defn get-syllables-train-and-test
+  "Returns the train and test cases."
+  [data-domains]
+  (map #(sort-by (comp count first) %)
+       (map syllables-test-cases
+            (test-and-train-data-from-domains data-domains))))
+
+; Define train and test cases
+(def syllables-train-and-test-cases
+  (get-syllables-train-and-test syllables-data-domains))
+
+(defn syllables-initial-report
+  [argmap]
+  (println "Train and test cases:")
+  (doseq [[i case] (map vector (range) (first syllables-train-and-test-cases))]
+    (println (format "Train Case: %3d | Input/Output: %s" i (str case))))
+  (doseq [[i case] (map vector (range) (second syllables-train-and-test-cases))]
+    (println (format "Test Case: %3d | Input/Output: %s" i (str case))))
+  (println ";;******************************"))
+
+(defn syllables-report
+  "Custom generational report."
+  [best population generation error-function report-simplifications]
+  (let [best-test-errors (:test-errors (error-function best :test))
+        best-total-test-error (apply +' best-test-errors)]
+    (println ";;******************************")
+    (printf ";; -*- Syllables problem report - generation %s\n" generation)(flush)
+    (println "Test total error for best:" best-total-test-error)
+    (println (format "Test mean error for best: %.5f" (double (/ best-total-test-error (count best-test-errors)))))
+    (when (zero? (:total-error best))
+      (doseq [[i error] (map vector
+                             (range)
+                             best-test-errors)]
+        (println (format "Test Case  %3d | Error: %s" i (str error)))))
+    (println ";;------------------------------")
+    (println "Outputs of best individual on training cases:")
+    (error-function best :train true)
+    (println ";;******************************")
+    )) ;; To do validation, could have this function return an altered best individual
+       ;; with total-error > 0 if it had error of zero on train but not on validation
+       ;; set. Would need a third category of data cases, or a defined split of training cases.
+
+
+; Define the argmap
+(def argmap
+  {:error-function (make-syllables-error-function-from-cases (first syllables-train-and-test-cases)
+                                                             (second syllables-train-and-test-cases))
+   :atom-generators syllables-atom-generators
+   :max-points 3200
+   :max-genome-size-in-initial-program 400
+   :evalpush-limit 1600
+   :population-size 1000
+   :max-generations 300
+   :parent-selection :lexicase
+   :genetic-operator-probabilities {:alternation 0.2
+                                    :uniform-mutation 0.2
+                                    :uniform-close-mutation 0.1
+                                    [:alternation :uniform-mutation] 0.5
+                                    }
+   :alternation-rate 0.01
+   :alignment-deviation 10
+   :uniform-mutation-rate 0.01
+   :problem-specific-report syllables-report
+   :problem-specific-initial-report syllables-initial-report
+   :report-simplifications 0
+   :final-report-simplifications 5000
+   :max-error 5000
+   })


Render train times alongside stations.

Render train times alongside stations.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/cstorey/nrod.git/'


add merge module

add merge module

error: pathspec 'src-cljs/cljs_workspace/om_branch_merge.cljs' did not match any file(s) known to git

--- src-cljs/cljs_workspace/om_branch_merge.cljs
+++ src-cljs/cljs_workspace/om_branch_merge.cljs
@@ -0,0 +1,117 @@
+(ns cljs-workspace.branch-merge
+  (:require [om.core :as om :include-macros true]
+            [om.dom :as dom :include-macros true]))
+
+(def preserve-list (atom {}))
+(def merge-candidate (atom nil))
+(def staged-for-merge (atom {:from nil :into nil}))
+
+(defn merge-state [remote-state local-state]
+    (let [merged (merge local-state remote-state)]
+      ; recover the old values, that ar on the exclude list and return
+      (reduce
+        (fn [state path] (assoc-in state path (get-in local-state path)))
+        merged
+        (keys @preserve-list))))
+
+(defn reachable-from [fp branch]
+  (if (not (contains? fp :fork))
+    nil ; this is not a fork point at all
+    (if (= (get-in fp [:cont :root]) branch)
+      [:cont]
+      (if (= (fp :fork) branch)
+        [:fork]
+        ; if not a single step suffices we recursively try to check if there
+        ; is any path to the branch through :fork or :cont
+        (if-let [path (reachable-from (last @(get-in fp [:fork :data])) branch)]
+          (apply conj [:fork] path)
+          (if-let [path (reachable-from (last @(get-in fp [:cont :data])) branch)]
+            (apply conj [:cont] path)
+            nil)))))) ; not reachable at all!
+
+(defn depth-search-fork-point [a b root-branch]
+  (let [fork-point (last @(root-branch :data))]
+    (if (contains? fork-point :cont) ; check if we reached leaf or not
+        (if-let [cont-reachable (depth-search-fork-point a b (fork-point :cont))]
+          cont-reachable
+          (if-let [fork-reachable (depth-search-fork-point a b (fork-point :fork))]
+            fork-reachable
+            (let [path-to-a (reachable-from fork-point a)
+                  path-to-b (reachable-from fork-point b)]
+              (if (and path-to-a path-to-b)
+                [fork-point path-to-a path-to-b]
+                nil))))
+        nil)))  ; this branch does not contain a potential fork point at all
+
+(defn fork-point [a b root-branch]
+  (if-let [res (depth-search-fork-point a b root-branch)]
+    res
+    [root-branch [] []])) ; find the path to branch from root
+
+(defn extract-branch [start-fp path]
+  (into [] (reduce (fn [[flattened fork] next-attr]
+                        (let [data @(get-in fork :next-attr :data)]
+                            [(concat flattened data) (last data)]))
+                    [[] start-fp]
+                    path)))
+
+(defn merge-from-flattened [branch flat-data]
+  (let [local-data @(branch :data)]
+    (if (< (count local-data) (count flat-data))
+        ; we need to either continue merging after a branching point
+        ; or append new merged entries to our local-branch
+        (if-let [last-entry (last local-data)]
+            (if (contains? last-entry :cont)
+                ; we have to now continue merging into BOTH forking branches, as we are NOT stroing diffs
+                (let [merged-part (map merge-state (butlast local-data) flat-data)
+                      cont (merge-from-flattened (last-entry :cont) (take (count local-data) flat-data))
+                      fork (merge-from-flattened (last-entry :fork) (take (count local-data) flat-data))]
+                    (conj merged-part {:cont {root: (last-entry :root) :data (atom cont)}
+                                       :fork {root: (last-entry :root) :data (atom fork)}}))
+                ; we reached the end of the branch and just append additional states to the branch
+                ; that are all merges with the last state in the branch
+                (map merge-state (concat local-data (repeat last-entry)) (flat-data)))
+            ; this is a weird case and the branch part we have to merge into
+            ; is actually a stub and contains no entries. In that case we just
+            ; append merged versions with the previous last entry to until the flattened
+            ; data is used up. But this is so weird, we can just aswell return the flat-data
+            (map merge-state (repeat {}) flat-data))
+        ; if not we have fewer entries to be merged into, we continue
+        ; merging the last state into all other proceeding local states
+        (conj (map merge-state (butlast local-data) (concat flat-data (repeat (last flat-data)))) (last local-data)))))
+
+(defn merge-branches [remote-branch local-branch root-branch]
+  (prn "Merging Branch " (remote-branch :id) " into " (local-branch :id))
+  (let [[fp remote-path local-path] (fork-point remote-branch local-branch root-branch)
+         ; we first flatten the remote branch such that it appears to be one continuous
+         ; vector of states, which makes it easier to perform the merge into our local branch
+         remote (extract-branch fp remote-path)
+         merged-branch (merge-from-flattened local-branch remote)]
+      (prn "merged branch of len " (count local) " and " (count remote) " into size " (len merged-branch))
+      (reset! (into-branch :data) merged-branch) ; maybe clean up the remote branch that got included?
+      ))
+
+(defn select-for-merge [branch cbk]
+  ; if we select the same twice, we deselect
+  (prn "select for marge: " cbk)
+  (if (= @merge-candidate branch)
+      (reset! merge-candidate nil)
+      (if @merge-candidate
+          (if cbk
+            (do
+              (reset! staged-for-merge {:from @merge-candidate :into branch})
+              (cbk @merge-candidate branch)
+              (reset! merge-candidate nil))
+            (merge-branch @merge-candidate branch)) ; perform merge immediately if not callback defined
+          (reset! merge-candidate branch))))
+
+(defn merge-staged-branches [root-branch]
+  (let [[a b] @staged-for-merge]
+    (merge-branches a b root-branch)))
+
+(defn toggle-preserve [morph-path]
+  (if (contains? @preserve-list morph-path)
+      (swap! preserve-list dissoc morph-path)
+      (swap! preserve-list assoc morph-path true))
+  (prn "PRESERVING: " @preserve-list))
+


scalarmult const tests

scalarmult const tests

error: pathspec 'test/caesium/crypto/scalarmult_test.clj' did not match any file(s) known to git

--- test/caesium/crypto/scalarmult_test.clj
+++ test/caesium/crypto/scalarmult_test.clj
@@ -0,0 +1,8 @@
+(ns caesium.crypto.scalarmult-test
+  (:require [caesium.crypto.scalarmult :as s]
+            [clojure.test :refer [deftest is]]))
+
+(deftest consts-tests
+  (is (= 32 s/bytes))
+  (is (= 32 s/scalarbytes))
+  (is (= "curve25519" s/primitive)))


Added helper namespace for making record pretty print with type information.

error: pathspec 'src/cmr/common/dev/record_pretty_printer.clj' did not match any file(s) known to git

--- src/cmr/common/dev/record_pretty_printer.clj
+++ src/cmr/common/dev/record_pretty_printer.clj
@@ -0,0 +1,37 @@
+(ns cmr.common.dev.record-pretty-printer
+  "Changes the way clojure records are pretty printed. Normally records that are pretty printed
+  do not include type information. This pretty prints the records as a map with a type field
+  that contains the object class type. It also prints out the fields in the order they are defined
+  in the record.
+
+  This pretty printing capability is not enabled by default. It must be enabled for a type by calling
+  the enable-record-pretty-printing macro with the record type. This would typically be done directly
+  in the namespace after defining the record."
+  (:require [cmr.common.util :as util]
+            [clojure.pprint :as pprint]))
+
+(defn mapify-record
+  "Converts a record into a map with a :_type key containing a simple name of the key. The map keys
+  are ordered to match the order defined in the record"
+  ([r]
+   (let [type-name (symbol (.getSimpleName ^Class (type r)))]
+     (util/remove-nil-keys (into {} (assoc r :_type type-name)))))
+  ([fields r]
+   (let [type-name (symbol (.getSimpleName ^Class (type r)))
+         key-order-map (into {} (map-indexed #(vector %2 %1) (cons :_type fields)))
+         sorted-by-key-order (sorted-map-by #(compare (key-order-map %1) (key-order-map %2)))]
+     (util/remove-nil-keys (into sorted-by-key-order (assoc r :_type type-name))))))
+
+(defmacro enable-record-pretty-printing
+  "Enables record pretty printing for the passed in record type."
+  [& record-types]
+  (cons
+    'do
+    (for [record-type record-types]
+      ;; Register the record type as getting pretty printing through the functions here.
+      `(.addMethod ^clojure.lang.MultiFn pprint/simple-dispatch
+                   ~record-type
+                   (fn [q#]
+                     (pprint/simple-dispatch
+                       (mapify-record (util/record-fields ~record-type) q#)))))))
+


Initial support for reading/writing content

error: pathspec 'lambdalf-lib/src/main/clojure/alfresco/content.clj' did not match any file(s) known to git

--- lambdalf-lib/src/main/clojure/alfresco/content.clj
+++ lambdalf-lib/src/main/clojure/alfresco/content.clj
@@ -0,0 +1,43 @@
+(ns alfresco.content
+  (:require [alfresco.core :as c])
+  (:import [org.alfresco.model ContentModel]
+           [java.io File ByteArrayInputStream]))
+
+(defn content-service
+  []
+  (.getContentService (c/alfresco-services)))
+
+(defprotocol Writer
+  "Provide a way to write content from an arbitrary source"
+  (write! [src node] "Writes the content coming from src into node"))
+
+(defn- is
+  "Retrieves an InputStream of the content for the provided node"
+  [node]
+  (.getContentInputStream (.getReader (content-service) node ContentModel/PROP_CONTENT)))
+
+;; as seen on
+;; https://groups.google.com/group/clojure/browse_thread/thread/e5fb47befe8b9199
+;; TODO: make sure we're not breaking utf-8 support
+(defn read!
+  "Returns a lazy seq of the content of the provided node"
+  [node]
+  (let [noderef (c/c2j node)
+        is (is noderef)]
+    (map char (take-while #(not= -1 %) (repeatedly #(.read is))))))
+
+(extend-protocol Writer
+
+  String
+  (write!
+   [^String src node]
+   (let [noderef (c/c2j node)
+         w (.getWriter (content-service) noderef ContentModel/PROP_CONTENT true)]
+     (.putContent w (ByteArrayInputStream. (.getBytes src "UTF-8")))))
+
+  File
+  (write!
+   [^File src node]
+   (let [noderef (c/c2j node)
+         w (.getWriter (content-service) noderef ContentModel/PROP_CONTENT true)]
+     (.putContent w src))))


Add functions to scrape pages from summitpost search results.

error: pathspec 'project/scrape-data/src/scrape_data/summitpost.clj' did not match any file(s) known to git

--- project/scrape-data/src/scrape_data/summitpost.clj
+++ project/scrape-data/src/scrape_data/summitpost.clj
@@ -0,0 +1,55 @@
+(ns scrape-data.summitpost
+  (:require [reaver]))
+
+(def base-url "http://www.summitpost.org")
+
+(defn- extract-search-result-links
+  "Given a Jsoup document containing a search results page from summitpost,
+  return a sequence of links representing the results on that page."
+  [page]
+  (reaver/extract
+    page
+    []
+    ".srch_results .srch_results_lft + .srch_results_rht > a" (attr :href)))
+
+(defn- extract-search-result-pager-links
+  "Given a Jsoup document containing a search results page from summitpost,
+  return a set of links representing other pages of search results from the
+  results pagination."
+  [page]
+  (let [pager-links (reaver/extract page [] ".pagertext" (attr :href))]
+    (->> pager-links
+         (remove nil?)
+         (map (partial re-find #".*page=\d+"))
+         (remove nil?)
+         (set))))
+
+(defn- all-search-result-pager-links
+  "Given a link to a paginated search results page,
+  GET the page and return a sequence of links to all search results pages."
+  [link]
+  (let [seed-url (str base-url link)
+        page (reaver/parse (slurp seed-url))
+        other-links (extract-search-result-pager-links page)]
+    (conj other-links link)))
+
+(defn- all-search-result-pages
+  "Given a link to a paginated search results page,
+  GET every search results page in the pagination and return them as a lazy
+  sequence."
+  [link]
+  (->> link
+       (all-search-result-pager-links)
+       (map (partial str base-url))
+       (map slurp)))
+
+(defn- all-search-result-links
+  "Given a link to a paginated search results page, return a sequence of
+  links to all of the search result items. This function performs multiple
+  GET requests."
+  [link]
+  (->> link
+       (all-search-result-pages)
+       (map reaver/parse)
+       (map extract-search-result-links)
+       (apply concat)))


Refactoring

Refactoring

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/DajanaStiberova/green-recipes.git/'


Fixing build.

Fixing build.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/B2W-Digital/pdg-core.git/'


Add conditional for printing keycodes in re-lorean

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Reefersleep/sleepydo.git/'


tweaks

tweaks

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/jeelabs/jet.git/'


solved

solved

error: pathspec 'src/4clojure/tree_table.clj' did not match any file(s) known to git

--- src/4clojure/tree_table.clj
+++ src/4clojure/tree_table.clj
@@ -0,0 +1,66 @@
+;; Trees into tables
+;; http://www.4clojure.com/problem/146
+
+;; Difficulty:	Easy
+;; Topics:	seqs maps
+
+
+;; Because Clojure's for macro allows you to "walk" over multiple sequences in a nested fashion, it is excellent for transforming all sorts of sequences. If you don't want a sequence as your final output (say you want a map), you are often still best-off using for, because you can produce a sequence and feed it into a map, for example.
+
+;; For this problem, your goal is to "flatten" a map of hashmaps. Each key in your output map should be the "path"1 that you would have to take in the original map to get to a value, so for example {1 {2 3}} should result in {[1 2] 3}. You only need to flatten one level of maps: if one of the values is a map, just leave it alone.
+
+;; 1 That is, (get-in original [k1 k2]) should be the same as (get result [k1 k2])
+
+
+(defn __ [tree]
+  (into {}
+        (flatten
+          (for [[k v] tree]
+            (for [[a b] v] {[k a] b})))))
+
+
+(__ '{a {p 1, q 2}
+      b {m 3, n 4}})
+
+(= (__ '{a {p 1, q 2}
+         b {m 3, n 4}})
+   '{[a p] 1, [a q] 2
+     [b m] 3, [b n] 4})
+
+(= (__ '{[1] {a b c d}
+         [2] {q r s t u v w x}})
+   '{[[1] a] b, [[1] c] d,
+     [[2] q] r, [[2] s] t,
+     [[2] u] v, [[2] w] x})
+
+(= (__ '{m {1 [a b c] 3 nil}})
+   '{[m 1] [a b c], [m 3] nil})
+
+
+;; 4clojure copy paste
+#(into {} (flatten (for [[k v] %] (for [[a b] v] {[k a] b}))))
+
+
+;; _artem_uv's solution:
+#(into {} (for [[x y] % [z w] y] [[x z] w]))
+
+;; _pcl's solution:
+(fn [xs] (apply merge (for [x xs, y (val x)] (hash-map [(key x) (key y)] (val y)))))
+
+;; aceeca1's solution:
+(fn [x] (into {} (for [[k v] x [vk vv] v] [[k vk] vv])))
+
+;; adereth's solution:
+(fn [m]
+  (->>
+   (for [[k1 v1] m
+         [k2 v2] v1]
+     {[k1 k2] v2})
+   (apply merge)))
+
+;; 0x89's solution:
+#(into {}
+       (for
+         [[k v] %
+          [k2 v2] v]
+         [[k k2] v2]))


add drafts of collection API methods including documentation

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/colophon/clarango.git/'


Add functional tests

Add functional tests

error: pathspec 'test/time_tracker/clients/functional_test.clj' did not match any file(s) known to git

--- test/time_tracker/clients/functional_test.clj
+++ test/time_tracker/clients/functional_test.clj
@@ -0,0 +1,111 @@
+(ns time-tracker.clients.functional-test
+  (:require
+   [clojure.string :as string]
+   [clojure.test :refer :all]
+   [clojure.walk]
+   [time-tracker.clients.test-helpers :as client-helpers]
+   [time-tracker.fixtures :as fixtures]
+   [time-tracker.test-helpers :as helpers]
+   [time-tracker.users.test-helpers :as user-helpers]))
+
+(use-fixtures :once fixtures/init! fixtures/migrate-test-db fixtures/serve-app)
+(use-fixtures :each fixtures/isolate-db)
+
+(defn client-api-url
+  ([] (string/join [(helpers/settings :api-root) "clients/"]))
+  ([id] (format (string/join [(helpers/settings :api-root) "clients/%s/"]) id)))
+
+(deftest retrieve-all-clients-test
+  (let [client1 {"name"    "Client1"
+                 "address" "High Road"
+                 "gstin"   "MEDONTHAVEONE!!"
+                 "pan"     "PAN192QWE4"}
+        client2 {"name"    "Client2"
+                 "address" "Yet another place"
+                 "gstin"   "JKHDF247897OPUH"
+                 "pan"     "JKHDF24789"}
+        data    (client-helpers/populate-data!
+                 {"gid1" [client1 client2]})
+        expected [client1 client2]]
+    (testing "Any user can view clients"
+      (let [{:keys [status body]} (helpers/http-request
+                                   :get
+                                   (client-api-url)
+                                   "gid1")
+            actual                (map #(select-keys % (keys client1)) body)]
+        (is (= 200 status))
+        (is (= expected actual))))))
+
+(deftest create-client-test
+  (user-helpers/create-users! ["Sai Abdul" "gid1" "admin"])
+  (user-helpers/create-users! ["Paul Graham" "gid2" "user"])
+  (let [client {"name"              "Client1"
+                "address"           "High Road"
+                "gstin"             "MEDONTHAVEONE!!"
+                "pan"               "PAN192QWE4"
+                "points-of-contact" []}]
+    (testing "Only authorized admin can create client"
+      (let [{:keys [status body]} (helpers/http-request
+                                   :post
+                                   (client-api-url)
+                                   "gid1"
+                                   client)
+            actual                (select-keys body (keys client))]
+        (is (= 201 status))
+        (is (= client actual))))
+
+    (testing "A new client cannot be created with an empty name"
+      (let [{:keys [status body]} (helpers/http-request
+                                   :post
+                                   (client-api-url)
+                                   "gid1"
+                                   (assoc client :name ""))]
+        (is (= 400 status))))
+
+    (testing "An authorized non-admin user cannot create a new client"
+      (let [{:keys [status body]} (helpers/http-request
+                                   :post
+                                   (client-api-url)
+                                   "gid2"
+                                   client)]
+        (is (= 403 status))))
+
+    (testing "A non-authorized admin/user cannot create a new client"
+      (let [{:keys [status body]} (helpers/http-request
+                                   :post
+                                   (client-api-url)
+                                   "gid3"
+                                   client)]
+        (is (= 403 status))))))
+
+(deftest update-client-test
+  (user-helpers/create-users! ["Sai Abdul" "gid1" "admin"])
+  (let [client                {"name"    "Client1"
+                               "address" "High Road"
+                               "gstin"   "MEDONTHAVEONE!!"
+                               "pan"     "PAN192QWE4"}
+        data                  (client-helpers/populate-data!
+                               {"gid2" [client]})]
+    (testing "Only authorized admin can update a client's details"
+      (let [{:keys [status body]} (helpers/http-request
+                                   :put
+                                   (client-api-url (get data (:name client)))
+                                   "gid1"
+                                   (assoc client :name "Client new"))]
+        (is (= 200 status))))
+
+    (testing "A non-admin user cannot upadte a client's details"
+      (let [{:keys [status body]} (helpers/http-request
+                                   :put
+                                   (client-api-url (get data (:name client)))
+                                   "gid2"
+                                   (assoc client :name "Client new"))]
+        (is (= 403 status))))
+
+    (testing "A client cannot be updated with an empty name"
+      (let [{:keys [status body]} (helpers/http-request
+                                   :post
+                                   (client-api-url)
+                                   "gid1"
+                                   (assoc client :name ""))]
+        (is (= 400 status))))))


Fixed

Fixed

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/physion/ovation-webapi.git/'


Removed unsupported :script test.

Removed unsupported :script test.

--- test/sablono/core_test.cljs
+++ test/sablono/core_test.cljs
@@ -31,7 +31,6 @@
   (testing "empty tags"
     (is (= (html-str [:div]) "

"))
     (is (= (html-str [:h1]) "

"))
-    ;; [:script] ""
     (is (= (html-str [:text]) "<text></text>"))
     (is (= (html-str [:a]) ""))
     (is (= (html-str [:iframe]) ""))


Add verify dev-src code

Add verify dev-src code

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/democracyworks/election-notification-works.git/'


Bikeshed cleanup

Bikeshed cleanup

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/couragelabs/coachbot.git/'


Send continuously when there are full batches

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/democracyworks/scheduled-message-works.git/'


Layout

Layout

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/aarkerio/cmsclj.git/'


tidy up unneeded code

tidy up unneeded code


Adding my first attempt

Adding my first attempt

error: pathspec 'src/main/cljc/x124_analyze_reversi/wmatson.cljc' did not match any file(s) known to git

--- src/main/cljc/x124_analyze_reversi/wmatson.cljc
+++ src/main/cljc/x124_analyze_reversi/wmatson.cljc
@@ -0,0 +1,46 @@
+(ns main.cljc.x124-analyze-reversi.wmatson)
+
+(def opponent
+  {'w 'b
+   'b 'w})
+
+(def directions
+  (remove #{[0 0]}
+    (for [row (map dec (range 3))
+          col (map dec (range 3))]
+      [row col])))
+
+(defn line [board row col [row-vec col-vec :as direction]]
+  (let [cell (get-in board [row col])]
+    (if (some? cell)
+      (lazy-seq
+       (cons [[row col] cell]
+         (line board (+ row row-vec) (+ col col-vec) direction))))))
+
+(defn flips [line color]
+  (let [split-pred #(= (opponent color) (second %))
+        [moves [[_ flipper]]] (split-with split-pred line)]
+    (if (= flipper color)
+      (map first moves))))
+
+(defn cell-moves [board row col color]
+  (if (= 'e (get-in board [row col]))
+    (let [lines (map #(rest (line board row col %)) directions)
+          moves (mapcat #(flips % color) lines)]
+      [[row col] (set (filter seq moves))])))
+
+(defn board-moves [board color]
+  (for [row-index (range (count board))
+        col-index (range (count (get board row-index)))]
+     (cell-moves board row-index col-index color)))
+
+(defn reversi-moves [board color]
+  (->> (board-moves board color)
+       (filter (comp seq second))
+       (apply concat)
+       (apply hash-map)))
+
+;; (reversi-moves '[[e e e e]
+;;                  [e w b e]
+;;                  [e b w e]
+;;                  [e e e e]] 'b)


Add clone display.

Add clone display.

error: pathspec 'src/org/playasophy/wonderdome/display/clone.clj' did not match any file(s) known to git

--- src/org/playasophy/wonderdome/display/clone.clj
+++ src/org/playasophy/wonderdome/display/clone.clj
@@ -0,0 +1,21 @@
+(ns org.playasophy.wonderdome.display.clone
+  (:require
+    [org.playasophy.wonderdome.display.core :as display]))
+
+
+(defrecord CloneDisplay
+  []
+
+  display/Display
+
+  (set-colors!
+    [this colors]
+    (dorun (map #(display/set-colors! % colors) (vals this)))
+    nil))
+
+
+(defn clone
+  "Creates a new display which broadcasts colors to any displays associated with
+  it as keys in the record."
+  [& displays]
+  (apply assoc (CloneDisplay.) displays))


Add tests for subs in clj

Add tests for subs in clj

error: pathspec 'test/re_frame/subs_test.clj' did not match any file(s) known to git

--- test/re_frame/subs_test.clj
+++ test/re_frame/subs_test.clj
@@ -0,0 +1,35 @@
+(ns re-frame.subs-test
+  (:require [clojure.test  :refer :all]
+            [re-frame.subs :as subs]
+            [re-frame.db   :as db]))
+
+(defn fixture-re-frame
+  [f]
+  (let [restore-re-frame (re-frame.core/make-restore-fn)]
+    (f)
+    (restore-re-frame)))
+
+(use-fixtures :each fixture-re-frame)
+
+(deftest test-reg-sub-clj-repl
+  (subs/reg-sub
+   :a-sub
+   (fn [db _] (:a db)))
+
+  (subs/reg-sub
+   :b-sub
+   (fn [db _] (:b db)))
+
+  (subs/reg-sub
+   :a-b-sub
+   (fn [_ _]
+     [(subs/subscribe [:a-sub])
+      (subs/subscribe [:b-sub])])
+   (fn [[a b] _]
+     {:a a :b b}))
+
+  (let [test-sub (subs/subscribe [:a-b-sub])]
+    (reset! db/app-db {:a 1 :b 2})
+    (is (= {:a 1 :b 2} @test-sub))
+    (swap! db/app-db assoc :b 3)
+    (is (= {:a 1 :b 3} @test-sub))))


Add conditional example to test-render.

Add conditional example to test-render.

--- test/sablono/core_test.cljs
+++ test/sablono/core_test.cljs
@@ -11,8 +11,12 @@
             [sablono.test :refer [render-dom]]))

 (deftest test-render
-  (is (re-matches #"
c
"
-                  (html/render (html [:div#a.b "c"])))))
+  (are [markup match]
+    (is (re-matches (re-pattern match) (html/render markup)))
+    (html [:div#a.b "c"])
+    "
c
"
+    (html [:div (when true [:p "data"]) (if true [:p "data"] nil)])
+    "

data

data

"))

 (deftest tag-names
   (testing "basic tags"


Just belt and suspenders.

Just belt and suspenders.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/mtravers/incidents.git/'


refactor

refactor

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/jtbeckha/alda.git/'


rev comment

rev comment

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/michaelsbradleyjr/transwarp.git/'


core-allocate-iter-doseq*

core-allocate-iter-doseq*


Egile zerrenda bidali

Egile zerrenda bidali

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/lnmnd/magnet.bez.git/'


Test creating and listing multiple circles.

--- spec/freefrog/rest_spec.clj
+++ spec/freefrog/rest_spec.clj
@@ -12,8 +12,7 @@
                                      :body circle-content}))
     (it "should return a bad request"
       (should= 400 (:status @response))
-      (should-contain response-content(:body @response)))))
-
+      (should-contain response-content (:body @response)))))

 (describe "governance rest api"
   (before-all (r/start-test-server))
@@ -23,26 +22,37 @@
   (it "should return status code 404 at the root"
     (should= 404 (:status (http-client/get "http://localhost:3000" {:throw-exceptions false}))))

-  (context "when no circles have been created"
-    (with response (http-client/get "http://localhost:3000/circles" {:throw-exceptions false}))
-    (it "should return an empty array"
-        (should= 200 (:status @response))
-        (should= "[]" (:body @response))))
-
-  (context "upon creating a circle"
-    (with response (http-client/post "http://localhost:3000/circles"
-                                    {:throw-exceptions false
-                                     :content-type :json
-                                     :body (json/generate-string
-                                             {:name "Test Circle!"
-                                              :lead-link-name "Bill"
-                                              :lead-link-email "bfinn@example.com"})}))
-    (it "should redirect"
-      (should= 303 (:status @response))
-      (should-contain "application/json" (get-in @response [:headers "Content-Type"]))
-      (should-contain #"^\/circles\/\d+" (get (json/parse-string (:body @response)) "location")))
-
-    (context "and getting the created circle"
+  (context "circles"
+    (context "when no circles have been created"
+      (with response (http-client/get "http://localhost:3000/circles" {:throw-exceptions false}))
+      (it "should return an empty array"
+          (should= 200 (:status @response))
+          (should= "[]" (:body @response))))
+
+    (context "when creating a circle"
+      (with response (http-client/post "http://localhost:3000/circles"
+                                      {:throw-exceptions false
+                                       :content-type :json
+                                       :body (json/generate-string
+                                               {:name "Test Circle!"
+                                                :lead-link-name "Bill"
+                                                :lead-link-email "bfinn@example.com"})}))
+      (it "should redirect"
+        (should= 303 (:status @response))
+        (should-contain "application/json" (get-in @response [:headers "Content-Type"]))
+        (should-contain #"^\/circles\/\d+" (get (json/parse-string (:body @response)) "location")))
+
+      (should-return-400 "with invalid paramaters"
+                         (json/generate-string {:foo "Test Circle!"})
+                         "IllegalArgumentException")
+      (should-return-400 "with missing paramaters"
+                         (json/generate-string {:name "Test Circle!"})
+                         "IllegalArgumentException")
+      (should-return-400 "with malformed JSON"
+                         "{\"name\" :: \"Bill\""
+                         "IOException"))
+
+    (context "when creating and retrieving the created circle"
       (with location (get (json/parse-string
                             (:body (http-client/post
                                      "http://localhost:3000/circles"
@@ -59,15 +69,39 @@
           (should= 200 (:status @get-response))
           (should= {"lead-link" {"email" "bfinn@example.com"
                                  "name" "Bill" } "name" "Test Circle!"}
-                   (json/parse-string (:body @get-response))))))
+                   (json/parse-string (:body @get-response)))))
+
+    (context "when creating multiple circles"
+      (before (http-client/post "http://localhost:3000/circles"
+                                      {:content-type :json
+                                       :body (json/generate-string
+                                               {:name "Test Circle!"
+                                                :lead-link-name "Bill"
+                                                :lead-link-email "bfinn@example.com"})}))

-  (context "upon creating a circle"
-    (should-return-400 "with invalid paramaters"
-                       (json/generate-string {:foo "Test Circle!"})
-                       "IllegalArgumentException")
-    (should-return-400 "with missing paramaters"
-                       (json/generate-string {:name "Test Circle!"})
-                       "IllegalArgumentException")
-    (should-return-400 "with malformed JSON"
-                       "{\"name\" :: \"Bill\""
-                       "IOException")))
+      (before (http-client/post "http://localhost:3000/circles"
+                                      {:content-type :json
+                                       :body (json/generate-string
+                                               {:name "Test Circle 2"
+                                                :lead-link-name "Bill"
+                                                :lead-link-email "bfinn@example.com"})}))
+      (with get-response (http-client/get "http://localhost:3000/circles"
+                                          {:throw-exceptions false}))
+
+      (it "should return an array of created circles"
+        (should= 200 (:status @get-response))
+        (should-contain "application/json" (get-in @get-response
+                                                   [:headers "Content-Type"]))
+        (should= 2 (count (json/parse-string (:body @get-response)))))))
+
+  (context "roles"
+    (context "with a new circle"
+      (before (http-client/post "http://localhost:3000/circles"
+                                {:content-type :json
+                                 :body (json/generate-string
+                                         {:name "Test Circle 2"
+                                          :lead-link-name "Bill"
+                                          :lead-link-email "bfinn@example.com"})}))
+
+        (context "when requesting the list of roles"
+           (it "should return an empty array")))))


Bug fix

Bug fix

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/B2W-Digital/pdg-core.git/'


Fix save-query

Fix save-query

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/B2W-BIT/restQL-server.git/'


prep for alloc,mult core variants

prep for alloc,mult core variants


using threadsafe http pool

using threadsafe http pool

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/narkisr/cloujsearch.git/'