Milimetric has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/181179

Change subject: Add basic flask server with highcharts
......................................................................

Add basic flask server with highcharts

Change-Id: Ia9132f884e59427cd349318efb9c8321f36169d6
---
A .gitignore
A README
A abacuserver/__init__.py
A abacuserver/static/bindings.js
A abacuserver/static/main.js
A abacuserver/static/style.css
A abacuserver/templates/graph.html
A abacuserver/templates/index.html
A abacuserver/views/__init__.py
A abacuserver/views/data.py
A abacuserver/views/graph.py
A run.py
A setup.py
A websiteconfig.py
14 files changed, 242 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/analytics/abacist 
refs/changes/79/181179/1

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a36e13e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+*.py[co]
+*.egg
+*swp
+*swo
+
+*egg-info
+
+.coverage
+.idea
+
+/.tox
diff --git a/README b/README
new file mode 100644
index 0000000..f5bbd4c
--- /dev/null
+++ b/README
@@ -0,0 +1,4 @@
+sudo pip install -e .
+python run.py
+
+Browse to localhost:5000/graph and type something in the input box at the top, 
then tab out to update
diff --git a/abacuserver/__init__.py b/abacuserver/__init__.py
new file mode 100644
index 0000000..d1ecfd8
--- /dev/null
+++ b/abacuserver/__init__.py
@@ -0,0 +1,13 @@
+from flask import Flask, render_template
+from abacuserver.views import data, graph
+
+app = Flask(__name__)
+app.config.from_object('websiteconfig')
+
+app.register_blueprint(data.mod)
+app.register_blueprint(graph.mod)
+
+
[email protected]('/')
+def index():
+    return render_template('index.html')
diff --git a/abacuserver/static/bindings.js b/abacuserver/static/bindings.js
new file mode 100644
index 0000000..76986bc
--- /dev/null
+++ b/abacuserver/static/bindings.js
@@ -0,0 +1,88 @@
+ko.bindingHandlers.graph = {
+    init: function (element) {
+        $(element).highcharts({
+            title: {
+                text: 'Hits To This Post',
+                x: -20 //center
+            },
+            xAxis: {},
+            yAxis: {
+                title: {text: 'Hits'},
+                plotLines: [{
+                    value: 0,
+                    width: 1,
+                    color: '#808080'
+                }]
+            },
+            legend: {
+                layout: 'vertical',
+                align: 'right',
+                verticalAlign: 'middle',
+                borderWidth: 0
+            },
+            series: [{
+                name: 'Total',
+                data: [],
+            }]
+        });
+    },
+    update: function (element, valueAccessor) {
+        var params = ko.unwrap(valueAccessor()),
+            title = ko.unwrap(params.title),
+            resolution = ko.unwrap(params.resolution),
+            data = ko.unwrap(params.data),
+            categories = [],
+            totalData = [],
+            newSeries = {};
+
+        if (!data || !resolution) { return; }
+
+        // assumes the data is sorted already on the server
+        data[resolution].forEach(function (d) {
+            Object.keys(d.topReferers).forEach(function (referer) {
+                if (!newSeries.hasOwnProperty(referer)) {
+                    // cath up with the main series
+                    newSeries[referer] = categories.map(function(){ return 0; 
});
+                }
+                newSeries[referer].push(d.topReferers[referer]);
+            });
+
+            categories.push(d.unitsAgo);
+            totalData.push(d.hits);
+
+            // catch up any series that didn't show up at this point
+            Object.keys(newSeries).forEach(function (referer) {
+                while (newSeries[referer].length < categories.length) {
+                    newSeries[referer].push(0);
+                }
+            });
+        });
+        newSeries.Total = totalData;
+
+        var chart = $(element).highcharts();
+
+        chart.setTitle({
+            text: title,
+        });
+        chart.xAxis[0].update({
+            categories: categories,
+            title: {text: resolution},
+        });
+
+        Object.keys(newSeries).forEach(function (series) {
+            var updated = false;
+            chart.series.forEach(function (existingSeries) {
+                if (existingSeries.name === series) {
+                    existingSeries.update({data: newSeries[series]});
+                    updated = true;
+                }
+            });
+            if (!updated) {
+                chart.addSeries({
+                    name: series,
+                    data: newSeries[series],
+                });
+            }
+        });
+    }
+};
diff --git a/abacuserver/static/main.js b/abacuserver/static/main.js
new file mode 100644
index 0000000..8bebfae
--- /dev/null
+++ b/abacuserver/static/main.js
@@ -0,0 +1,17 @@
+(function () {
+    var vm = {
+        post: ko.observable(),
+        data: ko.observable(),
+        resolution: ko.observable(),
+    };
+
+    vm.post.subscribe(function (post) {
+        $.get('data/' + post).done(this.data);
+    }, vm);
+
+    vm.title = ko.computed(function () {
+        return 'Hits to ' + this.post();
+    }, vm);
+
+    ko.applyBindings(vm);
+}());
diff --git a/abacuserver/static/style.css b/abacuserver/static/style.css
new file mode 100644
index 0000000..5ed9530
--- /dev/null
+++ b/abacuserver/static/style.css
@@ -0,0 +1 @@
+.graph { height: 90vh; }
diff --git a/abacuserver/templates/graph.html b/abacuserver/templates/graph.html
new file mode 100644
index 0000000..d1cee9e
--- /dev/null
+++ b/abacuserver/templates/graph.html
@@ -0,0 +1,25 @@
+<html>
+    <head>
+        <link rel="stylesheet" type="text/css" href="{{ url_for('static', 
filename='style.css') }}">
+    </head>
+    <body>
+
+        <input data-bind="value: post"/>
+        <select data-bind="value: resolution">
+            <option>hourly</option>
+            <option>daily</option>
+            <option>monthly</option>
+            <option>yearly</option>
+        </select>
+
+        <div class="graph" data-bind="graph: {data: data, resolution: 
resolution, title: title}"></div>
+
+
+        <script 
src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
+        <script 
src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
+        <script src="//code.highcharts.com/highcharts.js"></script>
+        <script src="//code.highcharts.com/modules/exporting.js"></script>
+        <script src="{{ url_for('static', filename='bindings.js') }}"></script>
+        <script src="{{ url_for('static', filename='main.js') }}"></script>
+    </body>
+</html>
diff --git a/abacuserver/templates/index.html b/abacuserver/templates/index.html
new file mode 100644
index 0000000..7b4438b
--- /dev/null
+++ b/abacuserver/templates/index.html
@@ -0,0 +1,3 @@
+<html>
+    <body>hello world</body>
+</html>
diff --git a/abacuserver/views/__init__.py b/abacuserver/views/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/abacuserver/views/__init__.py
diff --git a/abacuserver/views/data.py b/abacuserver/views/data.py
new file mode 100644
index 0000000..dd6297f
--- /dev/null
+++ b/abacuserver/views/data.py
@@ -0,0 +1,43 @@
+from flask import Blueprint, jsonify
+
+mod = Blueprint('data', __name__)
+
+
[email protected]('/data/<string:post>')
+def data_for_post(post):
+
+    # TODO: get post data from redis
+    from random import randint
+
+    def fake(index, lowest_hits, highest_hits, google, yahoo, bing):
+        stats = dict(
+            unitsAgo=index,
+            hits=randint(lowest_hits, highest_hits),
+            topReferers={
+                'Google': google,
+                'Yahoo': yahoo,
+                'Bing': bing,
+            }
+        )
+        # simulate some missing data
+        if index % 3 == 0:
+            stats['topReferers'].pop('Bing')
+        # simulate some missing data at the beginning
+        if index > 20:
+            stats['topReferers'].pop('Yahoo')
+
+        stats['topReferers']['Other'] = (
+            stats['hits'] - sum([v for k, v in stats['topReferers'].items()])
+        )
+
+        return stats
+
+    r = lambda x: range(x, 0, -1)
+    stats = dict(
+        hourly=[fake(i, 5, 30, 2, 2, 1) for i in r(25)],
+        daily=[fake(i, 120, 480, 20, 80, 10) for i in r(31)],
+        monthly=[fake(i, 3600, 7200, 400, 1000, 2000) for i in r(13)],
+        yearly=[fake(i, 43200, 100000, 30000, 10000, 3000) for i in r(5)],
+    )
+
+    return jsonify(stats)
diff --git a/abacuserver/views/graph.py b/abacuserver/views/graph.py
new file mode 100644
index 0000000..f7d1020
--- /dev/null
+++ b/abacuserver/views/graph.py
@@ -0,0 +1,9 @@
+from flask import Blueprint, render_template
+
+mod = Blueprint('graph', __name__)
+
+
[email protected]('/graph')
+def index():
+    # maybe get a list of the most recent posts or something
+    return render_template('graph.html')
diff --git a/run.py b/run.py
new file mode 100644
index 0000000..356ff2c
--- /dev/null
+++ b/run.py
@@ -0,0 +1,2 @@
+from abacuserver import app
+app.run(debug=True)
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..e5a2ee7
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+# follow the frog
+
+from setuptools import setup
+
+setup(
+    name='abacuserver',
+    version='0.0.1',
+    author='Ori Livneh, Dan Andreescu',
+    packages=[
+        'abacuserver',
+    ],
+    install_requires=[
+        'Flask',
+        'redis'
+    ],
+)
diff --git a/websiteconfig.py b/websiteconfig.py
new file mode 100644
index 0000000..fe2b5fd
--- /dev/null
+++ b/websiteconfig.py
@@ -0,0 +1,9 @@
+import os
+
+_basedir = os.path.abspath(os.path.dirname(__file__))
+
+DEBUG = True
+
+SECRET_KEY = 'testkey'
+
+del os

-- 
To view, visit https://gerrit.wikimedia.org/r/181179
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ia9132f884e59427cd349318efb9c8321f36169d6
Gerrit-PatchSet: 1
Gerrit-Project: analytics/abacist
Gerrit-Branch: master
Gerrit-Owner: Milimetric <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to