Completed
Push — master ( 7d09bc...049d19 )
by Chris
52s
created

flamegraph()   A

Complexity

Conditions 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
dl 0
loc 12
rs 9.4285
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('/numbergroup')
81
def numbergroup():
82
    """Fake endpoint."""
83
    dataset = int(request.args.get('dataset', 0))
84
    # multiple examples shown here for variadic demonstrations
85
    datas = [
86
        [
87
            {
88
                "title": "Number of widgets sold in last day",
89
                "description": 'This is a good sign',
90
                "data": 32515.0,
91
                "color": "green",
92
            },
93
            {
94
                "title": "New customers signed up this week",
95
                "description": 'New user accounts created',
96
                "data": 740,
97
            },
98
            {
99
                "title": "Average Daily Users",
100
                "description": "(aka DAU)",
101
                "data": 541200,
102
                "noformat": False,
103
            },
104
            {
105
                "title": "Max concurrent users this week",
106
                "description": "Server load peak",
107
                "data": 123401,
108
                "color": "orange",
109
                "noformat": True,
110
            },
111
        ],
112
        [
113
            {
114
                "title": "Simple thing",
115
                "data": 2,
116
                "width": "33%",
117
                "description": "Just a simple number"
118
            },
119
            {
120
                "title": "Simple thing 2",
121
                "data": 4033,
122
                "width": "33%",
123
                "description": "Just a simple number"
124
            },
125
            {
126
                "title": "Simple thing 3",
127
                "data": 49102,
128
                "width": "33%",
129
                "description": "Just a simple number"
130
            },
131
        ],
132
        [
133
            {
134
                "title": "Average time on site",
135
                "description": "Signed in to signed out (units nostyle)",
136
                "data": '20 minutes',
137
                "color": "#7D4EE4",
138
            },
139
            {
140
                "title": "Average time on site page X",
141
                "description": "Signed in to signed out (custom units style)",
142
                "data": 15,
143
                "units": "minutes",
144
            },
145
            {
146
                "title": "Average $ spent per day",
147
                "description": "Yeeehaw (custom units style)",
148
                "data": 130,
149
                "units": "dollars"
150
            },
151
        ]
152
    ]
153
    return jsonify(datas[dataset])
154
155
156
@cross_origin()
157
@app.route('/combination')
158
def combination():
159
    """Fake endpoint."""
160
    data = {
161
        'columns': [
162
            ['data1', 30, 20, 50, 40, 60, 50],
163
            ['data2', 200, 130, 90, 240, 130, 220],
164
            ['data3', 300, 200, 160, 400, 250, 250],
165
            ['data4', 200, 130, 90, 240, 130, 220],
166
            ['data5', 130, 120, 150, 140, 160, 150],
167
            ['data6', 90, 70, 20, 50, 60, 120],
168
        ],
169
        'type': 'bar',
170
        'types': {
171
            'data3': 'spline',
172
            'data4': 'line',
173
            'data6': 'area',
174
        },
175
        'groups': [
176
            ['data1', 'data2'],
177
        ]
178
    }
179
    return jsonify(dict(data=data))
180
181
182
@cross_origin()
183
@app.route('/timeseriesc3')
184
def timeseriesc3():
185
    """Fake endpoint."""
186
    return jsonify(dict(
187
        dates=[
188
            '19{}-{}-{}'.format(rr(10, 99), rr(10, 31), rr(10, 31))
189
            for _ in range(4)
190
        ],
191
        abc=rr_list(max_range=4),
192
        cde=rr_list(max_range=4),
193
    ))
194
195
196
@cross_origin()
197
@app.route('/stacked-bar')
198
def stackedbar():
199
    """Fake endpoint."""
200
    return jsonify({
201
        'data': {
202
            'columns': [
203
                ['data1', -30, 200, 200, 400, -150, 250],
204
                ['data2', 130, 100, -100, 200, -150, 50],
205
                ['data3', -230, 200, 200, -300, 250, 250]
206
            ],
207
            'type': 'bar',
208
            'groups': [
209
                ['data1', 'data2']
210
            ]
211
        },
212
        'grid': {
213
            'y': {
214
                'lines': [{'value': 0}]
215
            }
216
        }
217
    })
