diff --git a/caravel/assets/javascripts/datasource.js b/caravel/assets/javascripts/datasource.js new file mode 100644 index 000000000000..00c69cad8136 --- /dev/null +++ b/caravel/assets/javascripts/datasource.js @@ -0,0 +1,50 @@ +var $ = window.$ || require('jquery'); +var jsonGenerator = require("./modules/json_generator"); + +$(document).ready(function () { + var $modal = $('#json_gen_modal'); + + var getJsonOrWarn = function () { + var expression = $('#expression').val().trim(); + var $alert = $modal.find('#errorMsg'); + $alert + .removeClass('hidden') + .hide() + .html(""); + try { + var parse_tree = jsonGenerator.parse(expression); + return JSON.stringify(parse_tree); + } catch (e) { + $alert + .show() + .html(e.message); + } + return null; + }; + + $('#convertButton').click(function () { + var json = getJsonOrWarn(); + if (json) { + $('#json').val(json); + $(this).closest('.modal').modal('hide'); + } + }); + + $('#previewButton').click(function () { + var $preview = $modal.find('#preview'); + var json = getJsonOrWarn(); + $preview.html(json || ""); + }); + + $modal.on('shown.bs.modal', function () { + $('#expression').focus(); + }); + + var showJsonGenModal = function () { + $modal.modal('show'); + }; + + $.extend(window, { + showJsonGenModal: showJsonGenModal + }); +}); diff --git a/caravel/assets/javascripts/modules/json_generator.js b/caravel/assets/javascripts/modules/json_generator.js new file mode 100644 index 000000000000..4f4a2479cc85 --- /dev/null +++ b/caravel/assets/javascripts/modules/json_generator.js @@ -0,0 +1,92 @@ +var jsep = require("jsep"); + +var capitalize = function (str) { + return str.charAt(0).toUpperCase() + str.slice(1); +}; + +var convertFuncCall = function (node) { + var funcName = node.callee.name; + var fieldName = node.arguments[0].name || ""; + var m = funcName.toLowerCase().match(/^(double|long)?(sum|min|max)$/); + if (!m) { + throw new Error("Unsupported function call: " + funcName); + } + + var varType = m[1] || 'double'; // default to double + var funcType = m[2]; + + var type = varType + capitalize(funcType); + + return { + type: type, + name: fieldName, + fieldName: fieldName + }; +}; + +var convertField = function (node) { + var fieldName = node.name; + return { + type: "fieldAccess", + name: fieldName, + fieldName: fieldName + }; +}; + +var convertBinary = function (node) { + var op = node.operator; + // TODO: Support the "quotient" type + if (!/[+\-*\/]/.test(op)) { + throw new Error("Unsupported operation: " + op); + } + return { + type: "arithmetic", + fn: op, + fields: [node.left, node.right].map(function (child) { + return convertArithmetic(child); + }) + }; +}; + +var convertLiteral = function (node) { + return { + type: "constant", + value: node.value + }; +}; + +var arithmeticTokenHandler = { + Identifier: convertField, + BinaryExpression: convertBinary, + Literal: convertLiteral +}; + +var convertArithmetic = function (node) { + var converter = arithmeticTokenHandler[node.type]; + if (!converter) { + throw new Error("Invalid Expression (" + node.type + ")"); + } + return converter(node); +}; + +var parse = function (expression) { + expression = expression.trim().replace('\n', ''); + if (!expression) { + throw new Error("Please enter the expression"); + } + var parse_tree = jsep(expression); + var converterMap = { + CallExpression: convertFuncCall, + BinaryExpression: convertArithmetic, + Literal: convertArithmetic + }; + var converter = converterMap[parse_tree.type]; + if (!converter) { + throw new Error("Invalid Expression (" + parse_tree.type + ")"); + } + return converter(parse_tree); +}; + +module.exports = { + parse: parse +}; \ No newline at end of file diff --git a/caravel/assets/package.json b/caravel/assets/package.json index ccebcdb792d8..9085a8613e23 100644 --- a/caravel/assets/package.json +++ b/caravel/assets/package.json @@ -58,6 +58,7 @@ "imports-loader": "^0.6.5", "jquery": "^2.2.1", "jquery-ui": "^1.10.5", + "jsep": "^0.3.0", "less": "^2.6.1", "less-loader": "^2.2.2", "nvd3": "1.8.2", diff --git a/caravel/assets/webpack.config.js b/caravel/assets/webpack.config.js index d9253f3d5c41..2696f6e27302 100644 --- a/caravel/assets/webpack.config.js +++ b/caravel/assets/webpack.config.js @@ -10,7 +10,8 @@ var config = { explore: APP_DIR + '/javascripts/explore.js', welcome: APP_DIR + '/javascripts/welcome.js', sql: APP_DIR + '/javascripts/sql.js', - standalone: APP_DIR + '/javascripts/standalone.js' + standalone: APP_DIR + '/javascripts/standalone.js', + datasource: APP_DIR + '/javascripts/datasource.js' }, output: { path: BUILD_DIR, diff --git a/caravel/templates/caravel/models/datasource/edit.html b/caravel/templates/caravel/models/datasource/edit.html new file mode 100644 index 000000000000..66d7cdce0091 --- /dev/null +++ b/caravel/templates/caravel/models/datasource/edit.html @@ -0,0 +1,41 @@ +{% extends "appbuilder/general/model/edit.html" %} + +{% block content %} + {{ super() }} +