Barchart filters
This commit is contained in:
		
							parent
							
								
									2191a0c246
								
							
						
					
					
						commit
						c2fd42bed0
					
				@ -67,11 +67,9 @@ var Dashboard = React.createClass({
 | 
			
		||||
var OrgStats = React.createClass({
 | 
			
		||||
    mixins: [Router.Navigation, Router.State],
 | 
			
		||||
    render: function(){
 | 
			
		||||
        var topRepos = "/api/stat/orgs/top?org="+ this.getParams().org +"&item=repo",
 | 
			
		||||
            repoURL = "/app/"+ this.getParams().org +"/repos/";
 | 
			
		||||
        return (
 | 
			
		||||
            <section className="content">
 | 
			
		||||
                <BarChart api={topRepos} link={repoURL}/>
 | 
			
		||||
                <BarChart api="/api/stat/orgs/top" params={this.getParams()} items={["repo", "team", "user"]} />
 | 
			
		||||
            </section>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@ -80,14 +78,9 @@ var OrgStats = React.createClass({
 | 
			
		||||
var TeamStats = React.createClass({
 | 
			
		||||
    mixins: [Router.Navigation, Router.State],
 | 
			
		||||
    render: function(){
 | 
			
		||||
        var topRepos = "/api/stat/teams/top"+
 | 
			
		||||
                "?org="+ this.getParams().org +
 | 
			
		||||
                "&team="+ this.getParams().team +
 | 
			
		||||
                "&item=repo",
 | 
			
		||||
            repoURL = "/app/"+ this.getParams().org +"/repos/";
 | 
			
		||||
        return (
 | 
			
		||||
            <section className="content">
 | 
			
		||||
                <BarChart api={topRepos} link={repoURL}/>
 | 
			
		||||
                <BarChart api="/api/stat/teams/top" params={this.getParams()} items={["repo", "user"]} />
 | 
			
		||||
            </section>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@ -96,14 +89,9 @@ var TeamStats = React.createClass({
 | 
			
		||||
var UserStats = React.createClass({
 | 
			
		||||
    mixins: [Router.Navigation, Router.State],
 | 
			
		||||
    render: function(){
 | 
			
		||||
        var topRepos = "/api/stat/users/top"+
 | 
			
		||||
                "?org="+ this.getParams().org +
 | 
			
		||||
                "&author="+ this.getParams().user +
 | 
			
		||||
                "&item=repo",
 | 
			
		||||
            repoURL = "/app/"+ this.getParams().org +"/repos/";
 | 
			
		||||
        return (
 | 
			
		||||
            <section className="content">
 | 
			
		||||
                <BarChart api={topRepos} link={repoURL}/>
 | 
			
		||||
                <BarChart api="/api/stat/users/top" params={this.getParams()} items={["repo"]} />
 | 
			
		||||
            </section>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@ -112,14 +100,9 @@ var UserStats = React.createClass({
 | 
			
		||||
var RepoStats = React.createClass({
 | 
			
		||||
    mixins: [Router.Navigation, Router.State],
 | 
			
		||||
    render: function(){
 | 
			
		||||
        var topAuthors = "/api/stat/repos/top"+
 | 
			
		||||
                "?org="+ this.getParams().org +
 | 
			
		||||
                "&repo="+ this.getParams().repo +
 | 
			
		||||
                "&item=author",
 | 
			
		||||
            userURL = "/app/"+ this.getParams().org +"/users/";
 | 
			
		||||
        return (
 | 
			
		||||
            <section className="content">
 | 
			
		||||
                <BarChart api={topAuthors} link={userURL}/>
 | 
			
		||||
                <BarChart api="/api/stat/repos/top" params={this.getParams()} items={["team", "user"]} />
 | 
			
		||||
            </section>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -2,24 +2,77 @@ var SVGNS = "http://www.w3.org/2000/svg",
 | 
			
		||||
    Router = ReactRouter;
 | 
			
		||||
 | 
			
		||||
var BarChart = React.createClass({
 | 
			
		||||
    mixins: [Router.Navigation, Router.State],
 | 
			
		||||
    barHeight: 40,
 | 
			
		||||
    barMargin: 5,
 | 
			
		||||
 | 
			
		||||
    getInitialState: function() {
 | 
			
		||||
        return {points: [], max: 1};
 | 
			
		||||
        return {
 | 
			
		||||
            item: this.props.items[0],
 | 
			
		||||
            sort: 'commits',
 | 
			
		||||
            rawData: [],
 | 
			
		||||
            points: [],
 | 
			
		||||
            min: 0,
 | 
			
		||||
            max: 1
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    componentDidMount: function() {
 | 
			
		||||
        $.get(this.props.api, function(res){
 | 
			
		||||
            res = res.slice(0, 15);
 | 
			
		||||
            var max = 1;
 | 
			
		||||
            res.map(function(el) {
 | 
			
		||||
                if (el.commits > max) {
 | 
			
		||||
                    max = el.commits
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            this.setState({points: res, max: max});
 | 
			
		||||
        }.bind(this))
 | 
			
		||||
        this.fetchData();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    onFilter: function(thing, i) {
 | 
			
		||||
        if (thing === 'item' && this.props.items[i] !== this.state.item) {
 | 
			
		||||
            this.setState({
 | 
			
		||||
                item: this.props.items[i]
 | 
			
		||||
            }, this.fetchData);
 | 
			
		||||
        } else if (thing === 'sort' && ['commits', 'delta'][i] !== this.state.sort) {
 | 
			
		||||
            this.setState({
 | 
			
		||||
                sort: ['commits', 'delta'][i]
 | 
			
		||||
            }, this.sort);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    fetchData: function() {
 | 
			
		||||
        $.get(this.props.api, this.apiParams(), function(res){
 | 
			
		||||
            this.setState({
 | 
			
		||||
                rawData: res
 | 
			
		||||
            }, this.sort);
 | 
			
		||||
        }.bind(this));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    sort: function() {
 | 
			
		||||
        var sortFun = function(a, b) {
 | 
			
		||||
            return Math.abs(b[this.state.sort]) - Math.abs(a[this.state.sort]);
 | 
			
		||||
        }.bind(this);
 | 
			
		||||
        var points = this.state.rawData.sort(sortFun).slice(0, 15);
 | 
			
		||||
 | 
			
		||||
        var min = 0, max = 1;
 | 
			
		||||
        points.map(function(el) {
 | 
			
		||||
            var val = el[this.state.sort];
 | 
			
		||||
            if (val > max) {
 | 
			
		||||
                max = val;
 | 
			
		||||
            }
 | 
			
		||||
            if (val < min) {
 | 
			
		||||
                min = val;
 | 
			
		||||
            }
 | 
			
		||||
        }.bind(this));
 | 
			
		||||
 | 
			
		||||
        s = {
 | 
			
		||||
            points: points,
 | 
			
		||||
            min: min,
 | 
			
		||||
            max: max
 | 
			
		||||
        };
 | 
			
		||||
        console.log(s);
 | 
			
		||||
        this.setState(s);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    apiParams: function() {
 | 
			
		||||
        // Deep copy
 | 
			
		||||
        // Don't use jQuery.extend
 | 
			
		||||
        var params = JSON.parse(JSON.stringify(this.props.params));
 | 
			
		||||
        params['item'] = this.state.item;
 | 
			
		||||
        return params;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    height: function() {
 | 
			
		||||
@ -35,19 +88,43 @@ var BarChart = React.createClass({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    render: function() {
 | 
			
		||||
        console.log("State:", this.state)
 | 
			
		||||
        return (
 | 
			
		||||
            <svg className="barchart" width="100%" height={this.height()}>
 | 
			
		||||
                {this.state.points.map(this.renderBar)}
 | 
			
		||||
            </svg>
 | 
			
		||||
            <div className="barchart-container">
 | 
			
		||||
                <div className="filters">
 | 
			
		||||
                    <Selector thing="item"
 | 
			
		||||
                        items={this.props.items}
 | 
			
		||||
                        value={this.state.item}
 | 
			
		||||
                        onChange={this.onFilter.bind(this, 'item')} />
 | 
			
		||||
                    <Selector thing="sort"
 | 
			
		||||
                        items={['commits', 'delta']}
 | 
			
		||||
                        value={this.state.sort}
 | 
			
		||||
                        onChange={this.onFilter.bind(this, 'sort')} />
 | 
			
		||||
                </div>
 | 
			
		||||
                <svg className="barchart" width="100%" height={this.height()}>
 | 
			
		||||
                    {this.state.points.map(this.renderBar)}
 | 
			
		||||
                </svg>
 | 
			
		||||
            </div>
 | 
			
		||||
        );
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    renderBar: function(point, i) {
 | 
			
		||||
        var maxWidth = 400,
 | 
			
		||||
            val = point[this.state.sort],
 | 
			
		||||
            min = this.state.min,
 | 
			
		||||
            max = this.state.max,
 | 
			
		||||
            max2 = (min < 0 ? max - min : max),
 | 
			
		||||
            width = Math.abs(val)/max2*maxWidth,
 | 
			
		||||
            height = this.barHeight,
 | 
			
		||||
            x = (min >= 0 ? 0 : -min/max2*maxWidth - (val >= 0 ? 0 : width)),
 | 
			
		||||
            y = this.y(i);
 | 
			
		||||
            console.log(point.item, {val: val, max: max, x: x, y: y, width: width})
 | 
			
		||||
 | 
			
		||||
        return (
 | 
			
		||||
            <Bar key={point.item} point={point} i={i} link={this.props.link}
 | 
			
		||||
                y={this.y(i)}
 | 
			
		||||
                width={point.commits/this.state.max}
 | 
			
		||||
                height={this.barHeight} />
 | 
			
		||||
            <Bar key={point.item} point={point} i={i}
 | 
			
		||||
                metric={this.state.sort}
 | 
			
		||||
                x={x} y={y} width={width} height={height}
 | 
			
		||||
                link={this.props.link} />
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@ -59,11 +136,12 @@ var Bar = React.createClass({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    render: function() {
 | 
			
		||||
        var p = this.props.point
 | 
			
		||||
            w = this.props.width*500,
 | 
			
		||||
            label = p.item + ': ' + p.commits,
 | 
			
		||||
        var p = this.props.point,
 | 
			
		||||
            val = p[this.props.metric],
 | 
			
		||||
            w = this.props.width,
 | 
			
		||||
            label = p.item + ': ' + val,
 | 
			
		||||
            labelm = 10, // Margin
 | 
			
		||||
            labelw = label.length*9 + 2*labelm, // Width
 | 
			
		||||
            labelw = label.length*9.3 + 2*labelm, // Width
 | 
			
		||||
            textx = labelm;
 | 
			
		||||
        if (labelw + 2*labelm > w) {
 | 
			
		||||
            textx = w + textx;
 | 
			
		||||
@ -71,9 +149,8 @@ var Bar = React.createClass({
 | 
			
		||||
        return (
 | 
			
		||||
            <g onClick={this.handleClick}>
 | 
			
		||||
                <rect className="bar" fill={Colors2[this.props.i]}
 | 
			
		||||
                    width={this.props.width*500}
 | 
			
		||||
                    height={this.props.height}
 | 
			
		||||
                    x="0" y={this.props.y} rx="2" ry="2" />
 | 
			
		||||
                    width={w} height={this.props.height}
 | 
			
		||||
                    x={this.props.x} y={this.props.y} rx="2" ry="2" />
 | 
			
		||||
                <rect className="label_underlay"
 | 
			
		||||
                    x={textx-6} y={this.props.y+10}
 | 
			
		||||
                    height={20} width={labelw}
 | 
			
		||||
@ -83,3 +160,37 @@ var Bar = React.createClass({
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
var Selector = React.createClass({
 | 
			
		||||
    names: {
 | 
			
		||||
        "repo": "Repositories",
 | 
			
		||||
        "team": "Teams",
 | 
			
		||||
        "user": "Users",
 | 
			
		||||
        "commits": "Commits",
 | 
			
		||||
        "delta": "Delta"
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    itemWithName: function(name) {
 | 
			
		||||
        for (item in this.names) {
 | 
			
		||||
            if (this.names[item] === name) {
 | 
			
		||||
                return item;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    renderItem: function(item, i) {
 | 
			
		||||
        var itemClass = (item === this.props.value ? 'active' : ''),
 | 
			
		||||
            clickEvent = this.props.onChange.bind(this, i);
 | 
			
		||||
        return (
 | 
			
		||||
            <li key={item} onClick={clickEvent} className={itemClass}>{this.names[item]}</li>
 | 
			
		||||
        );
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    render: function() {
 | 
			
		||||
        return (
 | 
			
		||||
            <ul className={this.props.thing}>
 | 
			
		||||
                {this.props.items.map(this.renderItem)}
 | 
			
		||||
            </ul>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,6 @@
 | 
			
		||||
.barchart-container {
 | 
			
		||||
    width: 40%;
 | 
			
		||||
}
 | 
			
		||||
.barchart g {
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
@ -8,3 +11,34 @@
 | 
			
		||||
.barchart .label_underlay {
 | 
			
		||||
    fill: rgba(255, 255, 255, .8);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.filters {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
}
 | 
			
		||||
.filters .item {
 | 
			
		||||
    margin: 0; padding: 0;
 | 
			
		||||
    float: left;
 | 
			
		||||
}
 | 
			
		||||
.filters .item li {
 | 
			
		||||
    margin-right: 0.3em;
 | 
			
		||||
}
 | 
			
		||||
.filters .sort li {
 | 
			
		||||
    margin-left: 0.3em;
 | 
			
		||||
}
 | 
			
		||||
.filters .sort {
 | 
			
		||||
    margin: 0; padding: 0;
 | 
			
		||||
    float: right;
 | 
			
		||||
}
 | 
			
		||||
.filters li {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    font-size: 1.2em;
 | 
			
		||||
    line-height: 2em;
 | 
			
		||||
    font-weight: 300;
 | 
			
		||||
    color: #aaa;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
.filters li.active {
 | 
			
		||||
    font-weight: 400;
 | 
			
		||||
    color: #222;
 | 
			
		||||
    cursor: default;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -105,7 +105,7 @@ select
 | 
			
		||||
from contribs c
 | 
			
		||||
where
 | 
			
		||||
    c.owner = :org and
 | 
			
		||||
    c.author = :author and
 | 
			
		||||
    c.author = :user and
 | 
			
		||||
    c.week >= :from and
 | 
			
		||||
    c.week <= :to
 | 
			
		||||
group by item
 | 
			
		||||
@ -120,7 +120,7 @@ select
 | 
			
		||||
from contribs c
 | 
			
		||||
where
 | 
			
		||||
    c.owner = :org and
 | 
			
		||||
    c.author = :author and
 | 
			
		||||
    c.author = :user and
 | 
			
		||||
    c.week >= :from and
 | 
			
		||||
    c.week <= :to
 | 
			
		||||
group by item
 | 
			
		||||
 | 
			
		||||
@ -19,13 +19,13 @@ type (
 | 
			
		||||
		login string
 | 
			
		||||
	}
 | 
			
		||||
	statRequest struct {
 | 
			
		||||
		Org    string `structs:"org"`
 | 
			
		||||
		Team   string `structs:"team"`
 | 
			
		||||
		Author string `structs:"author"`
 | 
			
		||||
		Repo   string `structs:"repo"`
 | 
			
		||||
		From   int64  `structs:"from"`
 | 
			
		||||
		To     int64  `structs:"to"`
 | 
			
		||||
		Item   string `structs:"item"`
 | 
			
		||||
		Org  string `structs:"org"`
 | 
			
		||||
		Team string `structs:"team"`
 | 
			
		||||
		User string `structs:"user"`
 | 
			
		||||
		Repo string `structs:"repo"`
 | 
			
		||||
		From int64  `structs:"from"`
 | 
			
		||||
		To   int64  `structs:"to"`
 | 
			
		||||
		Item string `structs:"item"`
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -76,7 +76,7 @@ func parseStatRequest(r *http.Request) *statRequest {
 | 
			
		||||
 | 
			
		||||
	var item string
 | 
			
		||||
	switch val := r.FormValue("item"); val {
 | 
			
		||||
	case "author":
 | 
			
		||||
	case "author", "user":
 | 
			
		||||
		item = "c.author"
 | 
			
		||||
	case "team":
 | 
			
		||||
		item = "t.name"
 | 
			
		||||
@ -85,13 +85,13 @@ func parseStatRequest(r *http.Request) *statRequest {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &statRequest{
 | 
			
		||||
		Org:    r.FormValue("org"),
 | 
			
		||||
		Team:   r.FormValue("team"),
 | 
			
		||||
		Author: r.FormValue("author"),
 | 
			
		||||
		Repo:   r.FormValue("repo"),
 | 
			
		||||
		From:   from,
 | 
			
		||||
		To:     to,
 | 
			
		||||
		Item:   item,
 | 
			
		||||
		Org:  r.FormValue("org"),
 | 
			
		||||
		Team: r.FormValue("team"),
 | 
			
		||||
		User: r.FormValue("user"),
 | 
			
		||||
		Repo: r.FormValue("repo"),
 | 
			
		||||
		From: from,
 | 
			
		||||
		To:   to,
 | 
			
		||||
		Item: item,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user