PageRenderTime 68ms CodeModel.GetById 19ms app.highlight 44ms RepoModel.GetById 2ms app.codeStats 0ms

/wtf.go

https://code.google.com/p/webtf/
Go | 365 lines | 310 code | 50 blank | 5 comment | 38 complexity | aa8aa1ae686e18adb535d87dd6a9c45b MD5 | raw file
  1// wtf - web server for TweetFreq
  2
  3package main
  4
  5import (
  6	"json"
  7	"time"
  8	"strconv"
  9	"http"
 10	"io/ioutil"
 11	"io"
 12	"fmt"
 13	"strings"
 14	"os"
 15)
 16
 17
 18type JTweets struct {
 19	Results []Result
 20}
 21
 22type Result struct {
 23	Profile_image_url string
 24	Created_at        string
 25	From_user         string
 26}
 27
 28type JUsers struct {
 29	Users []User
 30}
 31
 32type User struct {
 33	Screen_name string
 34}
 35
 36const canvasWidth = 1000 // 960
 37const canvasHeight = 750 // 750
 38const lmargin = 50
 39const tmargin = 95
 40const rmargin = canvasWidth - lmargin
 41const tloc = 40
 42const daybegin = "T00:00:00Z"
 43const dayend = "T23:59:59Z"
 44const secondsPerDay = 24 * 60 * 60
 45const maxDays = 4
 46const listURIfmt = "http://%s@api.twitter.com/1/%s/%s/members.json"
 47const queryURI = "http://search.twitter.com/search.%s?q=%s+since:%s+until:%s&rpp=%d"
 48const defaultPic = "http://static.twitter.com/images/default_profile_normal.png"
 49const linefill = "rgba(128,128,128,.20)"
 50const markerfill = "rgba(127,0,0,.40)"
 51const textfill = "rgb(127,127,127)"
 52const canvasfill = "rgb(255,255,255)"
 53const fontname = "Calibri,Lucida,sans-serif"
 54const initfmt = `<html><head><title>%s</title><script type="application/javascript">
 55function draw() {
 56var canvas = document.getElementById("canvas");
 57if (canvas.getContext) {
 58var C = canvas.getContext("2d");var p2=Math.PI*2;
 59C.fillStyle="%s";C.fillRect(0,0,%d,%d);
 60C.font="32pt %s";C.textAlign="center";C.fillStyle="%s";C.fillText("%s",%d,%d);C.font="10pt %s";
 61`
 62const legendfmt = `C.beginPath();C.moveTo(%d,%d);C.lineTo(%d,%d);C.lineTo(%d,%d);C.closePath();C.fill();C.fillStyle="%s";C.fillText("%s",%d,%d);
 63`
 64const endfmt = `}}</script></head><body onload="draw();"><canvas id="canvas" width="%d" height="%d"></canvas></body></html>
 65`
 66const userfmt = `
 67var im%d=new Image();im%d.onload=function doim%d(){C.drawImage(im%d,%d,%d,%d,%d);}
 68im%d.src="%s";
 69C.fillStyle="%s";C.fillText("%s [%d]",%d,%d);C.fillStyle="%s";C.fillRect(%d,%d,%d,%d);C.fillStyle="%s";
 70`
 71const pubfmt = `C.beginPath();C.arc(%d,%d,%d,0,p2,true);C.fill();
 72`
 73const setfont = `C.textAlign="left";C.font="16pt ` + fontname + `";`
 74
 75var upass = "username:password"
 76var initbegin = "2009-12-01"
 77var initend = "2009-12-01"
 78var title = "Twitter Update Frequency"
 79var begindate = "2009-12-01"
 80var enddate = "2009-12-01"
 81var qformat = "json"
 82var tcount = 50
 83var spacing = 60
 84var picwidth int64 = 48
 85var markerwidth int64 = 16
 86var lineheight int64 = 24
 87
 88var monthsOfYear = map[string]int{
 89	"Jan": 1, "Feb": 2, "Mar": 3, "Apr": 4, "May": 5, "Jun": 6,
 90	"Jul": 7, "Aug": 8, "Sep": 9, "Oct": 10, "Nov": 11, "Dec": 12,
 91}
 92
 93func vmap(value int64, low1 int64, high1 int64, low2 int64, high2 int64) int64 {
 94	return low2 + (high2-low2)*(value-low1)/(high1-low1)
 95}
 96
 97func secbetween(b string, e string) int64 { return isosec(e) - isosec(b) }
 98
 99func isosec(s string) int64 { return isototime(s).Seconds() }
