add options to filter events and provide extra data if necessary

This commit is contained in:
Roman Volosovskyi 2018-04-24 08:08:32 +02:00
parent 4182cf566d
commit abff71d312
No known key found for this signature in database
GPG Key ID: 0238A4B5ECEE70DE
4 changed files with 259 additions and 222 deletions

View File

@ -9,7 +9,8 @@
[status-im.utils.http :as http] [status-im.utils.http :as http]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[taoensso.timbre :as log])) [taoensso.timbre :as log]
[status-im.utils.mixpanel-events :as mixpanel-events]))
(def base-url "http://api.mixpanel.com/") (def base-url "http://api.mixpanel.com/")
(def base-track-url (str base-url "track/")) (def base-track-url (str base-url "track/"))
@ -61,8 +62,8 @@
(recur (conj accumulator event)) (recur (conj accumulator event))
accumulator))] accumulator))]
(async/go (async/go
(doseq [batch (partition-all max-batch-size events)] (doseq [batch (partition-all max-batch-size events)]
(async/<! (callback batch))))))) (async/<! (callback batch)))))))
(defn track (defn track
"Track or accumulate an event" "Track or accumulate an event"
@ -75,20 +76,52 @@
(when-not offline? (when-not offline?
(async/go (async/<! (drain-events-queue!)))))) (async/go (async/<! (drain-events-queue!))))))
(def event-tag "events")
;; Mixpanel events definition ;; Mixpanel events definition
(def events (reader/read-string (slurp/slurp "./src/status_im/utils/mixpanel_events.edn"))) (defn event->triggers
(def event-by-trigger (reduce-kv #(assoc %1 (:trigger %3) %3) {} events)) "Transform definitions vector into map which will be used for matching later
(defn matches? [event trigger] [{:trigger [:en1]}
(cond (= 1 (count trigger)) {:trigger [:en1 :p1]}
(= (first event) (first trigger)) {:trigger [:en2 :p1]}
(= 2 (count trigger)) {:trigger [:en3 :p1 :p2 :p3]}]
(and
(= (first event) (first trigger))
(= (second event) (second trigger)))
:else
(= event trigger)))
(defn matching-events [event definitions] will be transformed into
(reduce-kv #(if (matches? event %2) (conj %1 %3) %1) [] definitions))
{:en1 {events ({:trigger [:en1]})
:p1 {events ({:trigger [:en1 :p1]})}}
:en2 {:p1 {events ({:trigger [:en2 :p1]})}}
:en3 {:p1 {:p2 {:p3 {events ({:trigger [:en3 :p1 :p2 :p3]})}}}}}"
[events]
(reduce (fn [m {:keys [trigger] :as event}]
(update-in m (conj trigger event-tag) conj event))
{}
events))
(def event-by-trigger
(event->triggers mixpanel-events/events))
(defn matching-events [[event-name first-arg :as event] triggers]
(let [cnt (count event)
triggers (cond->
;; first we get all events which are triggered by event name
;; (the case when :trigger contains only one element)
;; {:trigger [:only-event-name-here]}
(get-in triggers [event-name event-tag])
;; when event contains two or more elements we are trying
;; to match by first two elements of :trigger
;; {:trigger [:event-name :one-parameter]}
(>= cnt 2)
(concat (get-in triggers [event-name first-arg event-tag]))
;; also if event contains more than one parameter (more than
;; two elements) we are trying to match it with equal :trigger
;; {:trigger [:e-name :p1 :p2 :p3]}
;; will match only with [:e-name :p1 :p2 :p3] event
(> cnt 2)
(concat (get-in triggers (conj event event-tag))))]
(filter (fn [{:keys [filter-fn]}]
(or (not filter-fn) (filter-fn {:event event})))
triggers)))

View File

@ -0,0 +1,195 @@
(ns status-im.utils.mixpanel-events)
;; This file is supposed to be edited by Chad.
;; Chad loves mixpanel. When Chad was a child he dreamed of being a mixpanel tamer.
;; To add a new event definition, add a new map to this vector
;; :label is a free string that is displayed in mixpanel
;; :trigger is the re-frame style event that when matched will generate the emission of a mixpanel event
;; :properties is a map of key / value pair that will be added to the mixpanel event to provide more context
(def events
[;; Account creation
{:label "Account created"
:trigger [:account-finalized]}
{:label "Account restored"
:trigger [:account-recovered]}
;; Account lifecycle
{:label "Login"
:trigger [:initialize-account]}
{:label "Logout"
:trigger [:navigate-to :accounts]}
; Push notification settings
{:label "Granted push notifications"
:trigger [:request-notifications-granted]}
{:label "Denied push notifications"
:trigger [:request-notifications-denied]}
;; Tab navigation
{:label "Tap"
:trigger [:navigate-to-clean :home]
:properties {:target :home}}
{:label "Tap"
:trigger [:navigate-to-tab :home]
:properties {:target :home}}
{:label "Tap"
:trigger [:navigate-to-tab :my-profile]
:properties {:target :my-profile}}
{:label "Tap"
:trigger [:navigate-to-clean :wallet]
:properties {:target :wallet}}
{:label "Tap"
:trigger [:navigate-to-tab :wallet]
:properties {:target :wallet}}
;; New
{:label "Tap"
:trigger [:navigate-to-tab :new]
:properties {:target :new}}
{:label "Tap"
:trigger [:navigate-to :new-chat]
:properties {:target :new-chat}}
{:label "Tap"
:trigger [:scan-qr-code]
:properties {:target :new-chat-qr-code}}
{:label "Tap"
:trigger [:show-profile]
:properties {:target :show-profile}}
{:label "Tap"
:trigger [:open-contact-toggle-list :chat-group]
:properties {:target :new-group-chat}}
{:label "Tap"
:trigger [:navigate-to :new-public-chat]
:properties {:target :new-public-chat}}
{:label "Tap"
:trigger [:navigate-to :open-dapp]
:properties {:target :open-dapp}}
{:label "Tap"
:trigger [:navigate-to :dapp-description]
:properties {:target :open-dapp-description}}
{:label "Tap"
:trigger [:open-dapp-in-browser]
:properties {:target :open-selected-dapp}}
{:label "Tap"
:trigger [:navigate-to :new-group]
:properties {:target :start-group-chat-next}}
{:label "Tap"
:trigger [:create-new-public-chat]
:properties {:target :create-public-chat}}
{:label "Tap"
:trigger [:navigate-to :new-public-chat]
:properties {:target :join-public-chat}}
;; Chat
{:label "Tap"
:trigger [:navigate-to-chat]
:properties {:target :open-existing-chat}}
{:label "Tap"
:trigger [:navigate-to :browser]
:properties {:target :open-existing-dapp}}
{:label "Tap"
:trigger [:start-chat]
:properties {:target :start-chat}}
{:label "Tap"
:trigger [:send-current-message]
:properties {:target :send-current-message}}
;; Wallet
{:label "Tap"
:trigger [:navigate-to :wallet-send-transaction]
:properties {:target :wallet-send-transaction}}
{:label "Tap"
:trigger [:navigate-to :wallet-request-transaction]
:properties {:target :wallet-request-transaction}}
{:label "Tap"
:trigger [:navigate-to :transactions-history]
:properties {:target :transactions-history}}
{:label "Tap"
:trigger [:navigate-to :recent-recipients]
:properties {:target :select-recipient
:type :recent-recipients}}
{:label "Tap"
:trigger [:navigate-to :recipient-qr-code]
:properties {:target :select-recipient
:type :recipient-qr-code}}
{:label "Tap"
:trigger [:navigate-to :contact-code]
:properties {:target :select-recipient
:type :contact-code}}
{:label "Tap"
:trigger [:navigate-to :wallet-send-assets]
:properties {:target :wallet-send-assets}}
{:label "Tap"
:trigger [:wallet.send/toggle-advanced]
:properties {:target :wallet-advanced}}
{:label "Tap"
:trigger [:wallet.send/set-signing?]
:properties {:target :wallet-open-sign-transaction}}
{:label "Tap"
:trigger [:wallet/sign-transaction]
:properties {:target :wallet-sign-transaction}}
{:label "Tap"
:trigger [:navigate-to-clean :wallet]
:properties {:target :wallet-got-it}}
{:label "Tap"
:trigger [:navigate-to :wallet-transaction-sent]
:properties {:target :wallet-transaction-sent}}
;;Profile
{:label "Tap"
:trigger [:my-profile/start-editing-profile]
:properties {:target :edit-profile}}
{:label "Tap"
:trigger [:my-profile/update-picture]
:properties {:target :edit-image
:type :gallery}}
{:label "Tap"
:trigger [:navigate-to :profile-photo-capture]
:properties {:target :edit-image
:type :capture}}
{:label "Tap"
:trigger [:navigate-to :profile-qr-viewer]
:properties {:target :share-contact-code}}
{:label "Tap"
:trigger [:set :my-profile/advanced? true]
:properties {:target :profile-advanced
:type :open}}
{:label "Tap"
:trigger [:set :my-profile/advanced? false]
:properties {:target :profile-advanced
:type :closed}}
{:label "Tap"
:trigger [:switch-dev-mode true]
:properties {:target :profile-dev-mode
:type :on}}
{:label "Tap"
:trigger [:switch-dev-mode false]
:properties {:target :profile-dev-mode
:type :off}}
{:label "Tap"
:trigger [:navigate-to :backup-seed]
:properties {:target :backup-your-seed-phrase}}
{:label "Tap"
:trigger [:set-in [:my-profile/seed :step] :12-words]
:properties {:target :seed-phrase
:type :welcome-ok}}
{:label "Tap"
:trigger [:my-profile/enter-two-random-words]
:properties {:target :seed-phrase
:type :step1-next}}
{:label "Tap"
:trigger [:my-profile/set-step :second-word]
:properties {:target :seed-phrase
:type :step2-next}}
{:label "Tap"
:trigger [:my-profile/finish]
:properties {:target :seed-phrase
:type :step3-done}}])

View File

@ -1,194 +0,0 @@
;; This file is supposed to be edited by Chad.
;; Chad loves mixpanel. When Chad was a child he dreamed of being a mixpanel tamer.
;; To add a new event definition, add a new map to this vector
;; :label is a free string that is displayed in mixpanel
;; :trigger is the re-frame style event that when matched will generate the emission of a mixpanel event
;; :properties is a map of key / value pair that will be added to the mixpanel event to provide more context
[;; Account creation
{:label "Account created"
:trigger [:account-finalized]}
{:label "Account restored"
:trigger [:account-recovered]}
;; Account lifecycle
{:label "Login"
:trigger [:initialize-account]}
{:label "Logout"
:trigger [:navigate-to :accounts]}
; Push notification settings
{:label "Granted push notifications"
:trigger [:request-notifications-granted]}
{:label "Denied push notifications"
:trigger [:request-notifications-denied]}
;; Tab navigation
{:label "Tap"
:trigger [:navigate-to-clean :home]
:properties {:target :home}}
{:label "Tap"
:trigger [:navigate-to-tab :home]
:properties {:target :home}}
{:label "Tap"
:trigger [:navigate-to-tab :my-profile]
:properties {:target :my-profile}}
{:label "Tap"
:trigger [:navigate-to-clean :wallet]
:properties {:target :wallet}}
{:label "Tap"
:trigger [:navigate-to-tab :wallet]
:properties {:target :wallet}}
;; New
{:label "Tap"
:trigger [:navigate-to-tab :new]
:properties {:target :new}}
{:label "Tap"
:trigger [:navigate-to :new-chat]
:properties {:target :new-chat}}
{:label "Tap"
:trigger [:scan-qr-code]
:properties {:target :new-chat-qr-code}}
{:label "Tap"
:trigger [:show-profile]
:properties {:target :show-profile}}
{:label "Tap"
:trigger [:open-contact-toggle-list :chat-group]
:properties {:target :new-group-chat}}
{:label "Tap"
:trigger [:navigate-to :new-public-chat]
:properties {:target :new-public-chat}}
{:label "Tap"
:trigger [:navigate-to :open-dapp]
:properties {:target :open-dapp}}
{:label "Tap"
:trigger [:navigate-to :dapp-description]
:properties {:target :open-dapp-description}}
{:label "Tap"
:trigger [:open-dapp-in-browser]
:properties {:target :open-selected-dapp}}
{:label "Tap"
:trigger [:navigate-to :new-group]
:properties {:target :start-group-chat-next}}
{:label "Tap"
:trigger [:create-new-public-chat]
:properties {:target :create-public-chat}}
{:label "Tap"
:trigger [:navigate-to :new-public-chat]
:properties {:target :join-public-chat}}
;; Chat
{:label "Tap"
:trigger [:navigate-to-chat]
:properties {:target :open-existing-chat}}
{:label "Tap"
:trigger [:navigate-to :browser]
:properties {:target :open-existing-dapp}}
{:label "Tap"
:trigger [:start-chat]
:properties {:target :start-chat}}
{:label "Tap"
:trigger [:send-current-message]
:properties {:target :send-current-message}}
;; Wallet
{:label "Tap"
:trigger [:navigate-to :wallet-send-transaction]
:properties {:target :wallet-send-transaction}}
{:label "Tap"
:trigger [:navigate-to :wallet-request-transaction]
:properties {:target :wallet-request-transaction}}
{:label "Tap"
:trigger [:navigate-to :transactions-history]
:properties {:target :transactions-history}}
{:label "Tap"
:trigger [:navigate-to :recent-recipients]
:properties {:target :select-recipient
:type :recent-recipients}}
{:label "Tap"
:trigger [:navigate-to :recipient-qr-code]
:properties {:target :select-recipient
:type :recipient-qr-code}}
{:label "Tap"
:trigger [:navigate-to :contact-code]
:properties {:target :select-recipient
:type :contact-code}}
{:label "Tap"
:trigger [:navigate-to :wallet-send-assets]
:properties {:target :wallet-send-assets}}
{:label "Tap"
:trigger [:wallet.send/toggle-advanced]
:properties {:target :wallet-advanced}}
{:label "Tap"
:trigger [:wallet.send/set-signing?]
:properties {:target :wallet-open-sign-transaction}}
{:label "Tap"
:trigger [:wallet/sign-transaction]
:properties {:target :wallet-sign-transaction}}
{:label "Tap"
:trigger [:navigate-to-clean :wallet]
:properties {:target :wallet-got-it}}
{:label "Tap"
:trigger [:navigate-to :wallet-transaction-sent]
:properties {:target :wallet-transaction-sent}}
;;Profile
{:label "Tap"
:trigger [:my-profile/start-editing-profile]
:properties {:target :edit-profile}}
{:label "Tap"
:trigger [:my-profile/update-picture]
:properties {:target :edit-image
:type :gallery}}
{:label "Tap"
:trigger [:navigate-to :profile-photo-capture]
:properties {:target :edit-image
:type :capture}}
{:label "Tap"
:trigger [:navigate-to :profile-qr-viewer]
:properties {:target :share-contact-code}}
{:label "Tap"
:trigger [:set :my-profile/advanced? true]
:properties {:target :profile-advanced
:type :open}}
{:label "Tap"
:trigger [:set :my-profile/advanced? false]
:properties {:target :profile-advanced
:type :closed}}
{:label "Tap"
:trigger [:switch-dev-mode true]
:properties {:target :profile-dev-mode
:type :on}}
{:label "Tap"
:trigger [:switch-dev-mode false]
:properties {:target :profile-dev-mode
:type :off}}
{:label "Tap"
:trigger [:navigate-to :backup-seed]
:properties {:target :backup-your-seed-phrase}}
{:label "Tap"
:trigger [:set-in [:my-profile/seed :step] :12-words]
:properties {:target :seed-phrase
:type :welcome-ok}}
{:label "Tap"
:trigger [:my-profile/enter-two-random-words]
:properties {:target :seed-phrase
:type :step1-next}}
{:label "Tap"
:trigger [:my-profile/set-step :second-word]
:properties {:target :seed-phrase
:type :step2-next}}
{:label "Tap"
:trigger [:my-profile/finish]
:properties {:target :seed-phrase
:type :step3-done}}]

View File

@ -3,29 +3,32 @@
[status-im.utils.mixpanel :as mixpanel] [status-im.utils.mixpanel :as mixpanel]
[cljs.core.async :as async])) [cljs.core.async :as async]))
(deftest events (def definitions
(is (not (nil? mixpanel/events)))) (mixpanel/event->triggers
[{:trigger [:key]}
{:trigger [:key :subkey]}
{:trigger [:key2]
:filter-fn (fn [{[_ first-parameter] :event}]
(true? first-parameter))}
{:trigger [:key3 :p1 :p2 :p3]}]))
(deftest matches?
(is (true? (mixpanel/matches? [:key] [:key])))
(is (false? (mixpanel/matches? [:key1] [:key2])))
(is (true? (mixpanel/matches? [:key :subkey] [:key])))
(is (false? (mixpanel/matches? [:key] [:key :subkey]))))
(def definitions {[:key] {:trigger [:key]} [:key :subkey] {:trigger [:key :subkey]}})
(deftest matching-event (deftest matching-event
(is (empty? (mixpanel/matching-events [:non-existing] definitions))) (is (empty? (mixpanel/matching-events [:non-existing] definitions)))
(is (= 1 (count (mixpanel/matching-events [:key] definitions)))) (is (= 1 (count (mixpanel/matching-events [:key] definitions))))
(is (= 2 (count (mixpanel/matching-events [:key :subkey] definitions)))) (is (= 2 (count (mixpanel/matching-events [:key :subkey] definitions))))
(is (= 1 (count (mixpanel/matching-events [:key2 true] definitions))))
(is (= 1 (count (mixpanel/matching-events [:key3 :p1 :p2 :p3] definitions))))
(is (empty? (mixpanel/matching-events [:key3 :p1 :p2 :p4] definitions)))
(is (empty? (mixpanel/matching-events [:key2 false] definitions)))
(is (empty? (mixpanel/matching-events [:key1 :another-subkey] definitions)))) (is (empty? (mixpanel/matching-events [:key1 :another-subkey] definitions))))
(deftest drain-events-queue!-test (deftest drain-events-queue!-test
(async (async
done done
(let [queue (async/chan (async/sliding-buffer 2000)) (let [queue (async/chan (async/sliding-buffer 2000))
results (atom [])] results (atom [])]
(async/go (async/go
(async/<! (async/onto-chan queue (range 123) false)) (async/<! (async/onto-chan queue (range 123) false))
(async/<! (async/<!
(mixpanel/drain-events-queue! (mixpanel/drain-events-queue!