218
219
220
@cross_origin()
221
@app.route('/wordcloud')
222
def wordcloud():
223
    """Fake endpoint."""
224
    words = [
225
        'awesome', 'rad', 'neato', 'the', 'flask', 'jsondash', 'graphs',
226
        'charts', 'd3', 'js', 'dashboards', 'c3',
227
    ]
228
    sizes = range(len(words))
229
    data = [
230
        {
231
            'text': word,
232
            'size': sizes[i] * 12
233
        }
234
        for i, word in enumerate(words)
235
    ]
236
    return jsonify(data)
237
238
239
@cross_origin()
240
@app.route('/sigma')
241
def sigma():
242
    """Fake endpoint."""
243
    chart_name = request.args.get('name', 'basic')
244
    if chart_name == 'random':
245
        nodes = request.args.get('nodes', 'abcdefghij')
246
        _vertices = list(nodes)
247
        _edges = combinations(_vertices, 2)
248
        edges, vertices = [], []
249
        for (frm, to) in _edges:
250
            edges.append(dict(
251
                id='{}-{}'.format(frm, to),
252
                color=rand_hex_color(),
253
                source=frm,
254
                target=to,
255
                size=rr(1, 10),
256
                x=rr(1, 100),
257
                y=rr(1, 100),
258
            ))
259
        for vertex in _vertices:
260
            vertices.append(dict(
261
                id=vertex,
262
                size=rr(1, 10),
263
                x=rr(1, 100),
264
                y=rr(1, 100),
265
                color=rand_hex_color(),
266
                label='node {}'.format(vertex),
267
            ))
268
        data = dict(
269
            nodes=vertices,
270
            edges=edges,
271
        )
272
        return jsonify(data)
273
    filename = '{}/examples/sigma/{}.json'.format(cwd, chart_name)
274
    try:
275
        with open(filename, 'r') as chartjson:
276
            return chartjson.read()
277
    except IOError:
278
        pass
279
    return jsonify({})
280
281
282
@cross_origin()
283
@app.route('/flamegraph')
284
def flamegraph():
285
    """Fake endpoint."""
286
    chart_name = request.args.get('name', 'stacks')
287
    filename = '{}/examples/flamegraph/{}.json'.format(cwd, chart_name)
288
    try:
289
        with open(filename, 'r') as chartjson:
290
            return chartjson.read()
291
    except IOError:
292
        pass
293
    return jsonify({})
294
295
296
@cross_origin()
297
@app.route('/cytoscape')
298
def cytoscape():
299
    """Fake endpoint.
300
301
    Reads data from a local cytoscape spec, and if there is a
302
    remote url specified, (assuming it exists here), open and load it as well.
303
304
    This returns all required json as a single endpoint.
305
    """
306
    chart_name = request.args.get('name', 'dagre')
307
    filename = '{}/examples/cytoscape/{}.json'.format(cwd, chart_name)
308
    try:
309
        with open(filename, 'r') as chartjson:
310
            return chartjson.read()
311
    except IOError:
312
        pass
313
    return jsonify({})
314
315
316
@cross_origin()
317
@app.route('/vegalite')
318
def vegalite():
319
    """Fake endpoint.
320
321
    Reads data from a local vega spec, and if there is a
322
    remote url specified, (assuming it exists here), open and load it as well.
323
324
    This returns all required json as a single endpoint.
325
    """
326
    chart_type = request.args.get('type', 'bar')
327
    filename = '{}/examples/vegalite/{}.json'.format(cwd, chart_type)
328
    try:
329
        with open(filename, 'r') as chartjson:
330
            chartjson = chartjson.read()
331
            data = json.loads(chartjson)
332
            if data.get('data', {}).get('url') is not None:
333
                datapath = '{}/examples/vegalite/{}'.format(
334
                    cwd, data['data']['url']
335
                )
336
                with open(datapath, 'r') as datafile:
337
                    if datapath.endswith('.json'):
338
                        raw_data = datafile.read()
339
                        raw_data = json.loads(raw_data)
340
                    # TODO: adding csv support for example.
341
                    data.update(data=dict(
342
                        name='some data',
343
                        values=raw_data,
344
                    ))
345
                    return jsonify(data)
346
            else:
347
                return chartjson
348
    except IOError:
349
        pass
350
    return jsonify({})
351
352
353
@cross_origin()
354
@app.route('/plotly')
355
def plotly():
356
    """Fake endpoint."""
357
    chart_type = request.args.get('chart', 'line')
358
    filename = '{}/examples/plotly/{}.json'.format(cwd, chart_type)
359
    with open(filename, 'r') as chartjson:
360
        return chartjson.read()
361
    return jsonify({})
362
363
364
@cross_origin
365
@app.route('/plotly-dynamic')
366
def plotly_dynamic():
367
    """Fake endpoint."""
368
    filename = '{}/examples/plotly/bar_line_dynamic.json'.format(cwd)
369
    with open(filename, 'r') as chartjson:
370
        return chartjson.read()
371
    return jsonify({})
372
373
374
@cross_origin()
375
@app.route('/timeline')
376
def timeline():
377
    """Fake endpoint."""
378
    with open('{}/examples/timeline3.json'.format(cwd), 'r') as timelinejson:
379
        return timelinejson.read()
380
    return jsonify({})
381
382
383
@app.route('/dtable', methods=['GET'])
384
def dtable():
385
    """Fake endpoint."""
386
    if 'stress' in request.args:
387
        return jsonify([
388
            dict(
389
                foo=rr(1, 1000),
390
                bar=rr(1, 1000),
391
                baz=rr(1, 1000),
392
                quux=rr(1, 1000)) for _ in range(STRESS_MAX_POINTS)
393
        ])
394
    fname = 'dtable-override' if 'override' in request.args else 'dtable'
395
    with open('{}/examples/{}.json'.format(os.getcwd(), fname), 'r') as djson:
396
        return djson.read()
397
    return jsonify({})
398
399
400
@cross_origin()
401
@app.route('/timeseries')
402
def timeseries():
403
    """Fake endpoint."""
404
    return jsonify({
405
        "dates": dates_list(),
406
        "line1": rr_list(max_range=10),
407
        "line2": rr_list(max_range=10),
408
        "line3": rr_list(max_range=10),
409
    })
410
411
412
@cross_origin()
413
@app.route('/custom')
414
def custompage():
415
    """Fake endpoint."""
416
    kwargs = dict(number=rr(1, 1000))
417
    return render_template('examples/custom.html', **kwargs)
418
419
420
@cross_origin()
421
@app.route('/gauge')
422
def gauge():
423
    """Fake endpoint."""
424
    return jsonify({'data': rr(1, 100)})
425
426
427
@cross_origin()
428
@app.route('/area-custom')
429
def area_custom():
430
    """Fake endpoint."""
431
    return jsonify({
432
        "data": {
433
            "columns": [
434
                ["data1", 300, 350, 300, 0, 0, 0],
435
                ["data2", 130, 100, 140, 200, 150, 50]
436
            ],
437
            "types": {
438
                "data1": "area",
439
                "data2": "area-spline"
440
            }
441
        }
442
    })
443
444
445
@cross_origin()
446
@app.route('/scatter')
447
def scatter():
448
    """Fake endpoint."""
449
    if 'override' in request.args:
450
        with open('{}/examples/overrides.json'.format(cwd), 'r') as jsonfile:
451
            return jsonfile.read()
452
    return jsonify({
453
        "bar1": [1, 2, 30, 12, 100],
454
        "bar2": rr_list(max_range=40),
455
        "bar3": rr_list(max_range=40),
456
        "bar4": [-10, 1, 5, 4, 10, 20],
457
    })
458
459
460
@cross_origin()
461
@app.route('/pie')
462
def pie():
463
    """Fake endpoint."""
464
    letters = list('abcde')
465
    if 'stress' in request.args:
466
        letters = range(STRESS_MAX_POINTS)
467
    return jsonify({'data {}'.format(name): rr(1, 100) for name in letters})
