Skip to content

Commit 813b046

Browse files
(WIP) Push what i have so Alan can take over. Refs clojure#48
1 parent f9055b7 commit 813b046

File tree

4 files changed

+138
-12
lines changed

4 files changed

+138
-12
lines changed

samples/twitterbuzz/index.html

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ <h2>Timeline</h2>
7272
<script type="text/javascript">
7373
goog.require('twitterbuzz.core');
7474
goog.require('twitterbuzz.timeline');
75+
goog.require('twitterbuzz.leaderboard');
7576
goog.require('twitterbuzz.showgraph');
7677
</script>
7778

samples/twitterbuzz/src/twitterbuzz/core.cljs

+94-11
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,92 @@
1+
; Copyright (c) Rich Hickey. All rights reserved.
2+
; The use and distribution terms for this software are covered by the
3+
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4+
; which can be found in the file epl-v10.html at the root of this distribution.
5+
; By using this software in any fashion, you are agreeing to be bound by
6+
; the terms of this license.
7+
; You must not remove this notice, or any other, from this software.
8+
19
(ns twitterbuzz.core
210
(:require [goog.net.Jsonp :as jsonp]
311
[goog.Timer :as timer]
412
[goog.events :as events]
513
[goog.dom :as dom]))
614

15+
(def state (atom {:max-id 1
16+
:graph {}
17+
:new-tweets-listeners []
18+
:graph-update-listeners []
19+
:tweet-count 0}))
20+
21+
(defn add-listener [k f]
22+
(swap! state (fn [old] (assoc old k (conj (k old) f)))))
23+
724
(def twitter-uri (goog.Uri. "http://twitter.com/search.json"))
825

926
(defn retrieve [payload callback]
1027
(.send (goog.net.Jsonp. twitter-uri)
1128
payload
1229
callback))
1330

14-
(def state (atom {:max-id 1 :functions [] :tweet-count 0}))
15-
1631
(defn send-tweets [fns tweets]
1732
(when (seq fns)
1833
(do ((first fns) tweets)
1934
(recur (rest fns) tweets))))
2035

