add dynamic date range calculation for dates, and add color gradient component, add color gradient for language and chrono
authorymh <ymh.work@gmail.com>
Tue, 15 Nov 2016 17:42:57 +0100
changeset 424 feb0d3e0fef9
parent 423 788971813bdc
child 425 f99435a7006e
add dynamic date range calculation for dates, and add color gradient component, add color gradient for language and chrono
cms/app-client/app/adapters/application.js
cms/app-client/app/components/color-gradient.js
cms/app-client/app/components/visu-carto.js
cms/app-client/app/components/visu-chrono-year.js
cms/app-client/app/components/visu-chrono.js
cms/app-client/app/components/visu-langues.js
cms/app-client/app/helpers/.gitkeep
cms/app-client/app/helpers/color-gradient-style.js
cms/app-client/app/models/dateminmax.js
cms/app-client/app/routes/tabs/chrono.js
cms/app-client/app/serializers/dateminmax.js
cms/app-client/app/services/colors.js
cms/app-client/app/styles/app.scss
cms/app-client/app/styles/components/color-gradient.scss
cms/app-client/app/styles/components/notice-component.scss
cms/app-client/app/styles/tabs/chrono.scss
cms/app-client/app/styles/tabs/langues.scss
cms/app-client/app/templates/components/color-gradient.hbs
cms/app-client/app/templates/components/visu-chrono.hbs
cms/app-client/app/templates/components/visu-langues.hbs
cms/app-client/app/templates/tabs/chrono.hbs
cms/app-client/mirage/config.js
cms/app-client/mirage/fixtures/dateminmax.js
cms/app-client/mirage/models/dateminmax.js
cms/app-client/mirage/serializers/dateminmax.js
cms/app-client/tests/integration/components/color-gradient-test.js
server/src/app/Http/Controllers/Api/DateStatsController.php
server/src/routes/api.php
--- a/cms/app-client/app/adapters/application.js	Mon Nov 14 17:23:43 2016 +0100
+++ b/cms/app-client/app/adapters/application.js	Tue Nov 15 17:42:57 2016 +0100
@@ -5,6 +5,7 @@
     transcript: 'transcript',
     geostat: 'stats/geostats',
     datestat: 'stats/datestats',
+    dateminmax: 'stats/dateminmax',
     theme: 'stats/themes'
 };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/app/components/color-gradient.js	Tue Nov 15 17:42:57 2016 +0100
