Block access to server while catching up with the event stream

--- src/rill/event_channel.clj
+++ src/rill/event_channel.clj
@@ -10,23 +10,19 @@

 (defn push-event!!
   "push event to channel.
-returns false when we should not continue pushing."
+  returns false when we should not continue pushing."
   [ch event]
   (log/debug "Pushing" event "to channel")
   (boolean (>!! ch event)))

-(defn push-events!!
-  [ch cursor events]
-  (log/debug "pushing" (count events) "events to channel")
-  (let [r (loop [cursor cursor
-                 [event & events] events]
-            (if event
-              (if (push-event!! ch event)
-                (recur (:cursor (meta event)) events)
-                nil)
-              cursor))]
-    (log/debug "pushed - " r)
-    r))
+(defn push-events!!  [ch cursor events]
+  (loop [cursor cursor
+         [event & events] events]
+    (if event
+      (if (push-event!! ch event)
+        (recur (:cursor (meta event)) events)
+        nil)
+      cursor)))

 ;; This is the event you get from a channel when it has
 ;; caught up with the current head of the event stream
@@ -37,11 +33,13 @@
 (defn event-channel-listen!!
   "Push events from stream into ch. Blocking"
   [event-store stream-id cursor ch]
-  (loop [cursor cursor]
+  (loop [cursor cursor at-head false]
     (log/debug [:listen!! stream-id cursor])
     (when-let [new-cursor (push-events!! ch cursor (store/retrieve-events-since event-store stream-id cursor long-poll-seconds))]
-      (push-event!! ch (caught-up (Date.)))
-      (recur new-cursor))))
+      (when (not at-head)
+        (push-event!! ch (caught-up (Date.)))
+        (log/info "Caught up with head of event stream."))
+      (recur new-cursor true))))

 (defn event-channel
   "Start an event listener in a new thread and returns a channel


modify current route

modify current route

--- src_cljs/rrm/corecljs.cljs
+++ src_cljs/rrm/corecljs.cljs
@@ -3171,9 +3171,7 @@
 (defn main
   []
   (secretary/set-config! :prefix "#")
-  ;;(set-key-value :page-location [login])
-  (js/console.log (get-current-route!))
-  ;;(secretary/dispatch! (get-current-route!))
+  (secretary/dispatch! (get-current-route!))
   (r/render [page]
             (.getElementById js/document "app1"))
   (let [history (History.)]


Fix up corpus-to-tf-idf-data so that all values it returns are non-lazy and ready for serialization.

--- project/analyze-data/src/analyze_data/corpus_to_tf_idf.clj
+++ project/analyze-data/src/analyze_data/corpus_to_tf_idf.clj
@@ -15,8 +15,9 @@
     (map (partial zipmap header-row) data)))

 (defn corpus-to-tf-idf-data
-  "Transform a corpus of documents into a map containing tf-idf vectors and
-  other associated data.
+  "Transform a corpus of documents into a map containing a matrix of tf-idf
+  values and other associated data. All data will be non-lazy so as to be
+  suitable for serialization.

   corpus: a sequence of maps containing keys 'document-name' and
           'document-text', where 'document-name' is a unique identifier and
@@ -33,17 +34,17 @@
                 ...]}

   :all-terms is a sorted sequence of all terms found in the corpus.
-  :document-names is a sequence of the 'document-name' keys
+  :document-names is a sequence of the 'document-name' keys.
   :idf is a map from term to its inverse document frequency.
   :tf-idf is a core.matrix sparse matrix containing the tf-idf values. Each
           row corresponds to a document and each column to a term."
   [corpus]
-  (let [document-names (map #(% "document-name") corpus)
+  (let [document-names (mapv #(% "document-name") corpus)
         document-texts (map #(% "document-text") corpus)
         tf-idf-data (tf-idf (map to-terms document-texts))]
     (assoc tf-idf-data
-           :tf-idf (m/sparse-matrix (:tf-idf tf-idf-data))
-           :document-names document-names)))
+           :document-names document-names
+           :tf-idf (m/sparse-matrix (:tf-idf tf-idf-data)))))

 (defn csv-corpus-to-tf-idf-data!
   "Transform a csv containing a corpus of documents into a file of tf-idf


Visit Eductions as sequences

Visit Eductions as sequences

--- planck-cljs/src/planck/pprint/data.cljs
+++ planck-cljs/src/planck/pprint/data.cljs
@@ -56,8 +56,9 @@
   fipp.visit/IVisitor

   (visit-unknown [this x]
-    [:text (pr-str x)])
-
+    (if (instance? Eduction x)
+      (visit this (sequence x))
+      [:text (pr-str x)]))

   (visit-nil [this]
     (wrap-theme :results-font theme "nil"))


Riemann's Debian package depends on both Bash and Java.

--- tasks/leiningen/fatdeb.clj
+++ tasks/leiningen/fatdeb.clj
@@ -49,7 +49,7 @@
               :section "base"
               :priority "optional"
               :architecture "all"
-              :depends "bash"
+              :depends (join ", " ["bash" "default-jre | java2-runtime"])
               :maintainer (:email (:maintainer project))
               :description (:description project)})))


Add helper function for disabling request logging

--- dev-src/braid/dev/core.clj
+++ dev-src/braid/dev/core.clj
@@ -52,3 +52,8 @@
   "Helper function for stopping all Braid components."
   []
   (mount/stop))
+
+(defn disable-request-logging!
+  []
+  (taoensso.timbre/merge-config!
+    {:ns-filter {:allow #{"*"} :deny #{"braid.core.server.middleware"}}}))


figwheel.main: get rid of unused doc code

--- figwheel-main/src/figwheel/main/schema.clj
+++ figwheel-main/src/figwheel/main/schema.clj
@@ -406,28 +406,13 @@
 ;; Generate docs
 ;; ------------------------------------------------------------

-(defn last-line-example? [doc]
-  (try (when-let [lns (not-empty (string/split-lines doc))]
-         (when (> (count lns) 1)
-           (keyword? (read-string (last (string/split-lines doc))))))
-       (catch Throwable t nil)))
-
-(defn split-out-example [doc]
-  (if (last-line-example? doc)
-    (let [lns (string/split-lines doc)]
-      #_(prn lns)
-      [(string/join "\n" (butlast lns)) (last lns)])
-    [doc]))
-
 (defn markdown-option-docs [key-datas]
   (string/join
    "\n\n"
    (mapv (fn [{:keys [key doc]}]
            (let [k (keyword (name key))
                  [doc' example] (split-out-example doc)]
-             (format "## %s\n\n%s" (pr-str k) doc)
-             #_(cond-> (format "## %s\n\n%s" (pr-str k) doc')
-                 example (str "\n```\n" (string/trim example) "\n```"))))
+             (format "## %s\n\n%s" (pr-str k) doc)))
          key-datas)))

 (defn markdown-docs []


WIP - tests permissions

WIP - tests permissions

--- api/test/clojure/org/akvo/flow_api/sync.clj
+++ api/test/clojure/org/akvo/flow_api/sync.clj
@@ -22,15 +22,26 @@
                                    :entity {:formInstanceId form-instance-id
                                             :formId form-id}})})

-(def unilog-id (comp :unilog-id unilog/process-new-events-pure))
-(def form-instance-deletes (comp :form-instance-deleted unilog/process-new-events-pure))
-(def form-instance-changes (comp set :form-instances unilog/process-new-events-pure))
+(defn can-see [& form-ids]
+  (zipmap form-ids form-ids))
+
+(defn changes-with-permissions
+  ([events]
+   (let [r (unilog/process-new-events-pure events)]
+     (unilog/after-forms-loaded r (apply can-see (:forms-to-load r)))))
+  ([events perms]
+   (let [r (unilog/process-new-events-pure events)]
+     (unilog/after-forms-loaded r perms))))
+
+(def form-instance-changes (comp :form-instances-to-load changes-with-permissions))
+(def form-instance-deletes (comp :form-instance-deleted changes-with-permissions))
+(def unilog-id (comp :unilog-id changes-with-permissions))

 (deftest unilog-batch
   (testing "basic case"
-    (is (= #{{:form-id 24 :id 2} {:form-id 24 :id 10}}
+    (is (= #{{:form 24 :form-instance-ids #{2 10}}}
           (form-instance-changes [(form-instance 24 2)
-                                  (form-instance 24 10)])))
+                                   (form-instance 24 10)])))

     (let [last-form-instance (form-instance 100 99)]
       (is (= (:id last-form-instance)
@@ -39,18 +50,24 @@
                         last-form-instance])))))

   (testing "Multiple updates for the same form instance are grouped together"
-    (is (= #{{:form-id 23 :id 2}}
+    (is (= #{{:form 23 :form-instance-ids #{2}}}
           (form-instance-changes [(form-instance 23 2)
                                   (form-instance 23 2)]))))

   (testing "Answers"
-    (is (= #{{:form-id 8 :id 111}}
+    (is (= #{{:form 8 :form-instance-ids #{111}}}
           (form-instance-changes [(answer 8 111)]))))

   (testing "Answers and form instance updates are grouped together"
-    (is (= #{{:form-id 8 :id 111}}
+    (is (= #{{:form 8 :form-instance-ids #{111}}}
           (form-instance-changes [(answer 8 111)
                                   (form-instance 8 111)]))))
+
+  (testing "Not permission for some forms"
+    (is (= #{{:form {:any-form :definition} :form-instance-ids #{0}}}
+          (form-instance-changes [(answer 1 0)
+                                  (form-instance 2 0)]
+            {1 {:any-form :definition}}))))

   (testing "Delete form instance"
     (is (= #{10 11 112}


Fix: add missing require

Fix: add missing require

--- ssclj/jar/src/main/clojure/com/sixsq/slipstream/ssclj/middleware/proxy_redirect.clj
+++ ssclj/jar/src/main/clojure/com/sixsq/slipstream/ssclj/middleware/proxy_redirect.clj
@@ -11,6 +11,7 @@
     [clj-time.core :refer [in-seconds]]
     [clj-time.format :refer [formatters unparse with-locale]]
     [com.sixsq.slipstream.ssclj.middleware.base-uri :as buri]
+    [com.sixsq.slipstream.ssclj.util.config :as cf]
     [ring.util.response :as r]
     [clj-stacktrace.repl :as st])
   (:import


Fix order of waiting for all devices and opening the shutter

git-svn-id: 03a8048b5ee8463be5048a3801110fb50f378627@8993 d0ab736e-dc22-4aeb-8dc9-08def0aa14fd

--- acqEngine/src/org/micromanager/acq_engine.clj
+++ acqEngine/src/org/micromanager/acq_engine.clj
@@ -263,9 +263,10 @@
 (defn snap-image [open-before close-after]
   (with-core-setting [getAutoShutter setAutoShutter false]
     (let [shutter (core getShutterDevice)]
+      (wait-for-pending-devices)
       (when open-before
-        (set-shutter-open true))
-      (wait-for-pending-devices)
+        (set-shutter-open true)
+        (wait-for-device shutter))
       (device-best-effort (core getCameraDevice) (core snapImage))
       (swap! state assoc :last-image-time (elapsed-time @state))
       (when close-after


add cross-type test for update (param-setting)

--- test/asphalt/core_test.clj
+++ test/asphalt/core_test.clj
@@ -51,7 +51,8 @@
 -- ^boolean gender,
 ^int salary, ^string dept, ^date j_date FROM emp")

-(a/defsql t-crosstype "SELECT ^string j_date FROM emp")
+(a/defsql t-crosstype-select "SELECT ^string j_date FROM emp")
+(a/defsql t-crosstype-update "UPDATE emp SET j_date = ^string $jd")

 (a/defsql t-selfew "SELECT ^string name, ^int salary, ^string dept, ^date j_date FROM emp WHERE name = ?")

@@ -74,11 +75,18 @@
         row (zipmap [:name :salary :dept :joined] vs1)]
     (let [generated-key (a/genkey u/ds t-insert row)]
       (is (= 1 generated-key) "Verify that insertion generated a key"))
+    ;; retrieval test
     (is (= 1 (a/query a/fetch-single-value
                u/ds t-count [])) "Verify that row was inserted")
-    (is (= (str jd1) (a/query a/fetch-single-value u/ds t-crosstype [])))
-    (is (= [(str jd1)] (a/query a/fetch-single-row u/ds t-crosstype [])))
-    (is (= [[(str jd1)]] (a/query a/fetch-rows u/ds t-crosstype [])))))
+    (is (= (str jd1) (a/query a/fetch-single-value u/ds t-crosstype-select [])))
+    (is (= [(str jd1)] (a/query a/fetch-single-row u/ds t-crosstype-select [])))
+    (is (= [[(str jd1)]] (a/query a/fetch-rows u/ds t-crosstype-select [])))
+    ;; update test
+    (let [strdate "2016-02-01"]
+      (t-crosstype-update u/ds [strdate])
+      (is (= strdate (a/query a/fetch-single-value u/ds t-crosstype-select [])))
+      (is (= [strdate] (a/query a/fetch-single-row u/ds t-crosstype-select [])))
+      (is (= [[strdate]] (a/query a/fetch-rows u/ds t-crosstype-select []))))))

 (deftest test-params


Fix reflection warning in test

Fix reflection warning in test

--- test/mavlink/core_test.clj
+++ test/mavlink/core_test.clj
@@ -56,7 +56,7 @@
        (.update-checksum crc
                          (if (.isArray (class the-bytes))
                            (aget ^bytes the-bytes idx)
-                           (.get ^java.nio.ByteBuffer the-bytes idx))))
+                           (.get ^java.nio.ByteBuffer the-bytes (int idx)))))
      (when crc-seed
        (.update-checksum crc crc-seed))
      (.crcValue crc))))


(maint) Remove test reference to legacy "masterport" setting

--- test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj
+++ test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj
@@ -560,7 +560,7 @@
        (try
          (testing "Various data types"
            (is (= "ldap" (.getSetting jruby-puppet "ldapserver")))
-           (is (= 8140 (.getSetting jruby-puppet "masterport")))
+           (is (= 8140 (.getSetting jruby-puppet "serverport")))
            (is (= false (.getSetting jruby-puppet "onetime"))))
          (finally
            (jruby-testutils/return-instance jruby-service jruby-instance :settings-plumbed-test)))))))


