Skip to content

Instantly share code, notes, and snippets.

@Chouser
Created February 23, 2009 04:11
Show Gist options
  • Select an option

  • Save Chouser/68781 to your computer and use it in GitHub Desktop.

Select an option

Save Chouser/68781 to your computer and use it in GitHub Desktop.
(ns n01se.net.graph.issues
(:import (java.text SimpleDateFormat ParsePosition)
(java.util GregorianCalendar Calendar)
(org.jfree.chart ChartFrame))
(:use [clojure.zip :only (xml-zip node)]
[clojure.contrib.lazy-xml :only (parse-trim)]
[clojure.contrib.zip-filter.xml :only (xml-> attr text)]
[clojure.contrib.seq-utils :only (reductions)])
(:require [clojure.set :as set]
[clojure.contrib.zip-filter :as zf]
[com.markmfredrickson.dejcartes :as chart]))
(defn startparse-tagsoup [s ch]
(.parse (doto (org.ccil.cowan.tagsoup.Parser.) (.setContentHandler ch)) s))
(defn parse-html [url]
(xml-zip (parse-trim url startparse-tagsoup 10)))
(defn data-table-text [loc]
(xml-> loc [(attr :class) #(re-find #"col_" %)] text #(.trim %)))
(defn map-relation [relation fnmap]
(set (map #(reduce (fn [row [k v]]
(assoc row k ((or (fnmap k) identity) v)))
% %)
relation)))
(def now (GregorianCalendar.))
(defn add-days [cal i]
(doto (.clone cal) (.add Calendar/DATE i)))
(defn inc-date [cal]
(add-days cal 1))
(defn fixdate [datestr]
(when (not= datestr "----")
(let [[_ n type] (re-find #"(\d+) (days|hours|minutes) ago" datestr)]
(condp = type
"days" (add-days now (- (Integer. n)))
"hours" (add-days now (- (quot (Integer. n) 24)))
"minutes" now
(let [date (.parse (SimpleDateFormat. "MMM dd") datestr)]
(doto (.clone now)
(.set Calendar/MONTH (.getMonth date))
(.set Calendar/DAY_OF_MONTH (.getDate date))))))))
(defn make-relation [html-zip]
(let [headers (xml-> html-zip zf/descendants :th data-table-text)
relkeys (map #(keyword (.toLowerCase %)) headers)
row (apply create-struct relkeys)
relation (map #(apply struct row %)
(partition (count relkeys)
(xml-> html-zip zf/descendants :td
data-table-text)))]
(map-relation relation
{:id #(Integer. %)
:opened fixdate
:closed fixdate})))
(defn count-by-date [min-date k relation]
(reduce (fn [m rec] (let [o (k rec)]
(assoc m o (inc (m o 0)))))
{} relation))
(defn cal-range [start end]
(take-while #(.before % end) (iterate inc-date start)))
(defn sums [func coll]
(reductions (fn [sum value] (+ sum (func value))) 0 coll))
(defn chart-data [x-labels & set-pairs]
(apply map #(vector %1 (apply array-map (apply concat %&)))
x-labels
(map (fn [[label values]] (map #(list label %) values))
set-pairs)))
(defn count-over-time [rel]
(let [min-date (reduce #(if (.before %1 %2) %1 %2) (map :opened rel))
dates (cal-range min-date (inc-date now))
open-delta (count-by-date min-date :opened rel)
close-delta (count-by-date min-date :closed rel)]
(chart-data dates
["open" (sums #(+ (open-delta % 0) (- (close-delta % 0))) dates)]
["close" (sums #(open-delta % 0) dates)])))
(def data (-> (str "http://code.google.com/p/clojure/issues/list?can=1&q=&"
"sort=&colspec=ID+Type+Priority+Opened+Closed&nobtn=Update")
parse-html make-relation count-over-time))
(defn make-window [title chart]
(doto (ChartFrame. title chart)
(.pack)
(.setVisible true)))
(make-window "Clojure stats"
(chart/line "Clojure Issue Count Over Time" "Date" "Issues" data))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment