Completed
Push — master ( 71d9eb...368b76 )
by Chris
01:25
created

sigma()   B

Complexity

Conditions 6

Size

Total Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
dl 0
loc 41
rs 7.5384
c 0
b 0
f 0
1
"""A separate Flask app that serves fake endpoints for demo purposes."""
2
3
# -*- coding: utf-8 -*-
4
5
from itertools import combinations
6
import json
7
import locale
8
import os
9
from datetime import timedelta as td
10
from datetime import datetime as dt
11
from random import randrange as rr
12
from random import choice, random
13
import time
14
15
from flask import (
16
    Flask,
17
    abort,
18
    request,
19
    jsonify,
20
    render_template,
21
)
22
from flask_cors import CORS
23
from flask_cors import cross_origin
24
25
app = Flask('endpoints_test')
26
CORS(app)
27
app.config['SECRET_KEY'] = 'NOTSECURELOL'
28
app.debug = True
29
30
STRESS_MAX_POINTS = 300
31
32
locale.setlocale(locale.LC_ALL, '')
33
34
cwd = os.getcwd()
35
36
37
def recursive_d3_data(current=0, max_iters=12, data=None):
38
    """Generate d3js data for stress testing.
39
    Format is suitable in treemap, circlepack and dendrogram testing.
40
    """
41
    if current >= max_iters:
42
        return data
43
    if data is None:
44
        data = dict(name='foo', size=rr(10, 10000), children=[])
45
    data = dict(name='foo', size=rr(10, 10000),
46
                children=[data, data])
47
    return recursive_d3_data(
48
        current=current + 1,
49
        max_iters=max_iters,
50
        data=data)
51
52
53
def dates_list(max_dates=10):
54
    """Generate a timeseries dates list."""
55
    now = dt.now()
56
    return [str(now + td(days=i * 10))[0:10] for i in range(max_dates)]
57
58
59
def rr_list(max_range=10):
60
    """Generate a list of random integers."""
61
    return [rr(0, 100) for i in range(max_range)]
62
63
64
def rand_hex_color():
65
    """Generate a random hex color.
66
    e.g. #FF0000
67
    """
68
    chars = list('0123456789ABCDEF')
69
    return '#{0}{1}{2}{3}{4}{5}'.format(
70
        choice(chars),
71
        choice(chars),
72
        choice(chars),
73
        choice(chars),
74
        choice(chars),
75
        choice(chars),
76
    )
77
78
79
@cross_origin()
80
@app.route('/combination')
81
def combination():
82
    """Fake endpoint."""
83
    data = {
84
        'columns': [
85
            ['data1', 30, 20, 50, 40, 60, 50],
86
            ['data2', 200, 130, 90, 240, 130, 220],
87
            ['data3', 300, 200, 160, 400, 250, 250],
88
            ['data4', 200, 130, 90, 240, 130, 220],
89
            ['data5', 130, 120, 150, 140, 160, 150],
90
            ['data6', 90, 70, 20, 50, 60, 120],
91
        ],
92
        'type': 'bar',
93
        'types': {
94
            'data3': 'spline',
95
            'data4': 'line',
96
            'data6': 'area',
97
        },
98
        'groups': [
99
            ['data1', 'data2'],
100
        ]
101
    }
102
    return jsonify(dict(data=data))
103
104
105
@cross_origin()
106
@app.route('/stacked-bar')
107
def stackedbar():
108
    """Fake endpoint."""
109
    return jsonify({
110
        'data': {
111
            'columns': [
112
                ['data1', -30, 200, 200, 400, -150, 250],
113
                ['data2', 130, 100, -100, 200, -150, 50],
114
                ['data3', -230, 200, 200, -300, 250, 250]
115
            ],
116
            'type': 'bar',
117
            'groups': [
118
                ['data1', 'data2']
119
            ]
120
        },
121
        'grid': {
122
            'y': {
123
                'lines': [{'value': 0}]
124
            }
125
        }
126
    })
127
128
129
@cross_origin()
130
@app.route('/wordcloud')
131
def wordcloud():
132
    """Fake endpoint."""
133
    words = [
134
        'awesome', 'rad', 'neato', 'the', 'flask', 'jsondash', 'graphs',
135
        'charts', 'd3', 'js', 'dashboards', 'c3',
136
    ]
137
    sizes = range(len(words))
138
    return jsonify([
139
        {'text': word, 'size': sizes[i] * 12} for i, word in enumerate(words)
140
    ])
141
142
143
@cross_origin()
144
@app.route('/sigma')
145
def sigma():
146
    """Fake endpoint."""
147
    chart_name = request.args.get('name', 'basic')
148
    if chart_name == 'random':
149
        nodes = request.args.get('nodes', 'abcdefghij')
150
        _vertices = list(nodes)
151
        _edges = combinations(_vertices, 2)
152
        edges, vertices = [], []
153
        for (frm, to) in _edges:
154
            edges.append(dict(
155
                id='{}-{}'.format(frm, to),
156
                color=rand_hex_color(),
157
                source=frm,
158
                target=to,
159
                size=rr(1, 10),
160
                x=rr(1, 100),
161
                y=rr(1, 100),
162
            ))
163
        for vertex in _vertices:
164
            vertices.append(dict(
165
                id=vertex,
166
                size=rr(1, 10),
167
                x=rr(1, 100),
168
                y=rr(1, 100),
169
                color=rand_hex_color(),
170
                label='node {}'.format(vertex),
171
            ))
172
        data = dict(
173
            nodes=vertices,
174
            edges=edges,
175
        )
176
        return jsonify(data)
177
    filename = '{}/examples/sigma/{}.json'.format(cwd, chart_name)
178
    try:
179
        with open(filename, 'r') as chartjson:
180
            return chartjson.read()
181
    except IOError:
182
        pass
183
    return jsonify({})
184
185
186
@cross_origin()
187
@app.route('/cytoscape')
188
def cytoscape():
189
    """Fake endpoint.
190
191
    Reads data from a local cytoscape spec, and if there is a
192
    remote url specified, (assuming it exists here), open and load it as well.
193
194
    This returns all required json as a single endpoint.
195
    """
196
    chart_name = request.args.get('name', 'dagre')
197
    filename = '{}/examples/cytoscape/{}.json'.format(cwd, chart_name)
198
    try:
199
        with open(filename, 'r') as chartjson:
200
            return chartjson.read()
201
    except IOError:
202
        pass
203
    return jsonify({})
204
205
206
@cross_origin()
207
@app.route('/vegalite')
208
def vegalite():
209
    """Fake endpoint.
210
211
    Reads data from a local vega spec, and if there is a
212
    remote url specified, (assuming it exists here), open and load it as well.
213
214
    This returns all required json as a single endpoint.
215
    """
216
    chart_type = request.args.get('type', 'bar')
217
    filename = '{}/examples/vegalite/{}.json'.format(cwd, chart_type)
218
    try:
219
        with open(filename, 'r') as chartjson:
220
            chartjson = chartjson.read()
221
            data = json.loads(chartjson)
222
            if data.get('data', {}).get('url') is not None:
223
                datapath = '{}/examples/vegalite/{}'.format(
224
                    cwd, data['data']['url']
225
                )
226
                with open(datapath, 'r') as datafile:
227
                    if datapath.endswith('.json'):
228
                        raw_data = datafile.read()
229
                        raw_data = json.loads(raw_data)
230
                    # TODO: adding csv support for example.
231
                    data.update(data=dict(
232
                        name='some data',
233
                        values=raw_data,
234
                    ))
235
                    return jsonify(data)
236
            else:
237
                return chartjson
238
    except IOError:
239
        pass
240
    return jsonify({})
241
242
243
@cross_origin()
244
@app.route('/plotly')
245
def plotly():
246
    """Fake endpoint."""
247
    chart_type = request.args.get('chart', 'line')
248
    filename = '{}/examples/plotly/{}.json'.format(cwd, chart_type)
249
    with open(filename, 'r') as chartjson:
250
        return chartjson.read()
251
    return jsonify({})
252
253
254
@cross_origin
255
@app.route('/plotly-dynamic')
256
def plotly_dynamic():
257
    """Fake endpoint."""
258
    filename = '{}/examples/plotly/bar_line_dynamic.json'.format(cwd)
259
    with open(filename, 'r') as chartjson:
260
        return chartjson.read()
261
    return jsonify({})
262
263
264
@cross_origin()
265
@app.route('/timeline')
266
def timeline():
267
    """Fake endpoint."""
268
    with open('{}/examples/timeline3.json'.format(cwd), 'r') as timelinejson:
269
        return timelinejson.read()
270
    return jsonify({})
271
272
273
@app.route('/dtable', methods=['GET'])
274
def dtable():
275
    """Fake endpoint."""
276
    if 'stress' in request.args:
277
        return jsonify([
278
            dict(
279
                foo=rr(1, 1000),
280
                bar=rr(1, 1000),
281
                baz=rr(1, 1000),
282
                quux=rr(1, 1000)) for _ in range(STRESS_MAX_POINTS)
283
        ])
284
    fname = 'dtable-override' if 'override' in request.args else 'dtable'
285
    with open('{}/examples/{}.json'.format(os.getcwd(), fname), 'r') as djson:
286
        return djson.read()
287
    return jsonify({})
288
289
290
@cross_origin()
291
@app.route('/timeseries')
292
def timeseries():
293
    """Fake endpoint."""
294
    return jsonify({
295
        "dates": dates_list(),
296
        "line1": rr_list(max_range=10),
297
        "line2": rr_list(max_range=10),
298
        "line3": rr_list(max_range=10),
299
    })
300
301
302
@cross_origin()
303
@app.route('/custom')
304
def custompage():
305
    """Fake endpoint."""
306
    kwargs = dict(number=rr(1, 1000))
307
    return render_template('examples/custom.html', **kwargs)
308
309
310
@cross_origin()
311
@app.route('/gauge')
312
def gauge():
313
    """Fake endpoint."""
314
    return jsonify({'data': rr(1, 100)})
315
316
317
@cross_origin()
318
@app.route('/area-custom')
319
def area_custom():
320
    """Fake endpoint."""
321
    return jsonify({
322
        "data": {
323
            "columns": [
324
                ["data1", 300, 350, 300, 0, 0, 0],
325
                ["data2", 130, 100, 140, 200, 150, 50]
326
            ],
327
            "types": {
328
                "data1": "area",
329
                "data2": "area-spline"
330
            }
331
        }
332
    })
333
334
335
@cross_origin()
336
@app.route('/scatter')
337
def scatter():
338
    """Fake endpoint."""
339
    if 'override' in request.args:
340
        with open('{}/examples/overrides.json'.format(cwd), 'r') as jsonfile:
341
            return jsonfile.read()
342
    return jsonify({
343
        "bar1": [1, 2, 30, 12, 100],
344
        "bar2": rr_list(max_range=40),
345
        "bar3": rr_list(max_range=40),
346
        "bar4": [-10, 1, 5, 4, 10, 20],
347
    })
348
349
350
@cross_origin()
351
@app.route('/pie')
352
def pie():
353
    """Fake endpoint."""
354
    letters = list('abcde')
355
    if 'stress' in request.args:
356
        letters = range(STRESS_MAX_POINTS)
357
    return jsonify({'data {}'.format(name): rr(1, 100) for name in letters})
358
359
360
@cross_origin()
361
@app.route('/custom-inputs')
362
def custom_inputs():
363
    """Fake endpoint."""
364
    _range = int(request.args.get('range', 5))
365
    entries = int(request.args.get('entries', 3))
366
    starting = int(request.args.get('starting_num', 0))
367
    prefix = request.args.get('prefix', 'item')
368
    if 'override' in request.args:
369
        show_axes = request.args.get('show_axes', False)
370
        show_axes = show_axes == 'on'
371
        data = dict(
372
            data=dict(
373
                columns=[
374
                    ['{} {}'.format(prefix, i)] + rr_list(max_range=entries)
375
                    for i in range(starting, _range)
376
                ],
377
            )
378
        )
379
        if show_axes:
380
            data.update(axis=dict(
381
                x=dict(label='This is the X axis'),
382
                y=dict(label='This is the Y axis')))
383
        return jsonify(data)
384
    return jsonify({
385
        i: rr_list(max_range=_range) for i in range(starting, entries)
386
    })
387
388
389
@cross_origin()
390
@app.route('/bar')
391
def barchart():
392
    """Fake endpoint."""
393
    if 'stress' in request.args:
394
        return jsonify({
395
            'bar-{}'.format(k): rr_list(max_range=STRESS_MAX_POINTS)
396
            for k in range(STRESS_MAX_POINTS)
397
        })
398
    return jsonify({
399
        "bar1": [1, 2, 30, 12, 100],
400
        "bar2": rr_list(max_range=5),
401
        "bar3": rr_list(max_range=5),
402
    })
403
404
405
@cross_origin()
406
@app.route('/line')
407
def linechart():
408
    """Fake endpoint."""
409
    if 'stress' in request.args:
410
        return jsonify({
411
            'bar-{}'.format(k): rr_list(max_range=STRESS_MAX_POINTS)
412
            for k in range(STRESS_MAX_POINTS)
413
        })
414
    return jsonify({
415
        "line1": [1, 4, 3, 10, 12, 14, 18, 10],
416
        "line2": [1, 2, 10, 20, 30, 6, 10, 12, 18, 2],
417
        "line3": rr_list(),
418
    })
419
420
421
@cross_origin()
422
@app.route('/singlenum')
423
def singlenum():
424
    """Fake endpoint."""
425
    _min, _max = 10, 10000
426
    if 'sales' in request.args:
427
        val = locale.currency(float(rr(_min, _max)), grouping=True)
428
    else:
429
        val = rr(_min, _max)
430
    if 'negative' in request.args:
431
        val = '-{}'.format(val)
432
    return jsonify(val)
433
434
435
@cross_origin()
436
@app.route('/deadend')
437
def test_die():
438
    """Fake endpoint that ends in a random 50x error."""
439
    # Simulate slow connection
440
    sleep = request.args.get('sleep', True)
441
    if sleep != '':
442
        sleep_for = request.args.get('sleep_for')
443
        time.sleep(int(sleep_for) if sleep_for is not None else random())
444
    err_code = request.args.get('error_code')
445
    rand_err = choice([500, 501, 502, 503, 504])
446
    abort(int(err_code) if err_code is not None else rand_err)
447
448
449
@cross_origin()
450
@app.route('/venn')
451
def test_venn():
452
    """Fake endpoint."""
453
    data = [
454
        {'sets': ['A'], 'size': rr(10, 100)},
455
        {'sets': ['B'], 'size': rr(10, 100)},
456
        {'sets': ['C'], 'size': rr(10, 100)},
457
        {'sets': ['A', 'B'], 'size': rr(10, 100)},
458
        {'sets': ['A', 'B', 'C'], 'size': rr(10, 100)},
459
    ]
460
    return jsonify(data)
461
462
463
@cross_origin()
464
@app.route('/sparklines', methods=['GET'])
465
def sparklines():
466
    """Fake endpoint."""
467
    if any([
468
        'pie' in request.args,
469
        'discrete' in request.args,
470
    ]):
471
        return jsonify([rr(1, 100) for _ in range(10)])
472
    return jsonify([[i, rr(i, 100)] for i in range(10)])
473
474
475
@cross_origin()
476
@app.route('/circlepack', methods=['GET'])
477
def circlepack():
478
    """Fake endpoint."""
479
    if 'stress' in request.args:
480
        # Build a very large dataset
481
        return jsonify(recursive_d3_data())
482
    with open('{}/examples/flare.json'.format(cwd), 'r') as djson:
483
        return djson.read()
484
    return jsonify({})
485
486
487
@cross_origin()
488
@app.route('/treemap', methods=['GET'])
489
def treemap():
490
    """Fake endpoint."""
491
    if 'stress' in request.args:
492
        # Build a very large dataset
493
        return jsonify(recursive_d3_data())
494
    with open('{}/examples/flare.json'.format(cwd), 'r') as djson:
495
        return djson.read()
496
    return jsonify({})
497
498
499
@cross_origin()
500
@app.route('/map', methods=['GET'])
501
def datamap():
502
    """Fake endpoint."""
503
    return render_template('examples/map.html')
504
505
506
@cross_origin()
507
@app.route('/dendrogram', methods=['GET'])
508
def dendro():
509
    """Fake endpoint."""
510
    if 'stress' in request.args:
511
        # Build a very large dataset
512
        return jsonify(recursive_d3_data())
513
    filename = 'flare-simple' if 'simple' in request.args else 'flare'
514
    with open('{}/examples/{}.json'.format(cwd, filename), 'r') as djson:
515
        return djson.read()
516
    return jsonify({})
517
518
519
@cross_origin()
520
@app.route('/voronoi', methods=['GET'])
521
def voronoi():
522
    """Fake endpoint."""
523
    w, h = request.args.get('width', 800), request.args.get('height', 800)
524
    max_points = int(request.args.get('points', 100))
525
    if 'stress' in request.args:
526
        max_points = 500
527
    return jsonify([[rr(1, h), rr(1, w)] for _ in range(max_points)])
528
529
530
@cross_origin()
531
@app.route('/digraph', methods=['GET'])
532
def graphdata():
533
    """Fake endpoint."""
534
    if 'filetree' in request.args:
535
        with open('{}/examples/filetree_digraph.dot'.format(cwd), 'r') as dot:
536
            return jsonify(dict(graph=dot.read()))
537
    if 'simple' in request.args:
538
        graphdata = """
539
        digraph {
540
            a -> b;
541
            a -> c;
542
            b -> c;
543
            b -> a;
544
            b -> b;
545
        }
546
        """
547
        return jsonify(dict(graph=graphdata))
548
    nodes = list('abcdefghijkl')
549
    node_data = '\n'.join([
550
        '{0} -> {1};'.format(choice(nodes), choice(nodes))
551
        for _ in range(10)
552
    ])
553
    graphdata = """digraph {lb} {nodes} {rb}""".format(
554
        lb='{', rb='}', nodes=node_data)
555
    return jsonify(dict(
556
        graph=graphdata,
557
    ))
558
559
560
if __name__ == '__main__':
561
    PORT = int(os.getenv('PORT', 5004))
562
    HOST = os.getenv('HOST', '0.0.0.0')
563
    app.run(debug=True, host=HOST, port=PORT)
564