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