100
101func isototime(s string) *time.Time {
102	if len(s) != 20 {
103		return nil
104	}
105	var year, _ = strconv.Atoi64(s[0:4])
106	var month, _ = strconv.Atoi(s[5:7])
107	var day, _ = strconv.Atoi(s[8:10])
108	var hour, _ = strconv.Atoi(s[11:13])
109	var minute, _ = strconv.Atoi(s[14:16])
110	var second, _ = strconv.Atoi(s[17:19])
111
112	t := time.Time{Year: year, Month: month, Day: day,
113		Hour: hour, Minute: minute, Second: second,
114		Zone: "UTC",
115	}
116	return &t
117}
118
119
120func rfc1123sec(s string) int64 { return rfc1123totime(s).Seconds() }
121
122func rfc1123totime(s string) *time.Time {
123	if len(s) != 31 {
124		return nil
125	}
126	var year, _ = strconv.Atoi64(s[12:16])
127	var month, _ = monthsOfYear[s[8:11]]
128	var day, _ = strconv.Atoi(s[5:7])
129	var hour, _ = strconv.Atoi(s[17:19])
130	var minute, _ = strconv.Atoi(s[20:22])
131	var second, _ = strconv.Atoi(s[23:25])
132
133	t := time.Time{Year: year, Month: month, Day: day,
134		Hour: hour, Minute: minute, Second: second,
135		Zone: "UTC",
136	}
137	return &t
138}
139
140
141func isodatestring(t *time.Time) string {
142	return fmt.Sprintf("%04d-%02d-%02d", t.Year, t.Month, t.Day)
143}
144
145func initialcode(c *http.Conn, t string, b string, e string) {
146	io.WriteString(c, fmt.Sprintf(initfmt, t, canvasfill, canvasWidth,
147		canvasHeight, fontname, textfill, t, canvasWidth/2, tloc, fontname))
148	io.WriteString(c, legend(b+daybegin, e+dayend, tmargin, 10, 10))
149}
150
151func legend(b string, e string, y int, w int, h int) string {
152	var x int64
153
154	days := int(secbetween(b, e)/secondsPerDay) + 2
155	var w2 int64
156	var pl = picwidth + lmargin
157	var ds string
158
159	w2 = int64(w / 2)
160	yh := y - h
161	ib := isosec(b)
162	ie := isosec(e)
163	s := ""
164	lx := ib
165	for i := 0; i < days; i++ {
166		x = vmap(lx, ib, ie, pl, rmargin)
167		ds = isodatestring(time.SecondsToUTC(lx))
168		s = s + fmt.Sprintf(legendfmt, x, y, x-w2, yh, x+w2, yh,
169			textfill, ds[0:10], x, yh-3)
170		lx += secondsPerDay
171	}
172	s += setfont
173	return s
174}
175
176func readjson(c *http.Conn, r io.ReadCloser, b string, e string, yv int) int {
177	var twitter JTweets
178	var data []byte
179	var ntweets int
180	data, err := ioutil.ReadAll(r)
181
182	b += daybegin
183	e += dayend
184	var y = int64(yv)
185
186	if err == nil {
187		ok, errtok := json.Unmarshal(string(data), &twitter)
188		if ok {
189			ntweets = len(twitter.Results)
190			if ntweets > 0 {
191				var pl = picwidth + lmargin
192				io.WriteString(c, fmt.Sprintf(userfmt, y, y, y, y, lmargin, y,
193					picwidth, picwidth, y, twitter.Results[0].Profile_image_url, textfill,
194					twitter.Results[0].From_user, ntweets, pl+5, y+picwidth, linefill,
195					pl, y, rmargin-pl, lineheight, markerfill))
196
197				for i := 0; i < ntweets; i++ {
198					io.WriteString(c, fmt.Sprintf(pubfmt,
199						vmap(rfc1123sec(twitter.Results[i].Created_at),
200							isosec(b), isosec(e), pl, rmargin),
201						y+(lineheight/2), markerwidth/2))
202				}
203				return ntweets
204			}
205		} else {
206			fmt.Printf("Unable to parse the JSON : [%v]\n", errtok)
207		}
208	}
209	return ntweets
210}
211
212
213func finalcode(c *http.Conn) {
214	io.WriteString(c, fmt.Sprintf(endfmt, canvasWidth, canvasHeight))
215}
216
217func tf(c *http.Conn, b string, e string, n int, y int, s string) int {
218
219	var qs string
220	var ntf int = 0
221
222	if len(s) < 1 {
223		return ntf
224	}
225
226	if s[0] == '#' && len(s) > 1 {
227		qs = s
228	} else {
229		qs = "from:" + s
230	}
231	r, _, err := http.Get(fmt.Sprintf(queryURI,
232		qformat, http.URLEscape(qs), b, e, n))
233	if err == nil {
234		if r.StatusCode == http.StatusOK {
235			ntf = readjson(c, r.Body, b, e, y)
236		} else {
237			fmt.Printf("Twitter is unable to search for %s (%s)\n", s, r.Status)
238		}
239		r.Body.Close()
240	} else {
241		fmt.Printf("%v\n", err)
242	}
243	return ntf
244}
245
246func initparams() {
247	tcount = 50
248	lineheight = 24
249	picwidth = 48
250	spacing = 60
251	markerwidth = 16
252	title = "Twitter Update Frequency"
253	begindate = initbegin
254	enddate = initend
255}
256
257func tfquery(req *http.Request) {
258	query := strings.Split(req.URL.RawQuery, "&", 0)
259
260	//fmt.Printf("path : %v\n", path)
261	//fmt.Printf("query: %v\n", query)
262
263	for i := 0; i < len(query); i++ {
264		nv := strings.Split(query[i], "=", 2)
265		if len(nv) == 2 {
266			switch nv[0] {
267			case "b":
268				begindate = nv[1]
269			case "e":
270				enddate = nv[1]
271			case "t":
272				title, _ = http.URLUnescape(nv[1])
273			case "c":
274				tcount, _ = strconv.Atoi(nv[1])
275			case "l":
276				lineheight, _ = strconv.Atoi64(nv[1])
277			case "p":
278				picwidth, _ = strconv.Atoi64(nv[1])
279			case "s":
280				spacing, _ = strconv.Atoi(nv[1])
281			case "m":
282				markerwidth, _ = strconv.Atoi64(nv[1])
283			}
284		}
285		//fmt.Printf("nv: %v\n", nv)
286		//showparams("Using  ")
287	}
288}
289
290
291func tfusers(c *http.Conn, req *http.Request) {
292
293	initparams()
294	tfquery(req)
295	path := strings.Split(req.URL.Path, "/", 0)
296	fmt.Printf("path: %v\n", path)
297
298	if len(path) > 1 {
299		users := strings.Split(path[2], ",", 0)
300		fmt.Printf("%s %v\n", c.RemoteAddr, users)
301		tfdisplay(c, users)
302	} else {
303		fmt.Printf("bogus path: %v\n", path)
304	}
305
306}
307
308func tflist(c *http.Conn, req *http.Request) {
309	var twitter JUsers
310
311	initparams()
312	tfquery(req)
313	path := strings.Split(req.URL.Path, "/", 0)
314	fmt.Printf("path: %v\n", path)
315	if len(path) > 3 {
316		r, _, err := http.Get(fmt.Sprintf(listURIfmt, upass, path[2], path[3]))
317		if err == nil {
318			data, _ := ioutil.ReadAll(r.Body)
319			ok, _ := json.Unmarshal(string(data), &twitter)
320			if ok {
321				nu := len(twitter.Users)
322				users := make([]string, nu)
323				for i := 0; i < nu; i++ {
324					users[i] = twitter.Users[i].Screen_name
325				}
326				fmt.Printf("members: %v\n", users)
327				tfdisplay(c, users)
328			}
329		}
330	}
331}
332
333
334func tfdisplay(c *http.Conn, users []string) {
335	initialcode(c, title, begindate, enddate)
336	for i, y := 0, tmargin; i < len(users); i++ {
337		if tf(c, begindate, enddate, tcount, y, users[i]) > 0 {
338			y += spacing
339		}
340	}
341	finalcode(c)
342}
343
344func showparams(why string) {
345	fmt.Printf("%s: t=\"%s\" b=%s e=%s c=%d l=%d p=%d s=%d m=%d f=%s\n",
346		why, title, begindate, enddate, tcount,
347		lineheight, picwidth, spacing, markerwidth, qformat)
348}
349
350func main() {
351  if len(os.Args) > 1 {
352    upass = os.Args[1]
353  }
354	t := time.UTC()
355	initend = isodatestring(t)
356	initbegin = isodatestring(time.SecondsToUTC(t.Seconds() -
357		(secondsPerDay * maxDays)))
358	showparams("init")
359	http.Handle("/users/", http.HandlerFunc(tfusers))
360	http.Handle("/list/", http.HandlerFunc(tflist))
361	err := http.ListenAndServe(":1958", nil)
362	if err != nil {
363		panic("ListenAndServe: ", err.String())
364	}
365}