Update index tests.

Update index tests.

--- test/merkle_db/index_test.clj
+++ test/merkle_db/index_test.clj
@@ -15,23 +15,58 @@
       [index :as index]
       [key :as key]
       [partition :as part]
+      [patch :as patch]
       [record :as record]
-      #_[validate :as validate]
+      [validate :as validate]
       [test-utils :as tu])))
+
+
+(def k0 (key/create [0]))
+(def k1 (key/create [1]))
+(def k2 (key/create [2]))
+(def k3 (key/create [3]))
+(def k4 (key/create [4]))
+
+(def r0 {:x 0, :y 0, :a 0, :c 0})
+(def r1 {:x 1, :c 1, :d 1})
+(def r2 {:b 2, :c 2})
+(def r3 {:x 3, :y 3, :a 3, :b 3})
+(def r4 {:z 4, :d 4})
+
+(def params
+  {::index/branching-factor 4
+   ::part/limit 3
+   ::record/families {:ab #{:a :b}, :cd #{:c :d}}})
+
+
+(deftest record-assigment
+  (let [assign-records @#'index/assign-records]
+    (is (= [[:a [[k0 r0] [k1 ::patch/tombstone]]]
+            [:b [[k2 r2] [k3 ::patch/tombstone]]]]
+           (assign-records
+             {::index/keys [k2]
+              ::index/children [:a :b]}
+             [[k0 r0] [k1 ::patch/tombstone]
+              [k2 r2] [k3 ::patch/tombstone]])))
+    (is (= [[:a nil]
+            [:b [[k1 ::patch/tombstone]]]
+            [:c [[k2 r2] [k3 ::patch/tombstone]]]]
+           (assign-records
+             {::index/keys [k0 k2]
+              ::index/children [:a :b :c]}
+             [[k1 ::patch/tombstone] [k2 r2] [k3 ::patch/tombstone]])))
+    (is (= [[:a [[k0 r0] [k1 ::patch/tombstone]]]
+            [:b [[k2 r2] [k3 ::patch/tombstone]]]
+            [:c nil]]
+           (assign-records
+             {::index/keys [k2 k4]
+              ::index/children [:a :b :c]}
+             [[k0 r0] [k1 ::patch/tombstone]
+              [k2 r2] [k3 ::patch/tombstone]])))))

 (deftest index-reading
   (let [store (mdag/init-store :types graph/codec-types)
-        k0 (key/create [0])
-        k1 (key/create [1])
-        k2 (key/create [2])
-        k3 (key/create [3])
-        k4 (key/create [4])
-        r0 {:x 0, :y 0, :a 0, :c 0}
-        r1 {:x 1, :c 1, :d 1}
-        r2 {:b 2, :c 2}
-        r3 {:x 3, :y 3, :a 3, :b 3}
-        r4 {:z 4, :d 4}
         params {::index/branching-factor 4
                 ::part/limit 3
                 ::record/families {:ab #{:a :b}, :cd #{:c :d}}}
@@ -75,38 +110,62 @@
              (index/read-range store root #{:c} k2 k3))))))

-#_
-(deftest tree-validation
-  (let [store (mdag/init-store :types graph/codec-types)]
-    (testing "empty tree"
-      (tu/check-asserts
-        (validate/run!
-          store
-          nil
-          validate/validate-data-tree
-          {::record/count 0})))
-    (testing "partition tree"
-      (let [part (part/from-records
-                   store
-                   {::record/families {:ab #{:a :b}, :cd #{:c :d}}}
-                   {(key/create [0 1 2]) {:x 0, :y 0, :a 0, :c 0}
-                    (key/create [1 2 3]) {:x 1, :c 1, :d 1, }
-                    (key/create [2 3 4]) {:b 2, :c 2}
-                    (key/create [3 4 5]) {:x 3, :y 3, :a 3, :b 3}
-                    (key/create [4 5 6]) {:z 4, :d 4}})]
-        (tu/check-asserts
-          (validate/run!
-            store
-            (::node/id (meta part))
-            validate/validate-data-tree
-            {::record/count 5
-             ::part/limit 10}))))))
+(deftest index-update-empty-root
+  (let [store (mdag/init-store :types graph/codec-types)
+        root (index/update-tree store params nil [[k0 r0] [k1 ::patch/tombstone]])]
+    (is (nil? (index/update-tree store params nil nil)))
+    (is (= part/data-type (:data/type root)))
+    (is (= 1 (::record/count root)))
+    (is (= [[k0 r0]] (index/read-all store root nil)))))

-#_
-(deftest ^:generative index-construction
-  (checking "valid properties" 20
+(deftest index-updates-partition-root
+  (let [store (mdag/init-store :types graph/codec-types)
+        part0 (part/from-records store params {k0 r0, k1 r1, k2 r2})
+        root (index/update-tree store params part0 [[k3 r3] [k4 r4]])]
+    (is (= part0 (index/update-tree store params part0 [])))
+    (is (= index/data-type (:data/type root)))
+    (is (= 5 (::record/count root)))
+    (is (= 2 (count (::index/children root))))
+    (is (= [[k0 r0] [k1 r1] [k2 r2] [k3 r3] [k4 r4]]
+           (index/read-all store root nil)))))
+
+
+(deftest index-updates-index-root
+  (let [store (mdag/init-store :types graph/codec-types)
+        k5 (key/create [5])
+        k6 (key/create [6])
+        k7 (key/create [7])
+        k8 (key/create [8])
+        k9 (key/create [9])
+        r5 {:a 5, :x 5}
+        r6 {:b 6, :y 6}
+        r7 {:b 7, :y 7}
+        r8 {:b 8, :y 8}
+        r9 {:b 9, :y 9}
+        part0 (part/from-records store params {k0 r0, k1 r1})
+        part1 (part/from-records store params {k3 r3, k4 r4})
+        part2 (part/from-records store params {k5 r5, k7 r7})
+        part3 (part/from-records store params {k8 r8, k9 r9})
+        idx00 (index/build-tree store params [part0 part1])
+        idx01 (index/build-tree store params [part2 part3])
+        idx10 (index/build-tree store params [idx00 idx01])
+        root (index/update-tree store params idx10 [[k2 r2] [k6 r6] [k7 ::patch/tombstone]])]
+    ;(prn ::root root)
+    (is (= index/data-type (:data/type root)))
+    (is (= 9 (::record/count root)))
+    (is (= 4 (count (::index/children root))))
+    (is (= k0 (::record/first-key root)))
+    (is (= k9 (::record/last-key root)))
+    (is (= [[k0 r0] [k1 r1] [k2 r2] [k3 r3] [k4 r4]
+            [k5 r5] [k6 r6] [k8 r8] [k9 r9]]
+           (index/read-all store root nil)))))
+
+
+(deftest ^:generative index-updating
+  (checking "tree updates" 20
     [[field-keys families records] mdgen/data-context
+     ; TODO: pick extant, inserts, and deletes
      part-limit (gen/large-integer* {:min 4})
      branch-fac (gen/large-integer* {:min 4})]
     (is (valid? ::record/families families))
@@ -115,8 +174,7 @@
                   ::record/count (count records)
                   ::index/branching-factor branch-fac
                   ::part/limit part-limit}
-          parts (part/partition-records store params records)
-          root (index/build-tree store params parts)]
+          root (index/from-records store params records)]
       (tu/check-asserts
         (validate/run!
           store


constraints on {:pred :boil}

constraints on {:pred :boil}

--- src/babel/encyclopedia.cljc
+++ src/babel/encyclopedia.cljc
@@ -259,6 +259,11 @@
          :subj {:human true}
          :obj {:human true}}

+        {:pred :boil
+         :subj {:human true}
+         :obj {:liquid true
+               :consumable true}}
+
         {:pred :bring
          :subj {:human true}
          :obj {:buyable true}}


for now, only one demo to work on encyclopedia stuff

--- src/babel/english/demo.cljc
+++ src/babel/english/demo.cljc
@@ -13,23 +13,24 @@
           :synsem {:cat :noun
                    :sem {:pred :cane}}}

-         {:demo-name "Noun Phrases"
-          :synsem {:cat :noun}}
+;         {:demo-name "Noun Phrases"
+;          :synsem {:cat :noun}}

-         {:demo-name "Sentences about dogs eating"
-          :synsem {:cat :verb
-                   :sem {:subj {:pred :cane}
-                         :pred :mangiare}}}
+;         {:demo-name "Sentences about dogs eating"
+;          :synsem {:cat :verb
+;                   :sem {:subj {:pred :cane}
+;                         :pred :mangiare}}}

-         {:demo-name "Sentences about Eating"
-          :synsem {:cat :verb
-                   :sem {:pred :mangiare}}}
+;         {:demo-name "Sentences about Eating"
+;          :synsem {:cat :verb
+;                   :sem {:pred :mangiare}}}

-         {:synsem {:cat :verb}
-          :demo-name "Sentences"}
+;         {:synsem {:cat :verb}
+;          :demo-name "Sentences"}

-         {:synsem :top
-          :demo-name "Totally random expressions"}]]
+;         {:synsem :top
+;          :demo-name "Totally random expressions"}
+         ]]

     (count (map (fn [spec]
                   (let [log-message


fix text placement

fix text placement

--- src/clj/salava/page/pdf.clj
+++ src/clj/salava/page/pdf.clj
@@ -119,7 +119,7 @@
                                                                    (let [badge (badge-info-fn ctx user-id (conj () (:badge_id %)))]
                                                                     (when-not (empty? badge)
                                                                       [:phrase {:align :center}
-                                                                       [:chunk [:image {:align :center :width 75 :height 75  :base64 true} (or (:qr_code (first badge)) "")]]
+                                                                       [:chunk [:image {:align :center :width 75 :height 75  :base64 true} (or (:qr_code (first badge)) "")]]"\n"
 ;;                                                                        [:spacer 0]
                                                                        [:chunk.generic (t :badge/Scantobadge ul)]]))]
                                                                   ]


Data loading from Google Spreadsheet

Data loading from Google Spreadsheet

--- src/cljs/db_quiz/model.cljs
+++ src/cljs/db_quiz/model.cljs
@@ -3,55 +3,95 @@
   (:require-macros [cljs.core.async.macros :refer [go go-loop]])
   (:require [db-quiz.config :refer [config]]
             [db-quiz.state :refer [app-state]]
+            [db-quiz.modals :as modals]
+            [db-quiz.util :refer [number-of-fields]]
             [cljs-http.client :as http]
-            [reagent.core :as reagent :refer [atom]]
-            [cljs.core.async :refer [<! >! chan sliding-buffer]]
+            [cljs.core.async :refer [<! >! chan pipe]]
             [clojure.string :refer [join lower-case replace split trim]]
-            [cljsjs.mustache :as mustache]))
+            [cljsjs.mustache :as mustache]
+            [reagent-modals.modals :as reagent-modals]))

 ; ----- Private vars -----

-(def autocomplete-template
-  "PREFIX rdfs:    <http://www.w3.org/2000/01/rdf-schema#>
-
-  SELECT DISTINCT ?s (STR(?_label) AS ?label)
+(def load-labels-template
+  "PREFIX dbo:     <http://dbpedia.org/ontology/>
+  PREFIX rdfs:     <http://www.w3.org/2000/01/rdf-schema#>
+
+  SELECT ?label
+  WHERE {
+    {
+      SELECT (STR(?_label) AS ?label)
+      WHERE {
+        GRAPH <http://cs.dbpedia.org> {
+          VALUES ?class {
+            {{#classes}}
+            <{{{.}}}>
+            {{/classes}}
+          }
+          [] a ?class ;
+            rdfs:label ?_label ;
+            dbo:abstract ?_description .
+          FILTER (!REGEX(?_label, '^(\\\\p{Lu}\\\\.?)+$')
+                  &&
+                  !REGEX(?_label, '^.*\\\\d+.*$')
+                  &&
+                  (STRLEN(?_description) > 140)
+                  &&
+                  langMatches(lang(?_label), 'cs')
+                  &&
+                  langMatches(lang(?_description), 'cs'))
+        }
+      }
+      ORDER BY ?label
+    }
+  }
+  LIMIT {{limit}}
+  OFFSET {{offset}}")
+
+(def load-labels-group-template
+  "PREFIX dbo:     <http://dbpedia.org/ontology/>
+  PREFIX rdfs:     <http://www.w3.org/2000/01/rdf-schema#>
+
+  SELECT (GROUP_CONCAT(STR(?_label); separator = '|') AS ?labels)
   WHERE {
     GRAPH <http://cs.dbpedia.org> {
-      VALUES ?fragment {
-        '{{fragment}}'
-      }
       VALUES ?class {
         {{#classes}}
         <{{{.}}}>
         {{/classes}}
       }
-      ?s a ?class ;
-        rdfs:label ?_label .
-      FILTER (lang(?_label) = '' || langMatches(lang(?_label), 'cs'))
-      BIND (LCASE(?_label) AS ?lcaseLabel)
-      FILTER CONTAINS(?lcaseLabel, ?fragment)
-      BIND (IF(STRSTARTS(?lcaseLabel, ?fragment), 1, 0) AS ?order)
+      [] a ?class ;
+        rdfs:label ?_label ;
+        dbo:abstract ?_description .
+      FILTER (!REGEX(?_label, '^(\\\\p{Lu}\\\\.?)+$')
+              &&
+              !REGEX(?_label, '^.*\\\\d+.*$')
+              &&
+              (STRLEN(?_description) > 140)
+              &&
+              langMatches(lang(?_label), 'cs')
+              &&
+              langMatches(lang(?_description), 'cs'))
     }
-  }
-  ORDER BY DESC(?order)
-  LIMIT 10")
+  }")

 ; ----- Public functions -----

-(defn sparql-results-channel
+(def sparql-results-channel
   "Channel that extracts values from application/sparql-results+json format."
-  []
   (letfn [(extract-values [result] (into {} (map (fn [[k v]] [k (:value v)]) result)))]
-    (chan 1 (map (comp (partial map extract-values) :bindings :results :body)))))
+    (fn []
+      (chan 1 (map (comp (partial map extract-values) :bindings :results :body))))))

 (defn sparql-query-channel
   "Virtuoso-specific JSON-P request"
-  [query]
-  (http/jsonp (get-in config [:data :sparql :endpoint])
+  [endpoint query]
+  (http/jsonp endpoint
               {:query-params {; Since JSON-P does not allow setting Accept header,
                               ; we need to use Virtuoso-specific query parameter `format`.
                               :format "application/sparql-results+json"
-                              :query query}}))
+                              :query query
+                              :timeout 60000}}))

 (defn render-template
   "Render Mustache template with data."
@@ -65,12 +105,15 @@
       (replace #"\s*\[[^\]]+\]" "")))

 (defn clear-description
+  "TODO: Implement cleaning"
   [description]
   description)

 (defn truncate-description
+  "Truncate description to the configured maximum length.
+  Cuts the description on a complete sentence."
   [description]
-  (let [maximum-length 500]
+  (let [maximum-length (:max-question-length config)]
     (cond (> (count description) maximum-length)
           (reduce (fn [a b]
                     (if (> (count a) maximum-length)
@@ -79,15 +122,12 @@
                   (split description #"\.\s+"))
           :else description)))

-(defn clear-label
-  [label]
-  (-> label
-      delete-parenthesized-parts
-      trim))
+(def clear-label
+  (comp trim delete-parenthesized-parts))

 (defn clear-tokens
+  "Filter out roman numerals"
   [tokens]
-  ; Filter out roman numerals
   (filter (fn [token]
             (not (re-matches #"^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})\.?$" token)))
           tokens))
@@ -157,42 +197,120 @@
     (go
       (swap! app-state #(assoc % :loading? true))
       (let [query (render-template (<! query-channel) :data data)]
-        ;(.log js/console query)
-        (>! sparql-results (<! (sparql-query-channel query))))
+        ; I wonder why cljs.pipe/pipe doesn't block.
+        (>! sparql-results (<! (sparql-query-channel sparql-endpoint query))))
       (swap! app-state #(assoc % :loading? false)))
     sparql-results))

-(defn sparql-autocomplete
-  "Autocomplete from SPARQL endpoint.
-  request: jQuery UI autocomplete's request object
-  response: channel to put the response to"
-  [request response]
-  (let [fragment (lower-case (.-term request))
+(def load-labels
+  (let [{{classes :default} :classes
+         :keys [page-size endpoint]} (get-in config [:data :sparql])
+        render-fn (fn [offset] (render-template load-labels-template
+                                                :data {:classes classes
+                                                       :limit page-size
+                                                       :offset offset}))
+        query-fn (fn [offset] (sparql-query-channel endpoint (render-fn offset)))
         sparql-results (sparql-results-channel)
-        query (render-template autocomplete-template
-                               :data {:classes (get-in config [:data :sparql :classes :default])
-                                      :fragment fragment})
-        sparql-query (sparql-query-channel query)]
-    (go (let [results (map :label (<! (wrap-load sparql-query sparql-results)))]
-          (response (clj->js results))))))
-
-(defn wikipedia-autocomplete
-  "Autocomplete from Wikipedia API.
-  request: jQuery UI autocomplete's request object
-  response: channel to put the response to"
-  [request response]
-  (let [fragment (lower-case (.-term request))
-        query (http/jsonp "http://cs.wikipedia.org/w/api.php"
-                          {:query-params {:action "opensearch"
-                                          :format "json"
-                                          :search fragment}})]
-    (go (-> query <! :body second clj->js response))))
+        results (atom [])]
+    (fn []
+      (go-loop [offset 0]
+               (swap! app-state #(assoc % :loading? true))
+               (let [results (<! (query-fn offset))]
+                 (if results
+                   (do (js/console.log (clj->js results))
+                       (>! sparql-results results)
+                       (recur (+ offset page-size)))
+                   (js/console.log "Wut, no results?")))
+               (swap! app-state #(assoc % :loading? false)))
+      (go (let [labels (<! sparql-results)]
+            (swap! results #(conj % labels))))
+      results)))
+
+; (defn sparql-autocomplete
+;   "Autocomplete from SPARQL endpoint.
+;   request: jQuery UI autocomplete's request object
+;   response: channel to put the response to"
+;   [request response]
+;   (let [fragment (lower-case (.-term request))
+;         sparql-results (sparql-results-channel)
+;         query (render-template autocomplete-template
+;                                :data {:classes (get-in config [:data :sparql :classes :default])
+;                                       :fragment fragment})
+;         sparql-query (sparql-query-channel query)]
+;     (go (let [results (map :label (<! (wrap-load sparql-query sparql-results)))]
+;           (response (clj->js results))))))
+
+(defn spreadsheet-url-to-id
+  "Extract ID from Google Spreadsheet URL"
+  [url]
+  (let [a (.createElement js/document "a")
+        _ (set! (.-href a) url)
+        path-name (.-pathname a)
+        start "/spreadsheets/d/"]
+    (when (zero? (.indexOf path-name start))
+      (-> path-name
+          (replace start "")
+          (split #"/" 2)
+          first))))

 (defn load-gdocs-items
   "Load items from Google Spreadsheet's worksheet."
-  [spreadsheet-id worksheet-id]
-  (let [url (str "https://spreadsheets.google.com/feeds/list/"
+  [spreadsheet-url]
+  (let [worksheet-id "od6" ; FIXME: Is this value used every time?
+        spreadsheet-id (spreadsheet-url-to-id spreadsheet-url)
+        url (str "https://spreadsheets.google.com/feeds/list/"
                  spreadsheet-id "/" worksheet-id "/public/full")
-        results (chan 1 (map (comp :entry :feed :body)))]
-    (wrap-load (http/jsonp url {:query-params {:alt "json-in-script"}})
-               results)))
+        transform-row (fn [{{label :$t} :gsx$label
+                           {description :$t} :gsx$description}]
+                            {:label label
+                             :abbreviation (abbreviate label)
+                             :description description})
+        raw-results-chan (wrap-load (http/jsonp url {:query-params {:alt "json-in-script"}})
+                                (chan 1 (map (comp (partial map transform-row) :entry :feed :body))))
+        results-chan (chan)]
+    (if spreadsheet-id
+      (do (go (let [results (<! raw-results-chan)
+                    results-count (count results)]
+                (cond (not= results-count number-of-fields)
+                        (reagent-modals/modal! (modals/invalid-spreadsheet-rows results-count))
+                      ; TODO: More validation rules
+                      :else (>! results-chan results))))
+          results-chan)
+      (reagent-modals/modal! (modals/invalid-google-spreadsheet-url spreadsheet-url)))))
+
+(defn- merge-board-with-data
+  "Merge data with questions into the initialized board."
+  [board data]
+  (into {}
+        (map (fn [[k v] result] [k (merge v result)])
+             board
+             data)))
+
+(defmulti load-board-data (fn [] (get-in @app-state [:options :data-source])))
+
+(defmethod load-board-data :dbpedia
+  [board callback]
+  (let [{{:keys [classes difficulty language]} :options} @app-state
+        [endpoint query-file] (case language
+                                    :czech ["http://cs.dbpedia.org/sparql" "sparql/cs_dbpedia.mustache"]
+                                    :english ["http://dbpedia.org/sparql" "sparql/en_dbpedia.mustache"])
+        offset (+ (case difficulty
+                        :easy 0
+                        :normal 3750
+                        :hard 8750)
+                  (rand-int 2500))
+        query-channel (sparql-query endpoint
+                                    query-file
+                                    :data {:classes classes
+                                           :limit number-of-fields
+                                           :offset offset})]
+    (go (let [results (map despoilerify (<! query-channel))]
+          (swap! app-state #(assoc % :board (merge-board-with-data board results)))
+          (callback)))))
+
+(defmethod load-board-data :gdrive
+  [board callback]
+  (let [spreadsheet-url (get-in @app-state [:options :doc])]
+    (go (let [results (<! (load-gdocs-items spreadsheet-url))]
+          (swap! app-state #(assoc % :board (merge-board-with-data board results)))
+          (callback)))))


gitlab icon #341

gitlab icon
#341


Add a tracing middleware to the default asset pipeline

--- src/io/aviso/twixt.clj
+++ src/io/aviso/twixt.clj
@@ -13,7 +13,7 @@
              [less :as less]
              [memory-cache :as mem]
              [utils :as utils]
-             [tracker :as tracker]]
+             [tracker :as t]]
             [ring.util
              [response :as r]
              [mime-type :as mime]]))
@@ -120,6 +120,14 @@
   "Uses get-asset-uris to get a single URI for a single asset path."
   [twixt asset-path]
   (first (get-asset-uris twixt asset-path)))
+
+(defn wrap-with-tracing
+  "The first middleware in the asset pipeline, used to trace the constuction of the asset."
+  [handler]
+  (fn tracer [asset-path context]
+    (t/trace
+      #(format "Accessing asset `%s'" asset-path)
+      (handler asset-path context))))

 (defn default-asset-pipeline
   "Sets up the default pipeline in either development mode or production mode."
@@ -136,7 +144,8 @@
       ;; that might compile.
       development-mode (fs/wrap-with-filesystem-cache (:cache-folder twixt-options) resolver)
       production-mode mem/wrap-with-sticky-cache
-      development-mode mem/wrap-with-invalidating-cache)))
+      development-mode mem/wrap-with-invalidating-cache
+      true wrap-with-tracing)))

 (defn wrap-with-twixt-setup
   "Wraps a handler with another handler that provides the :twixt key in the request object.
@@ -199,7 +208,7 @@
   (let [path-prefix (-> request :twixt :path-prefix)
         path (:uri request)]
     (when (match? path-prefix path)
-      (tracker/trace
+      (t/trace
         #(format "Handling asset request `%s'." path)
         (let [[requested-checksum asset-path] (parse-path path-prefix path)
               twixt (:twixt request)


Change title

Change title

--- src/cljs/todo_clj/core.cljs
+++ src/cljs/todo_clj/core.cljs
@@ -42,7 +42,7 @@
     om/IRender
     (render [_]
       (dom/div nil
-        (dom/h1 nil "Todo")
+        (dom/h1 nil "My Todo")
         (dom/input #js {:ref "new-todo"
                         :type "text"
                         :placeholder "What needs to be done?"


Try to add pre-conditions

Try to add pre-conditions

--- src/cljs/triboard/core.cljs
+++ src/cljs/triboard/core.cljs
@@ -4,6 +4,7 @@
     [clojure.string :as str]))

 (enable-console-print!)
+(set! *assert* true)

 ;; TODO - Try to plug the history of brower for "back"
 ;; TODO - Try to bring some domain vocabular here: it gets too complicated to trace
@@ -20,11 +21,20 @@

 ;; -----------------------------------------
+;; EXTRINSIC TYPES
+;; -----------------------------------------
+
+(def player? (set players))
+(def cell-types? (conj player? :wall :empty))
+
+
+;; -----------------------------------------
 ;; UTILS
 ;; -----------------------------------------

 (defn fast-max-key
   "Fast max key that avoids recomputing things several times"
+  {:pre [(fn? key-fn) (seq? coll)]}
   [key-fn coll]
   (apply max-key (memoize key-fn) coll))

@@ -76,6 +86,7 @@

 (defn ^boolean is-cell-empty?
   "Indicates whether a cell is owned by any player"
+  {:pre (or (nil? cell) (cell-types? cell))}
   [cell]
   (or (nil? cell) (= cell :empty) (= cell :wall)))

@@ -144,7 +155,7 @@
 ;; -----------------------------------------

 (defn- next-player
-  [player]
+  [player]
   (case player
     :blue :red
     :red :green
@@ -214,6 +225,7 @@

 (defn- worst-immediate-loss
   "Return the next worse lost game move for 'looser' if 'player' plays"
+  {:pre [(player? player) (player? looser)]}
   [game player looser]
   (let [converted-filter #(= looser (:looser %))
         all-moves (get-in game [:moves player])]
@@ -226,6 +238,7 @@
   "[SIMPLISTIC] Return the best move for a player based on:
    * The immediate gain
    * The worse immediate lost afterwards"
+  {:pre (player? player)}
   [game player]
   (let [moves (get-in game [:moves player])
         others (remove #{player} players)]
@@ -288,7 +301,7 @@
      (fn on-click [] ;; TODO - UGLY FOR TEST ONLY
        (swap! app-state play-move [x y])
        (while (and (:player @app-state) (not= :blue (:player @app-state)))
-         (let [auto-move (best-move @app-state (:player @app-state))]
+         (let [auto-move (time (best-move @app-state (:player @app-state)))]
             (swap! app-state play-move auto-move))
          ))
      }))


fix ignored-change naming and comment

fix ignored-change naming and comment

--- src/cljs_api_gen/result.clj
+++ src/cljs_api_gen/result.clj
@@ -195,10 +195,11 @@
        ;; dangling defmethods probably means its defmulti is private
        (filtermap #(not= "method" (:type %)))))

-(def dont-mark-change
+(def ignored-change
   "Fix symbol history noise if a symbol was accidentally removed or added in
    the given versions.  Will still be listed in [:library :changes] structure though."
-  {;; These symbols were accidentally made private and then made public in 1.9.494
+  {;; These symbols were accidentally made private in 1.4.493 then fixed in 1.9.494
+   ;; so we want to ignore the resulting removal and addition, respectively.
    "cljs.core/copy-arguments" #{"1.9.493" "1.9.494"}
    "cljs.core/es6-iterable" #{"1.9.493" "1.9.494"}
    "cljs.core/gen-apply-to" #{"1.9.493" "1.9.494"}


Add link to deploy tokens wiki in error response

--- src/clojars/routes/repo.clj
+++ src/clojars/routes/repo.clj
@@ -316,8 +316,7 @@
   [session group artifact]
   (when-not (token-session-matches-group-artifact? session group artifact)
     (throw-forbidden
-      ;; TODO: (toby) add short link to a wiki page
-     "The provided token's scope doesn't allow deploying this artifact"
+     "The provided token's scope doesn't allow deploying this artifact (see https://git.io/JfwjM)"
      {:group group
       :artifact artifact})))


improve doc-string of `datafy+`

improve doc-string of `datafy+`

--- src/java_time/data.clj
+++ src/java_time/data.clj
@@ -97,8 +97,8 @@
   )

 (defn datafy+
-  "Calls `d/datafy`, and attaches Object <o>  in the
-   metadata of the result (under the `:object` key)."
+  "Calls `(d/datafy o)`, and attaches Object <o>  in
+   the metadata of the result (under the `:object` key)."
   [o]
   (some-> (d/datafy o)
           (with-meta {:object o})))


Set *print-newline* to be true

Set *print-newline* to be true

Fixes #126

--- Clojure/src/ambly/core.clj
+++ Clojure/src/ambly/core.clj
@@ -579,7 +579,9 @@
                    (when (or (not (contains? *loaded-libs* name)) reload)
                      (set! *loaded-libs* (conj (or *loaded-libs* #{}) name))
                      (js/CLOSURE_IMPORT_SCRIPT
-                       (js* "~{}[~{}]" (.. js/goog -dependencies_ -nameToPath) name))))))))
+                       (js* "~{}[~{}]" (.. js/goog -dependencies_ -nameToPath) name)))))))
+          (repl/evaluate-form repl-env env "<cljs repl>"
+            '(set! *print-newline* true)))
         (let [expected-clojurescript-version (cljs.util/clojurescript-version)
               actual-clojurescript-version (:value (jsc-eval repl-env "cljs.core._STAR_clojurescript_version_STAR_"))]
           (when (and (some? actual-clojurescript-version)


Fix syntax error

Fix syntax error

--- src/karmanaut/core.clj
+++ src/karmanaut/core.clj
@@ -1,7 +1,7 @@
 (ns karmanaut.core
   (:require [karmanaut.users :as users]
             [karmanaut.crawler :as crawler])
-  (:gen-class)
+  (:gen-class))

 (defn -main [& args]
   (let [user-maps (crawler/crawl-users users/subject-ids)]


Add 'git-trees tree reading function.

Add 'git-trees tree reading function.

--- src/leiningen/voom.clj
+++ src/leiningen/voom.clj
@@ -1035,6 +1035,15 @@
     @(d/transact conn txn)
     :done))

+(defn git-trees
+  [gitdir tree-shas]
+  (for [s tree-shas]
+    {s (into {}
+             (for [e (:lines (git {:gitdir gitdir} "cat-file" "-p" s))
+                   :let [[_ ftype sha fname] (s/split (s/trim e) #"\s" 4)]]
+               [fname {:sha sha
+                       :ftype ftype}]))}))
+
 (def subtasks [#'build-deps #'deploy #'find-box #'freshen #'install
                #'retag-all-repos #'ver-parse #'wrap])


added write datastore part of examples

added write datastore part of examples

--- src/datasplash/examples.clj
+++ src/datasplash/examples.clj
@@ -2,8 +2,10 @@
   (:require [clojure.string :as str]
             [datasplash
              [api :as ds]
-             [bq :as bq]]
+             [bq :as bq]
+             [datastore :as dts]]
             [clojure.edn :as edn])
+  (:import [java.util UUID])
   (:gen-class))

 ;;;;;;;;;;;;;;;
@@ -180,6 +182,52 @@
                          results)
       (ds/write-edn-file output results))))

+;;;;;;;;;;;;;;;;;;;;;;;;
+;; DatastoreWordCount ;;
+;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Port of https://github.com/GoogleCloudPlatform/DataflowJavaSDK/blob/master/examples/src/main/java/com/google/cloud/dataflow/examples/cookbook/DatastoreWordCount.java
+
+(ds/defoptions DatastoreWordCountOptions
+  {:input {:type String
+           :default "gs://dataflow-samples/shakespeare/kinglear.txt"
+           :description "Path of the file to read from"}
+   :output {:type String
+            :default "kinglear-freqs.txt"
+            :description "Path of the file to write to"}
+   :dataset {:type String
+             :description "Dataset ID to read from Cloud Datastore"}
+   :kind {:type String
+          :description "Cloud Datastore Entity Kind"}
+   :namespace {:type String
+               :description "Dataset Namespace"}
+   :isReadOnly {:type Boolean
+                :description "Read an existing dataset, do not write first"}
+   :numShards {:type Long
+               :description "Number of output shards"
+               :default 0}})
+
+(defn run-datastore-word-count
+  [str-args]
+  (let [p (ds/make-pipeline 'DatastoreWordCountOptions str-args)
+        {:keys [input output dataset kind
+                namespace isReadOnly numShards]} (ds/get-pipeline-configuration p)
+        root (dts/make-ds-key {:kind kind :namespace namespace :key "root"})]
+    (when-not isReadOnly
+      (->> p
+           (ds/read-text-file input {:name "King-Lear"})
+           (ds/map (fn [content]
+                     (dts/make-ds-entity
+                      {:content content}
+                      {:namespace namespace
+                       :key (-> (UUID/randomUUID) (.toString))
+                       :kind kind
+                       :path [root]}))
+                   {:name "create-entities"})
+           (dts/write-datastore-raw
+            {:project-id dataset :name :write-datastore})))))
+
+
 ;;;;;;;;;;
 ;; Main ;;
 ;;;;;;;;;;
@@ -192,5 +240,6 @@
         "dedup" (run-dedup args)
         "filter" (run-filter args)
         "combine-per-key" (run-combine-per-key args)
-        "max-per-key" (run-max-per-key args))
+        "max-per-key" (run-max-per-key args)
+        "datastore-word-count" (run-datastore-word-count args))
       (ds/run-pipeline)))


display results in sorted table

display results in sorted table

--- src/desdemona/ui/table.cljs
+++ src/desdemona/ui/table.cljs
@@ -1,5 +1,10 @@
-(ns desdemona.ui.table)
+(ns desdemona.ui.table
+  (:require [wilson.dom :as wd]
+            [reagent.session :as session]))

 (defn table-component
   [state]
-  [:div [:h2 "Table"]])
+  (let [state-deref @state
+        rows (:results state-deref)
+        all-keys (-> (mapcat wd/get-all-keys rows) distinct wd/prepare-keys)]
+    (wd/sorted-table all-keys rows session/state)))


Add --load arg to docker build commands

Add --load arg to docker build commands

--- src/docker_clojure/core.clj
+++ src/docker_clojure/core.clj
@@ -123,7 +123,7 @@
         ;; TODO: Build for all appropriate platforms instead of just linux/amd64.
         ;;       alpine & JDK 8 won't build for arm64.
         build-cmd ["docker" "build" "--no-cache" "--platform" "linux/amd64"
-                   "-t" image-tag "-f" dockerfile "."]]
+                   "--load" "-t" image-tag "-f" dockerfile "."]]
     (println "Pulling base image" base-image)
     (pull-image base-image)
     (df/write-file build-dir dockerfile variant)


Typo fix

Typo fix

--- src/flatgui/util/matrix.clj
+++ src/flatgui/util/matrix.clj
@@ -17,16 +17,16 @@
                       [0 0 1 0]
                       [0 0 0 1]])

-(defn transtation-matrix
+(defn translation-matrix
   "Constructs translation matrix"
   ([tx ty tz]
    [[1 0 0 tx]
     [0 1 0 ty]
     [0 0 1 tz]
     [0 0 0  1]])
-  ([tx ty] (transtation-matrix tx ty 0)))
+  ([tx ty] (translation-matrix tx ty 0)))

-(def transtation transtation-matrix)
+(def translation translation-matrix)

 (defn defmxcol [& values]
   "Defines 1-column matrix"


A-Z are valid to a filename

A-Z are valid to a filename

--- src/ontrail/import.clj
+++ src/ontrail/import.clj
@@ -100,7 +100,7 @@
                      (get-exercises imported-html)))))

 (defn import-from-tempfile [user tempfile]
-  (let [user-clean (string/replace user #"[^a-z09]+" "_")
+  (let [user-clean (string/replace user #"[^a-z09A-Z]+" "_")
         import-file (File. "imports" (str user-clean ".html"))]
     (.info logger (str "Importing " import-file " for user " user))
     (if (not (.exists import-file))


exercise validity tests

exercise validity tests

--- src/ontrail/mutate.clj
+++ src/ontrail/mutate.clj
@@ -17,11 +17,11 @@
                                     "dd/MM/yyyy"
                                     "yyyy/MM/dd"))

-(defn parse-time [time-str]
+(defn parse-date [time-str]
   (format/parse multi-parser time-str))

 (defn date-ok? [date]
-  (not-nil? (try (parse-time date)
+  (not-nil? (try (parse-date date)
                  (catch Exception e (.debug logger (str "ex in parse date" date e))))))

 (defn sport-ok? [sport]
@@ -58,8 +58,8 @@
   (let [bare-ex {:title (:title user-ex)
                  :duration (parse-duration (:duration user-ex))
                  :sport (:sport user-ex)
-                 :creationDate (parse-time (:date user-ex))
-                 :lastModifiedDate (parse-time (:date user-ex))
+                 :creationDate (parse-date (:date user-ex))
+                 :lastModifiedDate (parse-date (:date user-ex))
                  :user user}
         body (if (nil? (:body user-ex)) "" (:body user-ex)) ;; disallow nil body
         tags (parse-tags (:tags user-ex))


more comments

more comments

--- src/onyx_java/test.clj
+++ src/onyx_java/test.clj
@@ -1,4 +1,6 @@
 ;;THIS IS CURRENTLY UNDER DEVELOPMENT, ALTHOUGH ANY INSTRUCTIONS PUT HERE WILL WORK.
+;;ULTIMATELY EVERYTHING WILL BE MOVED TO THE PROPER DIRECTORY AND ALL FUNCTIONALITY
+;;WILL BE PROPERLY SORTED.
 ;;use: cd into the main directory. run 'lein repl'. the default namespace should
 ;currently be onyx-java.test/
 ;;useage: for a specific class named "Entity" with a edn spec named "Entity.edn",


shrink the helper gears

shrink the helper gears

--- src/parinfer/core.cljs
+++ src/parinfer/core.cljs
@@ -113,11 +113,11 @@
   (js/scrollMonitor.recalculateLocations))

 (def base-gears
-  {:paren  {:x 240 :y 70
+  {:paren  {:x 280 :y 70
             :factor 96 ;:hole-radius 0.5
             :classes ["paren-gear"]
             :caption {:text "Parens" :side :left}}
-   :indent {:x 460 :y 70
+   :indent {:x 420 :y 70
             :factor 96
             :classes ["indent-gear"]
             :caption {:text "Indentation" :side :right}}})
@@ -137,12 +137,12 @@
    {:svg-opts {:width "100%" :height 200}
     :data {:init-gears (merge
                          base-gears
-                         {:auto-indent {:x 509 :y 123
-                                        :factor 64
+                         {:auto-indent {:x 465 :y 116
+                                        :factor 48
                                         :classes ["auto-indent-gear"]
                                         :caption {:text "auto-indent" :side :right}}
-                          :paredit {:x 191 :y 123
-                                    :factor 64
+                          :paredit {:x 235 :y 116
+                                    :factor 48
                                     :classes ["paredit-gear"]
                                     :caption {:text "paredit" :side :left}}})
            :anim-frames [{:gear-attrs {:auto-indent {:classes {"invisible" false}}}


show: print deps tree if no options passed

--- boot/core/src/boot/task/built_in.clj
+++ boot/core/src/boot/task/built_in.clj
@@ -106,10 +106,11 @@
    s snapshots bool "Include snapshot versions in updates searches."]

   (core/with-pre-wrap fileset'
-    (when deps    (print (pod/with-call-worker (boot.aether/dep-tree ~(core/get-env)))))
-    (when env     (println (pr-str (core/get-env))))
-    (when fileset (println (pr-str fileset')))
-    (when updates (mapv prn (pod/outdated (core/get-env) :snapshots snapshots)))
+    (cond
+      env     (println (pr-str (core/get-env)))
+      fileset (println (pr-str fileset'))
+      updates (mapv prn (pod/outdated (core/get-env) :snapshots snapshots))
+      :else   (print (pod/with-call-worker (boot.aether/dep-tree ~(core/get-env)))))
     fileset'))

 (core/deftask wait