71 lines
1.5 KiB
Python
71 lines
1.5 KiB
Python
|
|
"""Response renderers.
|
||
|
|
|
||
|
|
Replaces renderers.js:
|
||
|
|
- JSON_OUTPUT: JSON with 2-decimal float formatting + JSONP callback support
|
||
|
|
- CSV_OUTPUT: semicolon-delimited CSV
|
||
|
|
"""
|
||
|
|
|
||
|
|
import csv
|
||
|
|
import io
|
||
|
|
import json
|
||
|
|
|
||
|
|
from flask import jsonify, Response
|
||
|
|
|
||
|
|
|
||
|
|
def json_output(ctx):
|
||
|
|
"""Render data as JSON.
|
||
|
|
|
||
|
|
Replaces JSON_OUTPUT from renderers.js.
|
||
|
|
Supports JSONP via ?callback= query parameter.
|
||
|
|
Rounds floats to 2 decimal places.
|
||
|
|
"""
|
||
|
|
data = ctx.get('data', {})
|
||
|
|
|
||
|
|
def _replacer(val):
|
||
|
|
if isinstance(val, float):
|
||
|
|
return round(val, 2)
|
||
|
|
return val
|
||
|
|
|
||
|
|
json_data = json.dumps(data, default=_replacer, indent=2)
|
||
|
|
|
||
|
|
from flask import request
|
||
|
|
callback = request.args.get('callback')
|
||
|
|
if callback:
|
||
|
|
return Response(
|
||
|
|
f'{callback}({json_data})',
|
||
|
|
mimetype='application/javascript',
|
||
|
|
)
|
||
|
|
return Response(
|
||
|
|
json_data,
|
||
|
|
mimetype='application/json; charset=utf-8',
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def csv_output(ctx):
|
||
|
|
"""Render data as semicolon-delimited CSV.
|
||
|
|
|
||
|
|
Replaces CSV_OUTPUT from renderers.js.
|
||
|
|
"""
|
||
|
|
data = ctx.get('data', [])
|
||
|
|
|
||
|
|
si = io.StringIO()
|
||
|
|
writer = csv.writer(
|
||
|
|
si,
|
||
|
|
delimiter=';',
|
||
|
|
lineterminator='\n',
|
||
|
|
)
|
||
|
|
|
||
|
|
if data and isinstance(data[0], dict):
|
||
|
|
# Write header
|
||
|
|
writer.writerow(data[0].keys())
|
||
|
|
for row in data:
|
||
|
|
writer.writerow(row.values())
|
||
|
|
|
||
|
|
body = si.getvalue()
|
||
|
|
si.close()
|
||
|
|
|
||
|
|
return Response(
|
||
|
|
body.encode('utf-8'),
|
||
|
|
mimetype='text/csv; charset=utf-8',
|
||
|
|
)
|