468
469
470
@cross_origin()
471
@app.route('/custom-inputs')
472
def custom_inputs():
473
    """Fake endpoint."""
474
    _range = int(request.args.get('range', 5))
475
    entries = int(request.args.get('entries', 3))
476
    starting = int(request.args.get('starting_num', 0))
477
    prefix = request.args.get('prefix', 'item')
478
    if 'override' in request.args:
479
        show_axes = request.args.get('show_axes', False)
480
        show_axes = show_axes == 'on'
481
        data = dict(
482
            data=dict(
483
                columns=[
484
                    ['{} {}'.format(prefix, i)] + rr_list(max_range=entries)
485
                    for i in range(starting, _range)
486
                ],
487
            )
488
        )
489
        if show_axes:
490
            data.update(axis=dict(
491
                x=dict(label='This is the X axis'),
492
                y=dict(label='This is the Y axis')))
493
        return jsonify(data)
494
    return jsonify({
495
        i: rr_list(max_range=_range) for i in range(starting, entries)
496
    })
497
498
499
@cross_origin()
500
@app.route('/bar')
501
def barchart():
502
    """Fake endpoint."""
503
    if 'stress' in request.args:
504
        return jsonify({
505
            'bar-{}'.format(k): rr_list(max_range=STRESS_MAX_POINTS)
506
            for k in range(STRESS_MAX_POINTS)
507
        })
508
    return jsonify({
509
        "bar1": [1, 2, 30, 12, 100],
510
        "bar2": rr_list(max_range=5),
511
        "bar3": rr_list(max_range=5),
512
    })
513
514
515
@cross_origin()
516
@app.route('/line')
517
def linechart():
518
    """Fake endpoint."""
519
    if 'stress' in request.args:
520
        return jsonify({
521
            'bar-{}'.format(k): rr_list(max_range=STRESS_MAX_POINTS)
522
            for k in range(STRESS_MAX_POINTS)
523
        })
524
    return jsonify({
525
        "line1": [1, 4, 3, 10, 12, 14, 18, 10],
526
        "line2": [1, 2, 10, 20, 30, 6, 10, 12, 18, 2],
527
        "line3": rr_list(),
528
    })
529
530
531
@cross_origin()
532
@app.route('/shared')
533
def shared_data():
534
    """Fake endpoint to demonstrate sharing data from one source."""
535
    letters = list('abcde')
536
    piedata = {'data {}'.format(name): rr(1, 100) for name in letters}
537
    bardata = {
538
        "bar1": [1, 2, 30, 12, 100],
539
        "bar2": rr_list(max_range=5),
540
        "bar3": rr_list(max_range=5),
541
    }
542
    linedata = {
543
        "line1": [1, 4, 3, 10, 12, 14, 18, 10],
544
        "line2": [1, 2, 10, 20, 30, 6, 10, 12, 18, 2],
545
        "line3": rr_list(),
546
    }
547
    return jsonify({
548
        'multicharts': {
549
            'line': linedata,
550
            'bar': bardata,
551
            'pie': piedata,
552
        }
553
    })
554
555
556
@cross_origin()
557
@app.route('/singlenum')
558
def singlenum():
559
    """Fake endpoint."""
560
    _min, _max = 10, 10000
561
    if 'sales' in request.args:
562
        val = locale.currency(float(rr(_min, _max)), grouping=True)
563
    else:
564
        val = rr(_min, _max)
565
    if 'negative' in request.args:
566
        val = '-{}'.format(val)
567
    return jsonify(data=val)
568
569
570
@cross_origin()
571
@app.route('/deadend')
572
def test_die():
573
    """Fake endpoint that ends in a random 50x error."""
574
    # Simulate slow connection
575
    sleep = request.args.get('sleep', True)
576
    if sleep != '':
577
        sleep_for = request.args.get('sleep_for')
578
        time.sleep(int(sleep_for) if sleep_for is not None else random())
579
    err_code = request.args.get('error_code')
580
    rand_err = choice([500, 501, 502, 503, 504])
581
    abort(int(err_code) if err_code is not None else rand_err)
