Org stat endpoints & queries
This commit is contained in:
parent
7d38440126
commit
3e27fd11ae
|
@ -15,7 +15,7 @@ type Contrib struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveContribQuery = `
|
const saveContribQuery = `
|
||||||
insert into contributions (week, author, owner, repo, commits, additions, deletions)
|
insert into contribs (week, author, owner, repo, commits, additions, deletions)
|
||||||
values (:week, :author, :owner, :repo, :commits, :additions, :deletions)
|
values (:week, :author, :owner, :repo, :commits, :additions, :deletions)
|
||||||
on duplicate key update
|
on duplicate key update
|
||||||
commits=values(commits), additions=values(additions), deletions=values(deletions)`
|
commits=values(commits), additions=values(additions), deletions=values(deletions)`
|
||||||
|
|
6
db/db.go
6
db/db.go
|
@ -34,9 +34,13 @@ func mustSelect(dest interface{}, query string, args ...interface{}) {
|
||||||
func measure(op string, start time.Time) {
|
func measure(op string, start time.Time) {
|
||||||
duration := time.Since(start).Nanoseconds()
|
duration := time.Since(start).Nanoseconds()
|
||||||
outcome := "succeeded"
|
outcome := "succeeded"
|
||||||
if err := recover(); err != nil {
|
err := recover()
|
||||||
|
if err != nil {
|
||||||
outcome = "failed"
|
outcome = "failed"
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Operation %s %s; time: %d (%dms)\n", op, outcome, duration, duration/1000000)
|
log.Printf("Operation %s %s; time: %d (%dms)\n", op, outcome, duration, duration/1000000)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
StatRequest struct {
|
||||||
|
Org string
|
||||||
|
Team string
|
||||||
|
User string
|
||||||
|
From int64
|
||||||
|
To int64
|
||||||
|
}
|
||||||
|
StatItem struct {
|
||||||
|
Item string `json:"item"`
|
||||||
|
Value int `json:"value"`
|
||||||
|
}
|
||||||
|
StatPoint struct {
|
||||||
|
StatItem
|
||||||
|
Timestamp uint64 `json:"ts"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const orgReposTopQuery = `
|
||||||
|
select
|
||||||
|
c.repo as item,
|
||||||
|
sum(c.commits) as value
|
||||||
|
from contribs c
|
||||||
|
join members m on
|
||||||
|
c.author = m.user and
|
||||||
|
c.owner = m.org
|
||||||
|
where
|
||||||
|
m.id is not null and
|
||||||
|
c.owner = ? and
|
||||||
|
c.week >= ? and
|
||||||
|
c.week <= ?
|
||||||
|
group by item
|
||||||
|
order by value desc`
|
||||||
|
|
||||||
|
const orgReposActivityQuery = `
|
||||||
|
select
|
||||||
|
c.week as ts,
|
||||||
|
c.repo as item,
|
||||||
|
sum(c.commits) as value
|
||||||
|
from contribs c
|
||||||
|
join members m on
|
||||||
|
c.author = m.user and
|
||||||
|
c.owner = m.org
|
||||||
|
where
|
||||||
|
m.id is not null and
|
||||||
|
c.owner = ? and
|
||||||
|
c.week >= ? and
|
||||||
|
c.week <= ?
|
||||||
|
group by ts, item
|
||||||
|
order by ts, item`
|
||||||
|
|
||||||
|
const orgTeamsTopQuery = `
|
||||||
|
select
|
||||||
|
t.name as item,
|
||||||
|
sum(c.commits) value
|
||||||
|
from contribs c
|
||||||
|
join members m on
|
||||||
|
c.author = m.user and
|
||||||
|
c.owner = m.org
|
||||||
|
join teams t on
|
||||||
|
m.team_id = t.id
|
||||||
|
where
|
||||||
|
m.id is not null and
|
||||||
|
c.owner = ? and
|
||||||
|
c.week >= ? and
|
||||||
|
c.week <= ?
|
||||||
|
group by item
|
||||||
|
order by value desc`
|
||||||
|
|
||||||
|
const orgTeamsActivityQuery = `
|
||||||
|
select
|
||||||
|
c.week as ts,
|
||||||
|
t.name as item,
|
||||||
|
sum(c.commits) as value
|
||||||
|
from contribs c
|
||||||
|
join members m on
|
||||||
|
c.author = m.user and
|
||||||
|
c.owner = m.org
|
||||||
|
join teams t on
|
||||||
|
m.team_id = t.id
|
||||||
|
where
|
||||||
|
m.id is not null and
|
||||||
|
c.owner = ? and
|
||||||
|
c.week >= ? and
|
||||||
|
c.week <= ?
|
||||||
|
group by ts, item
|
||||||
|
order by ts, item`
|
||||||
|
|
||||||
|
const orgUsersTopQuery = `
|
||||||
|
select
|
||||||
|
c.author as item,
|
||||||
|
sum(c.commits) value
|
||||||
|
from contribs c
|
||||||
|
join members m on
|
||||||
|
c.author = m.user and
|
||||||
|
c.owner = m.org
|
||||||
|
where
|
||||||
|
m.id is not null and
|
||||||
|
c.owner = ? and
|
||||||
|
c.week >= ? and
|
||||||
|
c.week <= ?
|
||||||
|
group by item
|
||||||
|
order by value desc`
|
||||||
|
|
||||||
|
func StatOrgReposTop(r *StatRequest) (res []StatItem) {
|
||||||
|
defer measure("StatOrgReposTop", time.Now())
|
||||||
|
mustSelect(&res, orgReposTopQuery, r.Org, r.From, r.To)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func StatOrgReposActivity(r *StatRequest) (res []StatPoint) {
|
||||||
|
defer measure("StatOrgReposActivity", time.Now())
|
||||||
|
mustSelect(&res, orgReposActivityQuery, r.Org, r.From, r.To)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func StatOrgTeamsTop(r *StatRequest) (res []StatItem) {
|
||||||
|
defer measure("StatOrgTeamsTop", time.Now())
|
||||||
|
mustSelect(&res, orgTeamsTopQuery, r.Org, r.From, r.To)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func StatOrgTeamsActivity(r *StatRequest) (res []StatPoint) {
|
||||||
|
defer measure("StatOrgTeamsActivity", time.Now())
|
||||||
|
mustSelect(&res, orgTeamsActivityQuery, r.Org, r.From, r.To)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func StatOrgUsersTop(r *StatRequest) (res []StatItem) {
|
||||||
|
defer measure("StatOrgUsersTop", time.Now())
|
||||||
|
mustSelect(&res, orgUsersTopQuery, r.Org, r.From, r.To)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseRequest(r *http.Request) *StatRequest {
|
||||||
|
var err error
|
||||||
|
var from, to int64
|
||||||
|
if len(r.FormValue("from")) > 0 {
|
||||||
|
if from, err = strconv.ParseInt(r.FormValue("from"), 10, 64); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(r.FormValue("to")) > 0 {
|
||||||
|
if to, err = strconv.ParseInt(r.FormValue("to"), 10, 64); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &StatRequest{
|
||||||
|
Org: r.FormValue("org"),
|
||||||
|
Team: r.FormValue("team"),
|
||||||
|
User: r.FormValue("user"),
|
||||||
|
From: from,
|
||||||
|
To: to,
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
type Team struct {
|
type Team struct {
|
||||||
ID uint64 `json:"id"`
|
ID uint64 `json:"id"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
Owner string `json:"owner"`
|
Owner string `json:"owner"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,11 @@ func init() {
|
||||||
http.HandleFunc("/api/orgs", apiOrgsHandler)
|
http.HandleFunc("/api/orgs", apiOrgsHandler)
|
||||||
http.HandleFunc("/api/teams", apiTeamsHandler)
|
http.HandleFunc("/api/teams", apiTeamsHandler)
|
||||||
http.HandleFunc("/api/repos", apiReposHandler)
|
http.HandleFunc("/api/repos", apiReposHandler)
|
||||||
|
http.HandleFunc("/api/stat/repos/top", statOrgReposTop)
|
||||||
|
http.HandleFunc("/api/stat/repos/activity", statOrgReposActivity)
|
||||||
|
http.HandleFunc("/api/stat/teams/top", statOrgTeamsTop)
|
||||||
|
http.HandleFunc("/api/stat/teams/activity", statOrgTeamsActivity)
|
||||||
|
http.HandleFunc("/api/stat/users/top", statOrgUsersTop)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start() {
|
func Start() {
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/localhots/empact/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
func statOrgReposTop(w http.ResponseWriter, r *http.Request) {
|
||||||
|
top := db.StatOrgReposTop(db.ParseRequest(r))
|
||||||
|
respondWith(w, top)
|
||||||
|
}
|
||||||
|
|
||||||
|
func statOrgReposActivity(w http.ResponseWriter, r *http.Request) {
|
||||||
|
activity := db.StatOrgReposActivity(db.ParseRequest(r))
|
||||||
|
respondWith(w, activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
func statOrgTeamsTop(w http.ResponseWriter, r *http.Request) {
|
||||||
|
top := db.StatOrgTeamsTop(db.ParseRequest(r))
|
||||||
|
respondWith(w, top)
|
||||||
|
}
|
||||||
|
|
||||||
|
func statOrgTeamsActivity(w http.ResponseWriter, r *http.Request) {
|
||||||
|
activity := db.StatOrgTeamsActivity(db.ParseRequest(r))
|
||||||
|
respondWith(w, activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
func statOrgUsersTop(w http.ResponseWriter, r *http.Request) {
|
||||||
|
top := db.StatOrgUsersTop(db.ParseRequest(r))
|
||||||
|
respondWith(w, top)
|
||||||
|
}
|
Loading…
Reference in New Issue