update revision api tests so that they are focused on testing the endpoint functionality and not a bunch of other stuff.

--- test/metabase/api/revision_test.clj
+++ test/metabase/api/revision_test.clj
@@ -4,88 +4,130 @@
             [medley.core :as m]
             [metabase.api.revision :refer :all]
             [metabase.db :as db]
-            (metabase.models [dashboard :refer [Dashboard]]
+            (metabase.models [card :refer [Card]]
+                             [dashboard :refer [Dashboard]]
                              [dashboard-card :refer [DashboardCard]]
+                             [revision :refer [push-revision revisions]]
                              [revision-test :refer [with-fake-card]])
-            [metabase.test.data.users :refer :all]))
-
-(defn- fake-dashboard [& {:as kwargs}]
-  (m/mapply db/ins Dashboard (merge {:name         (str (java.util.UUID/randomUUID))
-                                     :public_perms 0
-                                     :creator_id   (user->id :rasta)}
-                                    kwargs)))
-
-(defmacro with-fake-dashboard [[binding & {:as kwargs}] & body]
-  `(let [dash# (fake-dashboard ~@kwargs)
-         ~binding dash#]
-     (try ~@body
-          (finally
-            (db/cascade-delete Dashboard :id (:id dash#))))))
+            [metabase.test.data :refer :all]
+            [metabase.test.data.users :refer :all]
+            [metabase.test.util :refer [expect-eval-actual-first with-temp random-name]]))

 (def ^:private rasta-revision-info
   (delay {:id (user->id :rasta) :common_name "Rasta Toucan", :first_name "Rasta", :last_name "Toucan"}))

-;;; # TESTS FOR GET /api/revision
-(expect [{:description "First revision.", :user {}}]
-  (with-fake-card [{card-id :id}]
-    ((user->client :rasta) :get 200 "revision", :entity :card, :id card-id)))
-
-(defn- get-dashboard-revisions [dashboard-id]
-  (->> ((user->client :rasta) :get 200 "revision", :entity :dashboard, :id dashboard-id)
+(defn- get-revisions [entity object-id]
+  (->> ((user->client :rasta) :get 200 "revision", :entity entity, :id object-id)
        (mapv #(dissoc % :timestamp :id))))

-(defn- post-dashcard [dash-id card-id]
-  ((user->client :rasta) :post 200 (format "dashboard/%d/cards" dash-id), {:cardId card-id}))
+(defn- create-test-card []
+  (let [rand-name (random-name)]
+    (db/ins Card
+      :name                   rand-name
+      :description            rand-name
+      :public_perms           2
+      :display                "table"
+      :dataset_query          {:database (db-id)
+                               :type     "query"
+                               :query    {:aggregation ["rows"]
+                                          :source_table (id :categories)}}
+      :visualization_settings {}
+      :creator_id             (user->id :rasta))))

-(defn- delete-dashcard [dash-id card-id]
-  ((user->client :rasta) :delete 204 (format "dashboard/%d/cards" dash-id), :dashcardId (db/sel :one :id DashboardCard :dashboard_id dash-id, :card_id card-id)))
+(defn- create-card-revision [card is-creation? is-reversion?]
+  (push-revision
+    :object        card
+    :entity        Card
+    :id            (:id card)
+    :user-id       (user->id :rasta)
+    :is-creation?  is-creation?
+    :is-reversion? is-reversion?))

-(expect [{:is_reversion false, :is_creation false, :user @rasta-revision-info, :description "First revision."}]
-  (with-fake-dashboard [{dash-id :id}]
-    (with-fake-card [{card-id :id}]
-      (post-dashcard dash-id card-id)
-      (Thread/sleep 25)
-      (get-dashboard-revisions dash-id))))
+(defn- create-test-dashboard []
+  (let [rand-name (random-name)]
+    (db/ins Dashboard
+      :name                   rand-name
+      :description            rand-name
+      :public_perms           2
+      :creator_id             (user->id :rasta))))

-(expect [{:is_reversion false, :is_creation false, :user @rasta-revision-info, :description "added a card."}
-         {:is_reversion false, :is_creation false, :user @rasta-revision-info, :description "First revision."}]
-  (with-fake-dashboard [{dash-id :id}]
-    (with-fake-card [{card-id₁ :id}]
-      (with-fake-card [{card-id₂ :id}]
-        (post-dashcard dash-id card-id₁)
-        (post-dashcard dash-id card-id₂)
-        (Thread/sleep 25)
-        (get-dashboard-revisions dash-id)))))
+(defn- create-dashboard-revision [dash is-creation? is-reversion?]
+  (push-revision
+    :object        (Dashboard (:id dash))
+    :entity        Dashboard
+    :id            (:id dash)
+    :user-id       (user->id :rasta)
+    :is-creation?  is-creation?
+    :is-reversion? is-reversion?))

-(expect [{:is_reversion false, :is_creation false, :user @rasta-revision-info, :description "removed a card."}
-         {:is_reversion false, :is_creation false, :user @rasta-revision-info, :description "added a card."}
-         {:is_reversion false, :is_creation false, :user @rasta-revision-info, :description "First revision."}]
-  (with-fake-dashboard [{dash-id :id}]
-    (with-fake-card [{card-id₁ :id}]
-      (with-fake-card [{card-id₂ :id}]
-        (post-dashcard   dash-id card-id₁)
-        (post-dashcard   dash-id card-id₂)
-        (delete-dashcard dash-id card-id₂)
-        (Thread/sleep 25)
-        (get-dashboard-revisions dash-id)))))

-;;; # TESTS FOR POST /api/revision/revert
-(expect [2
-         [{:is_reversion true,  :is_creation false, :user @rasta-revision-info, :description "reverted to an earlier revision and added a card."}
-          {:is_reversion false, :is_creation false, :user @rasta-revision-info, :description "removed a card."}
-          {:is_reversion false, :is_creation false, :user @rasta-revision-info, :description "added a card."}
-          {:is_reversion false, :is_creation false, :user @rasta-revision-info, :description "First revision."}]]
-  (with-fake-dashboard [{dash-id :id}]
-    (with-fake-card [{card-id₁ :id}]
-      (with-fake-card [{card-id₂ :id}]
-        (post-dashcard   dash-id card-id₁)
-        (post-dashcard   dash-id card-id₂)
-        (delete-dashcard dash-id card-id₂)
-        (Thread/sleep 25)
-        (let [[_ {previous-revision-id :id}] (metabase.models.revision/revisions Dashboard dash-id)]
-          ;; Revert to the previous revision
-          ((user->client :rasta) :post 200 "revision/revert", {:entity :dashboard, :id dash-id, :revision_id previous-revision-id})
-          [ ;; [1] There should be 2 cards again
-           (count @(:ordered_cards (Dashboard dash-id)))
-           ;; [2] A new revision recording the reversion should have been pushed
-           (get-dashboard-revisions dash-id)])))))
+;;; # GET /revision
+
+; Things we are testing for:
+;  1. ordered by timestamp DESC
+;  2. :user is hydrated
+;  3. :description is calculated
+
+;; case with no revisions
+(expect-let [{:keys [id] :as card} (create-test-card)]
+  [{:user {}, :description "First revision."}]
+  (get-revisions :card id))
+
+;; case with single creation revision
+(expect-let [{:keys [id] :as card} (create-test-card)]
+  [{:is_reversion false, :is_creation true, :user @rasta-revision-info, :description "First revision."}]
+  (do
+    (create-card-revision card true false)
+    (get-revisions :card id)))
+
+;; case with multiple revisions, including reversion
+(expect-let [{:keys [id name] :as card} (create-test-card)]
+  [{:is_reversion true, :is_creation false, :user @rasta-revision-info, :description (format "reverted to an earlier revision and renamed this Card from \"something else\" to \"%s\"." name)}
+   {:is_reversion false, :is_creation false, :user @rasta-revision-info, :description (format "renamed this Card from \"%s\" to \"something else\"." name)}
+   {:is_reversion false, :is_creation true, :user @rasta-revision-info, :description "First revision."}]
+  (do
+    (create-card-revision card true false)
+    (create-card-revision (assoc card :name "something else") false false)
+    (create-card-revision card false true)
+    (get-revisions :card id)))
+
+;; dashboard with single revision
+(expect-let [{:keys [id] :as dash} (create-test-dashboard)]
+  [{:is_reversion false, :is_creation true, :user @rasta-revision-info, :description "First revision."}]
+  (do
+    (create-dashboard-revision dash true false)
+    (get-revisions :dashboard id)))
+
+;; dashboard with card add then delete
+(expect-let [{:keys [id] :as dash} (create-test-dashboard)
+             card                  (create-test-card)]
+  [{:is_reversion false, :is_creation false, :user @rasta-revision-info, :description "removed a card."}
+   {:is_reversion false, :is_creation false, :user @rasta-revision-info, :description "added a card."}
+   {:is_reversion false, :is_creation true, :user @rasta-revision-info, :description "First revision."}]
+  (do
+    (create-dashboard-revision dash true false)
+    (let [dashcard (db/ins DashboardCard :dashboard_id id :card_id (:id card))]
+      (create-dashboard-revision dash false false)
+      (db/del DashboardCard :id (:id dashcard)))
+    (create-dashboard-revision dash false false)
+    (get-revisions :dashboard id)))
+
+
+;;; # POST /revision/revert
+
+(expect-let [{:keys [id] :as dash} (create-test-dashboard)
+             card                  (create-test-card)]
+  [{:is_reversion true,  :is_creation false, :user @rasta-revision-info, :description "reverted to an earlier revision and added a card."}
+   {:is_reversion false, :is_creation false, :user @rasta-revision-info, :description "removed a card."}
+   {:is_reversion false, :is_creation false, :user @rasta-revision-info, :description "added a card."}
+   {:is_reversion false, :is_creation true, :user @rasta-revision-info, :description "First revision."}]
+  (do
+    (create-dashboard-revision dash true false)
+    (let [dashcard (db/ins DashboardCard :dashboard_id id :card_id (:id card))]
+      (create-dashboard-revision dash false false)
+      (db/del DashboardCard :id (:id dashcard)))
+    (create-dashboard-revision dash false false)
+    (let [[_ {previous-revision-id :id}] (revisions Dashboard id)]
+      ;; Revert to the previous revision
+      ((user->client :rasta) :post 200 "revision/revert", {:entity :dashboard, :id id, :revision_id previous-revision-id}))
+    (get-revisions :dashboard id)))


[TH] I missed the renaming of these, but the build compile did not

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/ballot-notification-works.git/'


Add stub for gigasecond exercise

Add stub for gigasecond exercise

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

--- exercises/gigasecond/src/gigasecond.clj
+++ exercises/gigasecond/src/gigasecond.clj
@@ -0,0 +1,5 @@
+(ns gigasecond)
+
+(defn from []  ;; <- arglist goes here
+    ;; your code goes here
+)


add o- n m

add o- n m

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


Added get-index-set to transmit-lib.

Added get-index-set to transmit-lib.

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

--- src/cmr/transmit/index_set.clj
+++ src/cmr/transmit/index_set.clj
@@ -0,0 +1,35 @@
+(ns cmr.transmit.index-set
+  "Provide functions to invoke index set app"
+  (:require [clj-http.client :as client]
+            [cmr.common.services.errors :as errors]
+            [cheshire.core :as cheshire]
+            [cmr.system-trace.core :refer [deftracefn]]))
+
+;; Defines the host and port of index set app
+(def endpoint
+  {:host "localhost"
+   :port "3005"})
+
+(def index-set-root-url
+  (format "http://%s:%s" (:host endpoint) (:port endpoint)))
+
+;; url applicable to create, get and delete index-sets
+(def index-set-url
+  (format "%s/%s" index-set-root-url "index-sets"))
+
+(defn get-index-set
+  "Submit a request to index-set app to fetch an index-set assoc with an id"
+  [id]
+  (let [response (client/request
+                   {:method :get
+                    :url (format "%s/%s" index-set-url (str id))
+                    :accept :json
+                    :throw-exceptions false})
+        status (:status response)
+        body (cheshire/decode (:body response) true)]
+    (case status
+      404 nil
+      200 body
+      (errors/internal-error! (format "Unexpected error fetching index-set with id: %s,
+                                      Index set app reported status: %s, error: %s"
+                                      id status (pr-str (flatten (:errors body))))))))


Create dsdd.boot

Create dsdd.boot

error: pathspec 'font-awesome/css/fonts/dsdd.boot' did not match any file(s) known to git

--- font-awesome/css/fonts/dsdd.boot
+++ font-awesome/css/fonts/dsdd.boot
@@ -0,0 +1 @@
+


Remove sleeps from exclusive consumer test

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


fix the channel construction

fix the channel construction

--- labs/architecture-examples/om/src/todomvc/app.cljs
+++ labs/architecture-examples/om/src/todomvc/app.cljs
@@ -82,7 +82,7 @@
     (into [] (remove :completed (:todos app)))))

 (defn todo-app []
-  (let [[toggle destroy edit save clear cancel :as cs] (take 6 (repeat chan))
+  (let [[toggle destroy edit save clear cancel :as cs] (take 6 (repeatedly chan))
         chans (zipmap [:toggle :destroy :edit :save :clear :cancel] cs)]
     (om/root
       app-state


Add snippets for Typography - Attributes category. Issue #87.

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

--- test/snippets/typography/attributes.clj
+++ test/snippets/typography/attributes.clj
@@ -0,0 +1,58 @@
+(ns snippets.typography.attributes
+  (:require [quil.snippet :refer [defsnippet]]
+            [quil.core :refer :all]))
+
+(defsnippet text-align-s {}
+  (fill 0)
+  (stroke 255 0 0)
+  (stroke-weight 5)
+  (let [h-align [:left :center :right]
+        v-align [:top :bottom :center :baseline]]
+
+    (doseq [ind (range (count h-align))
+            :let [x 50
+                  y (+ 20 (* ind 50))]]
+      (text-align (h-align ind))
+      (text (name (h-align ind)) x y)
+      (point x y))
+
+    (doseq [ind-h (range (count h-align))
+            ind-v (range (count v-align))
+            :let [x (+ 70 (* ind-v 100))
+                  y (+ 250 (* ind-h 50))
+                  h-al (h-align ind-h)
+                  v-al (v-align ind-v)
+                  txt (str (name h-al) "+" (name v-al))]]
+      (text-align h-al v-al)
+      (text txt x y)
+      (point x y))))
+
+(defsnippet text-leading-s {}
+  (fill 0)
+  (doseq [ind (range 4)
+          :let [leading (* ind 10)]]
+    (text-leading leading)
+    (text (str "text leading\n" leading) 20 (+ 20 (* ind 100)))))
+
+(defsnippet text-mode-s {:renderer :p2d}
+  (fill 0)
+  (text-mode :model)
+  (text "text-mode: model" 20 50)
+
+  (text-mode :shape)
+  (text "text-mode: shape" 20 100))
+
+(defsnippet text-size-s {}
+  (fill 0)
+  (doseq [ind (range 6)
+          :let [size (+ 10 (* ind 5))]]
+    (text-size size)
+    (text (str "Text size: " size) 20 (+ 20 (* ind 80)))))
+
+(defsnippet text-width-s {}
+  (fill 0)
+  (let [txt "Hello, world!"
+        width (text-width txt)]
+    (text txt 20 20)
+    (text (str "Width of text above is " width) 20 40)))
+


Add problem71.clj

Add problem71.clj

error: pathspec 'euler_clojure/src/euler/problem71.clj' did not match any file(s) known to git

--- euler_clojure/src/euler/problem71.clj
+++ euler_clojure/src/euler/problem71.clj
@@ -0,0 +1,31 @@
+(ns euler.problem71
+  "https://projecteuler.net/problem=71
+
+  Consider the fraction, n/d, where n and d are positive integers. If n<d and
+  HCF(n,d)=1, it is called a reduced proper fraction.
+
+  If we list the set of reduced proper fractions for d ≤ 8 in ascending order
+  of size, we get:
+
+  1/8, 1/7, 1/6, 1/5, 1/4, 2/7, 1/3, 3/8, 2/5, 3/7, 1/2, 4/7, 3/5, 5/8, 2/3, 5/7, 3/4, 4/5, 5/6, 6/7, 7/8
+
+  It can be seen that 2/5 is the fraction immediately to the left of 3/7.
+
+  By listing the set of reduced proper fractions for d ≤ 1,000,000 in ascending
+  order of size, find the numerator of the fraction immediately to the left of
+  3/7.")
+
+;; See https://en.wikipedia.org/wiki/Farey_sequence
+;; The value of the new term in between neighbours 2/5 and 3/7 is found by
+;; computing the mediant of those neighbours. We can take result to be the next
+;; left-hand neighbour of 3/7 iteratively until the denominator reaches 1e6.
+
+(defn mediant [a b]
+  (/ (+ (numerator a) (numerator b))
+     (+ (denominator a) (denominator b))))
+
+(defn problem71 []
+  (numerator
+   (last
+    (take-while #(< (denominator %) 1e6)
+                (iterate (partial mediant (/ 3 7)) (/ 2 5))))))


bum version to 3.5.0-SNAPSHOT

bum version to 3.5.0-SNAPSHOT

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


bump version to 2.8.14

bump version to 2.8.14


bump version to 2.8.27

bump version to 2.8.27


organize cljx output under target/cljx-out/

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


Upgrade ring-jetty-adapter

Upgrade ring-jetty-adapter

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


project: bump

project: bump

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


Version 165

Version 165

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/tobias/clojars-web.git/'


Bump tools.logging

Bump tools.logging

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


Use ring.middleware.cors

Use ring.middleware.cors

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


Need lein-shell for unit testing

Need lein-shell for unit testing

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/xcthulhu/clj-bitauth.git/'


Using sjcl-cljs from Sepia-Officinalis

Using sjcl-cljs from Sepia-Officinalis


Version 1.6.0-SNAPSHOT

Version 1.6.0-SNAPSHOT

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


2.0.0 Final

2.0.0 Final


updating restql core version

updating restql core version


Version 1.6.0

Version 1.6.0


update version

update version


Add datomic-toolbox dependencies and datomic repo.

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/ballot-notification-works.git/'


Version 181

Version 181


project: wire-in clojurescript app

project: wire-in clojurescript app


bump clojurescript version

bump clojurescript version

--- project.clj
+++ project.clj
@@ -9,7 +9,7 @@

   :dependencies [
     [org.clojure/clojure "1.6.0"]
-    [org.clojure/clojurescript "0.0-2411"]
+    [org.clojure/clojurescript "0.0-2511"]
     [hiccups "0.3.0"]]

   :plugins [[lein-cljsbuild "1.0.3"]]
@@ -54,4 +54,4 @@
         :target :nodejs
         :output-to "app.js"
         :optimizations :simple }}
-}})+}})


[WM] Update datomic-toolbox to 2.0.1

[WM] Update datomic-toolbox to 2.0.1

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


restored lein project

restored lein project

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

--- project.clj
+++ project.clj
@@ -0,0 +1,22 @@
+(defproject priyatam/replify "0.2.3"
+  :description "A minimalist Clojurescript repl and build system"
+  :url "https://github.com/priyatam/replify"
+  :scm {:name "git" :url "https://github.com/priyatam/replify"}
+  :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"}
+  :jvm-opts ^:replace ["-Xms512m" "-XX:+UseConcMarkSweepGC" "-XX:+CMSParallelRemarkEnabled" "-server"]
+  :dependencies [[org.clojure/clojure "1.7.0" :scope "provided"]
+                 [org.clojure/clojurescript "1.7.28" :scope "provided"]
+                 [org.clojure/tools.cli "0.3.1"]
+                 [me.raynes/fs "1.4.6"]
+                 [de.ubercode.clostache/clostache "1.4.0"]
+                 [alembic "0.3.2"]
+                 [garden "1.2.5"]]
+  :npm {:dependencies [[source-map-support "0.3.1"]]}
+  :plugins [[lein-npm "0.6.1"]
+            [lein-garden "0.2.5"]]
+  :source-paths ["src" "target/classes"]
+  :clean-targets ["target" "dist"]
+  :target-path "target"
+  :profiles {:dev {:dependencies [[org.clojure/tools.nrepl "0.2.10"]
+                                  [leiningen "2.5.0"]]}}
+  :uberjar-name "replify.jar")


Up XMX

Up XMX


Remove project.clj.

Remove project.clj.

--- project.clj
+++ project.clj
@@ -1,8 +0,0 @@
-(defproject s3-wagon-private "1.0.1"
-  :description "Deploy artifacts to private S3 URLs."
-  :dependencies [[org.clojure/clojure "1.2.1"]
-                 [org.springframework.build.aws/org.springframework.build.aws.maven
-                  "3.0.0.RELEASE"]
-                 [org.apache.maven.wagon/wagon-provider-api "1.0"]]
-  :resources-path "res"
-  :aot [s3-wagon.private])


Add odd test

Add odd test

error: pathspec 'test/search/graphs/problems/odd_test.clj' did not match any file(s) known to git

--- test/search/graphs/problems/odd_test.clj
+++ test/search/graphs/problems/odd_test.clj
@@ -0,0 +1,9 @@
+(ns search.graphs.problems.odd-test
+  (:require [clojure.test :refer :all]
+            [schema.test]
+
+            [search.graphs.problems.test-utils :refer [finishes]]))
+(use-fixtures :once schema.test/validate-schemas)
+
+(deftest ^:slow odd-test
+  (finishes ['search.graphs.problems.odd/graph 'search.graphs.algorithms.genetic/graph]))


Don't double-encode create-team POST body


Printing: Check file type based on magic bytes.

--- src/pepa/printing.clj
+++ src/pepa/printing.clj
@@ -13,6 +13,12 @@
   (:import [javax.jmdns JmDNS ServiceInfo]
            [java.net InetAddress]))

+(defn ^:private pdf? [bytes]
+  (= "%PDF" (String. bytes 0 4)))
+
+(defn ^:private ghostscript? [bytes]
+  (= "%!" (String. bytes 0 2)))
+
 (defn ^:private job-handler [lpd]
   (reify
     lpd-protocol/IPrintJobHandler
@@ -23,7 +29,14 @@
         (let [name (or (:source-filename job)
                        (:banner-name job)
                        "Printed File")
-              pdf (gs/ps->pdf (:data job))
+              pdf (let [data (:data job)]
+                    ;; Check magic bytes in the job data.
+                    (cond
+                      (pdf? data) data
+                      (ghostscript? data) (gs/ps->pdf data)
+                      true (do
+                             (log/warn lpd "Couldn't determine file type of print job. Interpreting it as Postscript")
+                             (gs/ps->pdf data))))
               file-props {:content-type "application/pdf"
                           :name name
                           :origin (str "printer/" queue)


[TH DG] declare events topic, notification queue

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


Gorilla repl work figuring out what was going on with repness

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

--- worksheets/repness_hacking.clj
+++ worksheets/repness_hacking.clj
@@ -0,0 +1,892 @@
+;; gorilla-repl.fileformat = 1
+
+;; **
+;;; # Repness Hacking
+;;;
+;;; Some time ago, I worked on some modifications to the repness code that attempt to get at least one "agree" statement before any disagrees for _every_ possible "disagree" comment. While this seemed to typically work (except in cases of moderation, where the 1st comment might be moderated out), there was a particular conversation where it did not, and where there was no moderation. This conversation has been saved to the file `badrepness.edn` (though may be moved in the future). (As a note to self, it had some issues with atoms and ndarrays not loading properly, and trying to analyze the structure was quite challenging, so we need a script for prettifying the edn).
+;;;
+;;; We also have to figure out how to get global agree/disagree consensus in, as this is something that should really happen on the math end.
+;;;
+;;; So let's get hacking.
+;; **
+
+;; @@
+(ns repness-hacking
+  (:require [gorilla-plot.core :as plot]
+            [clojure.core.matrix :refer :all]
+            [clojure.pprint :refer :all]
+            [clojure.tools.reader.edn :as edn]
+            [polismath [conversation :as conv]
+                       [clusters :as clust]
+                       [microscope :as ms]
+                       [named-matrix :as nm]
+                       [pretty-printers :as ppp]]))
+
+; Getting the data
+(def c (conv/read-vectorz-edn (slurp "badrepness.edn")))
+
+; Helper
+(defn head
+  [data & [n]]
+  (take (or n 7) data))
+
+;; @@
+;; =>
+;;; {"type":"html","content":"#'repness-hacking/head","value":"#'repness-hacking/head"}
+;; <=
+
+;; **
+;;; To start, let's pull out the repness as `cr`.
+;; **
+
+;; @@
+(def cr (:repness c))
+(pprint (map (fn [[gid rs]] [gid (take 5 rs)]) cr))
+;; @@
+;; ->
+;;; ([0
+;;;   ({:tid 20,
+;;;     :n-success 20,
+;;;     :n-trials 23,
+;;;     :p-success 21/25,
+;;;     :p-test 3.674234614174767,
+;;;     :repness 231/50,
+;;;     :repness-test 4.610256,
+;;;     :repful-for :agree}
+;;;    {:tid 0,
+;;;     :n-success 14,
+;;;     :n-trials 22,
+;;;     :p-success 5/8,
+;;;     :p-test 1.4596008983995232,
+;;;     :repness 15N,
+;;;     :repness-test 4.3339744,
+;;;     :repful-for :agree}
+;;;    {:tid 23,
+;;;     :n-success 18,
+;;;     :n-trials 20,
+;;;     :p-success 19/22,
+;;;     :p-test 3.7097041340118704,
+;;;     :repness 247/88,
+;;;     :repness-test 3.4360168,
+;;;     :repful-for :agree}
+;;;    {:tid 8,
+;;;     :n-success 21,
+;;;     :n-trials 23,
+;;;     :p-success 22/25,
+;;;     :p-test 4.08248290463863,
+;;;     :repness 46/25,
+;;;     :repness-test 3.135133,
+;;;     :repful-for :agree}
+;;;    {:tid 24,
+;;;     :n-success 9,
+;;;     :n-trials 11,
+;;;     :p-success 10/13,
+;;;     :p-test 2.3094010767585025,
+;;;     :repness 120/13,
+;;;     :repness-test 3.5605416,
+;;;     :repful-for :disagree})]
+;;;  [1
+;;;   ({:tid 22,
+;;;     :n-success 0,
+;;;     :n-trials 0,
+;;;     :p-success 1/2,
+;;;     :p-test 1.0,
+;;;     :repness 13/10,
+;;;     :repness-test 1.2146312,
+;;;     :repful-for :disagree}
+;;;    {:tid 7,
+;;;     :n-success 4,
+;;;     :n-trials 5,
+;;;     :p-success 5/7,
+;;;     :p-test 1.6329931618554516,
+;;;     :repness 205/14,
+;;;     :repness-test 4.981382,
+;;;     :repful-for :disagree}
+;;;    {:tid 15,
+;;;     :n-success 4,
+;;;     :n-trials 5,
+;;;     :p-success 5/7,
+;;;     :p-test 1.6329931618554516,
+;;;     :repness 205/28,
+;;;     :repness-test 4.2224436,
+;;;     :repful-for :disagree}
+;;;    {:tid 17,
+;;;     :n-success 4,
+;;;     :n-trials 5,
+;;;     :p-success 5/7,
+;;;     :p-test 1.6329931618554516,
+;;;     :repness 43/7,
+;;;     :repness-test 4.029963,
+;;;     :repful-for :disagree}
+;;;    {:tid 21,
+;;;     :n-success 4,
+;;;     :n-trials 5,
+;;;     :p-success 5/7,
+;;;     :p-test 1.6329931618554516,
+;;;     :repness 39/7,
+;;;     :repness-test 3.811882,
+;;;     :repful-for :disagree})]
+;;;  [2
+;;;   ({:tid 1,
+;;;     :n-success 15,
+;;;     :n-trials 15,
+;;;     :p-success 16/17,
+;;;     :p-test 4.0,
+;;;     :repness 96/17,
+;;;     :repness-test 5.3267612,
+;;;     :repful-for :agree}
+;;;    {:tid 24,
+;;;     :n-success 9,
+;;;     :n-trials 10,
+;;;     :p-success 5/6,
+;;;     :p-test 2.7136021011998723,
+;;;     :repness 65/18,
+;;;     :repness-test 3.1851106,
+;;;     :repful-for :agree}
+;;;    {:tid 22,
+;;;     :n-success 14,
+;;;     :n-trials 16,
+;;;     :p-success 5/6,
+;;;     :p-test 3.1529631254723283,
+;;;     :repness 115/42,
+;;;     :repness-test 3.5232475,
+;;;     :repful-for :agree}
+;;;    {:tid 0,
+;;;     :n-success 16,
+;;;     :n-trials 17,
+;;;     :p-success 17/19,
+;;;     :p-test 3.771236166328253,
+;;;     :repness 493/114,
+;;;     :repness-test 4.833744,
+;;;     :repful-for :disagree}
+;;;    {:tid 20,
+;;;     :n-success 11,
+;;;     :n-trials 15,
+;;;     :p-success 12/17,
+;;;     :p-test 2.0,
+;;;     :repness 72/17,
+;;;     :repness-test 3.8254151,
+;;;     :repful-for :disagree})])
+;;;
+;; <-
+;; =>
+;;; {"type":"html","content":"nil","value":"nil"}
+;; <=
+
+;; **
+;;; Ahah; I may not have noticed this before. The first one it's pulling for group 1 (comment tid=22) is a zero vote count comment. It's a little perplexing that it would have made it in through the filters set up for the "greedy agree", so let's take a closer look at the pre-compiled votes statistics.
+;; **
+
+;; @@
+(def rep-stats (:stats (apply clust/conv-repness (map c [:rating-mat :group-clusters :base-clusters]))))
+(ppp/wide-pp (take 3 rep-stats))
+;; @@
+;; ->
+;;; ([{:pat 1.4596008983995232, :pd 5/24, :pa 5/8, :na 14, :pdt -2.7106873827419715, :nd 4, :ns 22, :rdt -3.833490860027325, :rat 4.333974311568233, :rd 5/18, :ra 15N}
+;;;   {:pat -1.6329931618554516, :pd 2/7, :pa 1/7, :na 0, :pdt -0.816496580927726, :nd 1, :ns 5, :rdt -0.8755950357709134, :rat -0.9991315673568163, :rd 82/147, :ra 41/105}
+;;;   {:pat -3.771236166328253, :pd 17/19, :pa 1/19, :na 0, :pdt 3.771236166328253, :nd 16, :ns 17, :rdt 4.83374382493315, :rat -3.3370168140018737, :rd 493/114, :ra 29/285}]
+;;;  [{:pat -2.8577380332470415, :pd 3/5, :pa 1/5, :na 4, :pdt 1.224744871391589, :nd 14, :ns 23, :rdt 3.6567795003972843, :rat -3.7134700340621514, :rd 33/5, :ra 11/40}
+;;;   {:pat -1.6329931618554516, :pd 2/7, :pa 1/7, :na 0, :pdt -0.816496580927726, :nd 1, :ns 5, :rdt -0.24119933043158806, :rat -1.5822244044174223, :rd 16/21, :ra 2/7}
+;;;   {:pat 4.0, :pd 1/17, :pa 16/17, :na 15, :pdt -3.5, :nd 0, :ns 15, :rdt -3.240183808254108, :rat 5.326761139421704, :rd 15/136, :ra 96/17}]
+;;;  [{:pat -2.449489742783178, :pd 3/5, :pa 6/25, :na 5, :pdt 1.224744871391589, :nd 14, :ns 23, :rdt -0.8390721778042801, :rat 1.4867727839306146, :rd 72/85, :ra 72/25}
+;;;   {:pat -0.816496580927726, :pd 1/7, :pa 2/7, :na 1, :pdt -1.6329931618554516, :nd 0, :ns 5, :rdt -2.8928689424440197, :rat 1.138313561928329, :rd 6/31, :ra 2N}
+;;;   {:pat -3.771236166328253, :pd 17/19, :pa 1/19, :na 0, :pdt 3.771236166328253, :nd 16, :ns 17, :rdt 3.0542031576959974, :rat -1.6478137206500227, :rd 34/19, :ra 30/133}])
+;;;
+;; <-
+;; =>
+;;; {"type":"html","content":"nil","value":"nil"}
+;; <=
+
+;; **
+;;; Since we're specifically interested in what's going on for grp 1, let's just pull that one out from the rest:
+;; **
+
+;; @@
+(def gp1-rep-stats (mapv #(get % 1) rep-stats))
+(ppp/wide-pp (take 3 gp1-rep-stats))
+;; @@
+;; ->
+;;; ({:pat -1.6329931618554516, :pd 2/7, :pa 1/7, :na 0, :pdt -0.816496580927726, :nd 1, :ns 5, :rdt -0.8755950357709134, :rat -0.9991315673568163, :rd 82/147, :ra 41/105}
+;;;  {:pat -1.6329931618554516, :pd 2/7, :pa 1/7, :na 0, :pdt -0.816496580927726, :nd 1, :ns 5, :rdt -0.24119933043158806, :rat -1.5822244044174223, :rd 16/21, :ra 2/7}
+;;;  {:pat -0.816496580927726, :pd 1/7, :pa 2/7, :na 1, :pdt -1.6329931618554516, :nd 0, :ns 5, :rdt -2.8928689424440197, :rat 1.138313561928329, :rd 6/31, :ra 2N})
+;;;
+;; <-
+;; =>
+;;; {"type":"html","content":"nil","value":"nil"}
+;; <=
+
+;; **
+;;; And take a look at the raw stats for 22.
+;; **
+
+;; @@
+(gp1-rep-stats 22)
+;; @@
+;; =>
+;;; {"type":"list-like","open":"#polismath.clusters.graph-record20902{","close":"}","separator":" ","items":[{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":pat","value":":pat"},{"type":"html","content":"1.0","value":"1.0"}],"value":"[:pat 1.0]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":pd","value":":pd"},{"type":"html","content":"1/2","value":"1/2"}],"value":"[:pd 1/2]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":pa","value":":pa"},{"type":"html","content":"1/2","value":"1/2"}],"value":"[:pa 1/2]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":na","value":":na"},{"type":"html","content":"0","value":"0"}],"value":"[:na 0]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":pdt","value":":pdt"},{"type":"html","content":"1.0","value":"1.0"}],"value":"[:pdt 1.0]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":nd","value":":nd"},{"type":"html","content":"0","value":"0"}],"value":"[:nd 0]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":ns","value":":ns"},{"type":"html","content":"0","value":"0"}],"value":"[:ns 0]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":rdt","value":":rdt"},{"type":"html","content":"1.2146311980878892","value":"1.2146311980878892"}],"value":"[:rdt 1.2146311980878892]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":rat","value":":rat"},{"type":"html","content":"0.8905403982733955","value":"0.8905403982733955"}],"value":"[:rat 0.8905403982733955]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":rd","value":":rd"},{"type":"html","content":"13/10","value":"13/10"}],"value":"[:rd 13/10]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":ra","value":":ra"},{"type":"html","content":"13/14","value":"13/14"}],"value":"[:ra 13/14]"}],"value":"#polismath.clusters.graph-record20902{:pat 1.0, :pd 1/2, :pa 1/2, :na 0, :pdt 1.0, :nd 0, :ns 0, :rdt 1.2146311980878892, :rat 0.8905403982733955, :rd 13/10, :ra 13/14}"}
+;; <=
+
+;; **
+;;; Let's do some plotting to explore the stats
+;; **
+
+;; @@
+(defn stat-plot
+  [attr & opts]
+  (apply plot/list-plot (map attr gp1-rep-stats) opts))
+;; @@
+;; =>
+;;; {"type":"html","content":"#'repness-hacking/stat-plot","value":"#'repness-hacking/stat-plot"}
+;; <=
+
+;; @@
+(defn stat-plots
+  [attrs colors]
+  (apply plot/compose (map #(stat-plot %1 :color %2 :width 500) attrs colors)))
+
+(stat-plots [:pat :pdt] ["steelblue" "red"])
+;; @@
+;; =>
+;;; {"type":"vega","content":{"width":400,"height":247.2187957763672,"padding":{"bottom":20,"top":10,"right":10,"left":50},"scales":[{"name":"x","type":"linear","range":"width","zero":false,"domain":{"data":"5b0991e0-49d8-419a-a679-5b2ede214560","field":"data.x"}},{"name":"y","type":"linear","range":"height","nice":true,"zero":false,"domain":{"data":"5b0991e0-49d8-419a-a679-5b2ede214560","field":"data.y"}}],"axes":[{"type":"x","scale":"x"},{"type":"y","scale":"y"}],"data":[{"name":"5b0991e0-49d8-419a-a679-5b2ede214560","values":[{"x":0,"y":-1.6329931618554516},{"x":1,"y":-1.6329931618554516},{"x":2,"y":-0.816496580927726},{"x":3,"y":0.0},{"x":4,"y":0.0},{"x":5,"y":-0.816496580927726},{"x":6,"y":0.0},{"x":7,"y":-0.816496580927726},{"x":8,"y":0.0},{"x":9,"y":0.0},{"x":10,"y":0.0},{"x":11,"y":0.0},{"x":12,"y":-0.816496580927726},{"x":13,"y":0.0},{"x":14,"y":-0.816496580927726},{"x":15,"y":-0.816496580927726},{"x":16,"y":0.0},{"x":17,"y":-0.816496580927726},{"x":18,"y":-0.816496580927726},{"x":19,"y":0.0},{"x":20,"y":0.0},{"x":21,"y":-0.816496580927726},{"x":22,"y":1.0},{"x":23,"y":1.0},{"x":24,"y":1.0},{"x":25,"y":1.0},{"x":26,"y":1.0},{"x":27,"y":1.0},{"x":28,"y":1.0},{"x":29,"y":1.0}]},{"name":"ea86c238-4924-4883-a65b-dc563f3f792d","values":[{"x":0,"y":-0.816496580927726},{"x":1,"y":-0.816496580927726},{"x":2,"y":-1.6329931618554516},{"x":3,"y":0.8164965809277264},{"x":4,"y":0.8164965809277264},{"x":5,"y":1.6329931618554516},{"x":6,"y":0.8164965809277264},{"x":7,"y":1.6329931618554516},{"x":8,"y":0.8164965809277264},{"x":9,"y":0.8164965809277264},{"x":10,"y":0.8164965809277264},{"x":11,"y":0.8164965809277264},{"x":12,"y":1.6329931618554516},{"x":13,"y":0.8164965809277264},{"x":14,"y":1.6329931618554516},{"x":15,"y":1.6329931618554516},{"x":16,"y":0.8164965809277264},{"x":17,"y":1.6329931618554516},{"x":18,"y":1.6329931618554516},{"x":19,"y":0.8164965809277264},{"x":20,"y":0.8164965809277264},{"x":21,"y":1.6329931618554516},{"x":22,"y":1.0},{"x":23,"y":1.0},{"x":24,"y":1.0},{"x":25,"y":1.0},{"x":26,"y":1.0},{"x":27,"y":1.0},{"x":28,"y":1.0},{"x":29,"y":1.0}]}],"marks":[{"type":"symbol","from":{"data":"5b0991e0-49d8-419a-a679-5b2ede214560"},"properties":{"enter":{"x":{"field":"data.x","scale":"x"},"y":{"field":"data.y","scale":"y"},"fill":{"value":"steelblue"},"fillOpacity":{"value":1}},"update":{"shape":"circle","size":{"value":70},"stroke":{"value":"transparent"}},"hover":{"size":{"value":210},"stroke":{"value":"white"}}}},{"type":"symbol","from":{"data":"ea86c238-4924-4883-a65b-dc563f3f792d"},"properties":{"enter":{"x":{"field":"data.x","scale":"x"},"y":{"field":"data.y","scale":"y"},"fill":{"value":"red"},"fillOpacity":{"value":1}},"update":{"shape":"circle","size":{"value":70},"stroke":{"value":"transparent"}},"hover":{"size":{"value":210},"stroke":{"value":"white"}}}}]},"value":"#gorilla_repl.vega.VegaView{:content {:width 400, :height 247.2188, :padding {:bottom 20, :top 10, :right 10, :left 50}, :scales [{:name \"x\", :type \"linear\", :range \"width\", :zero false, :domain {:data \"5b0991e0-49d8-419a-a679-5b2ede214560\", :field \"data.x\"}} {:name \"y\", :type \"linear\", :range \"height\", :nice true, :zero false, :domain {:data \"5b0991e0-49d8-419a-a679-5b2ede214560\", :field \"data.y\"}}], :axes [{:type \"x\", :scale \"x\"} {:type \"y\", :scale \"y\"}], :data ({:name \"5b0991e0-49d8-419a-a679-5b2ede214560\", :values ({:x 0, :y -1.6329931618554516} {:x 1, :y -1.6329931618554516} {:x 2, :y -0.816496580927726} {:x 3, :y 0.0} {:x 4, :y 0.0} {:x 5, :y -0.816496580927726} {:x 6, :y 0.0} {:x 7, :y -0.816496580927726} {:x 8, :y 0.0} {:x 9, :y 0.0} {:x 10, :y 0.0} {:x 11, :y 0.0} {:x 12, :y -0.816496580927726} {:x 13, :y 0.0} {:x 14, :y -0.816496580927726} {:x 15, :y -0.816496580927726} {:x 16, :y 0.0} {:x 17, :y -0.816496580927726} {:x 18, :y -0.816496580927726} {:x 19, :y 0.0} {:x 20, :y 0.0} {:x 21, :y -0.816496580927726} {:x 22, :y 1.0} {:x 23, :y 1.0} {:x 24, :y 1.0} {:x 25, :y 1.0} {:x 26, :y 1.0} {:x 27, :y 1.0} {:x 28, :y 1.0} {:x 29, :y 1.0})} {:name \"ea86c238-4924-4883-a65b-dc563f3f792d\", :values ({:x 0, :y -0.816496580927726} {:x 1, :y -0.816496580927726} {:x 2, :y -1.6329931618554516} {:x 3, :y 0.8164965809277264} {:x 4, :y 0.8164965809277264} {:x 5, :y 1.6329931618554516} {:x 6, :y 0.8164965809277264} {:x 7, :y 1.6329931618554516} {:x 8, :y 0.8164965809277264} {:x 9, :y 0.8164965809277264} {:x 10, :y 0.8164965809277264} {:x 11, :y 0.8164965809277264} {:x 12, :y 1.6329931618554516} {:x 13, :y 0.8164965809277264} {:x 14, :y 1.6329931618554516} {:x 15, :y 1.6329931618554516} {:x 16, :y 0.8164965809277264} {:x 17, :y 1.6329931618554516} {:x 18, :y 1.6329931618554516} {:x 19, :y 0.8164965809277264} {:x 20, :y 0.8164965809277264} {:x 21, :y 1.6329931618554516} {:x 22, :y 1.0} {:x 23, :y 1.0} {:x 24, :y 1.0} {:x 25, :y 1.0} {:x 26, :y 1.0} {:x 27, :y 1.0} {:x 28, :y 1.0} {:x 29, :y 1.0})}), :marks ({:type \"symbol\", :from {:data \"5b0991e0-49d8-419a-a679-5b2ede214560\"}, :properties {:enter {:x {:field \"data.x\", :scale \"x\"}, :y {:field \"data.y\", :scale \"y\"}, :fill {:value \"steelblue\"}, :fillOpacity {:value 1}}, :update {:shape \"circle\", :size {:value 70}, :stroke {:value \"transparent\"}}, :hover {:size {:value 210}, :stroke {:value \"white\"}}}} {:type \"symbol\", :from {:data \"ea86c238-4924-4883-a65b-dc563f3f792d\"}, :properties {:enter {:x {:field \"data.x\", :scale \"x\"}, :y {:field \"data.y\", :scale \"y\"}, :fill {:value \"red\"}, :fillOpacity {:value 1}}, :update {:shape \"circle\", :size {:value 70}, :stroke {:value \"transparent\"}}, :hover {:size {:value 210}, :stroke {:value \"white\"}}}})}}"}
+;; <=
+
+;; **
+;;; Interesting. Maybe we don't actually have a problem here. It looks like they're actually _aren't_ any comments this group agrees with. It might have _appeared_ that way because of passes, which visually you wouldn't see as being any different than a "no-see-em". But above we can see that all of the agree probability tests are at 0 or below. But I'll have to look back to see how we're actually computing these. It's possible that by looking at `:pa` instead, we could get something better, but this leaves us exposed to problems of low confidence.
+;;;
+;;; Also, something else worth looking at here is the discrete nature of these test scores. Let's start with a quick check of that:
+;; **
+
+;; @@
+(set (map :pat gp1-rep-stats))
+;; @@
+;; =>
+;;; {"type":"list-like","open":"#{","close":"}","separator":" ","items":[{"type":"html","content":"0.0","value":"0.0"},{"type":"html","content":"1.0","value":"1.0"},{"type":"html","content":"-1.6329931618554516","value":"-1.6329931618554516"},{"type":"html","content":"-0.816496580927726","value":"-0.816496580927726"}],"value":"#{0.0 1.0 -1.6329931618554516 -0.816496580927726}"}
+;; <=
+
+;; **
+;;; Yup; that seems to check out. We must have low counts for this group; let's see:
+;; **
+
+;; @@
+(:group-clusters c)
+;; @@
+;; =>
+;;; {"type":"list-like","open":"[","close":"]","separator":" ","items":[{"type":"list-like","open":"{","close":"}","separator":", ","items":[{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":id","value":":id"},{"type":"html","content":"0","value":"0"}],"value":"[:id 0]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":members","value":":members"},{"type":"list-like","open":"[","close":"]","separator":" ","items":[{"type":"html","content":"0","value":"0"},{"type":"html","content":"7","value":"7"},{"type":"html","content":"10","value":"10"},{"type":"html","content":"13","value":"13"},{"type":"html","content":"14","value":"14"},{"type":"html","content":"15","value":"15"},{"type":"html","content":"16","value":"16"},{"type":"html","content":"17","value":"17"},{"type":"html","content":"18","value":"18"},{"type":"html","content":"20","value":"20"},{"type":"html","content":"21","value":"21"},{"type":"html","content":"22","value":"22"},{"type":"html","content":"24","value":"24"},{"type":"html","content":"25","value":"25"},{"type":"html","content":"26","value":"26"},{"type":"html","content":"31","value":"31"},{"type":"html","content":"32","value":"32"},{"type":"html","content":"33","value":"33"},{"type":"html","content":"36","value":"36"},{"type":"html","content":"39","value":"39"},{"type":"html","content":"40","value":"40"},{"type":"html","content":"41","value":"41"},{"type":"html","content":"44","value":"44"},{"type":"html","content":"45","value":"45"}],"value":"[0 7 10 13 14 15 16 17 18 20 21 22 24 25 26 31 32 33 36 39 40 41 44 45]"}],"value":"[:members [0 7 10 13 14 15 16 17 18 20 21 22 24 25 26 31 32 33 36 39 40 41 44 45]]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":center","value":":center"},{"type":"html","content":"#mikera.vectorz.Vector [-1.536237663142975 1.0653679098883733]","value":"#mikera.vectorz.Vector [-1.536237663142975 1.0653679098883733]"}],"value":"[:center #mikera.vectorz.Vector [-1.536237663142975 1.0653679098883733]]"}],"value":"{:id 0, :members [0 7 10 13 14 15 16 17 18 20 21 22 24 25 26 31 32 33 36 39 40 41 44 45], :center #mikera.vectorz.Vector [-1.536237663142975 1.0653679098883733]}"},{"type":"list-like","open":"{","close":"}","separator":", ","items":[{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":id","value":":id"},{"type":"html","content":"1","value":"1"}],"value":"[:id 1]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":members","value":":members"},{"type":"list-like","open":"[","close":"]","separator":" ","items":[{"type":"html","content":"1","value":"1"},{"type":"html","content":"2","value":"2"},{"type":"html","content":"3","value":"3"},{"type":"html","content":"4","value":"4"},{"type":"html","content":"6","value":"6"}],"value":"[1 2 3 4 6]"}],"value":"[:members [1 2 3 4 6]]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":center","value":":center"},{"type":"html","content":"#mikera.vectorz.Vector [3.912324626290324 0.792034445237892]","value":"#mikera.vectorz.Vector [3.912324626290324 0.792034445237892]"}],"value":"[:center #mikera.vectorz.Vector [3.912324626290324 0.792034445237892]]"}],"value":"{:id 1, :members [1 2 3 4 6], :center #mikera.vectorz.Vector [3.912324626290324 0.792034445237892]}"},{"type":"list-like","open":"{","close":"}","separator":", ","items":[{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":id","value":":id"},{"type":"html","content":"2","value":"2"}],"value":"[:id 2]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":members","value":":members"},{"type":"list-like","open":"[","close":"]","separator":" ","items":[{"type":"html","content":"5","value":"5"},{"type":"html","content":"8","value":"8"},{"type":"html","content":"9","value":"9"},{"type":"html","content":"11","value":"11"},{"type":"html","content":"12","value":"12"},{"type":"html","content":"19","value":"19"},{"type":"html","content":"23","value":"23"},{"type":"html","content":"27","value":"27"},{"type":"html","content":"28","value":"28"},{"type":"html","content":"29","value":"29"},{"type":"html","content":"30","value":"30"},{"type":"html","content":"34","value":"34"},{"type":"html","content":"35","value":"35"},{"type":"html","content":"37","value":"37"},{"type":"html","content":"38","value":"38"},{"type":"html","content":"42","value":"42"},{"type":"html","content":"43","value":"43"}],"value":"[5 8 9 11 12 19 23 27 28 29 30 34 35 37 38 42 43]"}],"value":"[:members [5 8 9 11 12 19 23 27 28 29 30 34 35 37 38 42 43]]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":center","value":":center"},{"type":"html","content":"#mikera.vectorz.Vector [-0.2700439057172588 -2.1974518306844097]","value":"#mikera.vectorz.Vector [-0.2700439057172588 -2.1974518306844097]"}],"value":"[:center #mikera.vectorz.Vector [-0.2700439057172588 -2.1974518306844097]]"}],"value":"{:id 2, :members [5 8 9 11 12 19 23 27 28 29 30 34 35 37 38 42 43], :center #mikera.vectorz.Vector [-0.2700439057172588 -2.1974518306844097]}"}],"value":"[{:id 0, :members [0 7 10 13 14 15 16 17 18 20 21 22 24 25 26 31 32 33 36 39 40 41 44 45], :center #mikera.vectorz.Vector [-1.536237663142975 1.0653679098883733]} {:id 1, :members [1 2 3 4 6], :center #mikera.vectorz.Vector [3.912324626290324 0.792034445237892]} {:id 2, :members [5 8 9 11 12 19 23 27 28 29 30 34 35 37 38 42 43], :center #mikera.vectorz.Vector [-0.2700439057172588 -2.1974518306844097]}]"}
+;; <=
+
+;; **
+;;; Sure enough; this group is only 5 ptpts. So there isn't much room for the stats to look to pretty. I'm guessing they didn't have much information about the votes as well.
+;;;
+;;; In any case, let's move on to looking at how things sort by agree prob.
+;; **
+
+;; @@
+(stat-plots [:rd :ra] ["red" "steelblue"])
+;; @@
+;; =>
+;;; {"type":"vega","content":{"width":400,"height":247.2187957763672,"padding":{"bottom":20,"top":10,"right":10,"left":50},"scales":[{"name":"x","type":"linear","range":"width","zero":false,"domain":{"data":"b34a9f24-0b11-4d95-b2f4-c674f6b51bc8","field":"data.x"}},{"name":"y","type":"linear","range":"height","nice":true,"zero":false,"domain":{"data":"b34a9f24-0b11-4d95-b2f4-c674f6b51bc8","field":"data.y"}}],"axes":[{"type":"x","scale":"x"},{"type":"y","scale":"y"}],"data":[{"name":"b34a9f24-0b11-4d95-b2f4-c674f6b51bc8","values":[{"x":0,"y":0.5578231292517007},{"x":1,"y":0.7619047619047619},{"x":2,"y":0.1935483870967742},{"x":3,"y":7.80952380952381},{"x":4,"y":4.685714285714286},{"x":5,"y":2.928571428571429},{"x":6,"y":5.571428571428571},{"x":7,"y":14.64285714285714},{"x":8,"y":3.346938775510204},{"x":9,"y":11.71428571428571},{"x":10,"y":4},{"x":11,"y":5.714285714285714},{"x":12,"y":4.880952380952381},{"x":13,"y":2.928571428571429},{"x":14,"y":3.75},{"x":15,"y":7.321428571428571},{"x":16,"y":1.904761904761905},{"x":17,"y":6.142857142857143},{"x":18,"y":2.662337662337662},{"x":19,"y":1.904761904761905},{"x":20,"y":1.758241758241758},{"x":21,"y":5.571428571428571},{"x":22,"y":1.3},{"x":23,"y":1.65},{"x":24,"y":1.15},{"x":25,"y":1},{"x":26,"y":1.1875},{"x":27,"y":1.166666666666667},{"x":28,"y":2.333333333333333},{"x":29,"y":1.75}]},{"name":"1cfd30ec-e6e7-40d2-9e94-976737bfc4a1","values":[{"x":0,"y":0.3904761904761905},{"x":1,"y":0.2857142857142857},{"x":2,"y":2},{"x":3,"y":0.4749034749034749},{"x":4,"y":0.5324675324675325},{"x":5,"y":0.4685714285714286},{"x":6,"y":0.5223214285714286},{"x":7,"y":0.3003663003663004},{"x":8,"y":0.5857142857142857},{"x":9,"y":0.5020408163265306},{"x":10,"y":0.5454545454545455},{"x":11,"y":0.5357142857142857},{"x":12,"y":0.354978354978355},{"x":13,"y":0.5857142857142857},{"x":14,"y":0.4137931034482759},{"x":15,"y":0.3445378151260504},{"x":16,"y":0.6349206349206349},{"x":17,"y":0.3722943722943723},{"x":18,"y":0.5093167701863354},{"x":19,"y":0.9022556390977444},{"x":20,"y":0.7792207792207791},{"x":21,"y":0.3482142857142857},{"x":22,"y":0.9285714285714286},{"x":23,"y":0.75},{"x":24,"y":0.9583333333333333},{"x":25,"y":1.111111111111111},{"x":26,"y":1.055555555555556},{"x":27,"y":0.9545454545454545},{"x":28,"y":0.6363636363636364},{"x":29,"y":1.166666666666667}]}],"marks":[{"type":"symbol","from":{"data":"b34a9f24-0b11-4d95-b2f4-c674f6b51bc8"},"properties":{"enter":{"x":{"field":"data.x","scale":"x"},"y":{"field":"data.y","scale":"y"},"fill":{"value":"red"},"fillOpacity":{"value":1}},"update":{"shape":"circle","size":{"value":70},"stroke":{"value":"transparent"}},"hover":{"size":{"value":210},"stroke":{"value":"white"}}}},{"type":"symbol","from":{"data":"1cfd30ec-e6e7-40d2-9e94-976737bfc4a1"},"properties":{"enter":{"x":{"field":"data.x","scale":"x"},"y":{"field":"data.y","scale":"y"},"fill":{"value":"steelblue"},"fillOpacity":{"value":1}},"update":{"shape":"circle","size":{"value":70},"stroke":{"value":"transparent"}},"hover":{"size":{"value":210},"stroke":{"value":"white"}}}}]},"value":"#gorilla_repl.vega.VegaView{:content {:width 400, :height 247.2188, :padding {:bottom 20, :top 10, :right 10, :left 50}, :scales [{:name \"x\", :type \"linear\", :range \"width\", :zero false, :domain {:data \"b34a9f24-0b11-4d95-b2f4-c674f6b51bc8\", :field \"data.x\"}} {:name \"y\", :type \"linear\", :range \"height\", :nice true, :zero false, :domain {:data \"b34a9f24-0b11-4d95-b2f4-c674f6b51bc8\", :field \"data.y\"}}], :axes [{:type \"x\", :scale \"x\"} {:type \"y\", :scale \"y\"}], :data ({:name \"b34a9f24-0b11-4d95-b2f4-c674f6b51bc8\", :values ({:x 0, :y 82/147} {:x 1, :y 16/21} {:x 2, :y 6/31} {:x 3, :y 164/21} {:x 4, :y 164/35} {:x 5, :y 41/14} {:x 6, :y 39/7} {:x 7, :y 205/14} {:x 8, :y 164/49} {:x 9, :y 82/7} {:x 10, :y 4N} {:x 11, :y 40/7} {:x 12, :y 205/42} {:x 13, :y 41/14} {:x 14, :y 15/4} {:x 15, :y 205/28} {:x 16, :y 40/21} {:x 17, :y 43/7} {:x 18, :y 205/77} {:x 19, :y 40/21} {:x 20, :y 160/91} {:x 21, :y 39/7} {:x 22, :y 13/10} {:x 23, :y 33/20} {:x 24, :y 23/20} {:x 25, :y 1N} {:x 26, :y 19/16} {:x 27, :y 7/6} {:x 28, :y 7/3} {:x 29, :y 7/4})} {:name \"1cfd30ec-e6e7-40d2-9e94-976737bfc4a1\", :values ({:x 0, :y 41/105} {:x 1, :y 2/7} {:x 2, :y 2N} {:x 3, :y 123/259} {:x 4, :y 41/77} {:x 5, :y 82/175} {:x 6, :y 117/224} {:x 7, :y 82/273} {:x 8, :y 41/70} {:x 9, :y 123/245} {:x 10, :y 6/11} {:x 11, :y 15/28} {:x 12, :y 82/231} {:x 13, :y 41/70} {:x 14, :y 12/29} {:x 15, :y 41/119} {:x 16, :y 40/63} {:x 17, :y 86/231} {:x 18, :y 82/161} {:x 19, :y 120/133} {:x 20, :y 60/77} {:x 21, :y 39/112} {:x 22, :y 13/14} {:x 23, :y 3/4} {:x 24, :y 23/24} {:x 25, :y 10/9} {:x 26, :y 19/18} {:x 27, :y 21/22} {:x 28, :y 7/11} {:x 29, :y 7/6})}), :marks ({:type \"symbol\", :from {:data \"b34a9f24-0b11-4d95-b2f4-c674f6b51bc8\"}, :properties {:enter {:x {:field \"data.x\", :scale \"x\"}, :y {:field \"data.y\", :scale \"y\"}, :fill {:value \"red\"}, :fillOpacity {:value 1}}, :update {:shape \"circle\", :size {:value 70}, :stroke {:value \"transparent\"}}, :hover {:size {:value 210}, :stroke {:value \"white\"}}}} {:type \"symbol\", :from {:data \"1cfd30ec-e6e7-40d2-9e94-976737bfc4a1\"}, :properties {:enter {:x {:field \"data.x\", :scale \"x\"}, :y {:field \"data.y\", :scale \"y\"}, :fill {:value \"steelblue\"}, :fillOpacity {:value 1}}, :update {:shape \"circle\", :size {:value 70}, :stroke {:value \"transparent\"}}, :hover {:size {:value 210}, :stroke {:value \"white\"}}}})}}"}
+;; <=
+
+;; **
+;;; Yeah... everything is below 0.5, which is not encouraging. There just isn't a way around it for this one.
+;;;
+;;; What conversation is this again?
+;; **
+
+;; @@
+(:zid c)
+;; @@
+;; =>
+;;; {"type":"html","content":"12272","value":"12272"}
+;; <=
+
+;; **
+;;; And for this the zinvite is 9kzprjkfep (see [preprod](http://preprod.pol.is/9kzprjkfep) and [prod](http://pol.is/9kzprjkfep))
+;; **
+
+;; **
+;;; Looking at that, we can see that there is still a problem, even if it isn't possible to get an agree comment. The issue is that, if it isn't possible to get such a comment, we shouldn't mess with the natural sorting _at all_. In the conversation in question, the first one shown (as we discovered earlier as well), has _no_ votes, which makes it seem very awkward.
+;;;
+;;; Let's look into the code for this a bit more.
+;; **
+
+;; @@
+(stat-plots [:ns :nd :na] ["lightgrey" "red" "steelblue"])
+;; @@
+;; =>
+;;; {"type":"vega","content":{"width":400,"height":247.2187957763672,"padding":{"bottom":20,"top":10,"right":10,"left":50},"scales":[{"name":"x","type":"linear","range":"width","zero":false,"domain":{"data":"cf2f6156-4b20-4882-a1c2-333eca0af636","field":"data.x"}},{"name":"y","type":"linear","range":"height","nice":true,"zero":false,"domain":{"data":"cf2f6156-4b20-4882-a1c2-333eca0af636","field":"data.y"}}],"axes":[{"type":"x","scale":"x"},{"type":"y","scale":"y"}],"data":[{"name":"cf2f6156-4b20-4882-a1c2-333eca0af636","values":[{"x":0,"y":5},{"x":1,"y":5},{"x":2,"y":5},{"x":3,"y":5},{"x":4,"y":5},{"x":5,"y":5},{"x":6,"y":5},{"x":7,"y":5},{"x":8,"y":5},{"x":9,"y":5},{"x":10,"y":5},{"x":11,"y":5},{"x":12,"y":5},{"x":13,"y":5},{"x":14,"y":5},{"x":15,"y":5},{"x":16,"y":5},{"x":17,"y":5},{"x":18,"y":5},{"x":19,"y":5},{"x":20,"y":5},{"x":21,"y":5},{"x":22,"y":0},{"x":23,"y":0},{"x":24,"y":0},{"x":25,"y":0},{"x":26,"y":0},{"x":27,"y":0},{"x":28,"y":0},{"x":29,"y":0}]},{"name":"e787ccaf-7b16-463d-9927-8bab4dd414ea","values":[{"x":0,"y":1},{"x":1,"y":1},{"x":2,"y":0},{"x":3,"y":3},{"x":4,"y":3},{"x":5,"y":4},{"x":6,"y":3},{"x":7,"y":4},{"x":8,"y":3},{"x":9,"y":3},{"x":10,"y":3},{"x":11,"y":3},{"x":12,"y":4},{"x":13,"y":3},{"x":14,"y":4},{"x":15,"y":4},{"x":16,"y":3},{"x":17,"y":4},{"x":18,"y":4},{"x":19,"y":3},{"x":20,"y":3},{"x":21,"y":4},{"x":22,"y":0},{"x":23,"y":0},{"x":24,"y":0},{"x":25,"y":0},{"x":26,"y":0},{"x":27,"y":0},{"x":28,"y":0},{"x":29,"y":0}]},{"name":"99d96db7-e12b-4650-a48c-4a3ebc93bbe1","values":[{"x":0,"y":0},{"x":1,"y":0},{"x":2,"y":1},{"x":3,"y":2},{"x":4,"y":2},{"x":5,"y":1},{"x":6,"y":2},{"x":7,"y":1},{"x":8,"y":2},{"x":9,"y":2},{"x":10,"y":2},{"x":11,"y":2},{"x":12,"y":1},{"x":13,"y":2},{"x":14,"y":1},{"x":15,"y":1},{"x":16,"y":2},{"x":17,"y":1},{"x":18,"y":1},{"x":19,"y":2},{"x":20,"y":2},{"x":21,"y":1},{"x":22,"y":0},{"x":23,"y":0},{"x":24,"y":0},{"x":25,"y":0},{"x":26,"y":0},{"x":27,"y":0},{"x":28,"y":0},{"x":29,"y":0}]}],"marks":[{"type":"symbol","from":{"data":"cf2f6156-4b20-4882-a1c2-333eca0af636"},"properties":{"enter":{"x":{"field":"data.x","scale":"x"},"y":{"field":"data.y","scale":"y"},"fill":{"value":"lightgrey"},"fillOpacity":{"value":1}},"update":{"shape":"circle","size":{"value":70},"stroke":{"value":"transparent"}},"hover":{"size":{"value":210},"stroke":{"value":"white"}}}},{"type":"symbol","from":{"data":"e787ccaf-7b16-463d-9927-8bab4dd414ea"},"properties":{"enter":{"x":{"field":"data.x","scale":"x"},"y":{"field":"data.y","scale":"y"},"fill":{"value":"red"},"fillOpacity":{"value":1}},"update":{"shape":"circle","size":{"value":70},"stroke":{"value":"transparent"}},"hover":{"size":{"value":210},"stroke":{"value":"white"}}}},{"type":"symbol","from":{"data":"99d96db7-e12b-4650-a48c-4a3ebc93bbe1"},"properties":{"enter":{"x":{"field":"data.x","scale":"x"},"y":{"field":"data.y","scale":"y"},"fill":{"value":"steelblue"},"fillOpacity":{"value":1}},"update":{"shape":"circle","size":{"value":70},"stroke":{"value":"transparent"}},"hover":{"size":{"value":210},"stroke":{"value":"white"}}}}]},"value":"#gorilla_repl.vega.VegaView{:content {:width 400, :height 247.2188, :padding {:bottom 20, :top 10, :right 10, :left 50}, :scales [{:name \"x\", :type \"linear\", :range \"width\", :zero false, :domain {:data \"cf2f6156-4b20-4882-a1c2-333eca0af636\", :field \"data.x\"}} {:name \"y\", :type \"linear\", :range \"height\", :nice true, :zero false, :domain {:data \"cf2f6156-4b20-4882-a1c2-333eca0af636\", :field \"data.y\"}}], :axes [{:type \"x\", :scale \"x\"} {:type \"y\", :scale \"y\"}], :data ({:name \"cf2f6156-4b20-4882-a1c2-333eca0af636\", :values ({:x 0, :y 5} {:x 1, :y 5} {:x 2, :y 5} {:x 3, :y 5} {:x 4, :y 5} {:x 5, :y 5} {:x 6, :y 5} {:x 7, :y 5} {:x 8, :y 5} {:x 9, :y 5} {:x 10, :y 5} {:x 11, :y 5} {:x 12, :y 5} {:x 13, :y 5} {:x 14, :y 5} {:x 15, :y 5} {:x 16, :y 5} {:x 17, :y 5} {:x 18, :y 5} {:x 19, :y 5} {:x 20, :y 5} {:x 21, :y 5} {:x 22, :y 0} {:x 23, :y 0} {:x 24, :y 0} {:x 25, :y 0} {:x 26, :y 0} {:x 27, :y 0} {:x 28, :y 0} {:x 29, :y 0})} {:name \"e787ccaf-7b16-463d-9927-8bab4dd414ea\", :values ({:x 0, :y 1} {:x 1, :y 1} {:x 2, :y 0} {:x 3, :y 3} {:x 4, :y 3} {:x 5, :y 4} {:x 6, :y 3} {:x 7, :y 4} {:x 8, :y 3} {:x 9, :y 3} {:x 10, :y 3} {:x 11, :y 3} {:x 12, :y 4} {:x 13, :y 3} {:x 14, :y 4} {:x 15, :y 4} {:x 16, :y 3} {:x 17, :y 4} {:x 18, :y 4} {:x 19, :y 3} {:x 20, :y 3} {:x 21, :y 4} {:x 22, :y 0} {:x 23, :y 0} {:x 24, :y 0} {:x 25, :y 0} {:x 26, :y 0} {:x 27, :y 0} {:x 28, :y 0} {:x 29, :y 0})} {:name \"99d96db7-e12b-4650-a48c-4a3ebc93bbe1\", :values ({:x 0, :y 0} {:x 1, :y 0} {:x 2, :y 1} {:x 3, :y 2} {:x 4, :y 2} {:x 5, :y 1} {:x 6, :y 2} {:x 7, :y 1} {:x 8, :y 2} {:x 9, :y 2} {:x 10, :y 2} {:x 11, :y 2} {:x 12, :y 1} {:x 13, :y 2} {:x 14, :y 1} {:x 15, :y 1} {:x 16, :y 2} {:x 17, :y 1} {:x 18, :y 1} {:x 19, :y 2} {:x 20, :y 2} {:x 21, :y 1} {:x 22, :y 0} {:x 23, :y 0} {:x 24, :y 0} {:x 25, :y 0} {:x 26, :y 0} {:x 27, :y 0} {:x 28, :y 0} {:x 29, :y 0})}), :marks ({:type \"symbol\", :from {:data \"cf2f6156-4b20-4882-a1c2-333eca0af636\"}, :properties {:enter {:x {:field \"data.x\", :scale \"x\"}, :y {:field \"data.y\", :scale \"y\"}, :fill {:value \"lightgrey\"}, :fillOpacity {:value 1}}, :update {:shape \"circle\", :size {:value 70}, :stroke {:value \"transparent\"}}, :hover {:size {:value 210}, :stroke {:value \"white\"}}}} {:type \"symbol\", :from {:data \"e787ccaf-7b16-463d-9927-8bab4dd414ea\"}, :properties {:enter {:x {:field \"data.x\", :scale \"x\"}, :y {:field \"data.y\", :scale \"y\"}, :fill {:value \"red\"}, :fillOpacity {:value 1}}, :update {:shape \"circle\", :size {:value 70}, :stroke {:value \"transparent\"}}, :hover {:size {:value 210}, :stroke {:value \"white\"}}}} {:type \"symbol\", :from {:data \"99d96db7-e12b-4650-a48c-4a3ebc93bbe1\"}, :properties {:enter {:x {:field \"data.x\", :scale \"x\"}, :y {:field \"data.y\", :scale \"y\"}, :fill {:value \"steelblue\"}, :fillOpacity {:value 1}}, :update {:shape \"circle\", :size {:value 70}, :stroke {:value \"transparent\"}}, :hover {:size {:value 210}, :stroke {:value \"white\"}}}})}}"}
+;; <=
+
+;; **
+;;; I've just updated the `beats-best-agrr?` so that it doesn't accept any comments which havne't actually been voted on. It's really just a very manual thing:
+;;;
+;; **
+
+;; @@
+(comment
+    (defn- beats-best-agrr?
+      "Like beats-best-by-test?, but only considers agrees. Additionally, doesn't focus solely on repness,
+      but also on raw probability of agreement, so as to ensure that there is some representation of what
+      people in the group agree on. Also, note that this takes the current-best, instead of just current-best-z."
+      [{:keys [ra rat pa pat ns] :as comment-conv-stats} current-best]
+      (cond
+       ; Explicitly don't let something that hasn't been voted on at all come into repness
+        (= ns 0)
+            false
+        ; If we have a current-best by repness estimate, use the more robust measurement
+        (and current-best (> (:ra current-best) 1.0))
+            ; XXX - a litte messy, since we're basicaly reimplimenting the repness sort function
+            (> (* ra rat pa pat) (apply * (map current-best [:ra :rat :pa :pat])))
+        ; If we have current-best, but only by prob estimate, just shoot for something that is generally agreed upon
+        current-best
+            (> (* pa pat) (apply * (map current-best [:pa :pat])))
+        ; Otherwise, accept if either repness or probability look generally good
+        :else
+            (or (> ra 1.0)
+                (> pa 0.5)))))
+
+;; @@
+;; =>
+;;; {"type":"html","content":"nil","value":"nil"}
+;; <=
+
+;; **
+;;; Before we reload this and try it out, let's save what we already have, so that we can load it in later:
+;; **
+
+;; @@
+
+(let [filename "old/oldbadrepness-reps.edn"]
+  (when-not (.exists (clojure.java.io/as-file filename))
+    (->> rep-stats
+         (map
+           #(map (partial into {}) %))
+         (pr-str)
+         (spit filename)))
+  (->> filename
+       slurp
+       (take 80)
+       (apply str)
+       ))
+;; @@
+;; =>
+;;; {"type":"html","content":""(({:rat 4.333974311568233, :ra 15N, :ns 22, :nd 4, :na 14, :rd 5/18, :pdt -2.710"","value":"\"(({:rat 4.333974311568233, :ra 15N, :ns 22, :nd 4, :na 14, :rd 5/18, :pdt -2.710\""}
+;; <=
+
+;; **
+;;; Now let's try out the reload:
+;; **
+
+;; @@
+(require '[polismath.clusters :as clust :reload true]
+         '[polismath.conversation :as conv :reload true])
+(-> c
+    (conv/conv-update [])
+    (:repness)
+    (get 1)
+    (pprint))
+
+;; @@
+;; ->
+;;; ({:tid 3,
+;;;   :n-success 3,
+;;;   :n-trials 5,
+;;;   :p-success 4/7,
+;;;   :p-test 0.8164965809277264,
+;;;   :repness 164/21,
+;;;   :repness-test 3.762533,
+;;;   :repful-for :disagree}
+;;;  {:tid 7,
+;;;   :n-success 4,
+;;;   :n-trials 5,
+;;;   :p-success 5/7,
+;;;   :p-test 1.6329931618554516,
+;;;   :repness 205/14,
+;;;   :repness-test 4.981382,
+;;;   :repful-for :disagree}
+;;;  {:tid 15,
+;;;   :n-success 4,
+;;;   :n-trials 5,
+;;;   :p-success 5/7,
+;;;   :p-test 1.6329931618554516,
+;;;   :repness 205/28,
+;;;   :repness-test 4.2224436,
+;;;   :repful-for :disagree}
+;;;  {:tid 17,
+;;;   :n-success 4,
+;;;   :n-trials 5,
+;;;   :p-success 5/7,
+;;;   :p-test 1.6329931618554516,
+;;;   :repness 43/7,
+;;;   :repness-test 4.029963,
+;;;   :repful-for :disagree}
+;;;  {:tid 21,
+;;;   :n-success 4,
+;;;   :n-trials 5,
+;;;   :p-success 5/7,
+;;;   :p-test 1.6329931618554516,
+;;;   :repness 39/7,
+;;;   :repness-test 3.811882,
+;;;   :repful-for :disagree})
+;;;
+;; <-
+;; =>
+;;; {"type":"html","content":"nil","value":"nil"}
+;; <=
+
+;; **
+;;; Hmm... well, that partly worked. But since we're still at all `:disagree` comments, it should be the case that the "best" such comment by repness should be first, but it's not. So let's take a look and see what's going on here with the actual raw stats.
+;; **
+
+;; @@
+(def new-raw
+  (->>
+    (apply clust/conv-repness (map c [:rating-mat :group-clusters :base-clusters]))
+    :stats
+    (mapv #(get % 1))
+    ))
+
+(pprint (nth new-raw 3))
+
+;; @@
+;; ->
+;;; {:pat 0.0,
+;;;  :pd 4/7,
+;;;  :pa 3/7,
+;;;  :na 2,
+;;;  :pdt 0.8164965809277264,
+;;;  :nd 3,
+;;;  :ns 5,
+;;;  :rdt 3.762532984128439,
+;;;  :rat -2.8824902428282386,
+;;;  :rd 164/21,
+;;;  :ra 123/259}
+;;;
+;; <-
+;; =>
+;;; {"type":"html","content":"nil","value":"nil"}
+;; <=
+
+;; **
+;;; Hm... ok. Let's just tinker around with this in here manually:
+;; **
+
+;; @@
+(defn beats-best-agrr?
+  "Like beats-best-by-test?, but only considers agrees. Additionally, doesn't focus solely on repness,
+  but also on raw probability of agreement, so as to ensure that there is some representation of what
+  people in the group agree on. Also, note that this takes the current-best, instead of just current-best-z."
+  [{:keys [ra rat pa pat ns] :as comment-conv-stats} current-best]
+  (cond
+   ; Explicitly don't let something that hasn't been voted on at all come into repness
+    (= ns 0)
+        false
+    ; If we have a current-best by repness estimate, use the more robust measurement
+    (and current-best (> (:ra current-best) 1.0))
+        ; XXX - a litte messy, since we're basicaly reimplimenting the repness sort function
+        (> (* ra rat pa pat) (apply * (map current-best [:ra :rat :pa :pat])))
+    ; If we have current-best, but only by prob estimate, just shoot for something that is generally agreed upon
+    current-best
+        (> (* pa pat) (apply * (map current-best [:pa :pat])))
+    ; Otherwise, accept if either repness or probability look generally good
+    :else
+        (or (> ra 1.0)
+            (> pa 0.5))))
+
+
+(beats-best-agrr? (nth new-raw 3) nil)
+;; @@
+;; =>
+;;; {"type":"html","content":"false","value":"false"}
+;; <=
+
+;; **
+;;; OK; So that's actually coming out exactly as it should be. So perhaps there is something wrong with the standard thing that puts the "best" first.
+;;;
+;;; I just did some code rewriting/cleanup. Let's see what pops out of this now.
+;; **
+
+;; @@
+(require '[polismath.clusters :as clust :reload true]
+         '[polismath.conversation :as conv :reload true])
+
+
+(->
+  (apply clust/conv-repness (map c [:rating-mat :group-clusters :base-clusters]))
+  (clust/select-rep-comments)
+  (get 1)
+  pprint)
+
+;; @@
+;; ->
+;;; {:tid 20, :n-success 20, :n-trials 23, :p-success 21/25, :p-test 3.674234614174767, :repness 231/50, :repness-test 4.610256, :repful-for :agree}
+;;; {:tid 3, :n-success 3, :n-trials 5, :p-success 4/7, :p-test 0.8164965809277264, :repness 164/21, :repness-test 3.762533, :repful-for :disagree}
+;;; {:tid 1, :n-success 15, :n-trials 15, :p-success 16/17, :p-test 4.0, :repness 96/17, :repness-test 5.3267612, :repful-for :agree}
+;;; ({:tid 3,
+;;;   :n-success 3,
+;;;   :n-trials 5,
+;;;   :p-success 4/7,
+;;;   :p-test 0.8164965809277264,
+;;;   :repness 164/21,
+;;;   :repness-test 3.762533,
+;;;   :repful-for :disagree}
+;;;  {:tid 7,
+;;;   :n-success 4,
+;;;   :n-trials 5,
+;;;   :p-success 5/7,
+;;;   :p-test 1.6329931618554516,
+;;;   :repness 205/14,
+;;;   :repness-test 4.981382,
+;;;   :repful-for :disagree}
+;;;  {:tid 15,
+;;;   :n-success 4,
+;;;   :n-trials 5,
+;;;   :p-success 5/7,
+;;;   :p-test 1.6329931618554516,
+;;;   :repness 205/28,
+;;;   :repness-test 4.2224436,
+;;;   :repful-for :disagree}
+;;;  {:tid 17,
+;;;   :n-success 4,
+;;;   :n-trials 5,
+;;;   :p-success 5/7,
+;;;   :p-test 1.6329931618554516,
+;;;   :repness 43/7,
+;;;   :repness-test 4.029963,
+;;;   :repful-for :disagree}
+;;;  {:tid 21,
+;;;   :n-success 4,
+;;;   :n-trials 5,
+;;;   :p-success 5/7,
+;;;   :p-test 1.6329931618554516,
+;;;   :repness 39/7,
+;;;   :repness-test 3.811882,
+;;;   :repful-for :disagree})
+;;;
+;; <-
+;; =>
+;;; {"type":"html","content":"nil","value":"nil"}
+;; <=
+
+;; **
+;;; Hmm... I've been having trouble getting this to actually work; Maybe 3 _is_ supposed to be in there because everyone else agrees. But then why was 7 the next one on the old data? Let's check the `repness-metric` values.
+;;;
+;; **
+
+;; @@
+(stat-plot (fn [{:keys [rd rdt pd pdt]}] (* rd rdt pd pdt)))
+;; @@
+;; =>
+;;; {"type":"vega","content":{"axes":[{"type":"x","scale":"x"},{"type":"y","scale":"y"}],"scales":[{"name":"x","type":"linear","range":"width","zero":false,"domain":{"data":"0d0cd9a5-ab29-40a9-a088-b0398af898f5","field":"data.x"}},{"name":"y","type":"linear","range":"height","nice":true,"zero":false,"domain":{"data":"0d0cd9a5-ab29-40a9-a088-b0398af898f5","field":"data.y"}}],"marks":[{"type":"symbol","from":{"data":"0d0cd9a5-ab29-40a9-a088-b0398af898f5"},"properties":{"enter":{"x":{"field":"data.x","scale":"x"},"y":{"field":"data.y","scale":"y"},"fill":{"value":"steelblue"},"fillOpacity":{"value":1}},"update":{"shape":"circle","size":{"value":70},"stroke":{"value":"transparent"}},"hover":{"size":{"value":210},"stroke":{"value":"white"}}}}],"data":[{"name":"0d0cd9a5-ab29-40a9-a088-b0398af898f5","values":[{"x":0,"y":0.11394260241925702},{"x":1,"y":0.04287095044777109},{"x":2,"y":0.13061848482456492},{"x":3,"y":13.709486585460168},{"x":4,"y":6.818465635313264},{"x":5,"y":9.709279555017597},{"x":6,"y":8.613021460275261},{"x":7,"y":85.08088065800538},{"x":8,"y":4.1113933334112405},{"x":9,"y":22.859226122533734},{"x":10,"y":5.428336932643211},{"x":11,"y":8.970252685131706},{"x":12,"y":20.832801671769513},{"x":13,"y":3.3169076420783856},{"x":14,"y":14.27767497909334},{"x":15,"y":36.05919326367589},{"x":16,"y":1.5197622138099365},{"x":17,"y":28.87538617420961},{"x":18,"y":8.315281210554804},{"x":19,"y":1.5197622138099365},{"x":20,"y":1.2861285134331333},{"x":21,"y":24.772072155833154},{"x":22,"y":0.789510278757128},{"x":23,"y":1.1848160458484682},{"x":24,"y":0.6140655065064246},{"x":25,"y":0.46401616866730944},{"x":26,"y":0.6430176824700359},{"x":27,"y":0.62691373675597},{"x":28,"y":1.9142961929988558},{"x":29,"y":1.0913167378090662}]}],"width":400,"height":247.2187957763672,"padding":{"bottom":20,"top":10,"right":10,"left":50}},"value":"#gorilla_repl.vega.VegaView{:content {:axes [{:type \"x\", :scale \"x\"} {:type \"y\", :scale \"y\"}], :scales [{:name \"x\", :type \"linear\", :range \"width\", :zero false, :domain {:data \"0d0cd9a5-ab29-40a9-a088-b0398af898f5\", :field \"data.x\"}} {:name \"y\", :type \"linear\", :range \"height\", :nice true, :zero false, :domain {:data \"0d0cd9a5-ab29-40a9-a088-b0398af898f5\", :field \"data.y\"}}], :marks [{:type \"symbol\", :from {:data \"0d0cd9a5-ab29-40a9-a088-b0398af898f5\"}, :properties {:enter {:x {:field \"data.x\", :scale \"x\"}, :y {:field \"data.y\", :scale \"y\"}, :fill {:value \"steelblue\"}, :fillOpacity {:value 1}}, :update {:shape \"circle\", :size {:value 70}, :stroke {:value \"transparent\"}}, :hover {:size {:value 210}, :stroke {:value \"white\"}}}}], :data [{:name \"0d0cd9a5-ab29-40a9-a088-b0398af898f5\", :values ({:x 0, :y 0.11394260241925702} {:x 1, :y 0.04287095044777109} {:x 2, :y 0.13061848482456492} {:x 3, :y 13.709486585460168} {:x 4, :y 6.818465635313264} {:x 5, :y 9.709279555017597} {:x 6, :y 8.613021460275261} {:x 7, :y 85.08088065800538} {:x 8, :y 4.1113933334112405} {:x 9, :y 22.859226122533734} {:x 10, :y 5.428336932643211} {:x 11, :y 8.970252685131706} {:x 12, :y 20.832801671769513} {:x 13, :y 3.3169076420783856} {:x 14, :y 14.27767497909334} {:x 15, :y 36.05919326367589} {:x 16, :y 1.5197622138099365} {:x 17, :y 28.87538617420961} {:x 18, :y 8.315281210554804} {:x 19, :y 1.5197622138099365} {:x 20, :y 1.2861285134331333} {:x 21, :y 24.772072155833154} {:x 22, :y 0.789510278757128} {:x 23, :y 1.1848160458484682} {:x 24, :y 0.6140655065064246} {:x 25, :y 0.46401616866730944} {:x 26, :y 0.6430176824700359} {:x 27, :y 0.62691373675597} {:x 28, :y 1.9142961929988558} {:x 29, :y 1.0913167378090662})}], :width 400, :height 247.2188, :padding {:bottom 20, :top 10, :right 10, :left 50}}}"}
+;; <=
+
+;; **
+;;; Yeah... there is no stinking reasong that 3 should be first there. Unless... it's actually coming up as positive?
+;; **
+
+;; @@
+(require '[polismath.clusters :as clust :reload true])
+
+(def new-stats
+  (->>
+    (apply clust/conv-repness (map c [:rating-mat :group-clusters :base-clusters]))
+    :stats
+    (mapv #(get % 1))))
+    ;(get 3)
+    ;pprint))
+    ;(clust/beats-best-agr? nil))
+
+;; @@
+;; =>
+;;; {"type":"html","content":"#'repness-hacking/new-stats","value":"#'repness-hacking/new-stats"}
+;; <=
+
+;; @@
+(require '[polismath.clusters :as clust :reload true])
+
+(->> (map #(assoc %1 :tid %2) new-stats (range))
+     (take 4)
+     (reduce
+       (fn [res s]
+         (if (clust/beats-best-agr? s res)
+           s
+           res))
+       nil))
+;; @@
+;; ->
+;;; ph 3
+;;; ph 3
+;;; ph 3
+;;; ph 1
+;;;
+;; <-
+;; =>
+;;; {"type":"list-like","open":"#polismath.clusters.graph-record103808{","close":"}","separator":" ","items":[{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":pat","value":":pat"},{"type":"html","content":"0.0","value":"0.0"}],"value":"[:pat 0.0]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":pd","value":":pd"},{"type":"html","content":"4/7","value":"4/7"}],"value":"[:pd 4/7]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":pa","value":":pa"},{"type":"html","content":"3/7","value":"3/7"}],"value":"[:pa 3/7]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":na","value":":na"},{"type":"html","content":"2","value":"2"}],"value":"[:na 2]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":pdt","value":":pdt"},{"type":"html","content":"0.8164965809277264","value":"0.8164965809277264"}],"value":"[:pdt 0.8164965809277264]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":nd","value":":nd"},{"type":"html","content":"3","value":"3"}],"value":"[:nd 3]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":ns","value":":ns"},{"type":"html","content":"5","value":"5"}],"value":"[:ns 5]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":tid","value":":tid"},{"type":"html","content":"3","value":"3"}],"value":"[:tid 3]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":rdt","value":":rdt"},{"type":"html","content":"3.762532984128439","value":"3.762532984128439"}],"value":"[:rdt 3.762532984128439]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":rat","value":":rat"},{"type":"html","content":"-2.8824902428282386","value":"-2.8824902428282386"}],"value":"[:rat -2.8824902428282386]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":rd","value":":rd"},{"type":"html","content":"164/21","value":"164/21"}],"value":"[:rd 164/21]"},{"type":"list-like","open":"","close":"","separator":" ","items":[{"type":"html","content":":ra","value":":ra"},{"type":"html","content":"123/259","value":"123/259"}],"value":"[:ra 123/259]"}],"value":"#polismath.clusters.graph-record103808{:pat 0.0, :pd 4/7, :pa 3/7, :na 2, :pdt 0.8164965809277264, :nd 3, :ns 5, :tid 3, :rdt 3.762532984128439, :rat -2.8824902428282386, :rd 164/21, :ra 123/259}"}
+;; <=
+
+;; @@
+(pprint (take 4 new-stats))
+;; @@
+;; ->
+;;; ({:pat -1.6329931618554516,
+;;;   :pd 2/7,
+;;;   :pa 1/7,
+;;;   :na 0,
+;;;   :pdt -0.816496580927726,
+;;;   :nd 1,
+;;;   :ns 5,
+;;;   :rdt -0.8755950357709134,
+;;;   :rat -0.9991315673568163,
+;;;   :rd 82/147,
+;;;   :ra 41/105}
+;;;  {:pat -1.6329931618554516,
+;;;   :pd 2/7,
+;;;   :pa 1/7,
+;;;   :na 0,
+;;;   :pdt -0.816496580927726,
+;;;   :nd 1,
+;;;   :ns 5,
+;;;   :rdt -0.24119933043158806,
+;;;   :rat -1.5822244044174223,
+;;;   :rd 16/21,
+;;;   :ra 2/7}
+;;;  {:pat -0.816496580927726,
+;;;   :pd 1/7,
+;;;   :pa 2/7,
+;;;   :na 1,
+;;;   :pdt -1.6329931618554516,
+;;;   :nd 0,
+;;;   :ns 5,
+;;;   :rdt -2.8928689424440197,
+;;;   :rat 1.138313561928329,
+;;;   :rd 6/31,
+;;;   :ra 2N}
+;;;  {:pat 0.0,
+;;;   :pd 4/7,
+;;;   :pa 3/7,
+;;;   :na 2,
+;;;   :pdt 0.8164965809277264,
+;;;   :nd 3,
+;;;   :ns 5,
+;;;   :rdt 3.762532984128439,
+;;;   :rat -2.8824902428282386,
+;;;   :rd 164/21,
+;;;   :ra 123/259})
+;;;
+;; <-
+;; =>
+;;; {"type":"html","content":"nil","value":"nil"}
+;; <=
+
+;; **
+;;; OK; I see what's happening. I need to make the third option here a little more strict. I've modified it; let's see what happens now.
+;; **
+
+;; @@
+(require '[polismath.clusters :as clust :reload true]
+         '[polismath.conversation :as conv :reload true])
+
+
+(->
+  (apply clust/conv-repness (map c [:rating-mat :group-clusters :base-clusters]))
+  (clust/select-rep-comments)
+  (get 1)
+  pprint)
+;; @@
+;; ->
+;;; {:tid 20, :n-success 20, :n-trials 23, :p-success 21/25, :p-test 3.674234614174767, :repness 231/50, :repness-test 4.610256, :repful-for :agree}
+;;; nil
+;;; {:tid 1, :n-success 15, :n-trials 15, :p-success 16/17, :p-test 4.0, :repness 96/17, :repness-test 5.3267612, :repful-for :agree}
+;;; ({:tid 7,
+;;;   :n-success 4,
+;;;   :n-trials 5,
+;;;   :p-success 5/7,
+;;;   :p-test 1.6329931618554516,
+;;;   :repness 205/14,
+;;;   :repness-test 4.981382,
+;;;   :repful-for :disagree}
+;;;  {:tid 15,
+;;;   :n-success 4,
+;;;   :n-trials 5,
+;;;   :p-success 5/7,
+;;;   :p-test 1.6329931618554516,
+;;;   :repness 205/28,
+;;;   :repness-test 4.2224436,
+;;;   :repful-for :disagree}
+;;;  {:tid 17,
+;;;   :n-success 4,
+;;;   :n-trials 5,
+;;;   :p-success 5/7,
+;;;   :p-test 1.6329931618554516,
+;;;   :repness 43/7,
+;;;   :repness-test 4.029963,
+;;;   :repful-for :disagree}
+;;;  {:tid 21,
+;;;   :n-success 4,
+;;;   :n-trials 5,
+;;;   :p-success 5/7,
+;;;   :p-test 1.6329931618554516,
+;;;   :repness 39/7,
+;;;   :repness-test 3.811882,
+;;;   :repful-for :disagree}
+;;;  {:tid 12,
+;;;   :n-success 4,
+;;;   :n-trials 5,
+;;;   :p-success 5/7,
+;;;   :p-test 1.6329931618554516,
+;;;   :repness 205/42,
+;;;   :repness-test 3.6592052,
+;;;   :repful-for :disagree})
+;;;
+;; <-
+;; =>
+;;; {"type":"html","content":"nil","value":"nil"}
+;; <=
+
+;; **
+;;; Woohoo! Sweet! Looks good. Let's do a sanity check on the rest of them.
+;; **
+
+;; @@
+
+(->
+  (apply clust/conv-repness (map c [:rating-mat :group-clusters :base-clusters]))
+  (clust/select-rep-comments)
+  pprint)
+;; @@
+;; ->
+;;; {:tid 20, :n-success 20, :n-trials 23, :p-success 21/25, :p-test 3.674234614174767, :repness 231/50, :repness-test 4.610256, :repful-for :agree}
+;;; nil
+;;; {:tid 1, :n-success 15, :n-trials 15, :p-success 16/17, :p-test 4.0, :repness 96/17, :repness-test 5.3267612, :repful-for :agree}
+;;; {0
+;;;  ({:tid 20,
+;;;    :n-success 20,
+;;;    :n-trials 23,
+;;;    :p-success 21/25,
+;;;    :p-test 3.674234614174767,
+;;;    :repness 231/50,
+;;;    :repness-test 4.610256,
+;;;    :repful-for :agree}
+;;;   {:tid 0,
+;;;    :n-success 14,
+;;;    :n-trials 22,
+;;;    :p-success 5/8,
+;;;    :p-test 1.4596008983995232,
+;;;    :repness 15N,
+;;;    :repness-test 4.3339744,
+;;;    :repful-for :agree}
+;;;   {:tid 23,
+;;;    :n-success 18,
+;;;    :n-trials 20,
+;;;    :p-success 19/22,
+;;;    :p-test 3.7097041340118704,
+;;;    :repness 247/88,
+;;;    :repness-test 3.4360168,
+;;;    :repful-for :agree}
+;;;   {:tid 8,
+;;;    :n-success 21,
+;;;    :n-trials 23,
+;;;    :p-success 22/25,
+;;;    :p-test 4.08248290463863,
+;;;    :repness 46/25,
+;;;    :repness-test 3.135133,
+;;;    :repful-for :agree}
+;;;   {:tid 24,
+;;;    :n-success 9,
+;;;    :n-trials 11,
+;;;    :p-success 10/13,
+;;;    :p-test 2.3094010767585025,
+;;;    :repness 120/13,
+;;;    :repness-test 3.5605416,
+;;;    :repful-for :disagree}),
+;;;  1
+;;;  ({:tid 7,
+;;;    :n-success 4,
+;;;    :n-trials 5,
+;;;    :p-success 5/7,
+;;;    :p-test 1.6329931618554516,
+;;;    :repness 205/14,
+;;;    :repness-test 4.981382,
+;;;    :repful-for :disagree}
+;;;   {:tid 15,
+;;;    :n-success 4,
+;;;    :n-trials 5,
+;;;    :p-success 5/7,
+;;;    :p-test 1.6329931618554516,
+;;;    :repness 205/28,
+;;;    :repness-test 4.2224436,
+;;;    :repful-for :disagree}
+;;;   {:tid 17,
+;;;    :n-success 4,
+;;;    :n-trials 5,
+;;;    :p-success 5/7,
+;;;    :p-test 1.6329931618554516,
+;;;    :repness 43/7,
+;;;    :repness-test 4.029963,
+;;;    :repful-for :disagree}
+;;;   {:tid 21,
+;;;    :n-success 4,
+;;;    :n-trials 5,
+;;;    :p-success 5/7,
+;;;    :p-test 1.6329931618554516,
+;;;    :repness 39/7,
+;;;    :repness-test 3.811882,
+;;;    :repful-for :disagree}
+;;;   {:tid 12,
+;;;    :n-success 4,
+;;;    :n-trials 5,
+;;;    :p-success 5/7,
+;;;    :p-test 1.6329931618554516,
+;;;    :repness 205/42,
+;;;    :repness-test 3.6592052,
+;;;    :repful-for :disagree}),
+;;;  2
+;;;  ({:tid 1,
+;;;    :n-success 15,
+;;;    :n-trials 15,
+;;;    :p-success 16/17,
+;;;    :p-test 4.0,
+;;;    :repness 96/17,
+;;;    :repness-test 5.3267612,
+;;;    :repful-for :agree}
+;;;   {:tid 24,
+;;;    :n-success 9,
+;;;    :n-trials 10,
+;;;    :p-success 5/6,
+;;;    :p-test 2.7136021011998723,
+;;;    :repness 65/18,
+;;;    :repness-test 3.1851106,
+;;;    :repful-for :agree}
+;;;   {:tid 22,
+;;;    :n-success 14,
+;;;    :n-trials 16,
+;;;    :p-success 5/6,
+;;;    :p-test 3.1529631254723283,
+;;;    :repness 115/42,
+;;;    :repness-test 3.5232475,
+;;;    :repful-for :agree}
+;;;   {:tid 0,
+;;;    :n-success 16,
+;;;    :n-trials 17,
+;;;    :p-success 17/19,
+;;;    :p-test 3.771236166328253,
+;;;    :repness 493/114,
+;;;    :repness-test 4.833744,
+;;;    :repful-for :disagree}
+;;;   {:tid 20,
+;;;    :n-success 11,
+;;;    :n-trials 15,
+;;;    :p-success 12/17,
+;;;    :p-test 2.0,
+;;;    :repness 72/17,
+;;;    :repness-test 3.8254151,
+;;;    :repful-for :disagree})}
+;;;
+;; <-
+;; =>
+;;; {"type":"html","content":"nil","value":"nil"}
+;; <=
+
+;; @@
+
+;; @@


Beginnings of state machine component for Clojure

I need to find a way to make the state-machine function prettier to use,
since it's going to end up everywhere in computer architecture

error: pathspec 'src/state-machine.clj' did not match any file(s) known to git

--- src/state-machine.clj
+++ src/state-machine.clj
@@ -0,0 +1,25 @@
+(ns state-machine
+  (:use computer-build.vhdl clojure.contrib.pprint))
+
+(defn state-machine [name inputs outputs states transitions]
+	"Create a state machine that operates from the given inputs, triggering
+	the specified transitions to the listed states that generate outputs on
+	the signals in the outputs array"
+  (let [inports (map #(list % :in "std_logic") inputs)
+        outports (map #(list % :out "std_logic") outputs)]
+	`(entity ~(gensym name)
+          ; Ports
+          ~(concat ['(:clock :in "std_logic")] inports outports)
+          ; Behavior
+          (process ~(conj inputs :clock) ()))))
+
+(def my-sm (state-machine "pushbutton" [:push] [:bulb]
+               {:on '(<= :bulb "1") :off '(<= :bulb "0")}
+               [
+                '(:on (= :push "1") :off)
+                '(:off (= :push "0") :on)
+                ]))
+
+(pprint my-sm)
+(println "=================")
+(generate-vhdl my-sm)


Move Block type to blocks.data ns.

Move Block type to blocks.data ns.

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

--- src/blocks/data.clj
+++ src/blocks/data.clj
@@ -0,0 +1,183 @@
+(ns blocks.data
+  "Block type and constructor functions.
+
+  Blocks have the following two primary attributes:
+
+  - `:id`       `Multihash` with the digest identifying the content
+  - `:size`     number of bytes in the block content
+  "
+  (:require
+    [multihash.core :as multihash])
+  (:import
+    blocks.data.PersistentBytes
+    clojure.lang.Tuple
+    multihash.core.Multihash))
+
+
+(deftype Block
+  [^Multihash -id
+   ^long -size
+   ^PersistentBytes -content
+   -reader
+   -attributes
+   -meta]
+
+  ;:load-ns true
+
+
+  java.lang.Object
+
+  (toString
+    [this]
+    (format "Block[%s %s %s]"
+            -id -size (if -content
+                        "realized"
+                        (if -reader
+                          "deferred"
+                          "empty")))
+
+  (hashCode
+    [this]
+    (hash-combine (hash Block) (hash -id)))
+
+  ; TODO: this should consider attributes
+  (equals
+    [this that]
+    (cond
+      (identical? this that) true
+      (instance? Block that)
+        (= -id  (:id that))
+      :else false))
+
+
+  java.lang.Comparable
+
+  (compareTo
+    [this that]
+    (compare [-id -size -attributes]
+             [(:id that) (:size that)
+              (when (instance? Block that)
+                      (.-attributes ^Block that))]))
+
+
+  clojure.lang.IHashEq
+
+  (hasheq
+    [this]
+    (hash-combine (hash Block) (hash [-id -size -attributes])))
+
+
+  clojure.lang.IObj
+
+  (meta [this] -meta)
+
+  (withMeta
+    [this meta-map]
+    (Block. -id -size -content -reader -attributes meta-map))
+
+
+  ; TODO: IKeywordLookup?
+  clojure.lang.ILookup
+
+  (valAt
+    [this k]
+    (.valAt this k nil))
+
+  (valAt
+    [this k not-found]
+    (case k
+      :id -id
+      :size -size
+      not-found))
+
+
+  clojure.lang.IPersistentMap
+
+  (count
+    [this]
+    (+ 2 (count -attributes)))
+
+  (empty
+    [this]
+    (Block. -id -size nil nil nil -meta))
+
+  (cons
+    [this element]
+    (cond
+      (map-entry? element)
+        (let [^java.util.Map$Entry entry element]
+          (.assoc this (.getKey entry) (.getValue entry)))
+      (vector? element)
+        (.assoc this (first element) (second element))
+      :else
+        (loop [result this
+               entries element]
+          (if (seq entries)
+            (let [^java.util.Map$Entry entry (first entries)]
+              (recur (.assoc result (.getKey entry) (.getValue entry))
+                     (rest entries)))
+            reult))))
+
+  (equiv
+    [this other]
+    (boolean (or (identical? this other)
+                 (when (identical? (class this) (class other))
+                   (let [other ^Block other
+                         other-content (. other -content)]
+                     (and (= -id (. other -id))
+                          (= -size (. other -size))
+                          (= -attributes (. other -attributes))
+                          (not (and -content other-content
+                                    (not= -content other-content))))))))
+
+  (containsKey
+    [this k]
+    (not (identical? this (.valAt this k this))))
+
+  (entryAt
+    [this k]
+    (let [v (.valAt this k this)]
+      (when-not (identical? this v)
+        (Tuple/create k v))))
+
+  (seq
+    [this]
+    (seq (concat [(Tuple/create :id -id)
+                  (Tuple/create :size -size)]
+                 -attributes)))
+
+  (iterator
+    [this]
+    (seq-iterator (seq this)))
+
+  (assoc
+    [this k v]
+    (case k
+      (:id :size :content :reader)
+        (throw (IllegalArgumentException.
+                 (str "Block " k " cannot be changed")))
+      (Block. -id -size -content -reader (assoc -attributes k v) -meta)))
+
+  (without
+    [this k]
+    (case k
+      (:id :size :content :reader)
+        (throw (IllegalArgumentException.
+                 (str "Block " k " cannot be changed")))
+      (Block. -id -size -content -reader (not-empty (dissoc -attributes k v)) -meta)))
+
+
+  ; IDeref?
+  ; (deref [this] ...)
+
+
+  clojure.lang.IPending
+
+  (isRealized
+    [this]
+    (some? -content))))
+
+
+(defmethod print-method Block
+  [v ^java.io.Writer w]
+  (.write w (str v)))


Optimization of db-spec

Optimization of db-spec

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


Add support for an annotated failure

Add support for an annotated failure

that contains extra data aside from the error message

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

--- src/clj/mdr2/failjure.clj
+++ src/clj/mdr2/failjure.clj
@@ -0,0 +1,7 @@
+(ns mdr2.failjure
+  (:require [failjure.core :as fail]))
+
+(defrecord AnnotatedFailure [message data]
+  fail/HasFailed
+  (failed? [self] true)
+  (message [self] (:message self)))