36+
(defn parse-mentions [tweet]
37+
(map second (re-seq (re-pattern "@(\\w*)") (:text tweet))))
38+
39+
(defn add-mentions
40+
"Add the user to the mentions map for each user she mentions."
41+
[graph user mentions]
42+
(reduce (fn [acc next-mention]
43+
(if-let [node (get graph next-mention)]
44+
(let [mentions-map (get node :mentions {})]
45+
(assoc-in acc [next-mention :mentions user] (inc (get mentions-map user 0))))
46+
graph))
47+
graph
48+
mentions))
49+
50+
(defn update-graph [graph tweet-maps]
51+
(reduce (fn [acc tweet]
52+
(let [user (:from_user tweet)
53+
mentions (parse-mentions tweet)]
54+
(-> (if-let [existing-node (get acc user)]
55+
(assoc acc user
56+
(assoc existing-node :last-tweet (:text tweet)))
57+
(assoc acc user
58+
{:image-url (:profile_image_url tweet)
59+
:last-tweet (:text tweet)
60+
:mentions {}}))
61+
(add-mentions user mentions))))
62+
graph
63+
(map #(select-keys % [:text :from_user :profile_image_url]) tweet-maps)))
64+
65+
(defn num-mentions [user]
66+
(reduce + (vals (:mentions user))))
67+
68+
(defn update-state [old-state max-id tweets]
69+
(-> old-state
70+
(assoc :max-id max-id)
71+
(update-in [:tweet-count] #(+ % (count tweets)))
72+
(assoc :graph (update-graph (:graph old-state) tweets))))
73+
2174
(defn my-callback [json]
2275
(let [result-map (js->clj json :keywordize-keys true)
2376
new-max (:max_id result-map)
2477
old-max (:max-id @state) ;; the filter won't work if you inline this
2578
tweets (filter #(> (:id %) old-max)
2679
(:results result-map))]
27-
(do (swap! state (fn [old] (-> old
28-
(assoc :max-id new-max)
29-
(assoc :tweet-count (+ (:tweet-count old) (count tweets)))
30-
;; this doesn't work
31-
#_(update-in :tweet-count #(+ % (count tweets))))))
32-
(send-tweets (:functions @state) tweets))))
80+
(do (swap! state update-state new-max tweets)
81+
(send-tweets (:new-tweets-listeners @state) tweets)
82+
(send-tweets (:graph-update-listeners @state) (:graph @state)))))
3383

3484
(defn register
35-
"Register fn to be called with new tweets."
36-
[f]
37-
(swap! state (fn [old] (assoc old :functions (conj (:functions old) f)))))
85+
"Register a function to be called when new data arrives specifying
86+
the event to receive updates for. Events can be :new-tweets or :graph-update."
87+
[event f]
88+
(cond (= event :new-tweets) (add-listener :new-tweets-listeners f)
89+
(= event :graph-update) (add-listener :graph-update-listeners f)))
3890

3991
(defn search-tag
4092
"Get the current tag value from the page."
@@ -54,3 +106,34 @@
54106
(events/listen timer goog.Timer/TICK listener))))
55107

56108
(poll)
109+
110+
(comment
111+
112+
(parse-mentions {:text "What's up @sue and @larry"})
113+
114+
(add-mentions {} "jim" ["sue"])
115+
(add-mentions {"sue" {}} "jim" ["sue"])
116+
117+
(def tweets [{:profile_image_url "url1"
118+
:from_user "jim"
119+
:text "I like cookies!"}
120+
{:profile_image_url "url2"
121+
:from_user "sue"
122+
:text "Me to @jim."}
123+
{:profile_image_url "url3"
124+
:from_user "bob"
125+
:text "You shouldn't eat so many cookies @sue"}
126+
{:profile_image_url "url4"
127+
:from_user "sam"
128+
:text "@bob that was a cruel thing to say to @sue."}])
129+
130+
(def graph (update-graph {} tweets))
131+
132+
(num-mentions (get graph "sue"))
133+
(num-mentions (get graph "bob"))
134+
(num-mentions (get graph "sam"))
135+
136+
(take 1 (reverse (sort-by #(num-mentions (second %)) (seq graph))))
137+
138+
)
139+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
(ns twitterbuzz.leaderboard
2+
(:require [twitterbuzz.core :as buzz]
3+
[goog.dom :as dom]))
4+
5+
(defn dom-element [element attrs]
6+
(dom/createDom element
7+
(.strobj (reduce (fn [m [k v]]
8+
(assoc m k v))
9+
{}
10+
(map #(vector (name %1) %2) (keys attrs) (vals attrs))))))
11+
12+
(defn add-leaderboard-node [node]
13+
(let [user (first node)
14+
user-info (second node)
15+
parent (dom/getElement "leaderboard-content")
16+
child (dom-element "div" {:class "tweet"})
17+
user-e (dom-element "div" {:class "user-name"})
18+
text (dom-element "div" {:class "tweet-text"})
19+
pic (dom-element "img" {:src (:image-url user-info) :class "profile-pic"})
20+
num-mentions (dom-element "div")]
21+
(do (dom/setTextContent text (:last-tweet user-info))
22+
(dom/setTextContent user-e user)
23+
(dom/setTextContent num-mentions (str (buzz/num-mentions user-info)))
24+
(dom/appendChild child pic)
25+
(dom/appendChild child user-e)
26+
(dom/appendChild child text)
27+
(dom/appendChild child num-mentions)
28+
(dom/appendChild parent child 0))))
29+
30+
(defn clear-leaderboard []
31+
(let [parent (dom/getElement "leaderboard-content")]
32+
(do (dom/removeChildren parent))))
33+
34+
(defn top-n [n nodes]
35+
(reverse (sort-by #(buzz/num-mentions (second %)) nodes)))
36+
37+
(defn update-leaderboard [graph]
38+
(do (clear-leaderboard)
39+
(doseq [next-node (top-n 10 (seq graph))]
40+
(add-leaderboard-node next-node))))
41+
42+
(buzz/register :graph-update update-leaderboard)

samples/twitterbuzz/src/twitterbuzz/timeline.cljs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,4 @@
3939
(do (add-timeline-tweet (first tweets))
4040
(recur (rest tweets))))))))
4141

42-
(core/register update-timeline)
42+
(core/register :new-tweets update-timeline)

0 commit comments

Comments
 (0)