Add a test for the compile task.

Add a test for the compile task.

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

--- test/test_compile.clj
+++ test/test_compile.clj
@@ -0,0 +1,10 @@
+(ns test-compile
+  (:use [clojure.test]
+        [clojure.contrib.java-utils :only [delete-file-recursively file]]
+        [clojure.contrib.shell-out :only [with-sh-dir sh]]))
+
+(deftest test-compile
+  (delete-file-recursively (file "sample" "classes" "nom") true)
+  (with-sh-dir "sample"
+    (sh "lein" "compile"))
+  (is (.exists (file "sample" "classes" "nom" "nom" "nom.class"))))


add in a quick fix to support ssl connections on postgres db connections if the user sets the :mb-postgres-ssl env value to "true"

--- src/metabase/driver/postgres/connection.clj
+++ src/metabase/driver/postgres/connection.clj
@@ -3,6 +3,7 @@
             [clojure.string :as s]
             [korma.db]
             [swiss.arrows :refer :all]
+            [metabase.config :as config]
             [metabase.driver :refer [connection connection-details]]))

 (defmethod connection-details :postgres [database]
@@ -17,6 +18,7 @@
         (assoc :host host                                     ; e.g. "localhost"
                :db-type :postgres                             ; HACK hardcoded to postgres for time being until API has a way to choose DB type !
                :port (Integer/parseInt port))                 ; convert :port to an Integer
+        (cond-> (config/config-bool :mb-postgres-ssl) (assoc :ssl true :sslfactory "org.postgresql.ssl.NonValidatingFactory"))
         (rename-keys {:dbname :db}))))

 (defmethod connection :postgres [database]


small fix on unit test.

small fix on unit test.

--- test/metabase/models/pulse_channel_test.clj
+++ test/metabase/models/pulse_channel_test.clj
@@ -82,6 +82,7 @@
   (when-let [new-channel-id (create-pulse-channel channel)]
     (-> (db/sel :one PulseChannel :id new-channel-id)
         (hydrate :recipients)
+        (update :recipients #(sort-by :email %))
         (dissoc :id :pulse_id :created_at :updated_at))))

 (defn update-channel-then-select
@@ -97,7 +98,9 @@
    :schedule_type schedule-type-daily
    :schedule_hour 18
    :schedule_day  nil
-   :recipients    [{:email "foo@bar.com"} (user-details :rasta) (user-details :crowberto)]
+   :recipients    [(user-details :crowberto)
+                   {:email "foo@bar.com"}
+                   (user-details :rasta)]
    :details       {}}
   (tu/with-temp Pulse [{:keys [id]} {:creator_id (user->id :rasta)
                                      :name       (tu/random-name)}]


Use getProperty for beanstalk

Use getProperty for beanstalk

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/'


Mastermind board game AI.

Mastermind board game AI.

error: pathspec 'Clojure/mastermind.clj' did not match any file(s) known to git

--- Clojure/mastermind.clj
+++ Clojure/mastermind.clj
@@ -0,0 +1,104 @@
+; :mode=clojure:
+
+; Plays the mastermind board game
+; http://en.wikipedia.org/w/index.php?title=Mastermind_(board_game)&oldid=416560743
+
+(defn third [s]
+    (first (rest (rest s))))
+
+(defn score-blacks
+    [answer guess]
+    (assert (= (count answer) (count guess)))
+    (if (empty? answer)
+        [0 [] []]
+        (let [
+            is-correct (= (first answer) (first guess))
+            remainder (score-blacks (rest answer) (rest guess))]
+            [
+                (+
+                    (if is-correct 1 0)
+                    (first remainder))
+                (if is-correct
+                    (second remainder)
+                    (cons (first answer) (second remainder)))
+                (if is-correct
+                    (third remainder)
+                    (cons (first guess) (third remainder)))
+            ])))
+
+(defn remove-first
+    [coll v]
+    (concat
+        (rest (filter (fn [e] (= e v)) coll))
+        (filter (fn [e] (not (= e v))) coll)))
+
+(defn score-whites
+    [answer guess]
+    (if (empty? guess)
+        0
+        (let [is-correct (contains? (set answer) (first guess))]
+            (if is-correct
+                (+ 1 (score-whites (remove-first answer (first guess)) (rest guess)))
+                (score-whites answer (rest guess))))))
+
+(defn score
+    [answer guess]
+    (assert (= (count answer) (count guess)))
+    (let [[blacks ra rg] (score-blacks answer guess)]
+        {
+            :blacks blacks
+            :whites (score-whites ra rg)
+        }))
+
+(defn rate-guess
+    [possible-codes guess]
+    (apply max (map second
+        (frequencies
+        (map (fn [code] (score code guess)) possible-codes)))))
+
+(defn gen-all-codes
+    [colours length]
+    (if (= length 1)
+        (map vector colours)
+        (for [h colours t (gen-all-codes colours (dec length))] (cons h t))))
+
+(defn next-guess
+    [all-codes possible-codes]
+    (assert (not (empty? possible-codes)))
+    (if (= 1 (count possible-codes))
+        (first possible-codes)
+        (first (reduce
+            (fn [a b] (if (< (second a) (second b)) a b))
+            (pmap (juxt identity (fn [g] (rate-guess possible-codes g))) all-codes)))))
+
+; ----------------------
+
+
+(def default-codes (gen-all-codes [:a :b :c :d :e :f] 4))
+
+(defn play-game
+    ([solution-code]
+        (play-game default-codes default-codes solution-code))
+
+    ([all-codes possible-codes solution-code]
+        (assert (not (empty? possible-codes)))
+        (assert (contains? (set possible-codes) solution-code))
+        (let [
+            my-guess (next-guess all-codes possible-codes)
+            my-guess-score (score solution-code my-guess)
+            ]
+            (do
+                (println "My guess: " my-guess " scored " my-guess-score)
+                (if (= my-guess solution-code)
+                    my-guess
+                    (play-game
+                        all-codes
+                        (filter (fn [p] (= (score p my-guess) my-guess-score)) possible-codes)
+                        solution-code))))))
+
+(try
+    (println (play-game [:a :b :c :d]))
+    (println (play-game [:c :c :c :c]))
+    (println (play-game [:f :a :f :d]))
+    (catch Exception e (.printStackTrace e)))
+


remove vertical padding on external link box

Prevents it from occluding itself if it line-wraps or other pill views
above/below.

--- src/braid/client/ui/styles/message.cljs
+++ src/braid/client/ui/styles/message.cljs
@@ -65,7 +65,7 @@
     [:a.external
      {:text-decoration "none"
       :color "white"
-      :padding "0.2em 0.25em 0.25em"
+      :padding "0em 0.25em"
       :border-radius "0.25em"
       :background "#607DE1"
       :outline "none"}


Remove debug render

Remove debug render

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/nvbn/rerenderer.git/'


Do not count bolt acks & fails in total stats

fatal: unable to access 'https://github.com/hmcl/storm-apache.git/': The requested URL returned error: 403


copypasta can get you in trouble

copypasta can get you in trouble

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/vincentstorme/clojureme.git/'


Remove `patch-election-id-if-vote-tomorrow`

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/'


finish clojure version

finish clojure version

error: pathspec 'Replace-Words/RW.clj' did not match any file(s) known to git

--- Replace-Words/RW.clj
+++ Replace-Words/RW.clj
@@ -0,0 +1,16 @@
+(require '[clojure.string :as str])
+
+(defn replace-words [dict sentence]
+  (let [sentlist (str/split sentence #" ")]
+    (loop [dict dict
+           sentlist sentlist]
+      (cond (empty? dict) sentlist
+            :else
+            (recur (rest dict)
+                   (map #(if (= (first dict)
+                                (try (subs % 0 (count (first dict)))
+                                     (catch StringIndexOutOfBoundsException e "")))
+                           (first dict)
+                           %) sentlist))))))
+
+(replace-words '("cat" "bat" "rat") "the cattle was rattled by the battery")


New module with functions to render front page

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

--- src/hurvinek/html_renderer.clj
+++ src/hurvinek/html_renderer.clj
@@ -0,0 +1,69 @@
+;
+;  (C) Copyright 2016  Pavel Tisnovsky
+;
+;  All rights reserved. This program and the accompanying materials
+;  are made available under the terms of the Eclipse Public License v1.0
+;  which accompanies this distribution, and is available at
+;  http://www.eclipse.org/legal/epl-v10.html
+;
+;  Contributors:
+;      Pavel Tisnovsky
+;
+
+(ns hurvinek.html-renderer
+    "Module that contains functions used to render HTML pages sent back to the browser.")
+
+(require '[hiccup.core            :as hiccup])
+(require '[hiccup.page            :as page])
+(require '[hiccup.form            :as form])
+
+(defn render-html-header
+    "Renders part of HTML page - the header."
+    [url-prefix title]
+    [:head
+        [:title "Hurvinek"]
+        [:meta {:name "Author"    :content "Pavel Tisnovsky"}]
+        [:meta {:name "Generator" :content "Clojure"}]
+        [:meta {:http-equiv "Content-type" :content "text/html; charset=utf-8"}]
+        (page/include-css (str url-prefix "bootstrap.min.css"))
+        (page/include-css (str url-prefix "hurvinek.css"))
+        (page/include-js  (str url-prefix "bootstrap.min.js"))
+    ] ; head
+)
+
+(defn render-html-footer
+    "Renders part of HTML page - the footer."
+    []
+    [:div "

Author: Pavel Tisnovsky <ptisnovs@redhat.com&gt"])
+
+(defn render-navigation-bar-section
+    "Renders whole navigation bar."
+    [url-prefix title]
+    [:nav {:class "navbar navbar-inverse navbar-fixed-top" :role "navigation"} ; use navbar-default instead of navbar-inverse
+        [:div {:class "container-fluid"}
+            [:div {:class "row"}
+                [:div {:class "col-md-7"}
+                    [:div {:class "navbar-header"}
+                        [:a {:href url-prefix :class "navbar-brand"} title]
+                    ] ; ./navbar-header
+                ] ; col-md-7 ends
+            ] ; row ends
+        ] ;

+]);
+
+(defn render-front-page
+    "Render front page of this application."
+    [url-prefix title]
+    (page/xhtml
+        (render-html-header url-prefix title)
+        [:body
+            [:div {:class "container"}
+                (render-navigation-bar-section url-prefix title)
+                [:div {:class "container-fluid"}
+                [:p 42]
+                ]
+                (render-html-footer)
+            ] ;

+        ] ;
+    ))
+


type hints

type hints

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/'


Refactor random letter game code

Refactor random letter game 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/codegeneralist/krest.git/'


Add stub for sum-of-multiples

Add stub for sum-of-multiples

error: pathspec 'exercises/sum-of-multiples/src/sum_of_multiples.clj' did not match any file(s) known to git

--- exercises/sum-of-multiples/src/sum_of_multiples.clj
+++ exercises/sum-of-multiples/src/sum_of_multiples.clj
@@ -0,0 +1,5 @@
+(ns sum-of-multiples)
+
+(defn sum-of-multiples [] ;; <- Arglist goes here
+      ;; your code goes here
+      )


fps's added

fps's added

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/hialin/hialin.git/'


GCD problem specification

GCD problem specification

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

--- src/clojush/problems/software/gcd.clj
+++ src/clojush/problems/software/gcd.clj
@@ -0,0 +1,153 @@
+;; gcd.clj
+;; Bill Tozier, bill@vagueinnovation.com
+;; updated February 4, 2020
+;;
+;; This is code for finding algorithms which return the greatest common
+;;   divisor (GCD) of a pair of integer arguments. See the Wikipedia
+;;   article for background:
+;;     https://en.wikipedia.org/wiki/Greatest_common_divisor
+;;
+;; The two inputs are given as integers using the :input stack, and the result
+;; is read from the top of :integer.
+;;
+;; Some evolved solutions are saved in comments at the end of this file
+
+(ns clojush.problems.software.gcd
+  (:use clojush.pushgp.pushgp
+        [clojush pushstate interpreter random util]
+        [clojure.math.numeric-tower]
+        ))
+
+
+; Convenience function to generate test cases
+(defn product-of-n-d-sided-dice
+  "Produce the product of n randomly-chosen integers from [1,d]"
+  [n d]
+  (reduce *' 1 (repeatedly n #(inc (rand-int d)))
+    ))
+
+
+; error function
+(defn nontrivial-gcd-training-case
+  "Given integer scale and multiplicity arguments, produce a triple of two (possibly huge) integers and their actual GCD"
+  [scale multiplicity]
+  (let [a (product-of-n-d-sided-dice multiplicity scale)
+        b (product-of-n-d-sided-dice multiplicity scale)]
+        [a b (gcd a b)]
+        ))
+
+
+(defn gcd-coprime-case
+  "Given a first integer argument, we scan upwards (by random increments of 1-100) until we find a second argument which is coprime with the first integer; that is, where the pair has GCD=1"
+  [arg1]
+  (loop [arg2 (+ arg1 (rand-int 100) 1)]
+    (if (= 1 (gcd arg1 arg2))
+      [arg1 arg2 (gcd arg1 arg2)]
+      (recur (+ arg2 (rand-int 100) 1))
+      )))
+
+
+(defn identical-case
+  "You can figure this out, I just know it."
+  [n]
+  [n n n]
+  )
+
+
+(def gcd-training-cases
+  "Produces a random sample of 100 training cases: some cases where the integer arguments are identical, some pairs of moderate integers (~10000) with nontrivial GCD values, some pairs of large integers (~10^8) with nontrivial GCD values, and some pairs of coprime integers with GCD=1"
+  (into []
+    (concat
+      (repeatedly 5 #(identical-case (product-of-n-d-sided-dice 20 7)))
+      (repeatedly 30 #(nontrivial-gcd-training-case 7 7))
+      (repeatedly 50 #(nontrivial-gcd-training-case 20 7))
+      (repeatedly 15
+        #(gcd-coprime-case (product-of-n-d-sided-dice 7 7)))
+        )))
+
+
+(defn abs-err
+  [observed expected]
+  (abs (-' observed expected)))
+
+
+(defn gcd-error-function
+  "Given an individual and a collection of training cases, we run the individual's program using the training cases' inputs, and return a vector of absolute errors comparing the observed vs expected GCD values. The behavior (observed results) and error (absolute difference) vectors are associated with the individual as a result."
+  [individual cases]
+  (let [behaviors (vec (for [a-case cases]
+                         (->> (make-push-state)
+                              (push-item , (first a-case) :input)
+                              (push-item , (nth a-case 1) :input)
+                              (run-push , (:program individual))
+                              (top-item , :integer))))
+        errors (mapv (fn [behavior a-case]
+                       (if (int? behavior)
+                        (abs-err behavior (last a-case))
+                        1000000000000))
+                     behaviors
+                     cases)]
+    (assoc individual
+      :behaviors behaviors
+      :errors errors)))
+
+
+
+; Atom generators
+(def gcd-atom-generators
+  "Collection of items which will be used to construct random programs: Some integer constants, boolean constants, the input and various standard Push instructions. The relative proportions are stupid and simply the result of poking and prodding with insufficient evidence; feel free to change them."
+  (concat
+    (repeat 10 'integer_mod)
+    (repeat 10 'in1)
+    (repeat 10 'in2)
+    (list
+      10
+      1000
+      1000000
+      1000000000
+      2
+      3
+      5
+      7
+      11
+      19
+      31
+      53
+      true
+      false
+      ;; constants
+      (fn [] (- (lrand-int 21) 10))    ;; Integer ERC [-10,10]
+      (fn [] (- (lrand-int 2001) 1000))  ;; Integer ERC [-1000,1000]
+      ;;; end input instructions
+      )
+    (registered-for-stacks [:integer :boolean :exec :code])
+      ))
+
+
+
+; Define the argmap
+(def argmap
+  "I literally have no idea what many of these things do. However, with the current settings, the first run I attempted did solve the problem to my satisfaction. YMMV."
+  {:error-function (fn [individual]
+                      (gcd-error-function
+                        individual
+                        gcd-training-cases
+                        ))
+   :atom-generators gcd-atom-generators
+   :max-points 2000
+   :max-genome-size-in-initial-program 500
+   :evalpush-limit 2000
+   :population-size 400
+   :max-generations 5000
+   :parent-selection :lexicase
+   :genetic-operator-probabilities {:uniform-addition-and-deletion 0.5
+                                    :alternation 0.5}
+   :uniform-addition-and-deletion-rate [0.001 0.01 0.1]
+   :alternation-rate [0.001 0.01 0.1]
+   :alignment-deviation [0 1 10 100]
+   :report-simplifications 0
+   :final-report-simplifications 5000
+   })
+
+;; An evolved solution (simplified):
+;;
+;; (in2 in1 integer_mod in1 in1 integer_dup_items integer_mod integer_mod 53 integer_dup_items in1 integer_mod in2 integer_mod integer_mod 2 integer_dup_items integer_mod integer_swap 2 integer_dup_items integer_dec integer_dec integer_mod integer_swap 2 integer_dup_items 2 integer_dup_items integer_mod integer_mod 2 integer_dup_items integer_mod 2 integer_dup_items integer_mod integer_swap 2 integer_dup_items integer_dec integer_dec integer_mod integer_mod 2 integer_dup_items integer_mod 2 integer_dup_items integer_mod integer_swap 2 integer_dup_items integer_dec integer_dec integer_mod integer_mod 2 integer_dup_items integer_mod 2 integer_dup_items integer_mod 2 integer_dup_items integer_mod exec_dup_times ())


more postgres column type mappings.

more postgres column type mappings.

--- src/metabase/driver/postgres/sync.clj
+++ src/metabase/driver/postgres/sync.clj
@@ -14,8 +14,12 @@
    :inet :TextField             ; This was `GenericIPAddressField` in some places in the Django code but not others ...
    :int2 :IntegerField
    :int4 :IntegerField
+   :int8 :BigIntegerField
+   :json :TextField
+   :numeric :DecimalField
    :serial :IntegerField
    :text :TextField
+   :time :TimeField
    :timestamp :DateTimeField
    :timestamptz :DateTimeField
    :varchar :TextField})


commented out println

commented out println

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


Stub for html_renderer_test.clj

Stub for html_renderer_test.clj

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

--- test/zg/html_renderer_test.clj
+++ test/zg/html_renderer_test.clj
@@ -0,0 +1,13 @@
+(ns zg.html-renderer-test
+  (:require [clojure.test :refer :all]
+            [zg.html-renderer :refer :all]))
+
+;
+; Common functions used by tests.
+;
+
+(defn callable?
+    "Test if given function-name is bound to the real function."
+    [function-name]
+    (clojure.test/function? function-name))
+


Removing db props

Removing db props

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


Basic ASCII canon with a Clojure theme.

Basic ASCII canon with a Clojure theme.

error: pathspec 'src/overtunes/songs/ascii_canon.clj' did not match any file(s) known to git

--- src/overtunes/songs/ascii_canon.clj
+++ src/overtunes/songs/ascii_canon.clj
@@ -0,0 +1,40 @@
+(ns overtunes.songs.ascii-canon
+  (:use
+    [clojure.string :only [blank?]]
+    [overtone.live :only [at now]]
+    [overtone.inst.sampled-piano :only [sampled-piano]]))
+
+(defn bpm [beats-per-minute start]
+  (let [ms-per-minute (* 60 1000)
+        ms-per-beat (/ ms-per-minute beats-per-minute)]
+    #(+ start (* ms-per-beat %))))
+
+(def lower #(- % 12))
+(defn after [timing offset] #(timing (+ offset %)))
+(defn tempo [timing factor] #(timing (/ % factor)))
+(defn note# [note]
+  (if-not (= \  note)
+    (-> note int lower lower lower lower sampled-piano)))
+
+
+(defn even-melody# [timing [note & notes]]
+  (do
+    (at (timing 0) (note# note))
+    (let [next (after timing 1)]
+      (if notes
+        (even-melody# next notes)
+        next))))
+
+
+(defn play# []
+      (let
+        [timing (bpm 120 (now))
+         melody "clojuring clojuring clojuring clojuring"]
+        (even-melody# timing melody)
+        (even-melody# (after timing 1/2) melody)
+        (even-melody# (after timing 1) melody)))
+
+(play#)
+
+
+


add jonathan to signers

add jonathan to signers

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/'


Define checkers.meta ns

Define checkers.meta ns

Contains a helpful marcro for defining sally specific meta on a var.

error: pathspec 'src/clj/sally/checkers/meta.clj' did not match any file(s) known to git

--- src/clj/sally/checkers/meta.clj
+++ src/clj/sally/checkers/meta.clj
@@ -0,0 +1,7 @@
+(ns sally.checkers.meta)
+
+(defn add-sally-metadata* [a-var & {:as md}]
+  (alter-meta! a-var assoc :sally md))
+
+(defmacro add-sally-metadata [a-var & args]
+  `(add-sally-metadata* (var ~a-var) ~@args))


catch Exceptions when running hook functions

--- src/metabase/task.clj
+++ src/metabase/task.clj
@@ -79,9 +79,14 @@
   {:pre [(var? hook)
          (= (:type (meta hook)) ::hook)]}
   (let [[parallel args] (u/optional (partial = :parallel) args)
-        map-fn (if parallel pmap #(dorun (map %1 %2)))]
-    (map-fn (u/rpartial apply args)
-            @(var-get hook))))
+        map-fn (if parallel pmap map)]
+    (dorun
+     (map-fn (fn [f]
+               (try (apply f args)
+                    (catch Throwable e
+                      (log/error (format "Caught exception when running %s function %s with args %s : %s"
+                                         (:name (meta hook)) f args e)))))
+             @(var-get hook)))))

 ;; # TASK RUNNER
@@ -96,7 +101,7 @@

     (defn some-task [hour]
         do-something ...)
-    (add-hook! #'hourly-tasks-hook some-task) ; now some-task will be called on a background thread every hour")
+    (add-hook! #'hourly-tasks-hook some-task) ; some-task will be called on a background thread every hour")

 (defhook nightly-tasks-hook
   "Tasks to run nightly at midnight (according to the system calendar).


Add math

Add math

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

--- src/play_clj/math.clj
+++ src/play_clj/math.clj
@@ -0,0 +1,8 @@
+(ns play-clj.math
+  (:import [com.badlogic.gdx.math Vector2 Vector3]))
+
+(defn vector
+  ([x y]
+    (Vector2. x y))
+  ([x y z]
+    (Vector3. x y z)))


Middleware kits added to hellhound.middlewares.core

These kits are shotcuts for wrapping handlers inside the most
popular middlewares

error: pathspec 'src/clj/hellhound/middlewares/core.clj' did not match any file(s) known to git

--- src/clj/hellhound/middlewares/core.clj
+++ src/clj/hellhound/middlewares/core.clj
@@ -0,0 +1,59 @@
+(ns hellhound.middlewares.core
+  "This namespace contains sets of middleware kits for different
+environments like `development`, `test` and `production`"
+  {:author "Sameer Rahmani <lxsameer@gnu.org>"}
+  (:require [ring.middleware.session        :refer [wrap-session]]
+            [ring.middleware.resource       :refer [wrap-resource]]
+            [ring.middleware.content-type   :refer [wrap-content-type]]
+            [ring.middleware.not-modified   :refer [wrap-not-modified]]
+            [ring.middleware.params         :refer [wrap-params]]
+            [ring.middleware.keyword-params :refer [wrap-keyword-params]]
+            [ring.middleware.reload         :refer [wrap-reload]]
+            [ring.middleware.anti-forgery   :refer [wrap-anti-forgery]]
+            [immutant.web.middleware        :refer [wrap-development wrap-write-error-handling]]
+            [hellhound.middlewares.logger   :refer [wrap-logger]]))
+
+
+(defn wrap-development-kit
+  "A short cut function to wrap the useful middlewares
+   for development environment around the given handler."
+  [handler]
+  (-> handler
+      wrap-keyword-params
+      wrap-params
+      wrap-logger
+      wrap-anti-forgery
+      wrap-session
+      (wrap-resource "assets")
+      wrap-content-type
+      wrap-not-modified
+      wrap-reload))
+
+(defn wrap-testing-kit
+  "A short cut function to wrap the useful middlewares
+   for testing environment around the given handler."
+  [handler]
+  (-> handler
+      wrap-keyword-params
+      wrap-params
+      wrap-logger
+      wrap-anti-forgery
+      wrap-session
+      (wrap-resource "assets")
+      wrap-content-type
+      wrap-not-modified
+      wrap-reload))
+
+(defn wrap-production-kit
+  "A short cut function to wrap the useful middlewares
+   for production around the given handler."
+  [handler]
+  (-> handler
+      wrap-keyword-params
+      wrap-params
+      wrap-logger
+      wrap-anti-forgery
+      wrap-session
+      (wrap-resource "assets")
+      wrap-content-type
+      wrap-not-modified))


Some notes for future work.

Some notes for future work.

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/'


Add some useful string manipulation fns

Add some useful string manipulation fns

error: pathspec 'src/overtone/helpers/string.clj' did not match any file(s) known to git

--- src/overtone/helpers/string.clj
+++ src/overtone/helpers/string.clj
@@ -0,0 +1,43 @@
+(ns
+    ^{:doc "Useful string manipulation fns"
+      :author "Sam Aaron"}
+  overtone.helpers.string)
+
+(defn chop-last
+  "Removes the last char in str. Returns empty string unmodified"
+  [str]
+  (let [size (count str)]
+    (if (zero? size)
+      str
+      (subs str 0 (dec size)))))
+
+(defn chop-first
+  "Removes the first char in str. Returns empty string unmodified"
+  [str]
+  (let [size (count str)]
+    (if (zero? size)
+      str
+      (subs str 1 size))))
+
+(defn chop-first-n
+  "Removes the first n chars in str. Returns empty string if n is >= str length."
+  [n str]
+  (let [size (count str)]
+    (if (< size n)
+      ""
+      (subs str n size))))
+
+(defn chop-last-n
+  "Removes the first n chars in str. Returns empty string if n is >= str length."
+  [n str]
+  (let [size (count str)]
+    (if (< size n)
+      ""
+      (subs str 0 (- size n)))))
+
+(defn str->regex
+  "Converts term to regex. If term is already a regex, leaves it unchanged."
+  [term]
+  (if (= java.util.regex.Pattern (type term))
+    term
+    (re-pattern (str term))))


Added tests for deamon threads

Added tests for deamon threads

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/meowcakes/ring.git/'


Fixed nil answers.

Fixed nil answers.

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


prevent "transfer to current unit"

prevent "transfer to current unit"

fatal: unable to access 'https://github.com/starcity-properties/odin.git/': The requested URL returned error: 403


begins nav highlighting within services view

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/'


update categories sub to pull from backend


add missing `:body` key

add missing `:body` key


saioa-hasi: erabiltzailea exisititzen ez denean sortu

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/'


Redirecting System.out to a string for testing purposes

--- jvm-runner/test/codewars/runner/java_test.clj
+++ jvm-runner/test/codewars/runner/java_test.clj
@@ -2,7 +2,17 @@
   (:require [clojure.test :refer :all]
             [codewars.core :refer [-main]]
             [cheshire.core :as json]
-            [codewars.runner :refer [run]]))
+            [codewars.runner :refer [run]])
+  (:import [org.apache.commons.io.output WriterOutputStream]
+           [java.io PrintStream ByteArrayOutputStream]))
+
+(defmacro with-java-out-str [& body]
+  `(let [original-out# (PrintStream. (. System out))
+         byte-out# (ByteArrayOutputStream.)]
+     (-> byte-out# (PrintStream.) System/setOut)
+     ~@body
+     (System/setOut original-out#)
+     (.toString byte-out#)))

 (deftest java-basic
   (testing "-main can handle a very basic java solution and fixture"
@@ -20,3 +30,12 @@
        {:language "java"
         :solution "public class FooFighters {public static int main() {return 1;}}"})
       (is (= 1 (-main))))))
+
+
+(deftest java-solution-print
+  (testing "-main can handle a java solution that prints to standard out"
+    (with-in-str
+      (json/generate-string
+       {:language "java"
+        :solution "public class Hello {public static void main() {System.out.print(\"Hellooo!\");}}"})
+      (is (= "Hellooo!" (with-java-out-str (-main)))))))


Remove dev interceptors for transition to dev-src or pedestal-toolbox

fatal: unable to access 'https://github.com/democracyworks/turbovote-http-api.git/': The requested URL returned error: 403


Change default SERVICES_API_URL

Change default SERVICES_API_URL

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/'


goiko-barra keys bidez jaso

goiko-barra keys bidez jaso

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/'


Added support for font families; fixed image file contents

fatal: unable to access 'https://github.com/hudren/ebook-novel.git/': The requested URL returned error: 403


remove fixtures file

remove fixtures file


Getting familiar with Clojure functions

Getting familiar with Clojure functions

error: pathspec 'ParlezVousClojure/clojure_functions.clj' did not match any file(s) known to git

--- ParlezVousClojure/clojure_functions.clj
+++ ParlezVousClojure/clojure_functions.clj
@@ -0,0 +1,52 @@
+; ------------------------------------------------------------------------------
+; Functions
+; ------------------------------------------------------------------------------
+
+; ------------------------------------------------------------------------------
+; Calling Functions
+
+;; We've seen many examples of calling functions, like these...
+(+ 6 6 6)
+(- 6 6 6)
+(* 6 6 6)
+;; ...
+(first [6 6 6])
+(last [6 6 6])
+(get [6 6 6] 0)
+;; ... or any other function we've covered, of course...
+
+(or + -) ; =>  #object[clojure.core$_PLUS_ 0x2d3f2884 "clojure.core$_PLUS_@2d3f2884"]
+((or + -) 6 6 6) ; => 18
+((and (= 6 6) +) 6 6 6) ; => 18
+((first [+ 0]) 6 6 6) ; => 18
+
+(6 6 6)
+; => ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn  user/eval1322 (form-init3568529770152210505.clj:1)
+("metal" 6 6 6)
+; => ClassCastException java.lang.String cannot be cast to clojure.lang.IFn  user/eval1316 (form-init3568529770152210505.clj:1)
+
+;; Just like in JavaScript, function expressions (a function that returns a function)
+;; can take any number of arguments, including other functions. Functions that
+;; can take functions as arguments or return another function are called
+;; higher-order functions. The presence of higher-order functions in a programming
+;; language is indicative of the language supporting first-class functions
+;; (that is we can treat functions as values)
+
+;; Examples
+(inc 665) ; => 666
+
+(map inc [5 5 5]) ; => (6 6 6)
+
+(+ (inc 599) (/ 396 (- 9 3))) ; => 666
+
+; ------------------------------------------------------------------------------
+; Function calls, Macro calls, and Special Forms
+
+
+
+
+
+
+
+
+;


Extract pause functionality to its own file

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/codegeneralist/krest.git/'


add substB

add substB

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/carolove/clojure-ring-app.git/'


isolate actions with try/catch

isolate actions with try/catch

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/'


(TK-437) Changed @@ to deref/deref in no graceful shutdown test

This commit changes the use of @@ to nested derefs in the no graceful
shutdown test.  This is being done to make it more visually obvious that
the test is derefing an atom and then derefing the promise held within
the atom.

fatal: unable to access 'https://github.com/camlow325/trapperkeeper-webserver-jetty9.git/': The requested URL returned error: 403


Add a simple job for Jepsen testing. Read from BK output to BK

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

--- src/onyx_jepsen/simple_job.clj
+++ src/onyx_jepsen/simple_job.clj
@@ -0,0 +1,68 @@
+(ns onyx-jepsen.simple-job
+  (:require [taoensso.timbre :refer [info error debug fatal]]))
+
+
+(defn add-read-ledgers [job batch-size zk-addr ledgers-root-path password ledger-ids]
+  (let [read-ledger-task-names (mapv (fn [id]
+                                       (keyword (str "read-ledger" id)))
+                                     ledger-ids)]
+    (-> job
+        (update :workflow
+                into
+                (mapv (fn [task] [task :identity-log])
+                      read-ledger-task-names))
+        (update :catalog
+                into
+                (mapv (fn [task ledger-id]
+                        {:onyx/name task
+                         :onyx/plugin :onyx.plugin.bookkeeper/read-ledgers
+                         :onyx/type :input
+                         :onyx/medium :bookkeeper
+                         ;; TODO: Vary pending timeout in different Jepsen tests
+                         :onyx/pending-timeout 10000
+                         ;; TODO: Vary read max chunk in different tests
+                         ;:bookkeeper/read-max-chunk-size 10
+                         :bookkeeper/zookeeper-addr zk-addr
+                         :bookkeeper/zookeeper-ledgers-root-path ledgers-root-path
+                         :bookkeeper/ledger-id ledger-id
+                         :bookkeeper/digest-type :mac
+                         :bookkeeper/deserializer-fn :onyx.compression.nippy/zookeeper-decompress
+                         :bookkeeper/password-bytes password
+                         :bookkeeper/no-recovery? true
+                         :onyx/restart-pred-fn :onyx-peers.functions.functions/restartable?
+                         :onyx/max-peers 1
+                         :onyx/batch-size batch-size
+                         :onyx/doc "Reads a sequence from a BookKeeper ledger"})
+                      read-ledger-task-names
+                      ledger-ids))
+        (update :lifecycles
+                into
+                (mapv (fn [task]
+                        {:lifecycle/task task
+                         :lifecycle/calls :onyx.plugin.bookkeeper/read-ledgers-calls})
+                      read-ledger-task-names)))))
+
+(defn build-job [batch-size zk-addr ledgers-root-path ledger-ids]
+  (let [password (.getBytes "INSECUREDEFAULTPASSWORD")
+        job {:catalog [{:onyx/name :persist
+                        :onyx/plugin :onyx.plugin.bookkeeper/write-ledger
+                        :onyx/type :output
+                        :onyx/medium :bookkeeper
+                        :onyx/restart-pred-fn :onyx-peers.functions.functions/restartable?
+                        :bookkeeper/serializer-fn :onyx.compression.nippy/zookeeper-compress
+                        :bookkeeper/password-bytes password
+                        :bookkeeper/ensemble-size 3
+                        :bookkeeper/quorum-size 3
+                        :bookkeeper/zookeeper-addr zk-addr
+                        :bookkeeper/digest-type :mac
+                        :onyx/batch-size batch-size
+                        :onyx/doc "Writes messages to a BookKeeper ledger"}
+                       {:onyx/name :identity-log
+                        :onyx/fn :onyx-peers.functions.functions/identity-log
+                        :onyx/type :function
+                        :onyx/batch-size batch-size}]
+             :lifecycles [{:lifecycle/task :persist
+                           :lifecycle/calls :onyx.plugin.bookkeeper/write-ledger-calls}]
+             :workflow [[:identity-log :persist]]
+             :task-scheduler :onyx.task-scheduler/balanced}]
+    (add-read-ledgers job batch-size zk-addr ledgers-root-path password ledger-ids)))


Moved simple check extensions to cmr-common.

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

--- src/cmr/common/tests/simple_check_ext.clj
+++ src/cmr/common/tests/simple_check_ext.clj
@@ -0,0 +1,60 @@
+(ns cmr.common.tests.simple-check-ext
+  (:require [simple-check.generators :as gen])
+  (:import java.util.Random))
+
+(defn model-gen
+  "Creates a generator for models. Takes the constructor function for the model and generators
+  for each argument."
+  [constructor & args]
+  (gen/fmap (partial apply constructor) (apply gen/tuple args)))
+
+(defn counter
+  "Creates a generator that returns numbers in sequence from 0 to infinity."
+  []
+  (let [a (atom -1)]
+    (gen/make-gen (fn [rnd size]
+                    (swap! a inc)))))
+
+(defn return-then
+  "Creates a generator that returns each of the values in constants then uses then-gen
+  for subsequent values. This is useful when there are certain values that you want
+  always generated during testing.
+
+  user=>(gen/sample (return-then [:a :b :c] (gen/symbol)))
+  (\"a\" \"b\" \"c\" \"N\" \"_nN\" \"dVH3\" \"t@\" \"k+\" \"?Oq->h\" \"X82-<6bVp\")"
+  [constants then-gen]
+  (gen/gen-bind (counter)
+                (fn [i]
+                  (if (< i (count constants))
+                    (gen/return (nth constants i))
+                    then-gen))))
+
+(defn- double-halfs
+  [n]
+  (take-while #(> (Math/abs (double %)) 0.0001) (iterate #(/ % 2.0) n)))
+
+(defn- shrink-double
+  [dbl]
+  (map (partial - dbl) (double-halfs dbl)))
+
+(defn- double-rose-tree
+  [value]
+  [value (map double-rose-tree (shrink-double value))])
+
+(defn- rand-double-range
+  [^Random rnd lower upper]
+  (let [diff (Math/abs (double (- upper lower)))]
+    (if (zero? diff)
+      lower
+      (+ (* diff (.nextDouble rnd)) lower))))
+
+(defn choose-double
+  "Creates a generator that returns double values between lower and upper inclusive."
+  [lower upper]
+  (gen/make-gen
+    (fn [^Random rnd _size]
+      (let [value (rand-double-range rnd lower upper)]
+        (gen/rose-filter
+          #(and (>= % lower) (<= % upper))
+          [value (map double-rose-tree (shrink-double value))])))))
+


CMR-4239: Added variable association table db migration to handle native-id length.

error: pathspec 'metadata-db-app/src/migrations/060_update_var_association_var_name_length.clj' did not match any file(s) known to git

--- metadata-db-app/src/migrations/060_update_var_association_var_name_length.clj
+++ metadata-db-app/src/migrations/060_update_var_association_var_name_length.clj
@@ -0,0 +1,19 @@
+(ns migrations.060-update-var-association-var-name-length
+  (:require
+   [config.mdb-migrate-helper :as h]))
+
+(defn up
+  "Migrates the database up to version 60."
+  []
+  (println "migrations.060-update-var-association-var-name-length up...")
+  ;; The variable_name field actually saves the native-id of the variable in the association
+  ;; It is temporary. We didn't change the name of the field to native_id as we will eventaully
+  ;; change this to concept_id later as described in CMR-4254
+  (h/sql "alter table METADATA_DB.cmr_variable_associations modify variable_name VARCHAR(1030)"))
+
+
+(defn down
+  "Migrates the database down from version 60."
+  []
+  (println "migrations.060-update-var-association-var-name-length down...")
+  (h/sql "alter table METADATA_DB.cmr_variable_associations modify variable_name VARCHAR(80)"))


main renders app not todos, this way we can pick up :editing changes

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


finally if-set statement

finally if-set statement

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


initial simple implementation for shelling out

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


Refactor bundle entry builder.

Refactor bundle entry builder.

Use existing bundle entry elements.

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


make whole panel header clickable, show no content by default

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


clojure-algo-refresher:: re-implementing merge-sort using pure functional data-structures and recursions

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

--- src/clojure_learning/merge_sorting.clj
+++ src/clojure_learning/merge_sorting.clj
@@ -0,0 +1,71 @@
+(ns clojure-learning.merge-sorting)
+
+;; cclaudiu's functional merge-sort :)
+
+(defn merge-fn [left right]
+  "In the merge-sort sorting algorithm the merge-fn is the core of the algorithm
+   It will receive 2 partitions, starting with [1-element] at least in a partition
+   It will attempt to traverse the partitions and recurring instead of in-place state
+   mutations => each time a new version of the persistent-ds is created => kick ass GC
+   i.e::
+     (merge-fn [5] [2 9]) => [2 5 9]
+     (merge-fn [1 3 4] [2 5 9]) => [1 2 3 4 5 9]"
+  (loop [[x & xs :as all-left] left
+         [y & ys :as all-right] right
+         sorted []]
+    (if (nil? y)
+      (reduce conj sorted all-left)
+      (if (nil? x)
+        (reduce conj sorted all-right)
+        (if (< x y)
+          (recur xs all-right (conj sorted x)) ;; < x y:: preserve right/exhaust left
+          (recur all-left ys (conj sorted y))  ;; > x y:: preserve left/exhaust right
+)))))
+
+(defn merge-sort [coll]
+  "the main merge-sort function is preocupied of partitioning the initial coll
+   into 2 sides, and then it will attempt to recur foreach branch/side(l/r)
+   the results of recurring are then passed to the merge-fn which will receive
+   from top-of-the-recur-stack the partitions in increasing size:: expansive recursion"
+  (letfn [(partition-by [side-fn pivot coll]
+            (map second
+              (side-fn
+                (fn [[idx x]] (< idx pivot))
+                (map-indexed vector coll))))]
+    (let [pivot (long (/ (count coll) 2))
+          left-side (partition-by take-while pivot coll)
+          right-side (partition-by drop-while pivot coll)]
+      (cond
+        (<= (count coll) 1)
+          coll
+        :else
+          (merge-fn
+            (merge-sort left-side)
+            (merge-sort right-side))
+))))
+
+;; coolio :)
+(merge-sort [1 3 4 5 2 9]) ;; [1 2 3 4 5 9]
+(merge-sort [1 3 4 5 3 2 1 9]) ;; [1 1 2 3 3 4 5 9]
+
+(def rand-100k-nums (repeatedly 100000 (partial rand-int 100000)))
+(def rand-1mil-nums (repeatedly 1000000 (partial rand-int 1000000)))
+
+(def s-100k
+  (time (merge-sort rand-100k-nums))) ;; "Elapsed time: 4111.425544 msecs"
+
+(def s-100k-core
+  (time (sort rand-100k-nums))) ;; "Elapsed time: 205.470737 msecs"
+
+(def s-1mil
+  (time (merge-sort rand-1mil-nums))) ;; "Elapsed time: 70130.117786 msecs"
+
+(def s-1mil-core
+  (time (sort rand-1mil-nums))) ;; "Elapsed time: 2719.555278 msecs"
+
+;; Notes::
+;; following those benchmarks => one can see clearly that using
+;; persistent-immutable-data-structures is again NOT the right way of
+;; handling algorithms which are not parallelizable
+;; however merge-sort can be...partially paralelizable when branching
+;; and NOT in the merge-fn which relies on the states of both partitions


STORM-913: modify zookeeper.clj fix EOF exception

--- storm-core/src/clj/backtype/storm/zookeeper.clj
+++ storm-core/src/clj/backtype/storm/zookeeper.clj
@@ -108,7 +108,8 @@
     (when (exists-node? zk path false)
       (try-cause  (.. zk (delete) (deletingChildrenIfNeeded) (forPath (normalize-path path)))
                   (catch KeeperException$NoNodeException e
-                    ;; do nothing)
+                    ;; do nothing
+                  )
                   (catch Exception e (throw (wrap-in-runtime e)))))))

 (defn mkdirs


Added forward declarations for entities.

Now the CRUD functions can be moved to another namespace/file.

--- src/ruuvi_server/database/entities.clj
+++ src/ruuvi_server/database/entities.clj
@@ -6,6 +6,11 @@
   (:import org.joda.time.DateTime)
   )

+(declare tracker)
+(declare event-extension-type)
+(declare event-extension-value)
+(declare event)
+
 (defn init-entities
   "Map entities and add them to ruuvi-server.models.entities namespace"
   []
@@ -45,60 +50,61 @@
     (has-many event-location {:fk :event_id})
     (has-many event-extension-value {:fk :event_id})
     )
-
-  ;; private functions
-  (defn- to-sql-timestamp [date]
-    (if date
-      (java.sql.Timestamp. (.getMillis date))
-      nil))
-
-  (defn- current-sql-timestamp []
-    (to-sql-timestamp (org.joda.time.DateTime)))
-
-  ;; public functions
-  (defn get-trackers [ids]
-    (select tracker
-            (where (in :id ids))))
-
-  (defn get-tracker-by-code [tracker-code]
-    (first (select tracker
-                   (where {:tracker_code tracker-code}))))
-
-  (defn get-tracker-by-code! [tracker-code & tracker-name]
-    (let [existing-tracker (get-tracker-by-code tracker-code)]
-      (if existing-tracker
-        existing-tracker
-        (insert tracker (values {:tracker_code tracker-code
-                                 :name (or tracker-name tracker-code)}))
-        )))
-
-  (defn get-extension-type-by-id [id]
-    (first (select event-extension-type
-                   (where {:name id}))))
-
-
-  (defn get-extension-type-by-name [type-name]
-    (first (select event-extension-type
-                   (where {:name (str (name type-name))}))))
-
-  (defn get-extension-type-by-name! [type-name]
-    (let [existing-extension-type (get-extension-type-by-name type-name)]
-      (if existing-extension-type
-        existing-extension-type
-        (insert event-extension-type (values {:name (str (name type-name))
-                                              :description "Autogenerated"}))
-        )))
-
-  (defn get-event [event_id]
-    (first (select event
-                   (with tracker)
-                   (with event-location)
-                   (with event-extension-value)
-                   (where {:id event_id})))
-    )
-
-  (defn search-events
-    "Search events: criteria is a map that can contain following keys.
+  )
+
+;; private functions
+(defn- to-sql-timestamp [date]
+  (if date
+    (java.sql.Timestamp. (.getMillis date))
+    nil))
+
+(defn- current-sql-timestamp []
+  (to-sql-timestamp (org.joda.time.DateTime)))
+
+;; public functions
+(defn get-trackers [ids]
+  (select tracker
+          (where (in :id ids))))
+
+(defn get-tracker-by-code [tracker-code]
+  (first (select tracker
+                 (where {:tracker_code tracker-code}))))
+
+(defn get-tracker-by-code! [tracker-code & tracker-name]
+  (let [existing-tracker (get-tracker-by-code tracker-code)]
+    (if existing-tracker
+      existing-tracker
+      (insert tracker (values {:tracker_code tracker-code
+                               :name (or tracker-name tracker-code)}))
+      )))
+
+(defn get-extension-type-by-id [id]
+  (first (select event-extension-type
+                 (where {:name id}))))
+
+
+(defn get-extension-type-by-name [type-name]
+  (first (select event-extension-type
+                 (where {:name (str (name type-name))}))))
+
+(defn get-extension-type-by-name! [type-name]
+  (let [existing-extension-type (get-extension-type-by-name type-name)]
+    (if existing-extension-type
+      existing-extension-type
+      (insert event-extension-type (values {:name (str (name type-name))
+                                            :description "Autogenerated"}))
+      )))
+
+(defn get-event [event_id]
+  (first (select event
+                 (with tracker)
+                 (with event-location)
+                 (with event-extension-value)
+                 (where {:id event_id})))
+  )
+
+(defn search-events
+  "Search events: criteria is a map that can contain following keys.
 - :storeTimeStart <DateTime>, find events that are created (stored) to database later than given time (inclusive).
 - :storeTimeDnd <DateTime>, find events that are created (stored) to database earlier than given time (inclusive).
 - :eventTimeStart <DateTime>, find events that are created in tracker later than given time (inclusive).
@@ -107,104 +113,103 @@
 TODO calculates milliseconds wrong (12:30:01.000 is rounded to 12:30:01 but 12:30:01.001 is rounded to 12:30:02)
 TODO make maxResults default configurable.
 "
-    [criteria]
-
-    (let [event-start (to-sql-timestamp (:eventTimeStart criteria))
-          event-end (to-sql-timestamp (:eventTimeEnd criteria))
-          store-start (to-sql-timestamp (:storeTimeStart criteria))
-          store-end (to-sql-timestamp (:storeTimeEnd criteria))
-          tracker-ids (:trackerIds criteria)
-          max-result-count 50
-          max-results (:maxResults criteria max-result-count)
-          result-limit (min max-result-count max-results)
-
-          tracker-ids-crit (when tracker-ids {:tracker_id ['in tracker-ids]})
-          event-start-crit (when event-start {:event_time ['>= event-start]})
-          event-end-crit (when event-end {:event_time ['<= event-end]})
-          store-start-crit (when store-start {:created_on ['>= store-start]})
-          store-end-crit (when store-end {:created_on ['<= store-end]})
-          conditions (filter identity (list event-start-crit event-end-crit tracker-ids-crit))
-          ]
-      (if (not (empty? conditions))
-        (let [results (select event
-                              (with event-location)
-                              (with event-extension-value (fields :value)
-                                    (with event-extension-type (fields :name)))
-                              (where (apply and conditions))
-                              (limit result-limit)) ]
-          results
-          )
-        '() )))
-
-  (defn get-events [ids]
-    (select event
-            (with event-location)
-            (with event-extension-value
-                  (with event-extension-type (fields :name)))
-            (where (in :id ids))))
-
-  (defn get-all-events []
-    (select event
-            (with event-location)
-            (with event-extension-value)
-            ))
-
-  (defn get-all-trackers []
-    (select tracker)
-    )
-
-  (defn get-tracker [id]
-    ;; TODO support also fetching with tracker_indentifier?
-    (first (select tracker
-                   (where {:id id}))))
-
-  (defn- update-tracker-latest-activity [id]
-    (update tracker
-            (set-fields {:latest_activity (java.sql.Timestamp. (System/currentTimeMillis)) })
-            (where {:id id})))
-
-  (defn create-tracker [code name shared-secret]
-    (info "Create new tracker" name "(" code ")")
-    (insert tracker (values
-                     {:tracker_code code
-                      :shared_secret shared-secret
-                      :name name})))
-
-  (defn create-event [data]
-    (let [tracker (get-tracker-by-code! (:tracker_code data))]
-      (transaction
-       (update-tracker-latest-activity (tracker :id))
-       (let [extension-keys (filter (fn [key]
-                                      (.startsWith (str (name key)) "X-"))
-                                    (keys data))
-             latitude (:latitude data)
-             longitude (:longitude data)
+  [criteria]
+
+  (let [event-start (to-sql-timestamp (:eventTimeStart criteria))
+        event-end (to-sql-timestamp (:eventTimeEnd criteria))
+        store-start (to-sql-timestamp (:storeTimeStart criteria))
+        store-end (to-sql-timestamp (:storeTimeEnd criteria))
+        tracker-ids (:trackerIds criteria)
+        max-result-count 50
+        max-results (:maxResults criteria max-result-count)
+        result-limit (min max-result-count max-results)
+
+        tracker-ids-crit (when tracker-ids {:tracker_id ['in tracker-ids]})
+        event-start-crit (when event-start {:event_time ['>= event-start]})
+        event-end-crit (when event-end {:event_time ['<= event-end]})
+        store-start-crit (when store-start {:created_on ['>= store-start]})
+        store-end-crit (when store-end {:created_on ['<= store-end]})
+        conditions (filter identity (list event-start-crit event-end-crit tracker-ids-crit))
+        ]
+    (if (not (empty? conditions))
+      (let [results (select event
+                            (with event-location)
+                            (with event-extension-value (fields :value)
+                                  (with event-extension-type (fields :name)))
+                            (where (apply and conditions))
+                            (limit result-limit)) ]
+        results
+        )
+      '() )))
+
+(defn get-events [ids]
+  (select event
+          (with event-location)
+          (with event-extension-value
+                (with event-extension-type (fields :name)))
+          (where (in :id ids))))
+
+(defn get-all-events []
+  (select event
+          (with event-location)
+          (with event-extension-value)
+          ))
+
+(defn get-all-trackers []
+  (select tracker)
+  )
+
+(defn get-tracker [id]
+  ;; TODO support also fetching with tracker_indentifier?
+  (first (select tracker
+                 (where {:id id}))))
+
+(defn- update-tracker-latest-activity [id]
+  (update tracker
+          (set-fields {:latest_activity (java.sql.Timestamp. (System/currentTimeMillis)) })
+          (where {:id id})))
+
+(defn create-tracker [code name shared-secret]
+  (info "Create new tracker" name "(" code ")")
+  (insert tracker (values
+                   {:tracker_code code
+                    :shared_secret shared-secret
+                    :name name})))
+
+(defn create-event [data]
+  (let [tracker (get-tracker-by-code! (:tracker_code data))]
+    (transaction
+     (update-tracker-latest-activity (tracker :id))
+     (let [extension-keys (filter (fn [key]
+                                    (.startsWith (str (name key)) "X-"))
+                                  (keys data))
+           latitude (:latitude data)
+           longitude (:longitude data)

-             event-entity (insert event (values
-                                         {:tracker_id (tracker :id)
-                                          :event_time (or (to-sql-timestamp (:event_time data))
-                                                          (current-sql-timestamp) )
-                                          }))]
-
-         (if (and latitude longitude)
-           (insert event-location (values
-                                   {:event_id (:id event-entity)
-                                    :latitude latitude
-                                    :longitude longitude
-                                    :accuracy (:accuracy data)
-                                    :satellite_count (:satellite_count data)
-                                    :altitude (:altitude data)}))
-           )
-
-         (doseq [key extension-keys]
-           (insert event-extension-value
-                   (values
-                    {:event_id (:id event-entity)
-                     :value (data key)
-                     :event_extension_type_id (:id (get-extension-type-by-name! key))
-                     }
-                    )))
-         event-entity
-         )))
-    )
-)+           event-entity (insert event (values
+                                       {:tracker_id (tracker :id)
+                                        :event_time (or (to-sql-timestamp (:event_time data))
+                                                        (current-sql-timestamp) )
+                                        }))]
+
+       (if (and latitude longitude)
+         (insert event-location (values
+                                 {:event_id (:id event-entity)
+                                  :latitude latitude
+                                  :longitude longitude
+                                  :accuracy (:accuracy data)
+                                  :satellite_count (:satellite_count data)
+                                  :altitude (:altitude data)}))
+         )
+
+       (doseq [key extension-keys]
+         (insert event-extension-value
+                 (values
+                  {:event_id (:id event-entity)
+                   :value (data key)
+                   :event_extension_type_id (:id (get-extension-type-by-name! key))
+                   }
+                  )))
+       event-entity
+       )))
+  )


fix src dir

fix src dir

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/bsima/aristotl.git/'


Style: Cosmetic code changes.

Style: Cosmetic code changes.

--- src-cljs/pepa/style.cljs
+++ src-cljs/pepa/style.cljs
@@ -1,13 +1,13 @@
 (ns ^:figwheel-always pepa.style
-  (:require [garden.core :refer [css]]
-            [garden.units :as u :refer [px em pt]]
-            [garden.stylesheet :refer [at-keyframes cssfn]]
-
-            [nom.ui :as ui]
-            [pepa.navigation :refer [navigation-elements]]
-
-            [clojure.string :as s]
-            [goog.string :as gstring]))
+    (:require [garden.core :refer [css]]
+              [garden.units :as u :refer [px em pt]]
+              [garden.stylesheet :refer [at-keyframes cssfn]]
+
+              [nom.ui :as ui]
+              [pepa.navigation :refer [navigation-elements]]
+
+              [clojure.string :as s]
+              [goog.string :as gstring]))

 ;;; Colors

@@ -405,12 +405,12 @@
                           :bottom (px margin)
                           :font-size (pt 9)
                           :color dashboard-page-count-color}])]
-        (let [title-padding 5
+        (let [title-padding (px 5)
               title-height (em 2)]
           [:.title {:width "100%"
                     :display :block
-                    :padding {:top (px title-padding)
-                              :bottom (px title-padding)}
+                    :padding {:top title-padding
+                              :bottom title-padding}
                     :height title-height
                     :line-height title-height
                     :overflow :hidden
@@ -444,7 +444,7 @@
              :flex-direction :column}
      [:ul.pages {:overflow :auto
                  :outline :none
-                 :margin 0, :padding 0}
+                 :margin 0 :padding 0}
       page-css
       [:li
        [:img {:max-width "100%"
@@ -473,9 +473,10 @@
     ;; Full Page View
     [:.full
      [:header {:text-align :right}]
-     [:ul.pages #_{:min-width (px 400)}
+     [:ul.pages
       [:li
-       (let [page-margin 20, page-border 1]
+       (let [page-margin 20
+             page-border 1]
          [:img (list {:margin {:left (px page-margin)
                                :right (px page-margin)
                                :top (px (/ page-margin 2))}
@@ -492,8 +493,7 @@
   (let [padding (px 5)
         height (px 14)
         radius (px 3)]
-    [:input {
-             :padding padding
+    [:input {:padding padding
              :color default-text
              :outline :none
              :font {:family default-font
@@ -646,19 +646,19 @@

 (def dropdown-css
   (let [font-size (px 14)
-        height 25]
+        height (px 25)]
     [:label.dropdown {:background "linear-gradient(to bottom, #ffffff 0%,#e4e4e4 100%)"
                       :font-size font-size
                       :position :relative
-                      :height (px height)
+                      :height height
                       :text-shadow "1px 1px #ffffff"
-                      :line-height (px height)
+                      :line-height height
                       :display :inline-flex
                       :padding  {:left (px 10)
-                                 :right (px 0)
-                                 :top (px 0)
-                                 :bottom (px 0)}
-                      :border (str "1px solid " border-buttons)
+                                 :right 0
+                                 :top 0
+                                 :bottom 0}
+                      :border [[(px 1) :solid border-buttons]]
                       :border-radius (px 4)
                       :box-shadow "0px 0px 3px #dddddd"}
      [:span {:padding {:left (px 20)
@@ -668,14 +668,15 @@
                      :border {:top :none
                               :bottom :none
                               :right :none
-                              :left (str "1px solid " border-buttons)}
+                              :left [[(px 1) :solid border-buttons]]}
                      :border-top {:right-radius (px 3)
-                                  :left-radius (px 0)}
+                                  :left-radius 0}
                      :border-bottom {:right-radius (px 3)
-                                     :left-radius (px 0)}
+                                     :left-radius 0}
                      :text-shadow "1px 1px #ffffff"
                      :font-size font-size
-                     :background (str (image-url "dropdown-arrow.svg") " 10px center no-repeat")
+                     :background (str (image-url "dropdown-arrow.svg")
+                                      " 10px center no-repeat")
                      :color blue-text
                      :outline :none
                      :font-family default-font
@@ -802,21 +803,19 @@
                   :display :flex
                   :flex-direction :row}

-     ;;this is the HEADER SHADOW ELEMENT
-     ;; it has to be the before element of the .container,
-     ;; because the header is splitted in three parts.
-     ;; and there are some bugs with the z-index and positioning
+     ;; This is the HEADER SHADOW ELEMENT. It has to be the :before
+     ;; element of the .container because the header is split in three
+     ;; parts and there are some bugs with the z-index and positioning
      [:&:before {:content (pr-str " ")
-                 :position :absolute ;absolute not fixed due to safari scroll special behaviour
+                 :position :absolute ;Not fixed due to safari scroll special behaviour
                  :box-shadow "0px 0px 8px 0px rgba(0,0,0,0.4)"
-                 :left (px 0)
-                 :right (px 0)
-                 :top (px 0)
+                 :left 0
+                 :right 0
+                 :top 0
                  :z-index 20
                  :pointer-events :none
                  :height (px (- header-height 2))
-                 :background "transparent)"
-                 }]]
+                 :background :transparent}]]
     [:&.file-drop
      {:background "red"}]
     clear-a-css


little bit of reorg.

little bit of reorg.

--- src/metabase/pulse.clj
+++ src/metabase/pulse.clj
@@ -7,35 +7,49 @@
             [clojure.tools.logging :as log]
             [clojure.pprint :refer [cl-format]]
             [clojure.string :refer [upper-case]]
-            (metabase.models [setting :refer [defsetting] :as setting])
-            [metabase.util :as u]))
+            [metabase.models.setting :as setting]))

 ;; NOTE: hiccup does not escape content by default so be sure to use "h" to escape any user-controlled content :-/

 ;;; ## CONFIG

-(def ^:private card-width 400)
-
-(def ^:private rows-limit 10)
-(def ^:private cols-limit 3)
+(def ^:const card-width 400)
+(def ^:const rows-limit 10)
+(def ^:const cols-limit 3)
+(def ^:const sparkline-dot-radius 6)
+(def ^:const sparkline-thickness 4)
+(def ^:const sparkline-pad 10)

 ;;; ## STYLES

-(def ^:private color-brand  "rgb(80,158,227)")
-(def ^:private color-purple "rgb(135,93,175)")
-(def ^:private color-grey-1 "rgb(248,248,248)")
-(def ^:private color-grey-2 "rgb(189,193,191)")
-(def ^:private color-grey-3 "rgb(124,131,129)")
-(def ^:private color-grey-4 "rgb(57,67,64)")
-
-(def ^:private font-style    "font-family: Lato, \"Helvetica Neue\", Helvetica, Arial, sans-serif;")
-(def ^:private section-style (str font-style))
-(def ^:private header-style  (str font-style "font-size: 16px; font-weight: 700; color: " color-grey-4 "; text-decoration: none;"))
-(def ^:private scalar-style  (str font-style "font-size: 32px; font-weight: 400; color: " color-brand ";"))
-(def ^:private bar-th-style  (str font-style "font-size: 10px; font-weight: 400; color: " color-grey-4 "; border-bottom: 4px solid " color-grey-1 "; padding-top: 0px; padding-bottom: 10px;"))
-(def ^:private bar-td-style  (str font-style "font-size: 16px; font-weight: 400; text-align: left; padding-right: 1em; padding-top: 8px;"))
-(def ^:private button-style  (str font-style "display: inline-block; box-sizing: border-box; padding: 8px; color: " color-brand "; border: 1px solid " color-brand "; border-radius: 4px; text-decoration: none; "))
-
+(def ^:const color-brand  "rgb(80,158,227)")
+(def ^:const color-purple "rgb(135,93,175)")
+(def ^:const color-grey-1 "rgb(248,248,248)")
+(def ^:const color-grey-2 "rgb(189,193,191)")
+(def ^:const color-grey-3 "rgb(124,131,129)")
+(def ^:const color-grey-4 "rgb(57,67,64)")
+
+(def ^:const font-style    "font-family: Lato, \"Helvetica Neue\", Helvetica, Arial, sans-serif;")
+(def ^:const section-style (str font-style))
+(def ^:const header-style  (str font-style "font-size: 16px; font-weight: 700; color: " color-grey-4 "; text-decoration: none;"))
+(def ^:const scalar-style  (str font-style "font-size: 32px; font-weight: 400; color: " color-brand ";"))
+(def ^:const bar-th-style  (str font-style "font-size: 10px; font-weight: 400; color: " color-grey-4 "; border-bottom: 4px solid " color-grey-1 "; padding-top: 0px; padding-bottom: 10px;"))
+(def ^:const bar-td-style  (str font-style "font-size: 16px; font-weight: 400; text-align: left; padding-right: 1em; padding-top: 8px;"))
+(def ^:const button-style  (str font-style "display: inline-block; box-sizing: border-box; padding: 8px; color: " color-brand "; border: 1px solid " color-brand "; border-radius: 4px; text-decoration: none; "))
+
+;;; ## HELPER FNS
+
+(defn datetime-field?
+  [field]
+  (or (contains? #{:DateTimeField :TimeField :DateField} (:base_type field))
+      (contains? #{:timestamp_seconds :timestamp_milliseconds} (:special_type field))))
+
+(defn number-field?
+  [field]
+  (or (contains? #{:IntegerField :DecimalField :FloatField :BigIntegerField} (:base_type field))
+      (contains? #{:number} (:special_type field))))
+
+;;; ## FORMATTING

 (defn- format-number
   [n]
@@ -64,6 +78,8 @@
     (instance? java.util.Date value) (format-timestamp (.getTime value))
     (number? value) (format-number value)
     :else (str value)))
+
+;;; ## RENDERING

 (defn card-href
   [card]
@@ -137,10 +153,6 @@
   [card data render-img]
   [:div {:style scalar-style}
     (-> data :rows first first format-cell h)])
-
-(def ^:private sparkline-dot-radius 6)
-(def ^:private sparkline-thickness 4)
-(def ^:private sparkline-pad 10)

 (defn render-sparkline-to-png
   "Takes two arrays of numbers between 0 and 1 and plots them as a sparkline"
@@ -191,16 +203,6 @@
       [:div {:style "margin-top: 20px; margin-left: 30px;"}
         (render-table card [(last rows) (first rows)] cols render-img [0 1] nil)]]))

-(defn datetime-field?
-  [field]
-  (or (contains? #{:DateTimeField :TimeField :DateField} (:base_type field))
-      (contains? #{:timestamp_seconds :timestamp_milliseconds} (:special_type field))))
-
-(defn number-field?
-  [field]
-  (or (contains? #{:IntegerField :DecimalField :FloatField :BigIntegerField} (:base_type field))
-      (contains? #{:number} (:special_type field))))
-
 (defn detect-pulse-card-type
   [card data]
   (let [col-count (-> data :cols count)


More code cleanup; move intrinsic decls to separate fn

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/kkroening/jackal.git/'


Add tests for the happy path of all of the routes

Includes a change to stop the HTTP server after tests are run in order
to facilitate REPL-driven tests.

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/partner-http-api.git/'


Remove redundant debug print

Remove redundant debug print

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/'


backend url/eval reader

backend url/eval reader

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

--- src/dasha/reader.clj
+++ src/dasha/reader.clj
@@ -0,0 +1,4 @@
+(ns dasha.reader)
+
+(defn eval-url-backend [url]
+  (-> url slurp read-string eval))


Adding query repl exploration

Adding query repl exploration

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

--- src/wealthpulse/query.clj
+++ src/wealthpulse/query.clj
@@ -0,0 +1,3 @@
+
+wealthpulse.core=> (clojure.set/project (clojure.set/select #(.contains (clojure.string/lower-case (:account %)) "assets") (set fjou
+rnal)) [:account :amount :commodity])


Add the repl convenience functions

Add the repl convenience functions

They might belong to the user namespace as stated in #41, but then
again these functions are useful in both the dev and the prod
environment. So for now we just add them as is to the catalog.repl
namespace.

Fixes part of #41

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

--- src/clj/catalog/repl.clj
+++ src/clj/catalog/repl.clj
@@ -0,0 +1,76 @@
+(ns catalog.repl
+  "Convenience functions for manipulations at the repl"
+  (:require [catalog.db.core :as db]
+            [catalog.layout.fop :as layout.fop]
+            [catalog.layout.text :as layout.text]
+            [catalog.validation :as validation]
+            [catalog.vubis :as vubis]
+            [schema.core :as s]))
+
+(defn neu-im-sortiment [year issue]
+  (let [temp-file (java.io.File/createTempFile (format "nis-%s-%s" year issue) ".pdf")]
+    (-> (db/read-catalog year issue)
+        vubis/order
+        vubis/duplicate-print-and-braille-items
+        vubis/group
+        (layout.fop/document :all-formats year issue nil nil)
+        (layout.fop/generate-pdf! temp-file))))
+
+(defn neu-in-grossdruck [year issue]
+  (let [temp-file (java.io.File/createTempFile (format "nig-%s-%s" year issue) ".pdf")
+        editorial (db/read-editorial year issue :grossdruck)
+        recommendation (db/read-recommendation year issue :grossdruck)]
+    (-> (db/read-catalog year issue)
+        vubis/order-and-group
+        (layout.fop/document :grossdruck year issue editorial recommendation)
+        (layout.fop/generate-pdf! temp-file))))
+
+(defn neu-im-sortiment-fop
+  "Generate the xsl-fo XML for the *Neu im Sortiment* catalog in file
+  `out` for given `year` and `issue`"
+  [year issue out]
+  (->
+   (db/read-catalog year issue)
+   vubis/order
+   vubis/duplicate-print-and-braille-items
+   vubis/group
+   (layout.fop/generate-document :all-formats year issue nil nil out)))
+
+(defn neu-in-grossdruck-fop
+  "Generate the xsl-fo XML for the *Neu in Grossdruck* catalog in file
+  `out` for given `year` and `issue`"
+  [year issue out]
+  (let [editorial (db/read-editorial year issue :grossdruck)
+        recommendations (db/read-recommendation year issue :grossdruck)]
+    (->
+     (db/read-catalog year issue)
+     vubis/order-and-group
+     (layout.fop/generate-document :grossdruck year issue editorial recommendations out))))
+
+(defn neu-im-sortiment-text
+  "Generate plain text for the *Neu im Sortiment* catalog for given
+  `year` and `issue`"
+  [year issue]
+  (->
+   (db/read-catalog year issue)
+   vubis/order
+   vubis/duplicate-print-and-braille-items
+   vubis/group
+   (layout.text/text {:year year :issue issue})))
+
+(defn custom-text
+  "Generate plain text for the *Katalog nach Mass* catalog for given
+  `year` and `issue`"
+  [in query customer]
+  (-> in
+   vubis/read-file
+   vubis/order
+   (layout.text/text {:query query :customer customer})))
+
+(defn validate [in]
+  (let [checker (s/checker validation/CatalogItem)]
+    (->>
+     in
+     vubis/read-file
+     vubis/collate-all-duplicate-items
+     (keep #(when-let [error (checker %)] [error %])))))


Fixed structure of core.clj.

Fixed structure of core.clj.

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/'


query - draft for recursive protocol

query - draft for recursive protocol

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

--- src/osmium/query.clj
+++ src/osmium/query.clj
@@ -0,0 +1,47 @@
+(ns osmium.query
+  (:require [clj-webdriver.taxi :as taxi]))
+
+(defprotocol IQuery
+  (q [sel dom]))
+
+(defn map-vals [f m]
+  (into {} (map (fn [[k v]] [k (f v)]) m)))
+
+(defn q-map [sel dom]
+  (->> (map-vals #(q % dom) sel)
+       (remove (fn [[k v]] (empty? v)))
+       (into {})))
+
+(extend-protocol IQuery
+  nil
+  (q [sel dom] nil)
+
+  java.lang.String
+  (q [sel dom]
+    (->> (taxi/find-elements dom {:css sel})
+         (remove (comp nil? :webelement))
+         vec))
+
+  clojure.lang.PersistentVector
+  (q [sel dom]
+    (let [[x xs] sel]
+      (if (nil? xs)
+        (->> (taxi/find-elements dom {:css x})
+             (remove (comp nil? :webelement))
+             vec)
+        (->> (taxi/find-elements dom {:css x})
+             (remove (comp nil? :webelement))
+             (mapv (partial q xs))
+             (remove empty?)
+             vec))))
+
+  clojure.lang.PersistentArrayMap
+  (q [sel dom]
+    (q-map sel dom))
+  clojure.lang.PersistentHashMap
+  (q [sel dom]
+    (q-map sel dom))
+
+  clojure.lang.Fn
+  (q [f dom]
+    (f dom)))


Add schema for HSTS

Add schema for HSTS

--- src/yada/schema.clj
+++ src/yada/schema.clj
@@ -367,6 +367,7 @@
           ResourceDocumentation
           {(s/optional-key :path-info?) Boolean
            (s/optional-key :sub-resource) (s/=> Resource Context)}
+          {(s/optional-key :strict-transport-security) {:max-age s/Num}}
           NamespacedEntries)
    (fn [v] (not (and (:sub-resource v) (not (:path-info? v)))))
    "If a sub-resource entry exists then path-info? must be true"))