(ns redisload (:import java.util.Calendar) (:use clojure.contrib.sql clojure.contrib.str-utils config datalayer utils) (:require redis)) (defn redis-days [n] (* 24 60 60)) ;;;; Tag counters (def tag-query " SELECT u.nick as author, u.user_id as author_id, m.message_id as message_id, m.content as message_content, m.is_image as is_image, m.created_on as message_ts, r.key as room, r.admin_only as admin_only, t.created_on as tagged_on, u2.nick as tagger, u2.user_id as tagger_id FROM users u, messages m, rooms r, tags t, users u2 WHERE u.user_id = m.user_id AND m.message_id = t.message_id AND r.room_id = m.room_id AND u2.user_id = t.user_id AND r.admin_only = false ") (def tag-counter (ref 0)) (defn stream-tags [fs] (with-connection *db* (with-query-results rs [tag-query] (doseq [r rs] (if (:admin_only r) (println r)) (dosync (ref-set tag-counter (inc @tag-counter))) (doseq [f fs] (f r)))))) (def hall-map (ref {})) (def popular-map (ref {})) (def score-map (ref {})) (def daily-hall-map (ref {})) (defn update-popular [r] (dosync (let [author (:author r) msg-id (:message_id r) msg-dt (format-yyyymmdd (:message_ts r)) user-map (get @popular-map author {}) msg-cnt (get user-map msg-id 0) hall-cnt (get @hall-map msg-id 0) usr-cnt (get @score-map author 0)] (alter score-map assoc author (inc usr-cnt)) (alter hall-map assoc msg-id (inc hall-cnt)) (when (:is_image r) (alter daily-hall-map update-in [msg-dt msg-id] #(inc (or % 0))) (alter popular-map assoc author (assoc user-map msg-id (inc msg-cnt))))))) (defn transmit-popular [] (doseq [[nick msgs] @popular-map] (let [sorted-msgs (sort #(>= (second %1) (second %2)) msgs) userkey (redis-popular-key nick)] (redis/atomically (redis/del key) (doseq [[msg-id score] (take (* num-popular-dumps 2) sorted-msgs)] (redis/zadd userkey score msg-id))))) (println "cached popular data for" (count @popular-map) "users")) (defn transmit-favscores [] (redis/atomically (redis/del redis-favscores-key) (doseq [[nick score] @score-map] (redis/zadd redis-favscores-key score (lower-case nick)))) (println "cached favscores for " (count @score-map) "users")) (defn transmit-hall [] (let [scores (take (* 2 num-hall-dumps) (sort #(>= (second %1) (second %2)) @hall-map))] (redis/atomically (redis/del redis-hall-key) (doseq [[msg-id score] scores] (redis/zadd redis-hall-key score msg-id))) (println "cached hall-of-fame"))) (defn transmit-daily-hall [] (doseq [[dt msgs] (sort-by first @daily-hall-map)] (let [datekey (redis-daily-hall-key dt)] (redis/atomically (redis/del datekey) (doseq [[msg-id score] (take 200 (sort-by #(- (second %)) msgs))] (redis/zadd datekey score msg-id)) (println "inserted daily hall into" datekey))))) (defn date-to-sql [dt] (java.sql.Date. (.getTime dt))) (defn date-to-cal [dt] (let [cal (Calendar/getInstance)] (.setTime cal dt) cal)) (defn cal-to-date [cal] (.getTime cal)) (defn next-cal [cal] (let [ret (.clone cal)] (.add ret Calendar/DAY_OF_MONTH 1) ret)) (defn get-fav-date-range [] (let [{min :min max :max} (first (do-select ["select min(created_on)::date, max(created_on)::date from tags"])) min (date-to-cal min) max (date-to-cal max)] (loop [acc [(cal-to-date min)] cur min] (let [next (next-cal cur)] (if (.after cur max) acc (recur (conj acc (cal-to-date next)) next)))))) (defn get-top-posts [dt limit] (do-select [" SELECT m.message_id as message_id, count(*) as score FROM messages m, tags t WHERE m.message_id = t.message_id AND m.created_on::date = ?::date GROUP BY m.message_id ORDER BY score DESC LIMIT ?" (date-to-sql dt) limit])) (defn build-daily-hall [dt] (let [top-posts (get-top-posts dt 200) datekey (redis-daily-hall-key (format-yyyymmdd dt))] (println (count top-posts) "posts for" datekey) (redis/with-server redis-server (redis/atomically (redis/del datekey) (doseq [m top-posts] (redis/zadd datekey (int (:score m)) (int (:message_id m)))))))) (defn build-all-daily-hall [] (doseq [dt (get-fav-date-range)] (build-daily-hall dt))) (build-all-daily-hall)