Beispiel #1
0
func HandleStats(process string, db *mgo.Database, w http.ResponseWriter, r *http.Request) error {
	r.ParseForm()

	amount := 2
	chartwidth := 800
	chartheight := 600

	if r.Form.Get("amount") != "" {
		pamount, err := strconv.ParseInt(r.Form.Get("amount"), 10, 16)
		if err == nil {
			amount = int(pamount)
		}
	}
	if r.Form.Get("chartwidth") != "" {
		pchartwidth, err := strconv.ParseInt(r.Form.Get("chartwidth"), 10, 16)
		if err == nil {
			chartwidth = int(pchartwidth)
		}
	}
	if r.Form.Get("chartheight") != "" {
		pchartheight, err := strconv.ParseInt(r.Form.Get("chartheight"), 10, 16)
		if err == nil {
			chartheight = int(pchartheight)
		}
	}
	if chartwidth < 40 {
		chartwidth = 40
	}
	if chartheight < 40 {
		chartheight = 40
	}

	q := &info.StatsQuery{
		Process: process,
		Data:    info.SplitParams(r.Form.Get("data")),
		Period:  r.Form.Get("period"),
		Filters: make(map[string]string),
		Groups:  info.SplitParams(r.Form.Get("group")),
		Amount:  amount,
		App:     r.Form.Get("app"),
	}

	output := r.Form.Get("output") // json, chart

	// filters
	for fname, _ := range r.Form {
		if strings.HasPrefix(fname, "f_") {
			q.Filters[strings.TrimPrefix(fname, "f_")] = r.Form.Get(fname)
		}
	}

	res, err := info.QueryStats(db, q)
	if err != nil {
		return err
	}

	if output == "chart" {
		// output chart
		p, err := plot.New()
		if err != nil {
			return err
		}

		p.Title.Text = fmt.Sprintf("Chart %s - %s", r.Form.Get("data"), q.Period)
		p.X.Label.Text = fmt.Sprintf("Day (%s to %s)", res.StartDate.String(), res.EndDate.String())
		//p.X.Tick.Marker = plot.ConstantTicks(res.Ticks())

		for didx, ditem := range q.Data {
			if resinfo, ok := res.Result.(*info.InfoResult); ok {
				resinfo.SetPlotItem(ditem)
				err = InfoAddLinePoints(p, didx, ditem, resinfo)
				if err != nil {
					return err
				}
			} else if resgroup, ok := res.Result.(*info.InfoResultGroup); ok {
				for rgidx, rg := range resgroup.Group {
					rg.SetPlotItem(ditem)
					err = InfoAddLinePoints(p, didx*len(resgroup.Group)+rgidx, ditem+" - "+rg.GroupId, rg)
					if err != nil {
						return err
					}
				}
			}
		}

		w.Header().Add("Content-Type", "image/png")

		width := vg.Length(chartwidth)
		height := vg.Length(chartheight)
		c := vgimg.PngCanvas{vgimg.New(width, height)}
		p.Draw(plot.MakeDrawArea(c))
		c.WriteTo(w)
	} else if output == "table" || output == "atable" {
		w.Header().Add("Content-Type", "text/html; charset=utf-8")

		w.Write([]byte("<table border=\"1\">"))

		for _, ditem := range q.Data {

			w.Write([]byte("<tr>"))
			w.Write([]byte(fmt.Sprintf("<td><b>%s</b></td>", html.EscapeString(ditem))))

			if resinfo, ok := res.Result.(*info.InfoResult); ok {
				resinfo.SetPlotItem(ditem)
				for ridx, rdata := range resinfo.List {
					var dvalue string
					if output == "atable" {
						_, dd := resinfo.XY(ridx)
						if dd != 0 {
							dvalue = fmt.Sprintf("%.1f", dd)
						} else {
							dvalue = "0"
						}
					} else {
						dvalue = fmt.Sprintf("%v", rdata[ditem])
					}
					if dvalue == "0" {
						dvalue = "-"
					}

					w.Write([]byte(fmt.Sprintf("<td align=\"center\">%s</td>", html.EscapeString(dvalue))))
				}

				w.Write([]byte("</tr>"))
			} else if resgroup, ok := res.Result.(*info.InfoResultGroup); ok {
				w.Write([]byte("</tr>"))

				for _, rg := range resgroup.Group {
					w.Write([]byte(fmt.Sprintf("<tr><td>%s</td>", rg.GroupId)))

					rg.SetPlotItem(ditem)
					for ridx, rdata := range rg.List {
						var dvalue string
						if output == "atable" {
							_, dd := rg.XY(ridx)
							if dd != 0 {
								dvalue = fmt.Sprintf("%.1f", dd)
							} else {
								dvalue = "0"
							}
						} else {
							dvalue = fmt.Sprintf("%v", rdata[ditem])
						}
						if dvalue == "0" {
							dvalue = "-"
						}

						w.Write([]byte(fmt.Sprintf("<td align=\"center\">%s</td>", html.EscapeString(dvalue))))
					}

					w.Write([]byte("</tr>"))
				}
			}
		}

		w.Write([]byte("</table>"))
	} else {
		// output json data
		stenc, err := json.Marshal(InfoResponse{ErrorCode: 0, Data: res.Result})
		if err != nil {
			return fmt.Errorf("Error encoding json data: %s", err)
		}

		w.Header().Add("Content-Type", "application/json; charset=utf-8")
		w.Write(stenc)

	}

	return nil
}
Beispiel #2
0
func drawPng(b *bytes.Buffer, p *plot.Plot, width, height float64) {
	w, h := vg.Inches(width), vg.Inches(height)
	c := vgimg.PngCanvas{Canvas: vgimg.New(w, h)}
	p.Draw(plot.MakeDrawArea(c))
	c.WriteTo(b)
}
Beispiel #3
0
func RunServer(config *Config) {
	// manually load fonts from resource
	for _, fontasset := range AssetNames() {
		if strings.HasPrefix(fontasset, "res/fonts/") {
			fontname := strings.TrimSuffix(path.Base(fontasset), path.Ext(fontasset))
			fontbytes, err := Asset(fontasset)
			if err != nil {
				panic(err)
			}
			fontreader := bytes.NewReader(fontbytes)
			vg.LoadFont(fontname, fontreader)
		}
	}

	// create memory caches
	homeCache := cache.New(10*time.Minute, 5*time.Minute)
	categoryCache := cache.New(30*time.Minute, 30*time.Minute)
	detailCache := cache.New(15*time.Minute, 10*time.Minute)

	initialCheck := func(w http.ResponseWriter, r *http.Request) bool {
		// clear cache if requested
		nocache := r.Form.Get("nocache")
		if nocache == "1" {
			homeCache.Flush()
			categoryCache.Flush()
			detailCache.Flush()
		}

		return true
	}

	http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "Not found", 404)
	})
	http.HandleFunc("/favicon.png", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "Not found", 404)
	})

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		//config.Logger.Printf("Connection: %s\n", r.URL.String())

		var err error

		r.ParseForm()

		if !initialCheck(w, r) {
			return
		}

		csession := config.Session.Clone()
		defer csession.Close()

		cat := r.Form.Get("category")
		chart := r.Form.Get("chart")
		p_page := r.Form.Get("pg")
		page := 1
		if p_page != "" {
			t_page, err := strconv.ParseInt(p_page, 10, 32)
			if err != nil {
				http.Error(w, err.Error(), 500)
				return
			}
			page = int(t_page)
		}
		if page < 1 {
			page = 1
		}

		i := fstopinfo.NewInfo(config.Logger, csession)
		i.Database = config.Database

		// load categories
		var c fstopinfo.FSCategoryList

		catcache, catfound := categoryCache.Get("category")
		if catfound {
			c = catcache.(fstopinfo.FSCategoryList)
		}

		if c == nil {
			c, err = i.Categories()
			if err != nil {
				http.Error(w, err.Error(), 500)
				return
			}
			if c != nil {
				categoryCache.Set("category", c, 0)
			}
		}

		if c == nil {
			http.Error(w, "Could not load categories", 500)
			return
		}

		// load home
		var d fstopimp.FSTopStatsList

		var dcache interface{}
		var dfound bool
		var dname string
		if cat == "" {
			dname = "index"
		} else {
			// check if category exists
			if !c.Exists(cat) {
				http.Error(w, "Category not found", 404)
				return
			}
			dname = cat
		}
		dcache, dfound = homeCache.Get(dname)
		if dfound {
			d = dcache.(fstopimp.FSTopStatsList)
		}

		// data not on cache, load
		if d == nil {
			if cat == "" {
				d, err = i.Top(config.TopId)
			} else {
				d, err = i.TopCategory(config.TopId, cat)
			}
			if err != nil {
				http.Error(w, err.Error(), 500)
				return
			}
			if len(d) > 0 {
				homeCache.Set(dname, d, 0)
			}
		}

		pagecount := d.PageCount(config.PageSize)
		d = d.Paged(page, config.PageSize)

		w.Header().Add("Content-Type", "text/html; charset=utf-8")

		var body *bytes.Buffer = new(bytes.Buffer)

		tmpldata := map[string]interface{}{
			"Title":      config.Title,
			"page":       page,
			"categories": c,
		}

		fmt.Fprintln(body, "<table class=\"main\">")

		fmt.Fprintln(body, "<tr><th>Chart</th><th>Title</th><th width=\"8%\">Added</th><th width=\"5%\">Score</th><th width=\"5%\">Count</th><th width=\"5%\">Comm.</th></tr>")

		for _, ii := range d {
			fmt.Fprintf(body, "<tr>\n")

			if chart == "" {
				fmt.Fprintf(body, "<td><img style=\"height: 107px;\" src=\"/chart?id=%s&size=short\"></td>",
					ii.Id)
			}

			fmt.Fprintf(body, "<td><a href=\"%s\">%s</a> <a href=\"/view?id=%s\">[data]</a> <a href=\"/chart?id=%s\">[chart]</a></td>"+
				"<td align=\"center\" style=\"%s\">%s</td>"+
				"<td align=\"right\">%d</td><td align=\"center\">%d</td><td align=\"center\">%d</td>\n",
				ii.Link, ii.Title, ii.Id, ii.Id,
				StyleAddDate(ii.Last.AddDate), FormatAddDate(ii.Last.AddDate), ii.Score, ii.Count, ii.Last.Comments)

			fmt.Fprintf(body, "</tr>\n")

			if chart == "1" {
				fmt.Fprintf(body, "<tr><td colspan=\"5\" align=\"center\"><img src=\"/chart?id=%s&size=small\"/></td></tr>\n", ii.Id)
			}
		}
		fmt.Fprintln(body, "</table>")

		pageparams := url.Values{}
		if cat != "" {
			pageparams.Add("category", cat)
		}
		if page > 1 {
			pageparams.Set("pg", "1")
			tmpldata["page_first"] = fmt.Sprintf("/?%s", pageparams.Encode())
		}
		if page > 1 {
			pageparams.Set("pg", strconv.Itoa(page-1))
			tmpldata["page_prev"] = fmt.Sprintf("/?%s", pageparams.Encode())
		}
		if len(d) > 0 && page < pagecount {
			pageparams.Set("pg", strconv.Itoa(page+1))
			tmpldata["page_next"] = fmt.Sprintf("/?%s", pageparams.Encode())
		}
		if page != pagecount {
			pageparams.Set("pg", strconv.Itoa(pagecount))
			tmpldata["page_last"] = fmt.Sprintf("/?%s", pageparams.Encode())
		}

		tmpl := LoadTemplates("index")
		tmpldata["Body"] = template.HTML(body.String())

		err = tmpl.ExecuteTemplate(w, "index", tmpldata)
		if err != nil {
			http.Error(w, "Error processing template", 500)
			return
		}

	})

	http.HandleFunc("/view", func(w http.ResponseWriter, r *http.Request) {
		r.ParseForm()

		if !initialCheck(w, r) {
			return
		}

		csession := config.Session.Clone()
		defer csession.Close()

		w.Header().Add("Content-Type", "text/html; charset=utf-8")

		id := r.Form.Get("id")
		if id == "" {
			http.Error(w, "ID not sent", 500)
			return
		}

		i := fstopinfo.NewInfo(config.Logger, csession)
		i.Database = config.Database

		var d []*fstopinfo.FSInfoHistory
		var err error

		dcache, found := detailCache.Get(id)
		if found {
			d = dcache.([]*fstopinfo.FSInfoHistory)
		}

		if d == nil {
			d, err = i.History(id, config.HistoryDays)
			if err != nil {
				http.Error(w, err.Error(), 500)
				return
			}
			if d != nil {
				detailCache.Set(id, d, 0)
			}
		}

		if d == nil {
			http.Error(w, "Not found", 500)
			return
		}

		var body *bytes.Buffer = new(bytes.Buffer)

		fmt.Fprintln(body, "<table class=\"main\" border=\"1\">")

		fmt.Fprintln(body, "<tr><th>Date</th><th>Hour</th><th>Seeders</th><th>Leechers</th><th>Complete</th><th>Comments</th></tr>")

		var last *fstopinfo.FSInfoHistory
		first := true

		for _, ii := range d {
			//if ii.Item != nil {
			if ii.Item != nil && first {
				fmt.Fprintf(body, "<tr><td colspan=\"7\">%s <a href=\"%s\">[goto]</a></td></tr>", strings.TrimSpace(ii.Item.Title), ii.Item.Link)
				first = false
			}

			if last != nil {
				if ii.Item != nil && last.Item != nil {
					//fmt.Printf("%s - [%d] [%d] [%d]\n", item.Title, pi.Seeders-item.Last.Seeders,
					//pi.Leechers-item.Last.Leechers, pi.Complete-item.Last.Complete)

					seeders := int64(ii.Item.Seeders - last.Item.Seeders)
					leechers := int64(ii.Item.Leechers - last.Item.Leechers)
					complete := int64(ii.Item.Complete - last.Item.Complete)
					comments := int64(ii.Item.Comments - last.Item.Comments)

					fmt.Fprintf(body, "<tr><td>%s</td><td>%d</td><td>%d (%d)</td><td>%d (%d)</td><td>%d (%d)</td><td>%d (%d)</td></tr>",
						ii.Date, ii.Hour,
						ii.Item.Seeders, seeders,
						ii.Item.Leechers, leechers,
						ii.Item.Complete, complete,
						ii.Item.Comments, comments)
				} else if ii.Item != nil && last.Item == nil {
					fmt.Fprintf(body, "<tr><td>%s</td><td>%d</td><td>%d (-)</td><td>%d (-)</td><td>%d (-)</td><td>%d (-)</td></tr>",
						ii.Date, ii.Hour,
						ii.Item.Seeders,
						ii.Item.Leechers,
						ii.Item.Complete,
						ii.Item.Comments)
				} else {
					fmt.Fprintf(body, "<tr><td>%s</td><td>%d</td></tr>",
						ii.Date, ii.Hour)
				}
			}

			last = ii
			//}
		}

		fmt.Fprintln(body, "</table>")

		tmpl := LoadTemplates("base")
		tmpldata := map[string]interface{}{
			"Title": config.Title,
			"Body":  template.HTML(body.String()),
		}
		err = tmpl.ExecuteTemplate(w, "base", tmpldata)
		if err != nil {
			http.Error(w, "Error processing template", 500)
			return
		}
	})

	http.HandleFunc("/chart", func(w http.ResponseWriter, r *http.Request) {
		r.ParseForm()

		if !initialCheck(w, r) {
			return
		}

		csession := config.Session.Clone()
		defer csession.Close()

		id := r.Form.Get("id")
		if id == "" {
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			http.Error(w, "ID not sent", 500)
			return
		}
		size := r.Form.Get("size")

		i := fstopinfo.NewInfo(config.Logger, csession)
		i.Database = config.Database

		var d []*fstopinfo.FSInfoHistory
		var err error

		dcache, found := detailCache.Get(id)
		if found {
			d = dcache.([]*fstopinfo.FSInfoHistory)
		}

		if d == nil {
			d, err = i.History(id, config.HistoryDays)
			if err != nil {
				w.Header().Add("Content-Type", "text/html; charset=utf-8")
				http.Error(w, err.Error(), 500)
				return
			}
			if d != nil {
				detailCache.Set(id, d, 0)
			}
		}

		if d == nil {
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			http.Error(w, "Not found", 500)
			return
		}

		p, err := plot.New()
		if err != nil {
			panic(err)
		}

		p.Title.Text = "Chart"
		p.X.Label.Text = "Time"
		p.Y.Label.Text = "Amount"

		c_seeders := make(plotter.XYs, 0)
		c_leechers := make(plotter.XYs, 0)
		c_complete := make(plotter.XYs, 0)
		c_comments := make(plotter.XYs, 0)

		var last *fstopinfo.FSInfoHistory
		first := true
		cttotal := int32(0)

		for _, ii := range d {
			if ii.Item != nil {
				cttotal++

				if first {
					p.Title.Text = strings.TrimSpace(ii.Item.Title)
					first = false
				}

				if last != nil && last.Item != nil {
					c_seeders = append(c_seeders, struct{ X, Y float64 }{float64(cttotal), float64(ii.Item.Seeders)})
					c_leechers = append(c_leechers, struct{ X, Y float64 }{float64(cttotal), float64(ii.Item.Leechers)})
					c_complete = append(c_complete, struct{ X, Y float64 }{float64(cttotal), float64(ii.Item.Complete - last.Item.Complete)})
					c_comments = append(c_comments, struct{ X, Y float64 }{float64(cttotal), float64(ii.Item.Comments)})
				}

				last = ii
			}
		}

		w.Header().Add("Content-Type", "image/png")

		pl_seeders, err := plotter.NewLine(c_seeders)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}
		pl_seeders.LineStyle.Width = vg.Length(1)
		pl_seeders.LineStyle.Color = color.RGBA{R: 255, A: 255}
		p.Add(pl_seeders)
		p.Legend.Add("Seeders", pl_seeders)

		pl_leechers, err := plotter.NewLine(c_leechers)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}
		pl_leechers.LineStyle.Width = vg.Length(1)
		pl_leechers.LineStyle.Color = color.RGBA{G: 255, A: 255}
		p.Add(pl_leechers)
		p.Legend.Add("Leechers", pl_leechers)

		pl_complete, err := plotter.NewLine(c_complete)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}
		pl_complete.LineStyle.Width = vg.Length(1)
		pl_complete.LineStyle.Color = color.RGBA{B: 255, A: 255}
		p.Add(pl_complete)
		p.Legend.Add("@Complete", pl_complete)

		pl_comments, err := plotter.NewLine(c_comments)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}
		pl_comments.LineStyle.Width = vg.Length(1)
		pl_comments.LineStyle.Color = color.RGBA{R: 255, B: 255, A: 255}
		p.Add(pl_comments)
		p.Legend.Add("Comments", pl_comments)

		width := vg.Length(640)
		height := vg.Length(480)
		if size == "small" {
			width = vg.Length(640)
			height = vg.Length(160)
		} else if size == "short" {
			width = vg.Length(200)
			height = vg.Length(80)
			p.Title.Text = ""
			p.X.Label.Text = ""
			p.Y.Label.Text = ""
		}

		c := vgimg.PngCanvas{vgimg.New(width, height)}
		p.Draw(plot.MakeDrawArea(c))
		c.WriteTo(w)
	})

	http.HandleFunc("/res/", func(w http.ResponseWriter, r *http.Request) {
		p := strings.Replace(path.Clean(r.URL.Path), "/res/", "res/files/", 1)

		d, err := Asset(p)
		if err != nil {
			http.Error(w, "Error loading resource", 500)
			return
		}

		w.Header().Add("Content-Type", mime.TypeByExtension(path.Ext(p)))
		w.Write(d)

		//fmt.Fprintf(w, "RES %s\n", p)
	})

	http.ListenAndServe(fmt.Sprintf("localhost:%d", config.Port), nil)

}