- Added information on global loading markers - Integrated sections about Om philosophy and loading - Added section on modifying the AST when sending to the server - Added section on returning tempids from server mutations

--- src/tutorial/untangled_tutorial/H_Server_Interactions.cljs
+++ src/tutorial/untangled_tutorial/H_Server_Interactions.cljs
@@ -4,9 +4,6 @@
             [om.dom :as dom]
             [devcards.core :as dc :refer-macros [defcard defcard-doc]]))

-; TODO: Lots of TODO items below
-; TODO: Modifying a remote AST (e.g. add parameters to query)
-; TODO: WHY you don't return a value from a mutation (ask Ethan for my "blog" post on this)
 ; TODO: Explain Om's HATEOS (keyword listing in :keys)
 ; see: https://github.com/omcljs/om/wiki/Quick-Start-%28om.next%29
 ; TODO: Error handling (UI and server), exceptions, fallbacks, status codes
@@ -28,7 +25,178 @@

   ## Reads

-  Remote reads in Untangled are explicit. There is no Om parser to write on the client side.
+  Remote reads in Untangled are explicit calls to the load functions in the `untangled.client.data-fetch` namespace, of
+  which there are two pairs:
+
+  1. `load-data` and `load-data-action`
+  2. `load-field` and `load-field-action`
+
+  ### Load Data vs. Load Field
+
+  Both of these function families are calls to the built-in `untangled/load` mutation, so requests made by either function
+  go through the same networking layer and have the same customizations through their named parameters (see the doc strings
+  in the [data-fetch namespace](https://github.com/untangled-web/untangled-client/blob/master/src/untangled/client/data_fetch.cljs)).
+  The only difference is that `load-data` must be passed a complete query while `load-field` uses the passed-in component
+  to create its query.
+
+  So, `load-field` focuses the component's query to the specified field, associates the component's ident with the query,
+  and asks the UI to re-render all components with the calling component's ident after the load is complete. Since `load-field`
+  requires a component to build the query sent to the server, it cannot be used with the reconciler. If you want to load data
+  from your server when the app initially loads, you must use `load-data` with the reconciler passed to the
+  `:started-callback` function when creating a new untangled client application.
+
+  #### Use case - Lazy loading
+
+  One common operation is to load data in response to a user interaction. Interestingly, the query that you might
+  have used in the initial load use case might have included UI queries for data you didn't want to fetch yet. So, we want
+  to note that the initial load use-case supports eliding part of the query. For example, you can load an item without,
+  say, comments. Later, when the user wants to see comments you can supply a button that can load the comments on demand.
+
+  This is directly supported by `load-field`, which derives the query to send to the server from the component itself!
+
+  ```
+  (load-field this :comments)
+  ```
+
+  The only requirements are that the component has an Ident and the query for the component includes a join or property
+  named `:comments` in the query.
+
+  For example, say you had:
+
+  ```
+  (defui Item
+     static om/IQuery
+     (query [this] [:id :value {:comments (om/get-query Comment)}])
+     static om/Ident
+     (ident [this props] [:item/by-id (:id props)])
+     Object
+     (render [this]
+        ...
+        (dom/button #js { :onClick #(df/load-field this :comments) } \"Load comments\")
+        ...)
+
+  ;; Note that the call to load field above is the same as:
+      (df/load-data this (om/full-query this [:comments])
+        :ident (om/get-ident this)
+        :refresh [(om/get-ident this)])
+  ```
+
+  then clicking the button will result in the following query to the server:
+
+  ```
+  [{[:item/by-id 32] [{:comments [:other :props]]}]
+  ```
+
+  and the code to write for the server is now trivial. The dispatch key is :item/by-id, the 32 is accessible on the AST,
+  and the query is a pull fragment that will work relative to an item in your (assuming Datomic) database!
+
+  Furthermore, the underlying code can easily put a marker in place of that data in the app state so you can show the
+  'load in progress' marker of your choice.
+
+  Untangled has supplied all of the Om plumbing for you.
+
+  ### Load vs. Load-Action
+
+  Both `load-field` and `load-data` will call `om/transact!` under the hood, targeting untangled's built-in `untangled/load`
+  mutation, which is responsible for sending your request to the server. By contrast, `load-field-action` and `load-data-action`,
+  **do not** call `om/transact!`, and are used to initialize a load inside of one of your custom client-side mutations.
+
+  Let's look at an example of a standard load. Say you want to load a list of people from the server:
+  ```
+  (require [untangled.client.data-fetch :as df])
+
+  (defui Person
+    static om/IQuery (query [this] [:id :name :ui/fetch-state])
+    ... )
+  (def ui-person (om/factory Person))
+
+  (defui PeopleList
+    static om/IQuery (query [this] [:db/id :list-title {:people (om/get-query Person}]
+    static om/Ident (ident [this props] [:people-list/by-id (:db/id props)])
+    Object
+    (render [this]
+      (let [{:keys [people]} (om/props this)]
+        ;; people starts out as nil
+        (dom/div nil
+          (df/lazily-loaded #(map ui-person %) people
+            :not-present-render #(dom/button #js {:onClick #(df/load-field this :people)}
+                                   \"Load People\"))))))
+  ```
+
+  Since we are in the UI and not inside of a mutation's action thunk, we want to use `load-field` to initialize the
+  call to `om/transact!`. The use of `lazily-loaded` above will show a button to load people when `people` is `nil`
+  (for example, when the app initially loads), and will render each person in the list of people once the button is
+  clicked and the data has been loaded. By including `:ui/fetch-state` in the subcomponent's query, `lazily-loaded`
+  is able to render different UIs for ready, loading, and failure states as well. See the
+  [lazy loading cookbook recipe](https://github.com/untangled-web/untangled-cookbook/tree/master/recipes/lazy-loading-visual-indicators)
+  for a running example.
+
+  The action-suffixed load functions are useful when performing an action in the user interface that must *both* modify
+  the client-side database *and* load data from the server. They must be used with `untangled.client.data-fetch/remote-load`,
+  which converts the remote mutation key for your mutation to the `untangled/load` key:
+
+  ```
+  (require [untangled.client.data-fetch :as df]
+           [untangled.client.mutations :refer [mutate]]
+           [app.ui :as ui] ;; namespace with defuis
+           )
+
+  (defmethod mutate 'app/change-view [{:keys [state] as env} _ {:keys [new-view]}]
+    {:remote (df/remote-load env)
+     :action (fn []
+                (let [new-view-query (cond
+                                       (= new-view :main) (om/get-query ui/Main)
+                                       (= new-view :settings) (om/get-query ui/Settings)]
+                (df/load-data-action state new-view-query)
+                (swap! state update :app/current-view new-view))})
+  ```
+  This snippet defines a mutation that (1) modifies the app state to display the view passed in via the mutation parameters,
+  and (2) loads the data for that view. A few important points:
+
+  1. If an action thunk calls an action-suffixed load, then it MUST call `remote-load` for the remote keyword.
+  2. The `remote-load` function *changes* the mutation's dispatch key to `untangled/load` when sending it to the remote.
+  So, a mutation that uses an action-suffixed load cannot have a server-side implementation.
+  3. If you find yourself wanting to put a call to `load-data` or `load-field` in a React Lifecycle method, try reworking
+  the code to use the action-suffixed load instead. To learn more about the dangers of loads and lifecycle methods, see
+  the [reference on loading data]().
+
+  ### How reads work : `untangled/load`
+
+  The helper functions described above simply trigger a built-in Untangled mutation called `untangled/load`, which you are
+  allowed (and sometimes encouraged) to use directly. It is the Untangled method of doing follow-on reads after a remote
+  mutation:
+
+  ```
+  (om/transact! this '[(app/do-some-thing) (untangled/load {:query [:a]})])
+  ```
+
+  The normal form of follow-on keywords (for re-rendering the UI) works fine, it will just never trigger remote
+  reads.
+
+  The `untangled/load` mutation does a very simple thing: It puts a state marker in a well-known location in your app state
+  to indicate that you're wanting to load something (and returns `:remote true`). This causes the network
+  plumbing to be triggered. The network plumbing only receives mutations that are marked remote, so it does the following:
+
+  - It looks for the special mutations `untangled/load` and `tx/fallback`. The latter is part of the unhappy path handling.
+     - For each load, it places a state marker in the app state at the target destination for the query data
+     - All loads that are present are combined together into a single Om query
+  - It looks for other mutations
+  - It puts the 'other mutations' on the send queue
+  - It puts the derived query from the `untangled/load` onto the send queue
+
+  A separate \"thread\" (core async go block) watches the send queue, and sends things one-at-a-time (e.g. each entry
+  in the queue is processed in a sequence, ensuring you can reason about things sequentially). The one-at-a-time
+  semantics are very important for handling tempid reassignment, rational optimistic updates, and unhappy path handling.
+
+  The send processing block (uses core async to make a thread-like infinite loop):
+
+  - Pulls an item from the queue (or \"blocks\" when empty)
+  - Sends it over the network
+  - Updates the marker in the app state to `loading` (which causes a re-render, so you can render loading UI)
+  - \"waits\" for the response
+      - On success: merges the data
+      - On error: updates the state marker to an error state (which re-renders allowing the UI to show error UI)
+  - Repeats in an infinite loop

   ### UI attributes

@@ -43,7 +211,7 @@
   it component local), then you have a problem when that component is composed into another component that
   is to be used when generating a server query. You don't want the UI-specific attributes to *go* to the server!

-  Untangled handles this for you. Any attributes in you component queries that are namespaced to `ui` are automatically
+  Untangled handles this for you. Any attributes in your component queries that are namespaced to `ui` are automatically
   (and recursively) stripped from queries before they are sent to the server. There is nothing for you to do except
   namespace these local-only attributes in the queries! (Additionally, there are local mutation
   helpers that can be used to update these without writing custom mutation code. See the section on Mutation)
@@ -73,7 +241,7 @@
   - However, if you do it, Untangled merges with the following rules:
       - If the query *asks* for an attribute, and the *response does not include it*, then it is always removed from the app state since the
       server has clearly indicated it is gone.
-      - The the query *does not ask* for an attribute (which means the response cannot possibly contain it), then Untangled
+      - If the query *does not ask* for an attribute (which means the response cannot possibly contain it), then Untangled
       will avoid removing it, even if other attributes come back (e.g. it will be a merge leaving the property that was
       not asked for alone). This does indicate that your UI is possibly in a state inconsistent with the server, which
       is the reason for the \"avoid this case\" advice.
@@ -99,22 +267,70 @@

   ### Optimistic (client) changes

-  TODO: Just like Om
-
-  ### Server writes
-
-  TODO: Just like Om (`:remote true` or `:remote ast`); however Untangled adds in error handling triggering via tx fallbacks:
-
-  ```
-  (om/transact! this '[(app/f) (tx/fallback {:action mutation-symbol :param 1})])
-  ```
-
-  assuming the `app/f` mutation returns `{:remote true}` (or `{:remote AST}`)  this sends `app/f` to the server.
-  If the server throws an error
-  (via ex-info) then the fallback action's mutation symbol (a dispatch key for mutate) is invoked on the client with
-  params that include the client fallback params (`:param 1` in the example) and an `:error` key that includes the
-  details of the server exception (error type, message, and ex-info's data). Be sure to only include serializable data
-  in the server exception!
+  There is no difference between optimistic updates in Untangled and in standard Om Next. You define a mutation, that
+   mutation does a `swap!` on the `state` atom from the mutation's `env` parameter, and you're done. The details are
+   covered in [Section G - Mutations](http://localhost:3449/#!/untangled_tutorial.G_Mutation).
+
+  ### Sending and responding to server writes
+
+  Sending mutations to the server also behaves the same way that it does in standard Om Next. A mutation is sent to
+  the server when it returns a map with a key `:remote` and either a value of `true` or of a modified query AST (abstract
+  syntax tree). Here are examples of both:
+
+  ```clojure
+  (require [untangled.client.mutations :refer [mutate]])
+
+  (defmutation mutate 'some/mutation [env k params]
+    ;; sends this mutation with the same `env`, `k`, and `params` arguments to the server
+    {:remote true
+     :action (fn[] ... )})
+  ```
+
+  ```clojure
+  (defmutation mutate 'some/mutation [{:keys [ast] :as env} k params]
+    ;; adds the key-value pair {:extra :data} to the `params` that are sent to the server
+    {:remote (assoc-in ast [:params :extra] :data)
+     :action (fn[] ...)})
+  )
+  ```
+
+  ```clojure
+  (defmutation mutate 'some/mutation [{:keys [ast] :as env} k params]
+    ;; changes the mutation dispatch key -- the assumption is that the server processes
+    ;; 'some/mutation as part of a different server-side mutation
+    {:remote (assoc ast :key 'server/mutation :dispatch-key 'server/mutation)
+     :action (fn[] ...)})
+  )
+  ```
+
+  **Note:** Om action thunks are executed **before** the remote read, so if you want to delete data from the client that you
+  need to pass to the server, you will have to keep track of that state before removing it from your app state.
+
+  #### Mutation Fallbacks
+
+  One of the advantages to using Untangled is its support for error handling via what are called transaction fallbacks:
+
+  ```
+  (om/transact! this '[(some/mutation) (tx/fallback {:action handle-failure})])
+  ```
+
+  ```
+  (require [untangled.client.mutations :refer [mutate]])
+
+  (defmutation mutate 'some/mutation [{:keys [state] :as env} k params]
+    {:remote true
+     :action (fn [] (swap! state do-stuff)})
+
+  (defmutation mutate 'handle-failure [{:keys [state] :as env} k {:keys [error] :as params}]
+    ;; fallback mutations are designed to recover the client-side app state from server failures
+    ;; so, no need to send to the server
+    {:action (fn [] (swap! state undo-stuff error)))
+  ```
+
+  Assuming that `some/mutation` returns `{:remote true}` (or `{:remote AST}`)  this sends `some/mutation` to the server.
+  If the server throws an error then the fallback action's mutation symbol (a dispatch key for mutate) is invoked on the
+  client with params that include an `:error` key that includes the details of the server exception (error type, message,
+  and ex-info's data). Be sure to only include serializable data in the server exception!

   You can have any number of fallbacks in a tx, and they will run in order if the transaction fails.

@@ -123,29 +339,84 @@
   where the tx that failed indicates the app state is invalid...additional network interactions are probably all going to
   fail. What you want to do is trigger some kind of state reload to restore sanity.

-  #### Updating an existing item
-
-  TODO: Just like Om
+  #### Remote reads after a mutation
+
+  In Om, you can list properties after your mutation to indicate re-renders. You can force them to be remote reads by
+  quoting them. All of this requires complex logic in your parser to compare flags on the AST, process the resulting
+  query in send (e.g. via process-roots), etc. It is more flexible, but the very common case can be made a lot more direct.
+
+  ```
+  (om/transact! this '[(app/f) ':thing]) ; run mutation and re-read thing on the server...
+  ; BUT YOU implement the logic to make sure it is understood that way!
+  ```
+
+  In Untangled, follow-on keywords are always local re-render reads, and nothing more:
+
+  ```
+  (om/transact! this '[(app/f) :thing])
+  ; Om and Untangled: Do mutation, and re-render anything that has :thing in a query
+  ```
+
+  Instead, we supply access to the internal mutation we use to queue loads, so that remote reads are simple and explicit:
+
+  ```
+  ; Do mutation, then run a remote read of the given query, along with a post-mutation
+  ; to alter app state when the load is complete.
+  (om/transact! this `[(app/f) (untangled/load {:query ~(om/get-query Thing) :post-mutation after-load-sym}])
+  ```
+
+  Of course, you can (and *should*) use syntax quoting to embed a query from (om/get-query) so that normalization works,
+  and the post-mutation can be used to deal with the fact that other parts of the UI may want to, for example, point
+  to this newly-loaded thing. The `after-load-sym` is a symbol (e.g. dispatch key to the mutate multimethod). The multi-method
+  is guaranteed to be called with the app state in the environment, but no other Om environment items are ensured at
+  the moment.
+
+  ### Writing Server Mutations
+
+  Server-side mutations in Untangled are written the same way as in Om. A mutation returns a map with a key `:action`
+  and a function of no variables as a value. That function then creates, updates, and/or deletes data from the data
+  storage passed in the `env` parameter to the mutation.

   #### New item creation – Temporary IDs

-  TODO: Similar to Om, but `{ :tempids tempidmap }` can be returned from `:action` of server mutation and it will
-  just work. The plumbing is pre-written.
-
-  #### Reading results after a mutation
-
-  See notes on built-in call (untangled/load)
+  Om has a built in function `tempid` that will generate an om-specific temporary ID. This allows the normalization
+  and denormalization of the client side database to continue working while the server processes the new data and returns
+  the permanent identifiers. Here are the client-side and server-side implementations of the same mutation:
+
+  ```
+  ;; client
+  (require [untangled.client.mutations :refer [mutate]])
+
+  (defmethod mutate 'item/new [{:keys [state]} _ {:keys [tempid text]}]
+    {:remote true
+     :action (fn [] (swap! state assoc-in [:item/by-id tempid] {:db/id tempid :item/text text})))})
+  ```
+
+  ```
+  (defmulti server-mutate)
+
+  (defmethod server-mutate 'item/new [{:keys [database]} _ {:keys [tempid text]}]
+    {:action (fn []
+              (let [database-tempid (make-database-tempid)
+                    database-id (add-item-to-database database {:db/id database-tempid :item/text text})]
+
+                {:tempids {tempid database-id}})))
+  ```
+
+  Assuming that `server-mutate` is specified as the mutation function for your server-side parser, the tempid remaps
+  built on the server will be passed back to the client, where Untangled will do a recursive walk of the client-side
+  database to replace every instance of the tempid with the database id. Note that the map at the :tempids key can have
+  more than one tempid to database-id pair.

   ## Differences from stock Om (Next)

   For those that are used to Om, you may be interested in the differences and rationale behind the way Untangled
-  handles server interactions, particularly remote reads.
+  handles server interactions, particularly remote reads. There is no Om parser to do remote reads on the client side.

   In Om, you have a fully customizable experience for reads/writes; however, to get this power you must write
   a parser to process the queries and mutations, including analyzing application state to figure out when to talk
   to a remote. While this is fully general and quite flexible (Untangled is implemented on top of it, after all),
   there is a lot of head scratching to get the result you want.
-
   Our realization when building Untangled was that remote reads happen in two basic cases: initial load (based on
   initial application state) and event-driven load (e.g. routing, \"load comments\", etc). Then we had a few more
   facts that we threw into the mix:
@@ -206,127 +477,29 @@
   (e.g. showing a dialog that has user-driven recovery choices).

-  ### Use case - Lazy loading
-
-  The other major case is wanting to load data in response to a user interaction. Interestingly, the query that you might
-  have used in the initial load use case might have included UI queries for data you didn't want to fetch yet. So, we want
-  to note that the initial load use-case supports eliding part of the query. For example, you can load an item without,
-  say, comments. Later, when the user wants to see comments you can supply a button that can load the comments on demand.
-
-  This is directly supported by `load-field`, which derives the query to send to the server from the component itself!
-
-  ```
-  (load-field this :comments)
-  ```
-
-  The only requirements are that the component has an Ident and the query for the component includes a join or property
-  named `:comments` in the query.
-
-  For example, say you had:
-
-  ```
-  (defui Item
-     static om/IQuery
-     (query [this] [:id :value {:comments (om/get-query Comment)}])
-     static om/Ident
-     (ident [this props] [:item/by-id (:id props)])
-     Object
-     (render [this]
-        ...
-        (dom/button #js { :onClick #(df/load-field this :comments) } \"Load comments\")
-        ...)
-  ```
-
-  then clicking the button will result in the following query to the server:
-
-  ```
-  [{[:item/by-id 32] [{:comments [:other :props]]}]
-  ```
-
-  and the code to write for the server is now trivial. The dispatch key is :item/by-id, the 32 is accessible on the AST,
-  and the query is a pull fragment that will work relative to an item in your (assuming Datomic) database!
-
-  Furthermore, the underlying code can easily put a marker in place of that data in the app state so you can show the
-  'load in progress' marker of your choice.
-
-  Untangled has supplied all of the Om plumbing for you.
-
-  #### How reads work : `untangled/load`
-
-  The helper functions described above simply trigger a built-in Untangled mutation called `untangled/load`, which you are
-  allowed (and sometimes encouraged) to use directly. It is the Untangled method of doing follow-on reads after a remote
-  mutation:
-
-  ```
-  (om/transact! this '[(app/do-some-thing) (untangled/load {:query [:a]})])
-  ```
-
-  The normal form of follow-on keywords (for re-rendering the UI) works fine, it will just never trigger remote
-  reads.
-
-  The `untangled/load` mutation does a very simple thing: It puts a state marker in a well-known location in your app state
-  to indicate that you're wanting to load something (and returns `:remote true`). This causes the network
-  plumbing to be triggered. The network plumbing only receives mutations that are marked remote, so it does the following:
-
-  - It looks for the special mutations `untangled/load` and `tx/fallback`. The latter is part of the unhappy path handling.
-     - For each load, it places a state marker in the app state at the target destination for the query data
-     - All loads that are present are combined together into a single Om query
-  - It looks for other mutations
-  - It puts the 'other mutations' on the send queue
-  - It puts the derived query from the `untangled/load` onto the send queue
-
-  A separate \"thread\" (core async go block) watches the send queue, and sends things one-at-a-time (e.g. each entry
-  in the queue is processed in a sequence, ensuring you can reason about things sequentially). The one-at-a-time
-  semantics are very important for handling tempid reassignment, rational optimistic updates, and unhappy path handling.
-
-  The send processing block (uses core async to make a thread-like infinite loop):
-
-  - Pulls an item from the queue (or \"blocks\" when empty)
-  - Sends it over the network
-  - Updates the marker in the app state to `loading` (which causes a re-render, so you can render loading UI)
-  - \"waits\" for the response
-      - On success: merges the data
-      - On error: updates the state marker to an error state (which re-renders allowing the UI to show error UI)
-  - Repeats in an infinite loop
-
-  #### Using `untangled/load` directly
-
-  TODO: See the helper functions `load-data` and `load-field`. We might add more specific versions of `untangled/load`
-  that provide a clearer end-user API.
-
-  ### Remote reads after a mutation
-
-  In Om, you can list properties after your mutation to indicate re-renders. You can force them to be remote reads by
-  quoting them. All of this requires complex logic in your parser to compare flags on the AST, process the resulting
-  query in send (e.g. via process-roots), etc. It is more flexible, but the very common case can be made a lot more direct.
-
-  ```
-  (om/transact! this '[(app/f) ':thing]) ; run mutation and re-read thing on the server...
-  ; BUT YOU implement the logic to make sure it is understood that way!
-  ```
-
-  In Untangled, follow-on keywords are always local re-render reads, and nothing more:
-
-  ```
-  (om/transact! this '[(app/f) :thing]) ; Om and Untangled: Do mutation, and re-render anything that has :thing in a query
-  ```
-
-  Instead, we supply access to the internal mutation we use to queue loads, so that remote reads are simple and explicit:
-
-  ```
-  ; Do mutation, then run a remote read of the given query, along with a post-mutation to alter app state when the load is complete
-  (om/transact! this `[(app/f) (untangled/load {:query ~(om/get-query Thing) :post-mutation after-load-sym}])
-  ```
-
-  Of course, you can (and *should) use syntax quoting to embed a query from (om/get-query) so that normalization works,
-  and the post-mutation can be used to deal with the fact that other parts of the UI may want to, for example, point
-  to this newly-loaded thing. The `after-load-sym` is a symbol (e.g. dispatch key to the mutate multimethod). The multi-method
-  is guaranteed to be called with the app state in the environment, but no other Om environment items are ensured at
-  the moment.
-
-
   ### Global network activity marker

-  TODO: Document how to use the network activity marker to show a general purpose loading marker in the UI. Basically query
-  to the top-level (via an Om query link).
+  Untangled will automatically maintain a global network activity marker at the top level of the app state under the
+  keyword `:ui/loading-data`. This key will have a `true` value when there are network requests awaiting a response from
+  the server, and will have a `false` value when there are no network requests in process.
+
+  You can access this marker from any component that composes to root by including a link in your component's query:
+
+  ```
+  (defui Item ... )
+  (def ui-item (om/factory Item {:keyfn :id}))
+
+  (defui List
+    static om/IQuery (query [this] [:id :title {:items (om/get-query Item)} [:ui/loading-data '_]]
+    ...
+    (render [this]
+      (let [{:keys [title items ui/loading-data]} (om/props this)]
+        (if (and loading-data (empty? items))
+          (dom/div nil \"Loading...\")
+          (dom/div nil
+            (dom/h1 nil title)
+            (map ui-item items))))
+  ```
+  Because the global loading marker is at the top level of the application state, do not use the keyword as a follow-on
+  read to mutations because it may unnecessarily trigger a re-render of the entire application.
 ")


fixed typos

fixed typos

--- src/tutorial/untangled_tutorial/H_Server_Interactions.cljs
+++ src/tutorial/untangled_tutorial/H_Server_Interactions.cljs
@@ -619,14 +619,14 @@
   ```clojure
   (require [untangled.client.mutations :refer [mutate]])

-  (defmutation mutate 'some/mutation [env k params]
+  (defmethod mutate 'some/mutation [env k params]
     ;; sends this mutation with the same `env`, `k`, and `params` arguments to the server
     {:remote true
      :action (fn[] ... )})
   ```

   ```clojure
-  (defmutation mutate 'some/mutation [{:keys [ast] :as env} k params]
+  (defmethod mutate 'some/mutation [{:keys [ast] :as env} k params]
     ;; adds the key-value pair {:extra :data} to the `params` that are sent to the server
     {:remote (assoc-in ast [:params :extra] :data)
      :action (fn[] ...)})
@@ -634,7 +634,7 @@
   ```

   ```clojure
-  (defmutation mutate 'some/mutation [{:keys [ast] :as env} k params]
+  (defmethod mutate 'some/mutation [{:keys [ast] :as env} k params]
     ;; changes the mutation dispatch key -- the assumption is that the server processes
     ;; 'some/mutation as part of a different server-side mutation
     {:remote (assoc ast :key 'server/mutation :dispatch-key 'server/mutation)
@@ -656,11 +656,11 @@
   ```
   (require [untangled.client.mutations :refer [mutate]])

-  (defmutation mutate 'some/mutation [{:keys [state] :as env} k params]
+  (defmethod mutate 'some/mutation [{:keys [state] :as env} k params]
     {:remote true
      :action (fn [] (swap! state do-stuff)})

-  (defmutation mutate 'handle-failure [{:keys [state] :as env} k {:keys [error] :as params}]
+  (defmethod mutate 'handle-failure [{:keys [state] :as env} k {:keys [error] :as params}]
     ;; fallback mutations are designed to recover the client-side app state from server failures
     ;; so, no need to send to the server
     {:action (fn [] (swap! state undo-stuff error)))


with-state-changes working.

with-state-changes working.

--- test/poky/kv/jdbc_test.clj
+++ test/poky/kv/jdbc_test.clj
@@ -54,18 +54,15 @@
   (kv.jdbc/purge-bucket (kv/connection state) bucket))

-; this is a little less than ideal, but it works. to use with-state-changes,
-; you have to nest the facts like so as opposed to the wrapping convention
-; above. i believe it is the let that is causing the issue but I haven't tested
-; it
-(with-state-changes [(before :facts (do (reset! S (kv.jdbc/create (env :database-url)))
-                                 (clear-testing-bucket @S bucket)))
-                     (after :facts (do (kv/close @S)))]
-  (let [k (util/random-string 10)
-        v (util/random-string 10)]
-    (fact :integration
-          (kv.jdbc/jdbc-set (kv/connection @S) bucket k v) => (contains {:key k :data v}))
-    (fact :integration
-          (do
-            (kv.jdbc/jdbc-set (kv/connection @S) bucket k v)
-            (kv.jdbc/jdbc-set (kv/connection @S) bucket k v)) => '(1))))
+(with-state-changes [(around :facts (do (reset! S (kv.jdbc/create (env :database-url)))
+                                        ; alternatively, use sql/transaction
+                                        ; and rollback. that's possible, but
+                                        ; would require some refactoring of
+                                        ; kv.jdbc to support in nested within
+                                        ; sql/connection.
+                                        (clear-testing-bucket @S bucket)
+                                        ?form
+                                        (kv/close @S)))]
+    (facts :integration
+           (kv.jdbc/jdbc-set (kv/connection @S) bucket "key" "value") => (contains {:key "key" :data "value"})
+           (kv.jdbc/jdbc-set (kv/connection @S) bucket "key" "value") => '(1)))


frames basicaly working

frames basicaly working

--- clojure_dmap/utilities/frame.clj
+++ clojure_dmap/utilities/frame.clj
@@ -9,10 +9,10 @@

 ;; TODO
 ;; - abstractions and specializatons are linked lists, you only go up or down one level and then that frame tells you the next level up/down
-;;   - working on adding the abs when you add a spec, and vice-versa (see START HERE)
-;; ------------ not getting both, crashes
+;; - desperately need a testing framework
 ;; - switch from defstructs to the new thing...records
 ;; - features not slots
+;; - *** (nil) for specialization lists instead of ()

 (defstruct frame :name :features :specializations :abstractions)

@@ -37,17 +37,23 @@
 								))  ))
 				; specializations
 				(cond (frame-of frame-name)
-						(conj ((frame-of frame-name) :specializations (first stuff)))
+						(conj ((frame-of frame-name) :specializations
+							;(first stuff)
+					 		(cond (empty? stuff) (list) :t (list (first stuff)))
+						))
 					:t 	(cond (empty? stuff) (list) :t (list (first stuff))))

 				; abstractions
 				(cond (frame-of frame-name)
-						(conj ((frame-of frame-name) :abstractions (second stuff)))
+						(conj ((frame-of frame-name) :abstractions
+							;(second stuff)
+					 		(cond (empty? stuff) (list) :t (list (second stuff)))
+						))
 					:t 	(cond (empty? stuff) (list) :t (list (second stuff))))
 ))))

 (defn get-specializations [frame-name]
-	(:specializations (frame-of frame-name) :specializations))
+	(:specializations (frame-of frame-name) ))
 	;;(cond (not (nil? (frame-of frame-name))) ((frame-of frame-name) :specializations)))

 (defn get-abstractions [frame-name]
@@ -95,20 +101,14 @@
 (defn get-feature
 	"looks first in the native frame, then abstractions, then specializations, returning the first found"
  [frame-name feature-name]
-	(println "x"  (frame-name frame-of) )
 	(cond (feature-name (:features (frame-name frame-of)) )
-		; look local
-		 (feature-name (:features (frame-name frame-of)))
-		:t
-		; look in abstractions and specializations
-		(and
-			:t
-			;; for the list of frames in the specializations list, try to get the feature there recursively
-			(map (fn [fr-name]  (get-feature fr-name feature-name))
-				 	(get-specializations frame-name ))
-			(map (fn [fr-name]  (get-feature fr-name feature-name))
-				 	(get-abstractions frame-name ))
-)))
+			(feature-name (:features (frame-name frame-of)))
+		  :t
+			(and
+				(map (fn [fr-name] 	(get-feature fr-name feature-name))
+				 	(get-specializations frame-name ))
+				(map (fn [fr-name]  (get-feature fr-name feature-name))
+			 		(get-abstractions frame-name ))  )))

 (defn print-frame [frame-name]
 	(cond (and
@@ -123,41 +123,46 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

-(print-frame nil)
-(create-frame :empty-person (list :fname :lname :mi)  nil  )
-(print-frame :empty-person)
-(println "empty person:")
-(print-frame :empty-person)
-(println "--------------")
+;;(print-frame nil)
+;;(create-frame :empty-person (list :fname :lname :mi)  nil  )
+;;(print-frame :empty-person)
+;;(println "empty person:")
+;;(print-frame :empty-person)
+;;(println "--------------")

-(create-frame :new-person (list :fname :lname :mi) (list "Bob" "Jones" "X") nil nil)
-(println "new person:")
-(print-frame :new-person)
-(println "last name of new person:")
-(println (base-slot :new-person :lname))
-(println "--------------")
+(create-frame :new-person (list :fname :lname :mi) (list "Bob" "Jones" "X") )
+;;(println "new person:")
+;;(print-frame :new-person)
+;;(println "last name of new person:")
+;;(println (base-slot :new-person :lname))
+;;(println "--------------")

 (create-frame :base-thing (list :dbid :mode) (list 123 :test))
 (create-frame :new-director (list :company :title :salary) (list "The" "Director" 1000000) :new-person :base-thing)
-(println "director with base-thing as abstraction, and new-person as specialization")
-(print-frame :new-director)
-(println "--------------")
-(println "specializations:" (get-specializations :new-director))
-(println "THIS IS NULL ?print-frame of specializations") (print-frame (first(get-specializations :new-director)))
-(print-frame :base-thing)
-(println "------------_________________--")
-(println "abstractions:" (get-abstractions :new-director))
-(println "print-frame of abstractions") (print-frame (first(get-abstractions :new-director)))
-(print-frame :new-person)
-(println "------------_________________--")
-(println "------------_________________--")
-(print-frame :new-director)
-(println "------------_________________--")
-(println "------------_________________--")
-(println "_________________")
+;;(println "director with base-thing as abstraction, and new-person as specialization")
+;;(print-frame :new-director)
+;;(println "--------------")
+;;(println "specializations:" (get-specializations :new-director))
+;;(println "THIS IS NULL ?print-frame of specializations") (print-frame (first(get-specializations :new-director)))
+;;(print-frame :base-thing)
+;;(println "------------_________________--")
+;;(println "abstractions:" (get-abstractions :new-director))
+;;(println "print-frame of abstractions") (print-frame (first(get-abstractions :new-director)))
+;;(print-frame :new-person)
+;;(println "------------_________________--")
+;;(println "------------_________________--")
+;;(print-frame :new-director)
+;;(println "------------_________________--")
+;;(println "------------_________________--")
+(println "lname?:" (get-feature :new-director :lname))
 (println "title?:" (get-feature :new-director :title))
-(println "_________________")
-(println "lname?:" (get-feature :new-director :lname))
-(println "_________________")
 (println "dbid?:" (get-feature :new-director :dbid))
-(println "_________________")
+;`;(println "------------_________________--")
+;(println "spec" (get-specializations :new-director))
+;(println "xx-----")
+(println  (map (fn [x]  (get-feature x :lname))
+	(get-specializations :new-director)))
+(println "-----xx")
+;(println "get frame ----\\/\\/\\/----")
+;(print-frame :new-director)
+


More on atoms.

More on atoms.


task list -> remap node name

task list -> remap node name

#72

--- src/cljs/swarmpit/component/task/list.cljs
+++ src/cljs/swarmpit/component/task/list.cljs
@@ -23,7 +23,7 @@
                :width "15%"}])

 (def render-item-keys
-  [[:taskName] [:serviceName] [:repository :image] [:node :nodeName] [:state]])
+  [[:taskName] [:serviceName] [:repository :image] [:nodeName] [:state]])

 (defn render-item-state [value]
   (case value


added start button... behavior is not quite right but start button is there

--- src/clojure/com/example/gt/metime/main.clj
+++ src/clojure/com/example/gt/metime/main.clj
@@ -9,6 +9,7 @@
             [neko.threading :refer [on-ui]])
   (:import [android.widget CursorAdapter TextView LinearLayout]
            android.graphics.Color
+           [android.os SystemClock Handler]
            java.util.Calendar
            [android.app Activity DialogFragment]
            android.view.View
@@ -18,6 +19,8 @@
 (declare add-event)
 (declare date-picker)
 (declare time-picker)
+
+;(def my-handler (Handler.))

 (defrecord Task [date name duration date-index])

@@ -31,21 +34,47 @@
                           (into [] (map-indexed (fn [i ele] (apply ->Task (concat [(first sorted-map-data)] ele [i]))) (second sorted-map-data))))
                         lst)))

+(defn create-update-timer-method [event-timer-view start-time time-swap ]
+  (defn update-timer-method []
+    (let [time-in-millis (- (SystemClock/uptimeMillis) start-time)
+          final-time (+ time-swap time-in-millis)
+          total-seconds (mod final-time 1000)
+          minutes (quot total-seconds 1000)
+          seconds (mod total-seconds 60)
+          milliseconds (mod final-time 1000)]
+      (config event-timer-view :text (str "" minutes ":" (format "%02d" seconds) ":" (format "%02d" milliseconds))))))
+
+;myHandler.postDelayed(this, 0);
+
 (defn make-date-adapter []
   (adapters/ref-adapter
     (fn [_]
       [:linear-layout {:id-holder   true
                        :orientation :vertical}
        [:text-view {:id ::date-tv}]
-       [:text-view {:id ::event-tv}]])
+       [:linear-layout {:id ::event-ll
+                        :id-holder true
+                        :orientation :horizontal}
+        [:text-view {:id ::event-tv}]
+        [:text-view {:id ::time-tv}]
+        [:button {:id ::event-btn
+                  :text  "start",
+                  ;:on-click
+                  }]
+        ]])
     (fn [_ view _ task]
-      (let [date-view (find-view view ::date-tv)
-            event-view (find-view view ::event-tv)]
+      (let [date-text-view (find-view view ::date-tv)
+            event-linear-layout-view (find-view view ::event-ll)
+            event-text-view (find-view event-linear-layout-view ::event-tv)
+            event-timer-view (find-view event-linear-layout-view ::time-tv)
+            event-button-view (find-view event-linear-layout-view ::event-btn)
+            my-handler (Handler.)]
         ;symptom of mutability
-        (config date-view :visibility (if (= (:date-index task) 0) View/VISIBLE View/GONE))
-        (config date-view :text (str (:date task)))
+        (config date-text-view :visibility (if (= (:date-index task) 0) View/VISIBLE View/GONE))
+        (config date-text-view :text (str (:date task)))

-        (config event-view :text (str (:name task) " " (:duration task) " " (:date-index task)))))
+        (config event-button-view :on-click (fn [_] (.postDelayed my-handler (create-update-timer-method event-timer-view 0 0) 0)))
+        (config event-text-view :text (str (:name task) " " (:duration task) " " (:date-index task)))))
     listing
     sorted-map-array-to-array-task))

@@ -78,6 +107,8 @@
 (defn set-elmt-text [activity elmt s]
   (on-ui (config (find-view activity elmt) :text s)))

+
+
 (defn update-ui [activity]
   (set-elmt-text activity ::name ""))

@@ -97,21 +128,21 @@

 (defn date-picker [activity]
   (proxy [DialogFragment DatePickerDialog$OnDateSetListener] []
-    (onCreateDialog [savedInstanceState]
+    (onCreateDialog [_]
       (let [c (Calendar/getInstance)
             year (.get c Calendar/YEAR)
             month (.get c Calendar/MONTH)
             day (.get c Calendar/DAY_OF_MONTH)]
         (DatePickerDialog. activity this year month day)))
-    (onDateSet [view year month day]
+    (onDateSet [_ year month day]
       (set-elmt-text activity ::date
                      (format "%d%02d%02d" year (inc month) day)))))

 (defn time-picker [activity]
   (proxy [DialogFragment TimePickerDialog$OnTimeSetListener] []
-    (onCreateDialog [savedInstanceState]
+    (onCreateDialog [_]
       (TimePickerDialog. activity this 0 0 true))
-    (onTimeSet [view hourOfDay minute]
+    (onTimeSet [_ hourOfDay minute]
       (set-elmt-text activity ::time
                      (format "%d" (+ (* hourOfDay 60) minute))))))


Added second fitness case for distance of last character

--- src/clojush/problems/software/checksum.clj
+++ src/clojush/problems/software/checksum.clj
@@ -114,23 +114,30 @@
         (the-actual-checksum-error-function program data-cases false))
       ([program data-cases print-outputs]
         (let [behavior (atom '())
-              errors (doall
-                       (for [[input correct-output] (case data-cases
-                                                                 :train train-cases
-                                                                 :test test-cases
-                                                                 [])]
-                         (let [final-state (run-push program
-                                                     (->> (make-push-state)
-                                                       (push-item input :input)
-                                                       (push-item "" :output)))
-                               printed-result (stack-ref :output 0 final-state)]
-                           (when print-outputs
-                             (println (format "Correct output: %-19s | Program output: %-19s" correct-output printed-result)))
-                           ; Record the behavior
-                           (when @global-print-behavioral-diversity
-                             (swap! behavior conj printed-result))
-                           ; Error is Levenshtein distance
-                           (levenshtein-distance correct-output printed-result))))]
+              errors (flatten
+                       (doall
+                         (for [[input correct-output] (case data-cases
+                                                                   :train train-cases
+                                                                   :test test-cases
+                                                                   [])]
+                           (let [final-state (run-push program
+                                                       (->> (make-push-state)
+                                                         (push-item input :input)
+                                                         (push-item "" :output)))
+                                 printed-result (stack-ref :output 0 final-state)]
+                             (when print-outputs
+                               (println (format "Correct output: %-19s | Program output: %-19s" correct-output printed-result)))
+                             ; Record the behavior
+                             (when @global-print-behavioral-diversity
+                               (swap! behavior conj printed-result))
+                             ; Error is Levenshtein distance and, if correct format, distance from correct character
+                             (vector
+                               (levenshtein-distance correct-output printed-result)
+                               (if (and (= (count correct-output) (count printed-result))
+                                        (= "Check sum is " (subs printed-result 0 (count "Check sum is "))))
+                                 (abs (- (int (last correct-output)) (int (last printed-result)))) ;distance from correct last character
+                                 1000) ;penalty for wrong format
+                               )))))]
           (when @global-print-behavioral-diversity
             (swap! population-behaviors conj @behavior))
           errors)))))


Add more data to investigation maximal example

--- src/ctia/entity/investigation/examples.clj
+++ src/ctia/entity/investigation/examples.clj
@@ -17,9 +17,12 @@
                           :description "Description text"}]
    :timestamp #inst "2017-10-23T19:25:27.278-00:00"
    :language "language"
-   :object_ids []
-   :investigated_observables []
-   :targets []
+   :object_ids ["https://intel.test.iroh.site:443/ctia/indicator/indicator-97c97157-6592-43e3-a54e-f126b4950787"]
+   :investigated_observables ["sha256:585c2a90e4928f67af7be2d0bdc282b7eb6c90113ae588461a441d31b5268e88"]
+   :targets [{:type "endpoint"
+              :observables [{:value "Demo_iOS_3", :type "hostname"}]
+              :observed_time {:start_time "2019-04-01T20:45:11.000Z"}
+              :os "iOS 10.3 (1)"}]
    :title "investigation-title"
    :description "description"
    :short_description "short desc"


not ready for attribute-array test yet

not ready for attribute-array test yet

--- src/test/clojure/org/dada/CoreTestCase.clj
+++ src/test/clojure/org/dada/CoreTestCase.clj
@@ -5,7 +5,9 @@
     (:gen-class
      :extends junit.framework.TestCase
      ;;:init init
-     :methods [[testCore [] void] [test-attribute-array [] void]]
+     :methods [[testCore [] void]
+	       ;;[test-attribute-array [] void]
+	       ]
      )
     (:use [org.dada.core])
     (:import [junit.framework TestCase] ;;[]
@@ -15,11 +17,11 @@
   (TestCase/assertTrue (Boolean/TRUE))
   (println "TESTED!"))

-(defn -test-attribute-array [this]
-  (let [a (attribute-array :a Integer :b String)]
-    (TestCase/assertTrue (= (alength a) 2))
-    (TestCase/assertTrue (= (aget a 0 0) "java.lang.Integer"))
-    (TestCase/assertTrue (= (aget a 0 1) "a"))
-    (TestCase/assertTrue (= (aget a 1 0) "java.lang.String"))
-    (TestCase/assertTrue (= (aget a 1 1) "b"))
-    ))
+;; (defn -test-attribute-array [this]
+;;   (let [a (attribute-array :a Integer :b String)]
+;;     (TestCase/assertTrue (= (alength a) 2))
+;;     (TestCase/assertTrue (= (aget a 0 0) "java.lang.Integer"))
+;;     (TestCase/assertTrue (= (aget a 0 1) "a"))
+;;     (TestCase/assertTrue (= (aget a 1 0) "java.lang.String"))
+;;     (TestCase/assertTrue (= (aget a 1 1) "b"))
+;;     ))


Small update for day one of Clojure

Small update for day one of Clojure

Fixes an issue in which the 'big' method didn't exactly follow spec.

--- Clojure/seven_lang/src/seven_lang/dayone.clj
+++ Clojure/seven_lang/src/seven_lang/dayone.clj
@@ -1,8 +1,9 @@
 (ns seven-lang.dayone)

+;;exercise one - implement a function called (big st n) that returns true if a string st is longer than n characters
 (defn big
   "returns true if a string is big"
-  [st]
-  (if (> (count st) 4)
+  [st, n]
+  (if (> (count st) n)
     true false)
   )


model.connection: should not make new conncetion from map missing duration

--- test/busa_server/model/connection_test.clj
+++ test/busa_server/model/connection_test.clj
@@ -10,3 +10,6 @@

 (fact "should not make new connection from map missing departure time"
   (connection/new-connection (dissoc valid-connection-map :departure-time)) => (throws Exception))
+
+(fact "should not make new connection from map missing duration"
+  (connection/new-connection (dissoc valid-connection-map :duration)) => (throws Exception))


more TODO

more TODO

--- test/com/billpiel/mem_tracer/core_test.clj
+++ test/com/billpiel/mem_tracer/core_test.clj
@@ -576,7 +576,14 @@
   -- add pmap mode to walk18???
 x ws-load bug???
 - profiling features
-  - use joda
+  x use joda
+  x arg cardinality
+- have all core fns optionally take an arg
+  - ws
+  - rec
+  - ws ns and rec ns fns should not take atoms
+- aliases for core fns
+- never return ws or recs from core fns
 - cursors
    - bisect recording trees to find bugs
 - split ns and fn name in trace rec and output


Fix up and clean beatbox example

Fix up and clean beatbox example

--- examples/beatbox.clj
+++ examples/beatbox.clj
@@ -3,39 +3,43 @@
   (:require [polynome.core :as poly]))

 ;;design a sc synth to play the samples
-(definst loop-synth [buf 0 vol 1 rate 1 amp 1 wet-dry 0.2 room-size 0  dampening 1]
+(definst loop-synth [buf 0 vol 1 rate 1]
   (let [src (play-buf 1 buf rate 1.0 0.0 1.0 1)]
-    (* src vol amp)))
+    (* src vol)))

 ;;change m to point to your monome (use dummy if you don't have one...)
 (defonce m (poly/init "/dev/tty.usbserial-m64-0790"))
 ;;(def m (poly/init "/dev/tty.usbserial-m128-115"))
 ;;(def m (poly/init "dummy"))

-(def samples (load-samples "assets/*.{aif,AIF,wav,WAV}"))
+(defonce samples (load-samples "assets/*.{aif,AIF,wav,WAV}"))

 (defn start-samples
   "Starts all samples playing at init-vol. Returns a seq containing info
   regarding all running samples. Samples start playing 1s after the this
   fn is called to ensure that they're all started in sync"
-  (let [init-vol 0]
-    (at (+ 1000 (now))
-        (doall
-         (reduce (fn [res samp]
-                   (let [id (loop-synth samp init-vol)]
-                     (assoc res id {:vol init-vol
-                                    :id
-                                    :samp samp})))
-                 []
-                 samples)))))
+  []
+  (at (+ 1000 (now))
+      (doall
+       (reduce (fn [res samp]
+                 (let [id (loop-synth samp 0)]
+                   (conj res  {:vol 0
+                               :id id
+                               :samp samp})))
+               []
+               samples))))

 (def playing-samples* (agent (start-samples)))

 (defn reset-samples!
+  []
   (send playing-samples*
-        (fn [playing-samples])
-        (doall (map #(kill (:id %)) playing-samples))
-        (start-samples)))
+        (fn [playing-samples]
+          (doall (map #(kill (:id %)) playing-samples))
+          (poly/clear m)
+          (start-samples))))
+
+(reset-samples!)

 (defn toggle
   "Invert the vol from 1 to 0 or 0 to 1"
@@ -45,12 +49,12 @@
 (defn toggle-sample
   [n]
   (send playing-samples* (fn [playing-samples]
-                           (let [samp (nth playing-samples n)
-                                 id (:id samp)
-                                 vol (:vol samp)
-                                 new-vol (toggle vol)]
-                             (ctl id :vol new-vol))
-)))
+                           (let [samp     (nth playing-samples n)
+                                 id       (:id samp)
+                                 new-vol  (toggle (:vol samp))
+                                 new-samp (assoc samp :vol new-vol)]
+                             (ctl id :vol new-vol)
+                             (assoc playing-samples n new-samp)))))

 (defn change-vol
   "Update vol with update-fn and change led state and loop vol accordingly"
@@ -69,63 +73,19 @@
   "Invert the volume for the loop corresponding to the given x y coords. Also
    update the associated agent's state and monome LED state."
   [x y]
-  (let [looper (get loopers [x y])
-        state (looper :state)]
-    (send state change-vol toggle looper x y)))
+  (poly/toggle-led m x y)
+  (toggle-sample (poly/button-id m x y)))

-(defn mute
-  "Silences all loopers."
-  []
-  (doseq [[coords looper] loopers] (apply send (looper :state) change-vol silence looper coords )))

 (poly/on-press m (fn [x y s] (trigger x y)))

-(comment
-  ;;play about with synth params:
-  (def rate (atom 1))
-  (ctl loop-synth :rate 1)
-  (ctl loop-synth :amp 0.1)
-  (ctl loop-synth :room-size 1)
-  (ctl loop-synth :dampening 1)
-  (ctl loop-synth :wet-dry 0.1)
+(def rate* (atom 1))

-  (defn tempo-slide [to]
-    (let [from @rate
-          step (if (< from to) 0.01 -0.01)
-          vals (range from to step)]
-      (doall (map #(do (ctl loop-synth :rate (reset! rate %)) (Thread/sleep 35)) vals))))
+(defn tempo-slide [to]
+  (let [from @rate*
+        step (if (< from to) 0.01 -0.01)
+        vals (range from to step)]
+    (dorun (map #(do (ctl loop-synth :rate (reset! rate* %)) (Thread/sleep 35)) vals))))

-  (ctl loop-synth :rate 1)

-  (tempo-slide 1)
-
-  (comment
-    (def s (space/space-navigator))
-
-    (space/on-diff-vals s
-                        (fn [vals]
-                          (ctl looper :amp (:rz vals)))
-                        {:min-rz 0
-                         :max-rz 1}
-                        space/SAMPLE-RANGES)
-
-    (space/on-diff-vals s
-                        (fn [vals]
-                          (ctl looper :wet-dry (:x vals)))
-                        {:min-x 0
-                         :max-x 1}
-                        space/SAMPLE-RANGES)
-
-    (space/on-diff-vals s
-                        (fn [vals]
-                          (ctl looper :room-size (:z vals)))
-                        {:min-z 0
-                         :max-z 1}
-                        space/SAMPLE-RANGES)
-
-    (space/on-diff-vals s
-                        (fn [vals]
-                          (ctl looper :dampening (:y vals)))
-                        {:min-y 0
-                         :max-y 1}
-                        space/SAMPLE-RANGES)))
+;;(tempo-slide 1.5)


remove println statement

remove println statement

git-svn-id: 03a8048b5ee8463be5048a3801110fb50f378627@8999 d0ab736e-dc22-4aeb-8dc9-08def0aa14fd

--- acqEngine/src/org/micromanager/acq_engine.clj
+++ acqEngine/src/org/micromanager/acq_engine.clj
@@ -430,7 +430,6 @@
   (when (and (@state :init-continuous-focus)
              (not (core isContinuousFocusEnabled)))
     (core enableContinuousFocus true))
-  (println (@state :live-mode-on))
   (when (and (@state :live-mode-on)
              (not (.isLiveModeOn gui)))
     (.enableLiveMode gui true))


organize behavior.clj

organize behavior.clj

--- pedestal-app/app/src/semtag_web/behavior.clj
+++ pedestal-app/app/src/semtag_web/behavior.clj
@@ -2,12 +2,23 @@
     (:require [io.pedestal.app :as app]
               [io.pedestal.app.messages :as msg]))

+;; Transform fns
+;;
 (defn set-value [old-value message]
   (:value message))

 (defn map-value [_ message]
   (dissoc message msg/topic msg/type))

+;; Effect fns
+;;
+;; pass full message so we can differentiate between effects in services.cljs
+(defn publish-message [{:keys [message]}]
+  ;; needs to return a collection
+  [message])
+
+;; Emit fns
+;;
 (defn home-deltas []
   [[:transform-enable [:app-model :home :create-url] :create-url [{msg/type :set-value msg/topic [:create-url] (msg/param :value) {}}]]
    [:transform-enable [:app-model :home :search] :search [{msg/type :map-value msg/topic [:search] (msg/param :query) {} (msg/param :search-type) {}}]]])
@@ -20,11 +31,6 @@
   [(set-focus-delta :home)
    (set-focus-delta :types)
    (set-focus-delta :tag-stats)])
-
-;; pass full message so we can differentiate between effects in services.cljs
-(defn publish-message [{:keys [message]}]
-  ;; needs to return a collection
-  [message])

 (defn init-home [_]
   (into [[:node-create [:app-model :home]]]


Fixed bug with dashes in id/class sugar

Fixed bug with dashes in id/class sugar

--- lib/compojure/html/html.clj
+++ lib/compojure/html/html.clj
@@ -121,21 +121,20 @@
   #{:br :dd :dt :h1 :h2 :h3 :h4 :h5 :h6 :hr :li :link
     :option :td :textarea :title})

-(defn- merge-id-and-class
-  "Merge a given id and class into an attributes map."
-  [attrs id class]
-  (merge attrs
-    (if id    {:id id})
-    (if class {:class class})))
-
 (defn- parse-css-tag
   "Pulls the id and class attributes from a tag name formatted in a CSS style.
   e.g. :div#content -> [:div  {:id \"content\"}]
        :span.error  -> [:span {:class \"error\"}]"
   [tag attrs]
-  (let [lexer-regex        #"(\\w+)(#(\\w+))?(\\.(\\w+))?"
-        [_ tag _ id _ cls] (re-matches lexer-regex (str* tag))
-        attrs              (merge-id-and-class attrs id cls)]
+  (let [word  "([^\\s\\.#]+)"
+        lexer (str word "(#" word ")?" "(\\." word ")?")
+
+        [_ tag _ id _ class]
+          (re-matches (re-pattern lexer) (str* tag))
+
+        attrs (merge attrs
+                (if id    {:id id})
+                (if class {:class class}))]
     [tag attrs]))

 (defn- html-formatter


Added functionality to update existing Compojure servlets

--- lib/compojure/http/http.clj
+++ lib/compojure/http/http.clj
@@ -234,22 +234,38 @@

 ;;;; Servlet creation ;;;;

+(defn- http-service
+  "Represents the service method called by a HttpServlet."
+  [object request response handlers]
+  (.setCharacterEncoding response "UTF-8")
+  (apply-http-handler handlers
+                      (.getServletContext object)
+                      request
+                      response))
 (defn servlet
   "Create a servlet from a sequence of handlers."
   [& handlers]
   (proxy [HttpServlet] []
     (service [request response]
-      (.setCharacterEncoding response "UTF-8")
-      (apply-http-handler handlers
-                          (.getServletContext this)
-                          request
-                          response))))
-
+      (http-service this request response handlers))))
+
+(defn update-servlet
+  "Update an existing servlet proxy with a new set of handlers."
+  [object & handlers]
+  (update-proxy object
+    {'service (fn [this request response]
+                (http-service this request response handlers))}))
+
 (defmacro defservlet
-  "Shortcut for (def name doc? (servlet handlers))"
+  "Defines a new servlet with an optional doc-string, or if a servlet is
+  already defined, it updates the existing servlet with the supplied handlers.
+  Note that updating is not a thread-safe operation."
   [name doc & handlers]
   (if (string? doc)
-    `(def
-       ~(with-meta name (assoc (meta name) :doc doc))
-        (servlet ~@handlers))
-    `(def ~name (servlet ~doc ~@handlers))))
+    `(do (defonce
+          ~(with-meta name (assoc (meta name) :doc doc))
+           (proxy [HttpServlet] []))
+         (update-servlet ~name ~@handlers))
+    `(do (defonce ~name
+           (proxy [HttpServlet] []))
+         (update-servlet ~name ~doc ~@handlers))))


only check z position for manual adjustments if more than 1 second elapses between frames

git-svn-id: 03a8048b5ee8463be5048a3801110fb50f378627@9732 d0ab736e-dc22-4aeb-8dc9-08def0aa14fd

--- acqEngine/src/org/micromanager/acq_engine.clj
+++ acqEngine/src/org/micromanager/acq_engine.clj
@@ -571,7 +571,7 @@
         check-z-ref (and z-drive
                          (or (:autofocus event)
                              (when-let [t (:wait-time-ms event)]
-                               (pos? t))))]
+                               (< 1000 t))))]
     (filter identity
       (flatten
         (list


allow a sleep to be interrupted by the stop button

git-svn-id: 03a8048b5ee8463be5048a3801110fb50f378627@6095 d0ab736e-dc22-4aeb-8dc9-08def0aa14fd

--- acqEngine/src/org/micromanager/acq_engine.clj
+++ acqEngine/src/org/micromanager/acq_engine.clj
@@ -26,6 +26,7 @@
            [org.micromanager.navigation MultiStagePosition StagePosition]
            [mmcorej TaggedImage Configuration]
            [java.util.prefs Preferences]
+           [java.util.concurrent TimeUnit CountDownLatch]
            [org.micromanager.utils ChannelSpec GentleLinkedBlockingQueue MDUtils]
            [org.json JSONObject]
            [java.util Date UUID]
@@ -139,12 +140,18 @@

 ;; acq-engine

+
+(defn interruptible-sleep [time-ms]
+  (let [sleepy (CountDownLatch. 1)]
+    (swap! state assoc :sleepy sleepy)
+    (.await sleepy time-ms TimeUnit/MILLISECONDS)))
+
 (defn acq-sleep [interval-ms]
   (let [target-time (+ (@state :last-wake-time) interval-ms)
         sleep-time (- target-time (clock-ms))]
     (log-cmd (@state :last-wake-time))
     (when (pos? sleep-time)
-      (Thread/sleep sleep-time)
+      (interruptible-sleep sleep-time)
       (swap! state assoc :last-wake-time target-time))))

 (declare device-agents)
@@ -346,8 +353,10 @@

 (defn -stop [this]
   (log "stop requested!")
-  (swap! (.state this) assoc :stop true)
-  (log @(.state this)))
+  (let [state (.state this)]
+    (swap! state assoc :stop true)
+    (do-when #(.countDown %) (:sleepy @state))
+    (log @state)))

 (defn -isRunning [this]
   (:running @(.state this)))


suppress exceptions when there isn't a z drive installed.

git-svn-id: 03a8048b5ee8463be5048a3801110fb50f378627@6323 d0ab736e-dc22-4aeb-8dc9-08def0aa14fd

--- acqEngine/src/org/micromanager/acq_engine.clj
+++ acqEngine/src/org/micromanager/acq_engine.clj
@@ -189,7 +189,10 @@
       (zipmap devs (repeatedly (count devs) #(agent nil))))))

 (defn get-z-stage-position [stage]
-  (core getPosition stage))
+  (if (pos? (count stage)) (core getPosition stage) 0))
+
+(defn set-z-stage-position [stage pos]
+  (if (pos? (count stage)) (core setPosition stage pos)))

 (defn set-stage-position
   ([stage-dev z] (log "setting z position to " z)
@@ -318,7 +321,7 @@
     (core stopSequenceAcquisition))
   (core setAutoShutter (@state :init-auto-shutter))
   (core setExposure (@state :init-exposure))
-  (core setPosition (core getFocusDevice) (@state :init-z-position))
+  (set-z-stage-position (core getFocusDevice) (@state :init-z-position))
   (when (and (@state :init-continuous-focus)
              (not (core isContinuousFocusEnabled)))
     (core enableContinuousFocus true))


Adds information to the matching offers log (#1664)

--- scheduler/src/cook/scheduler/scheduler.clj
+++ scheduler/src/cook/scheduler/scheduler.clj
@@ -601,18 +601,6 @@
    Returns {:matches (list of tasks that got matched to the offer)
             :failures (list of unmatched tasks, and why they weren't matched)}"
   [db ^TaskScheduler fenzo considerable offers rebalancer-reservation-atom pool-name]
-  (log/info "In" pool-name "pool, matching offers to considerable jobs"
-            {:count-considerable-jobs (count considerable)
-             :count-offers (count offers)
-             :resource-type->total-considerable (-> considerable
-                                                    jobs->resource-totals
-                                                    format-resource-map)
-             :resource-type->total-offered (-> offers
-                                               offers->resource-totals
-                                               format-resource-map)
-             :user->number-jobs (->> considerable
-                                     (map tools/job-ent->user)
-                                     frequencies)})
   (if (and (-> considerable count zero?)
            (-> offers count pos?)
            (every? :reject-after-match-attempt offers))
@@ -745,12 +733,18 @@
     considerable-jobs))

+(defn matches->jobs
+  "Given a collection of matches, returns the matched jobs"
+  [matches]
+  (->> matches
+       (mapcat :tasks)
+       (map #(-> % .getRequest :job))))
+
+
 (defn matches->job-uuids
   "Returns the matched job uuids."
   [matches pool-name]
-  (let [jobs (->> matches
-                  (mapcat #(-> % :tasks))
-                  (map #(-> % .getRequest :job)))
+  (let [jobs (matches->jobs matches)
         job-uuids (set (map :job/uuid jobs))]
     (log/debug "In" pool-name "pool, matched jobs:" (count job-uuids))
     (when (seq matches)
@@ -979,7 +973,37 @@
                                   (timers/timer (metric-title "handle-resource-offer!-match-job-uuids-duration" pool-name))
                                   (matches->job-uuids matches pool-name))
               first-considerable-job-resources (-> considerable-jobs first tools/job-ent->resources)
-              matched-considerable-jobs-head? (contains? matched-job-uuids (-> considerable-jobs first :job/uuid))]
+              matched-considerable-jobs-head? (contains? matched-job-uuids (-> considerable-jobs first :job/uuid))
+              user->number-matched-considerable-jobs (->> matches
+                                                          matches->jobs
+                                                          (map tools/job-ent->user)
+                                                          frequencies)
+              user->number-total-considerable-jobs (->> considerable-jobs
+                                                        (map tools/job-ent->user)
+                                                        frequencies)]
+
+          (log/info "In" pool-name "pool, matching offers to considerable jobs"
+                    {:jobs-considerable {:head-matched? matched-considerable-jobs-head?
+                                         :head-resources first-considerable-job-resources
+                                         :number-matched (count matched-job-uuids)
+                                         :number-total (count considerable-jobs)
+                                         :number-unmatched (- (count considerable-jobs)
+                                                              (count matched-job-uuids))
+                                         :resource-type->total (-> considerable-jobs
+                                                                   jobs->resource-totals
+                                                                   format-resource-map)
+                                         :user->number-matched user->number-matched-considerable-jobs
+                                         :user->number-total user->number-total-considerable-jobs
+                                         :user->number-unmatched (merge-with
+                                                                   -
+                                                                   user->number-total-considerable-jobs
+                                                                   user->number-matched-considerable-jobs)}
+                     :offers {:number-matched (count offers-scheduled)
+                              :number-total (count offers)
+                              :number-unmatched (- (count offers) (count offers-scheduled))
+                              :resource-type->total (-> offers
+                                                        offers->resource-totals
+                                                        format-resource-map)}})

           (fenzo/record-placement-failures! conn failures)


removed empty core test file

removed empty core test file

--- test/bløggr/core_test.clj
+++ test/bløggr/core_test.clj
@@ -1,3 +0,0 @@
-(ns bløggr.core-test
-  (:use midje.sweet)
-  (:require [bløggr.core :refer :all]))


API Version bump to 1.1.0

API Version bump to 1.1.0

--- api/project.clj
+++ api/project.clj
@@ -1,4 +1,4 @@
-(defproject geocommit "1.0.0-SNAPSHOT"
+(defproject geocommit "1.1.0"
   :description "geocommit.com HTTP API"
   :dependencies [[org.clojure/clojure "1.2.0"]
                  [org.clojure/clojure-contrib "1.2.0"]


:fire: Remove unused prn's.

:fire: Remove unused prn's.

--- frontend/src/uxbox/main/data/workspace.cljs
+++ frontend/src/uxbox/main/data/workspace.cljs
@@ -335,7 +335,6 @@
   (ptk/reify ::initialize-page
     ptk/UpdateEvent
     (update [_ state]
-      ;; (prn "initialize-page" page-id)
       (let [page (get-in state [:pages page-id])
             data (get-in state [:pages-data page-id])
             local (get-in state [:workspace-cache page-id] workspace-default)]
@@ -356,7 +355,6 @@
   (ptk/reify ::finalize
     ptk/UpdateEvent
     (update [_ state]
-      ;; (prn "finalize-page" page-id)
       (let [local (:workspace-local state)]
         (assoc-in state [:workspace-cache page-id] local)))))

@@ -409,8 +407,6 @@

             changes (generate-changes prev curr)
             undo-changes (generate-changes curr prev)]
-        ;; (prn "diff-and-commit-changes1" changes)
-        ;; (prn "diff-and-commit-changes2" undo-changes)
         (when-not (empty? changes)
           (rx/of (commit-changes changes undo-changes)))))))


Added ClojureScript dependency to the project.

--- app/project.clj
+++ app/project.clj
@@ -12,7 +12,8 @@
                  [metosin/compojure-api "0.21.0"]
                  [metosin/ring-swagger-ui "2.1.5-M2"]
                  [prismatic/schema "0.4.3"]
-                 [hiccup "1.0.5"]]
+                 [hiccup "1.0.5"]
+                 [org.clojure/clojurescript "0.0-3269"]]
   :main ^:skip-aot clj-web-example.main
   :target-path "target/%s"
   :profiles {:uberjar {:aot :all}})


Fix missing scheduler in log test.

Fix missing scheduler in log test.

--- test/onyx/log/log_test.clj
+++ test/onyx/log/log_test.clj
@@ -15,6 +15,8 @@
 (def peer-config (assoc (:peer-config config) :onyx/id onyx-id))

 (def env (onyx.api/start-env env-config))
+
+(def scheduler :onyx.job-scheduler/round-robin)

 (extensions/write-chunk (:log env) :job-scheduler {:job-scheduler scheduler} nil)


test/riemann/core: up the tolerance on the expired test.

We're hitting thread scheduling issues. :/

--- test/riemann/test/core.clj
+++ test/riemann/test/core.clj
@@ -90,7 +90,7 @@
            (update-index core {:service 2 :ttl 1 :time (unix-time)})

            ; Wait for reaper to eat them
-           (Thread/sleep 10)
+           (Thread/sleep 30)

            ; Kill reaper
            (future-cancel reaper)


Added a test requiring specific nodes on session..

creation.

--- test/shale/test/client.clj
+++ test/shale/test/client.clj
@@ -78,7 +78,19 @@
                           {:browser-name "phantomjs"
                            :tags ["some-unknown-tag"]}))]
         (is (= 3 (session-diff
-                   #(logged-in-sessions-fixture test-fn))))))))
+                   #(logged-in-sessions-fixture test-fn))))))
+    (testing "that sessions are created on the specified node"
+      (let [node-id (-> shale.client/nodes
+                        first
+                        :id)]
+
+        (dotimes [_ 4]
+          (shale.client/get-or-create-session!
+            {:browser-name "phantomjs"
+             :node {:id node-id}}))
+        (is (= 4 (count
+                   (filter #(= (:id %) node-id)
+                           (shale.client/nodes)))))))))

 (deftest ^:integration test-force-create
   (testing "force create a new session"


Streams: docs for percentiles.

Streams: docs for percentiles.

--- reimann/src/reimann/streams.clj
+++ reimann/src/reimann/streams.clj
@@ -118,6 +118,12 @@
           (call-rescue event children)))))

 (defn percentiles [interval points & children]
+  "Over each period of interval seconds, aggregates events and selects one
+  event from that period for each point. If point is 0, takes the lowest metric
+  event.  If point is 1, takes the highest metric event. 0.5 is the median
+  event, and so forth. Forwards each of these events to children. The service
+  name has the point appended to it; e.g. 'response time' becomes 'response
+  time .95'."
   (part-time-fast interval
                 (fn [] (ref []))
                 (fn [r event] (dosync (alter r conj event)))


+essere-aux-subject-agreement

+essere-aux-subject-agreement

--- src/babel/italiano/lexicon.cljc
+++ src/babel/italiano/lexicon.cljc
@@ -37,6 +37,9 @@
         comparative it-pos/comparative
         countable-noun it-pos/countable-noun
         determiner it-pos/determiner
+        essere-aux-subject-agreement (let [subject-agreement (atom :top)]
+                                       {:synsem {:subcat {:1 {:agr subject-agreement}
+                                                          :2 {:subcat {:1 {:agr subject-agreement}}}}}})
         gender-and-number-agreement-1
         (let [gender (atom :top)
               number (atom :top)]
@@ -1014,7 +1017,8 @@
                 ;; TODO: should not need agreement: should be covered by
                 ;; essere-common.
                 :subcat {:1 {:cat :noun}
-                         :2 {:cat :prep}
+                         :2 {:cat :prep
+                             :subcat '()}
                          :3 '()}}}

       ;; TODO: do we need this? maybe remove?
@@ -1060,7 +1064,8 @@
 ;                              :intensified true
 ;                              :obj comp-sem}}}))

-      {:unify [essere-common verb-aux verb-subjective]
+      {:unify [essere-common verb-aux verb-subjective essere-aux-subject-agreement
+       ]
        :notes "essere-aux"
        :italiano {:notes "essere-aux"}}])


fix typo

fix typo

--- src/babel/italiano/lexicon.cljc
+++ src/babel/italiano/lexicon.cljc
@@ -273,7 +273,7 @@
 ;                                    :cat :prep}}}
 ;              :note "andare-pp"}))))

-   "anno" {:synsem {:agr {:gender :noun}
+   "anno" {:synsem {:agr {:gender :masc}
                     :cat :noun
                     :sem {:pred :year
                           :time true}}}


Whitespace

Whitespace

--- src/circle/web/views/common.clj
+++ src/circle/web/views/common.clj
@@ -13,10 +13,10 @@
   wrapping it's contents in more divs. It relies on additional.css having
   .vcenter{1,2,3} defined."
   [[tag & args]]
-    (let [[m & remaining] args
-          property-map (into {:class "vcenter1"} (if (map? m) m {}))
-          inner-tags (if (map? m) remaining args)]
-         [tag property-map [:div.vcenter2 (apply vector :div.vcenter3 inner-tags)]]))
+  (let [[m & remaining] args
+        property-map (into {:class "vcenter1"} (if (map? m) m {}))
+        inner-tags (if (map? m) remaining args)]
+    [tag property-map [:div.vcenter2 (apply vector :div.vcenter3 inner-tags)]]))

 (defn center-vertically-span
@@ -24,10 +24,10 @@
   wrapping it's contents in more divs. It relies on additional.css having
   .vcenter{1,2,3} defined."
   [[tag & args]]
-    (let [[m & remaining] args
-          property-map (into {:class "vcenter1"} (if (map? m) m {}))
-          inner-tags (if (map? m) remaining args)]
-         [tag property-map [:span.vcenter2 (apply vector :span.vcenter3 inner-tags)]]))
+  (let [[m & remaining] args
+        property-map (into {:class "vcenter1"} (if (map? m) m {}))
+        inner-tags (if (map? m) remaining args)]
+    [tag property-map [:span.vcenter2 (apply vector :span.vcenter3 inner-tags)]]))

@@ -45,53 +45,53 @@
   })();"]))

 (defpartial layout [& content]
-            (html5
-             [:head
-              (google-analytics)
-              [:meta {:name "google-site-verification" :content "rCckS33lTuN6tiIrxLMykv_uRR0dMoHNM4XrR4yYUQ0"}]
-              [:meta {:http-equiv "Content-Type"
-                      :content "text/html; charset=utf-8"}]
-              [:meta {:name "description"
-                      :content "Continuous Integration made easy"}]
-              [:meta {:name "keywords"
-                      :content "Circle, heroku, continuous integration, continuous deployment, CI, github"}]
-              (css "css/reset.css" :rel "stylesheet" :type "text/css" :media "screen")
-              (css "css/core.css" :rel "stylesheet" :type "text/css" :media "screen")
-              (css "css/colors_blue_and_green.css" :rel "stylesheet" :type "text/css" :title "Blue and green" :media "screen")
-              (css "css/additional.css" :rel "stylesheet" :type "text/css" :media "screen")
-              (css "css/wufoo.css" :rel "stylesheet" :type "text/css" :media "screen")
-              "<!--[if lte IE 8]>
+  (html5
+   [:head
+    (google-analytics)
+    [:meta {:name "google-site-verification" :content "rCckS33lTuN6tiIrxLMykv_uRR0dMoHNM4XrR4yYUQ0"}]
+    [:meta {:http-equiv "Content-Type"
+            :content "text/html; charset=utf-8"}]
+    [:meta {:name "description"
+            :content "Continuous Integration made easy"}]
+    [:meta {:name "keywords"
+            :content "Circle, heroku, continuous integration, continuous deployment, CI, github"}]
+    (css "css/reset.css" :rel "stylesheet" :type "text/css" :media "screen")
+    (css "css/core.css" :rel "stylesheet" :type "text/css" :media "screen")
+    (css "css/colors_blue_and_green.css" :rel "stylesheet" :type "text/css" :title "Blue and green" :media "screen")
+    (css "css/additional.css" :rel "stylesheet" :type "text/css" :media "screen")
+    (css "css/wufoo.css" :rel "stylesheet" :type "text/css" :media "screen")
+    "<!--[if lte IE 8]>
 		<link href=\"css/ie.css\" rel=\"stylesheet\" type=\"text/css\" media=\"screen\" />
               <![endif]-->"
-              "<!--[if lte IE 10]>
+    "<!--[if lte IE 10]>
               <script src=\"http://html5shiv.googlecode.com/svn/trunk/html5.js\"></script>
               <![endif]-->"
-              (css "http://fonts.googleapis.com/css?family=PT+Sans" :rel "stylesheet" :type "text/css" :media "screen")
-              (include-js "js/jquery_minimized_core.js" "js/wufoo.js")
-              [:title "Circle - Continuous Integration made easy"]]
-             [:body
-              [:div#notthefooter
-              [:div#header_wrap
-               [:div#header
-                [:h1#logo (link-to {:title "Go to Circle homepage"} "/" [:img#circle {:src "img/circle-transparent.png"}]
-                                                                        [:span#circle-word "Circle"])]
-                (unordered-list {:id "nav"}
-                                [(link-to {:class "current_page"}
-                                          "/" "Signup")
-                                 ])
-                [:div.clear]]]
-              content
-              [:div.clear]
-              [:div#notthefooterclear]]
-              [:div#footer_wrap
-               [:div#footer
-                [:div.box_wide.separator_r
-                 [:p#copyright "Copyright © 2011 Circle"]]
-                [:div#social_info.box_small.separator_r
-                 [:ul
-                  [:li#twitter [:a {:href "http://twitter.com/circleci"} "follow @circleci"]]]]
-                [:div#contact_info.box_small
-                 [:h5
-                  (unordered-list
-                   ["questions@circleci.com"])]]
-                [:div.clear]]]]))
+    (css "http://fonts.googleapis.com/css?family=PT+Sans" :rel "stylesheet" :type "text/css" :media "screen")
+    (include-js "js/jquery_minimized_core.js" "js/wufoo.js")
+    [:title "Circle - Continuous Integration made easy"]]
+   [:body
+    [:div#notthefooter
+     [:div#header_wrap
+      [:div#header
+       [:h1#logo (link-to {:title "Go to Circle homepage"} "/" [:img#circle {:src "img/circle-transparent.png"}]
+                          [:span#circle-word "Circle"])]
+       (unordered-list {:id "nav"}
+                       [(link-to {:class "current_page"}
+                                 "/" "Signup")
+                        ])
+       [:div.clear]]]
+     content
+     [:div.clear]
+     [:div#notthefooterclear]]
+    [:div#footer_wrap
+     [:div#footer
+      [:div.box_wide.separator_r
+       [:p#copyright "Copyright © 2011 Circle"]]
+      [:div#social_info.box_small.separator_r
+       [:ul
+        [:li#twitter [:a {:href "http://twitter.com/circleci"} "follow @circleci"]]]]
+      [:div#contact_info.box_small
+       [:h5
+        (unordered-list
+         ["questions@circleci.com"])]]
+      [:div.clear]]]]))


Make the site buttons do stuff!

Make the site buttons do stuff!

--- src/cljs/comic_reader/main.cljs
+++ src/cljs/comic_reader/main.cljs
@@ -47,10 +47,7 @@
     (fn []
       (when-let [site-list @site-list]
         [:ul (map (btn-for-id-name
-                   #(.log js/console
-                          (str "You clicked the "
-                               (:id %)
-                               " button!"))) site-list)]))))
+                   #(go-to (comics-path {:site (:id %)}))) site-list)]))))

 (defn comic-list []
   (let [comics-list (rf/subscribe [:comic-list])]