@@ -0,0 +1,30 @@
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+
+    classNames: ['color-gradient'],
+
+    colors: Ember.inject.service(),
+
+    domainStart: 0,
+    domainEnd: 100,
+    scale: null,
+
+    scaleProxy: Ember.computed('scale', function() {
+        let scaleRaw = this.get('scale');
+        if(scaleRaw === null) {
+            return this.get('colors').getScaleLinear(this.get('domainStart'), this.get('domainEnd'));
+        } else {
+            return scaleRaw;
+        }
+    }),
+
+    domainMed: Ember.computed('domainStart', 'domainEnd', function() {
+        return Math.round((this.get('domainEnd')-this.get('domainStart'))/2);
+    }),
+
+    colorList: Ember.computed('scale', function() {
+        return this.get('scaleProxy').colors(100);
+    })
+
+});
--- a/cms/app-client/app/components/visu-carto.js	Mon Nov 14 17:23:43 2016 +0100
+++ b/cms/app-client/app/components/visu-carto.js	Tue Nov 15 17:42:57 2016 +0100
@@ -7,6 +7,7 @@
 export default Ember.Component.extend({
 
     constants: Ember.inject.service(),
+    colors: Ember.inject.service(),
 
     map: null,
 
@@ -175,8 +176,8 @@
             'areasSettings': {
                 'autoZoom': false,
                 'selectable': true,
-                'color': '#777777',
-                'colorSolid': '#333333',
+                'color': this.get('colors').LINEAR_COLOR_START,
+                'colorSolid': this.get('colors').LINEAR_COLOR_END,
                 'colorOutline': '#253946',
                 'selectedColor': '#0085cb',
                 'rollOverColor': '#0085cb',
--- a/cms/app-client/app/components/visu-chrono-year.js	Mon Nov 14 17:23:43 2016 +0100
+++ b/cms/app-client/app/components/visu-chrono-year.js	Tue Nov 15 17:42:57 2016 +0100
@@ -10,6 +10,8 @@
     classNameBindings: ['isDisabled:disabled', 'isDark:light-color', 'isHighlighted:highlighted'],
     attributeBindings: ['style', 'title', 'id'],
 
+    colorScale: null,
+
     isDisabled: Ember.computed('range', 'year', 'count', function() {
         let year = parseInt(this.get('year'));
         let count = this.get('count');
@@ -27,8 +29,9 @@
         return this.get('colors').getPerceptiveLuminance(backgroundColor) >= 0.5;
     }),
 
-    backgroundColor: Ember.computed('count', 'maxCount', 'minCount', function() {
-        return this.get('colors').shadeLinear(this.get('count'), this.get('minCount'), this.get('maxCount'));
+    backgroundColor: Ember.computed('count', function() {
+        return this.get('scale')(this.get('count'));
+        //return this.get('colors').shadeLinear(this.get('count'), this.get('minCount'), this.get('maxCount'));
     }),
 
     style: Ember.computed('backgroundColor', 'isDisabled', 'isHighlighted', function() {
@@ -56,6 +59,4 @@
     year: null,
     datestats: null,
     range: null,
-    maxCount: null,
-    minCount: null,
 });
--- a/cms/app-client/app/components/visu-chrono.js	Mon Nov 14 17:23:43 2016 +0100
+++ b/cms/app-client/app/components/visu-chrono.js	Tue Nov 15 17:42:57 2016 +0100
@@ -3,11 +3,21 @@
 
 export default Ember.Component.extend({
 
-    range: [],
+    colors: Ember.inject.service(),
+    filter: Ember.inject.service(),
+
+    range: null,
     rawdatestats: null,
 
-    decades: Ember.computed('range', function() {
-        var range = this.get('range');
+    rangeArray: Ember.computed('range', 'range.[]', function() {
+        let range = this.get('range');
+        let resArray = [];
+        range.forEach(function(s) { resArray.push(parseInt(s.id)); });
+        return resArray.sort();
+    }),
+
+    decades: Ember.computed('rangeArray', function() {
+        var range = this.get('rangeArray');
         return _.range(Math.floor(range[0]/10)*10, (Math.floor(range[1]/10)+1)*10, 10);
     }),
 
@@ -22,8 +32,9 @@
     counts: Ember.computed.mapBy('rawdatestats', 'count'),
     maxCount: Ember.computed.max('counts'),
     minCount: Ember.computed.min('counts'),
-
-    filter: Ember.inject.service(),
+    colorScale: Ember.computed('maxCount', 'minCount', function() {
+        return this.get('colors').getScaleLinear(this.get('minCount'), this.get('maxCount'));
+    }),
 
     date: Ember.computed.alias('filter.dateList'),
 
@@ -43,8 +54,10 @@
             if(event.button === 0) {
 
                 isMouseDown = true;
-                var element = parseInt(Ember.$(this).attr('id'));
-                if(!_.inRange(element, self.get('range')[0], self.get('range')[1]+1)) {
+                var $elem = Ember.$(this);
+                var element = parseInt($elem.attr('id'));
+                var range = self.get('rangeArray');
+                if(!$elem.hasClass('highlighted') && !_.inRange(element, range[0], range[1]+1)) {
                     return false;
                 }
                 var elements = [element];
--- a/cms/app-client/app/components/visu-langues.js	Mon Nov 14 17:23:43 2016 +0100
+++ b/cms/app-client/app/components/visu-langues.js	Tue Nov 15 17:42:57 2016 +0100
@@ -10,6 +10,14 @@
     filter: Ember.inject.service(),
     colors: Ember.inject.service(),
 
+    scale: Ember.computed('maxCount', 'minCount', function() {
+      let maxCount = this.get('maxCount');
+      let minCount = this.get('minCount');
+      return this.get('colors').getScaleLinear(minCount, maxCount);
+    }),
+    maxCount: 0,
+    minCount: 0,
+
     filterObserver: Ember.observer('filter.language', function() {
         Ember.$('.node').removeClass("selected");
         Ember.$('.node[data-id="' + this.get('filter').get('language') + '"]').addClass("selected");
@@ -142,10 +150,14 @@
 
                 var dMin = Math.min.apply(null, d._children.map(function(d){ return d.count; }));
                 var dMax = Math.max.apply(null, d._children.map(function(d){ return d.count; }));
+                self.setProperties({minCount: dMin, maxCount: dMax});
+                var scale = self.get('scale');
+                var backgroundColor = function(_d) { return scale(_d.count);};
+                var colorClass = function(_d) { return (self.get('colors').getPerceptiveLuminance(backgroundColor(_d)) >= 0.5)?'light-color':'dark-color'; };
 
-                node.attr("class", function(d) { return "node" + ( d.id === self.get('filter').get('language') ? " selected" : "" ); })
+                node.attr("class", function(_d) { return "node " + colorClass(_d) + ( _d.id === self.get('filter').get('language') ? " selected" : "" ); })
                     .call(position)
-                    .style("background-color", function(d) { return self.get('colors').shadeLinear(d.count,dMin,dMax); })
+                    .style("background-color", backgroundColor)
                     .on("click", selectHandler);
 
                 node.filter(function(d) { return d._children; })
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/app/helpers/color-gradient-style.js	Tue Nov 15 17:42:57 2016 +0100
@@ -0,0 +1,7 @@
+import Ember from 'ember';
+
+export function colorGradientStyle(color) {
+    return Ember.String.htmlSafe(`background-color: ${color}`);
+}
+
+export default Ember.Helper.helper(colorGradientStyle);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/app/models/dateminmax.js	Tue Nov 15 17:42:57 2016 +0100
@@ -0,0 +1,4 @@
+import DS from 'ember-data';
+
+export default DS.Model.extend({
+});
--- a/cms/app-client/app/routes/tabs/chrono.js	Mon Nov 14 17:23:43 2016 +0100
+++ b/cms/app-client/app/routes/tabs/chrono.js	Tue Nov 15 17:42:57 2016 +0100
@@ -8,7 +8,7 @@
 
     model: function() {
         return RSVP.hash({
-            range: [1948, 2015], // TODO: make it dynamic
+            range: this.get('store').query('dateminmax', {}),
             datestats: this.get('store').query('datestat', this.get('filter').get('queryParamsValues'))
         });
     },
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/app/serializers/dateminmax.js	Tue Nov 15 17:42:57 2016 +0100
@@ -0,0 +1,18 @@
+import DS from 'ember-data';
+
+export default DS.Serializer.extend({
+
+    normalizeResponse: function(store, primaryModelClass, payload) {
+        var data = [];
+        payload['dateminmax'].forEach(function(key) {
+            data.push({
+                'id': key,
+                'type': 'dateminmax'
+            });
+        });
+        return {
+            'data': data
+        };
+    }
+
+});
--- a/cms/app-client/app/services/colors.js	Mon Nov 14 17:23:43 2016 +0100
+++ b/cms/app-client/app/services/colors.js	Tue Nov 15 17:42:57 2016 +0100
@@ -4,8 +4,12 @@
 export const LINEAR_COLOR_START = "#777777";
 export const LINEAR_COLOR_END   = "#333333";
 
+export function getScaleLinear(vmin, vmax) {
+    return chroma.scale([LINEAR_COLOR_START, LINEAR_COLOR_END]).mode('lab').domain([vmin,vmax]);
+}
+
 export function shadeLinear(v, vmin, vmax) {
-    var s = chroma.scale([LINEAR_COLOR_START, LINEAR_COLOR_END]).mode('lab').domain([vmin,vmax]);
+    var s = getScaleLinear(vmin, vmax);
     return s(v).hex();
 }
 
@@ -37,6 +41,7 @@
 }
 
 export default Ember.Service.extend({
+    getScaleLinear: getScaleLinear,
     shadeLinear: shadeLinear,
     getComplement: getComplement,
     getPerceptiveLuminance: getPerceptiveLuminance,
--- a/cms/app-client/app/styles/app.scss	Mon Nov 14 17:23:43 2016 +0100
+++ b/cms/app-client/app/styles/app.scss	Tue Nov 15 17:42:57 2016 +0100
@@ -60,6 +60,7 @@
 @import 'components/toolbar-component';
 @import 'components/notice-component';
 @import 'components/transcript-component';
+@import 'components/color-gradient';
 
 
 h1, h2, h3, h4, h5, h6 {
@@ -122,7 +123,7 @@
 
 .corpus-app-wrapper {
     background-color: $corpus-light-grey;
-    border-left: 1px solid $corpus-black; 
+    border-left: 1px solid $corpus-black;
     position: relative;
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/app/styles/components/color-gradient.scss	Tue Nov 15 17:42:57 2016 +0100
@@ -0,0 +1,37 @@
+.color-gradient {
+    width: 85%;
+    white-space: nowrap;
+    position: relative;
+    display: inline-block;
+    top: 4px;
+    padding-bottom: 1.2em;
+}
+
+.color-gradient .grad-step {
+    display: inline-block;
+    height: 1.5em;
+    width: 1%;
+}
+
+.color-gradient .domain-min {
+    position: absolute;
+    left: 0;
+    font-size: 0.8em;
+    bottom: 3px;
+}
+
+.color-gradient .domain-med {
+    position: absolute;
+    right: 25%;
+    left: 25%;
+    text-align: center;
+    font-size: 0.8em;
+    bottom: 3px;
+}
+
+.color-gradient .domain-max {
+    position: absolute;
+    right: 0;
+    font-size: 0.8em;
+    bottom: 3px;
+}
--- a/cms/app-client/app/styles/components/notice-component.scss	Mon Nov 14 17:23:43 2016 +0100
+++ b/cms/app-client/app/styles/components/notice-component.scss	Tue Nov 15 17:42:57 2016 +0100
@@ -22,7 +22,7 @@
     color: $corpus-grey;
     padding-right: 0.5em;
     padding-left: 0.5em;
-
+    text-decoration: none;
 }
 
 body.videoscreen .notice-component {
--- a/cms/app-client/app/styles/tabs/chrono.scss	Mon Nov 14 17:23:43 2016 +0100
+++ b/cms/app-client/app/styles/tabs/chrono.scss	Tue Nov 15 17:42:57 2016 +0100
@@ -1,7 +1,7 @@
 #tabs-chrono p {
     padding: 0px 20px;
     text-align: center;
-    margin: 25px 0px 50px 0px;
+    margin: 25px 0px 25px 0px;
     line-height: 22px;
     color: $corpus-black;
 }
@@ -46,12 +46,18 @@
 #chrono-table li.disabled {
     cursor: default;
     color: $corpus-grey;
+    pointer-events: none;
+}
+
+#chrono-table .color-gradient {
+    width: 150px;
 }
 
 #chrono-table li.highlighted {
     font-weight: bold;
     color: $corpus-white;
     background-color: $corpus-blue;
+    pointer-events: initial;
 }
 
 #chrono-table li.light-color {
--- a/cms/app-client/app/styles/tabs/langues.scss	Mon Nov 14 17:23:43 2016 +0100
+++ b/cms/app-client/app/styles/tabs/langues.scss	Tue Nov 15 17:42:57 2016 +0100
@@ -71,4 +71,26 @@
 
 #tabs-langues .node .count {
 	font-weight: bold;
-}
\ No newline at end of file
+}
+
+#tabs-langues .light-color {
+    color: $corpus-white;
+}
+
+#tabs-langues .dark-color {
+    color: $corpus-black;
+}
+
+#tabs-langues .color-gradient-wrapper {
+    position: absolute;
+    width: 100px;
+    top: 1px;
+    right: 5px;
+}
+
+#tabs-langues .color-gradient-wrapper .color-gradient {
+    position: relative;
+    width: 100%;
+    height: 1.75em;
+    font-size: 0.75em;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/app/templates/components/color-gradient.hbs	Tue Nov 15 17:42:57 2016 +0100
@@ -0,0 +1,4 @@
+{{#each colorList as |c index|}}<span class="grad-step" style={{color-gradient-style c}}></span>{{/each}}
+<span class="domain-min">{{domainStart}}</span>
+<span class="domain-med">{{domainMed}}</span>
+<span class="domain-max">{{domainEnd}}</span>
\ No newline at end of file
--- a/cms/app-client/app/templates/components/visu-chrono.hbs	Mon Nov 14 17:23:43 2016 +0100
+++ b/cms/app-client/app/templates/components/visu-chrono.hbs	Tue Nov 15 17:42:57 2016 +0100
@@ -1,9 +1,10 @@
+<div>{{ color-gradient domainStart=minCount domainEnd=maxCount scale=colorScale }}</div>
 {{#each decades as |decade| }}
 <div class="interval">
     {{input type="checkbox" checked=(is-checked date decade) indeterminate=(is-indeterminate date decade) click=(action 'selectDecade' decade)}}
     <ul class="{{ decade }}">
     {{#each (range decade 10) as |year| }}
-        {{ visu-chrono-year datestats=datestats year=year range=range maxCount=maxCount minCount=minCount }}
+        {{ visu-chrono-year datestats=datestats year=year range=rangeArray scale=colorScale }}
     {{/each}}
     </ul>
 </div>
--- a/cms/app-client/app/templates/components/visu-langues.hbs	Mon Nov 14 17:23:43 2016 +0100
+++ b/cms/app-client/app/templates/components/visu-langues.hbs	Tue Nov 15 17:42:57 2016 +0100
@@ -0,0 +1,3 @@
+<div class="color-gradient-wrapper">
+{{ color-gradient domainStart=minCount domainEnd=maxCount scale=scale }}
+</div>
\ No newline at end of file
--- a/cms/app-client/app/templates/tabs/chrono.hbs	Mon Nov 14 17:23:43 2016 +0100
+++ b/cms/app-client/app/templates/tabs/chrono.hbs	Tue Nov 15 17:42:57 2016 +0100
@@ -1,4 +1,4 @@
 <div id="tabs-chrono">
-	<p>Cocher les cases de gauche pour sélectionner des décennies ou cliquez pour sélectionner une année et glisser pour en selectionner plusieurs.</p>
+    <p>Cocher les cases de gauche pour sélectionner des décennies ou cliquez pour sélectionner une année et glisser pour en selectionner plusieurs.</p>
     {{ visu-chrono range=model.range rawdatestats=model.datestats }}
 </div>
--- a/cms/app-client/mirage/config.js	Mon Nov 14 17:23:43 2016 +0100
+++ b/cms/app-client/mirage/config.js	Tue Nov 15 17:42:57 2016 +0100
@@ -40,6 +40,8 @@
 
     this.get('/stats/datestats', 'datestats');
 
+    this.get('/stats/dateminmax', 'dateminmax');
+
     this.get('/resolvers/lexvo/:ids', ({lexvos}, request) => {
         var langIds = decodeURIComponent(request.params.ids);
         var resMap = _.reduce(langIds.split(','), function(res, id) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/mirage/fixtures/dateminmax.js	Tue Nov 15 17:42:57 2016 +0100
@@ -0,0 +1,4 @@
+export default [
+  { 'id': "1948" },
+  { 'id': "2015" }
+];
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/mirage/models/dateminmax.js	Tue Nov 15 17:42:57 2016 +0100
@@ -0,0 +1,4 @@
+import { Model } from 'ember-cli-mirage';
+
+export default Model.extend({
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/mirage/serializers/dateminmax.js	Tue Nov 15 17:42:57 2016 +0100
@@ -0,0 +1,9 @@
+import { JSONAPISerializer } from 'ember-cli-mirage';
+
+import _ from 'lodash/lodash';
+
+export default JSONAPISerializer.extend({
+    serialize(response) {
+        return {'dateminmax': _(response.models).map((dateinfo) => { return parseInt(dateinfo.id);}).value()};
+    }
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/tests/integration/components/color-gradient-test.js	Tue Nov 15 17:42:57 2016 +0100
@@ -0,0 +1,24 @@
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('color-gradient', 'Integration | Component | color gradient', {
+  integration: true
+});
+
+test('it renders', function(assert) {
+  // Set any properties with this.set('myProperty', 'value');
+  // Handle any actions with this.on('myAction', function(val) { ... });
+
+  this.render(hbs`{{color-gradient}}`);
+
+  assert.equal(this.$().text().trim(), '');
+
+  // Template block usage:
+  this.render(hbs`
+    {{#color-gradient}}
+      template block text
+    {{/color-gradient}}
+  `);
+
+  assert.equal(this.$().text().trim(), 'template block text');
+});
--- a/server/src/app/Http/Controllers/Api/DateStatsController.php	Mon Nov 14 17:23:43 2016 +0100
+++ b/server/src/app/Http/Controllers/Api/DateStatsController.php	Tue Nov 15 17:42:57 2016 +0100
@@ -12,23 +12,8 @@
 
 class DateStatsController extends Controller
 {
-    /**
-     * Display the specified resource.
-     *
-     * @return \Illuminate\Http\Response
-     */
-    public function index(Request $request)
-    {
-
-
-        $filterManager = new CorpusFilterManager();
-        $filters = $filterManager->prepareFilters($request);
-        unset($filters['dates']);
-        $qFilterParts = $filterManager->buildESFilters($filters);
-
-        $query = $filterManager->buildQuery($qFilterParts);
-
-        $esQuery = [
+    private function getStatQuery($query) {
+        return [
             'index' => config('elasticsearch.index'),
             'body' => [
                 "size" => 0,
@@ -60,6 +45,70 @@
                 ]
             ]
         ];
+    }
+
+    private function getMinMaxQuery($query) {
+        return [
+            'index' => config('elasticsearch.index'),
+            'body' => [
+                "size" => 0,
+                "query" => $query,
+                "aggs" => [
+                    "datestats" => [
+                        "nested"=> [
+                            "path" => "creation_years"
+                        ],
+                        "aggs" => [
+                            "minyear" => [
+                                "min" => [ "field"=> "creation_years.year" ]
+                            ],
+                            "maxyear" => [
+                                "max" => [ "field"=> "creation_years.year" ]
+                            ]
+                        ]
+                    ]
+                ]
+            ]
+        ];
+    }
+
+    private function getDocQuery(Request $request) {
+        $filterManager = new CorpusFilterManager();
+        $filters = $filterManager->prepareFilters($request);
+        unset($filters['dates']);
+        $qFilterParts = $filterManager->buildESFilters($filters);
+
+        return $filterManager->buildQuery($qFilterParts);
+    }
+
+    /**
+     * Display the min max date stats
+     */
+    public function minmax(Request $request) {
+        $query = $this->getDocQuery($request);
+
+        $esQuery = $this->getMinMaxQuery($query);
+        $esRes = Es::search($esQuery);
+
+        $datestats = [];
+
+        $max = intval($esRes['aggregations']['datestats']['maxyear']['value']);
+        $min = intval($esRes['aggregations']['datestats']['maxyear']['value']);
+
+        return response()->json(['dateminmax' => [ $min, $max ] ]);
+    }
+
+    /**
+     * Display the dates stats
+     *
+     * @return \Illuminate\Http\Response
+     */
+    public function index(Request $request)
+    {
+
+        $query = $this->getDocQuery($request);
+
+        $esQuery = $this->getStatQuery($query);
         $esRes = Es::search($esQuery);
 
         $datestats = [];
--- a/server/src/routes/api.php	Mon Nov 14 17:23:43 2016 +0100
+++ b/server/src/routes/api.php	Tue Nov 15 17:42:57 2016 +0100
@@ -31,6 +31,7 @@
         Route::get('themes', 'Api\ThemeController@index');
         Route::get('discourses', 'Api\DiscourseController@index');
         Route::get('datestats', 'Api\DateStatsController@index');
+        Route::get('dateminmax', 'Api\DateStatsController@minmax');
         Route::get('geostats', 'Api\GeoStatsController@index');
     });
 });