diff --git a/client/bower.json b/client/bower.json
index 4ccb628..f5d838d 100644
--- a/client/bower.json
+++ b/client/bower.json
@@ -22,6 +22,8 @@
"codemirror": "~3.19.0",
"angular-ui-codemirror": "~0.1.1",
"spin.js": "~2.0.1",
- "angular-spinner": "~0.4.0"
+ "angular-spinner": "~0.4.0",
+ "highcharts-ng": "~0.0.6",
+ "highcharts": "~3.0.7"
}
}
diff --git a/client/coffee/controllers/keyStatCtrl.coffee b/client/coffee/controllers/keyStatCtrl.coffee
index acc60e3..04dd716 100644
--- a/client/coffee/controllers/keyStatCtrl.coffee
+++ b/client/coffee/controllers/keyStatCtrl.coffee
@@ -4,10 +4,17 @@ humanValueOfStatisticalAbreviation =
mean: "mean"
std: "standard deviation"
var: "variance"
+ min: "minimum"
+ max: "maximum"
+ n: "number of documents"
+
+getAxisLabel = (min, stepSize, numberOfBins) ->
+ return (min + 0.5 * stepSize + i * stepSize for i in [0... numberOfBins])
+
root.controllers.controller('keyStatCtrl', ['$scope', '$routeParams', 'util', ($scope, $routeParams, util) ->
- $scope.name = "didier!"
+ $scope.name = $routeParams.key
$scope.stats = []
url = "/api/#{ $routeParams.server }/#{ $routeParams.database }/#{ $routeParams.collection }"
@@ -18,5 +25,25 @@ root.controllers.controller('keyStatCtrl', ['$scope', '$routeParams', 'util', ($
$scope.stats.push({name: humanValueOfStatisticalAbreviation[key], value: val })
)
+ $scope.chartConfig =
+ options:
+ chart:
+ type: 'column'
+ plotOptions:
+ column:
+ groupPadding: 0
+ pointPadding: 0
+ borderWidth: 0
+ xAxis:
+ categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+ title:
+ text: 'Distribution'
+ loading: false
+ series: [{
+ data: [10, 15, 12, 8, 7, 4, 12, 12, 9, 11],
+ name: $routeParams.key
+ color: '#008CBA'
+ }]
+
])
diff --git a/client/coffee/vultureApp.coffee b/client/coffee/vultureApp.coffee
index 9b5b9c7..a875f40 100644
--- a/client/coffee/vultureApp.coffee
+++ b/client/coffee/vultureApp.coffee
@@ -5,6 +5,7 @@ angular.module('vulture', [
'ngRoute'
'ui.codemirror'
'angularSpinner'
+ 'highcharts-ng'
'leaflet-directive'
'vulture.filters'
'vulture.services'
diff --git a/client/index.html b/client/index.html
index 046eb7e..090188c 100644
--- a/client/index.html
+++ b/client/index.html
@@ -56,6 +56,8 @@
+
+
diff --git a/client/partials/keyStat.html b/client/partials/keyStat.html
index f1cb921..147227b 100644
--- a/client/partials/keyStat.html
+++ b/client/partials/keyStat.html
@@ -8,4 +8,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/main.go b/main.go
index 879598f..50ea622 100644
--- a/main.go
+++ b/main.go
@@ -96,7 +96,6 @@ func serveApi(router *mux.Router) {
router.Handle("/{server}/{database}/{collection}/query/{query}/idx/{index:[0-9]+}", ApiHandler(getDocumentByQueryAndIndex))
router.Handle("/{server}/{database}/{collection}/query/{query}/all", ApiHandler(getAllDocuments))
- // getAllDocuments
}
func createMuxRouter() http.Handler {
diff --git a/stats.go b/stats.go
index af0e185..e098a62 100644
--- a/stats.go
+++ b/stats.go
@@ -11,10 +11,55 @@ const initialLength int = 1024
const verySmall float64 = -100000
const veryBig float64 = 100000
+type Histogram struct {
+ Min float64 `json:"min"`
+ StepSize float64 `json:"step_size"`
+ Values []int `json:"values"`
+}
+
type Stats struct {
Mean float64 `json:"mean"`
Var float64 `json:"var"`
Std float64 `json:"std"`
+ Min float64 `json:"min"`
+ Max float64 `json:"max"`
+ N int `json:"n"`
+}
+
+func (this *Stats) findMinHistogramValue() float64 {
+ return math.Max(this.Min, this.Mean-3*this.Std)
+}
+
+func (this *Stats) findMaxHistogramValue() float64 {
+ return math.Min(this.Max, this.Mean+3*this.Std)
+}
+
+func (this *Stats) findSuitableBinForDatum(min, step, val float64) int {
+ var i int
+ var act float64 = min
+ for i = 0; act < val; act += step {
+ i++
+ }
+ return i - 1
+
+}
+
+func (this *Stats) MakeHistogram(numberOfBins int, dataChannel chan float64, outputChannel chan *Histogram) {
+ min := this.findMinHistogramValue()
+ max := this.findMaxHistogramValue()
+ step := (max - min) / float64(numberOfBins)
+ hist := make([]int, numberOfBins)
+
+ for datum := range dataChannel {
+ if datum < min || datum > max {
+ continue
+ }
+ i := this.findSuitableBinForDatum(min, step, datum)
+ hist[i]++
+ }
+
+ outputChannel <- &Histogram{min, step, hist}
+
}
type StatAggregator struct {
@@ -50,7 +95,9 @@ func (this *StatAggregator) getFinalStats() *Stats {
stats.Mean = this.sum / float64(this.n)
stats.Var = this.sumOfTheSquares/float64(this.n) - stats.Mean*stats.Mean
stats.Std = math.Sqrt(stats.Var)
+ stats.Min = this.min
+ stats.Max = this.max
+ stats.N = this.n
return stats
-
}
diff --git a/stats_test.go b/stats_test.go
index 01019b3..a7b045a 100644
--- a/stats_test.go
+++ b/stats_test.go
@@ -37,6 +37,8 @@ func TestAddStatsBasic(t *testing.T) {
func TestGetFinalStats(t *testing.T) {
s := NewStatAggregator()
s.sumOfTheSquares = 5000
+ s.max = 900
+ s.min = -900
s.sum = 200
s.n = 10
stats := s.getFinalStats()
@@ -49,4 +51,64 @@ func TestGetFinalStats(t *testing.T) {
if stats.Std != 10. {
t.Fail()
}
+ if stats.Min != -900 {
+ t.Fail()
+ }
+ if stats.Max != 900 {
+ t.Fail()
+ }
+ if stats.N != 10 {
+ t.Fail()
+ }
+}
+
+func TestFindMinHistogramValue(t *testing.T) {
+ stats := &Stats{
+ Mean: 10,
+ Std: 2,
+ Min: 1,
+ }
+
+ if stats.findMinHistogramValue() != 4 {
+ t.Fail()
+ }
+
+ stats.Min = 7
+ if stats.findMinHistogramValue() != 7 {
+ t.Fail()
+ }
+
+}
+
+func TestFindMaxHistogramValue(t *testing.T) {
+ stats := &Stats{
+ Mean: 10,
+ Std: 2,
+ Max: 22,
+ }
+
+ if stats.findMaxHistogramValue() != 16 {
+ t.Fail()
+ }
+
+ stats.Max = 14
+ if stats.findMaxHistogramValue() != 14 {
+ t.Fail()
+ }
+}
+
+func TestFindSuitableBinForDatum(t *testing.T) {
+ stats := new(Stats)
+ if bin := stats.findSuitableBinForDatum(-4, 1, -3.2); bin != 0 {
+ t.Error("wrong bin: ", bin)
+ }
+
+ if bin := stats.findSuitableBinForDatum(-4, 1, -2.2); bin != 1 {
+ t.Error("wrong bin: ", bin)
+ }
+
+ if bin := stats.findSuitableBinForDatum(-4, 1, 10.1); bin != 14 {
+ t.Error("wrong bin: ", bin)
+ }
+
}