582
583
584
@cross_origin()
585
@app.route('/venn')
586
def test_venn():
587
    """Fake endpoint."""
588
    data = [
589
        {'sets': ['A'], 'size': rr(10, 100)},
590
        {'sets': ['B'], 'size': rr(10, 100)},
591
        {'sets': ['C'], 'size': rr(10, 100)},
592
        {'sets': ['A', 'B'], 'size': rr(10, 100)},
593
        {'sets': ['A', 'B', 'C'], 'size': rr(10, 100)},
594
    ]
595
    return jsonify(data)
596
597
598
@cross_origin()
599
@app.route('/sparklines', methods=['GET'])
600
def sparklines():
601
    """Fake endpoint."""
602
    if any([
603
        'pie' in request.args,
604
        'discrete' in request.args,
605
    ]):
606
        return jsonify([rr(1, 100) for _ in range(10)])
607
    return jsonify([[i, rr(i, 100)] for i in range(10)])
608
609
610
@cross_origin()
611
@app.route('/circlepack', methods=['GET'])
612
def circlepack():
613
    """Fake endpoint."""
614
    if 'stress' in request.args:
615
        # Build a very large dataset
616
        return jsonify(recursive_d3_data())
617
    with open('{}/examples/flare.json'.format(cwd), 'r') as djson:
618
        return djson.read()
619
    return jsonify({})
620
621
622
@cross_origin()
623
@app.route('/treemap', methods=['GET'])
624
def treemap():
625
    """Fake endpoint."""
626
    if 'stress' in request.args:
627
        # Build a very large dataset
628
        return jsonify(recursive_d3_data())
629
    with open('{}/examples/flare.json'.format(cwd), 'r') as djson:
630
        return djson.read()
631
    return jsonify({})
632
633
634
@cross_origin()
635
@app.route('/map', methods=['GET'])
636
def datamap():
637
    """Fake endpoint."""
638
    return render_template('examples/map.html')
639
640
641
@cross_origin()
642
@app.route('/dendrogram', methods=['GET'])
643
def dendro():
644
    """Fake endpoint."""
645
    if 'stress' in request.args:
646
        # Build a very large dataset
647
        return jsonify(recursive_d3_data())
648
    filename = 'flare-simple' if 'simple' in request.args else 'flare'
649
    with open('{}/examples/{}.json'.format(cwd, filename), 'r') as djson:
650
        return djson.read()
651
    return jsonify({})
652
653
654
@cross_origin()
655
@app.route('/voronoi', methods=['GET'])
656
def voronoi():
657
    """Fake endpoint."""
658
    w, h = request.args.get('width', 800), request.args.get('height', 800)
659
    max_points = int(request.args.get('points', 100))
660
    if 'stress' in request.args:
661
        max_points = 500
662
    return jsonify([[rr(1, h), rr(1, w)] for _ in range(max_points)])
663
664
665
@cross_origin()
666
@app.route('/digraph', methods=['GET'])
667
def graphdata():
668
    """Fake endpoint."""
669
    if 'filetree' in request.args:
670
        with open('{}/examples/filetree_digraph.dot'.format(cwd), 'r') as dot:
671
            return jsonify(dict(graph=dot.read()))
672
    if 'simple' in request.args:
673
        graphdata = """
674
        digraph {
675
            a -> b;
676
            a -> c;
677
            b -> c;
678
            b -> a;
679
            b -> b;
680
        }
681
        """
682
        return jsonify(dict(graph=graphdata))
683
    nodes = list('abcdefghijkl')
684
    node_data = '\n'.join([
685
        '{0} -> {1};'.format(choice(nodes), choice(nodes))
686
        for _ in range(10)
687
    ])
688
    graphdata = """digraph {lb} {nodes} {rb}""".format(
689
        lb='{', rb='}', nodes=node_data)
690
    return jsonify(dict(
691
        graph=graphdata,
692
    ))
693
694
695
if __name__ == '__main__':
696
    PORT = int(os.getenv('PORT', 5004))
697
    HOST = os.getenv('HOST', '0.0.0.0')
698
    app.run(debug=True, host=HOST, port=PORT)
699