rename - 2nd phase
|
Before Width: | Height: | Size: 459 KiB After Width: | Height: | Size: 459 KiB |
|
Before Width: | Height: | Size: 702 KiB After Width: | Height: | Size: 702 KiB |
|
Before Width: | Height: | Size: 328 KiB After Width: | Height: | Size: 328 KiB |
|
Before Width: | Height: | Size: 552 KiB After Width: | Height: | Size: 552 KiB |
|
Before Width: | Height: | Size: 236 KiB After Width: | Height: | Size: 236 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 147 KiB After Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
|
@ -36,7 +36,8 @@ function tableVis(slice) {
|
|||
}
|
||||
|
||||
var table = d3.select(slice.selector).append('table')
|
||||
.classed('dataframe dataframe table table-striped table-bordered table-condensed table-hover dataTable no-footer', true);
|
||||
.classed('dataframe dataframe table table-striped table-bordered table-condensed table-hover dataTable no-footer', true)
|
||||
.attr('width', '100%');
|
||||
|
||||
table.append('thead').append('tr')
|
||||
.selectAll('th')
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
|
@ -3,7 +3,7 @@
|
|||
{% block head_css %}
|
||||
{{super()}}
|
||||
<link rel="stylesheet" type="text/css" href="/static/assets/stylesheets/dashed.css" />
|
||||
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||
<link rel="icon" type="image/png" href="/static/img/favicon.png">
|
||||
{% endblock %}
|
||||
|
||||
{% block head_js %}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
{% block head_meta %}{% endblock %}
|
||||
{% block head_css %}
|
||||
<link rel="stylesheet" type="text/css" href="/static/assets/node_modules/font-awesome/css/font-awesome.min.css" />
|
||||
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||
<link rel="icon" type="image/png" href="/static/img/favicon.png">
|
||||
{% endblock %}
|
||||
{% block head_js %}
|
||||
<script src="/static/assets/javascripts/dist/css-theme.entry.js"></script>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
rm -r _build
|
||||
make html
|
||||
cp -r _build/html/ ../../dashed-docs/
|
||||
cp -r ../dashed/assets/images/ _build/html/_static/img/
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
"""Package's main module!"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
from flask import Flask, redirect
|
||||
from flask.ext.appbuilder import SQLA, AppBuilder, IndexView
|
||||
from flask.ext.appbuilder.baseviews import expose
|
||||
from flask.ext.migrate import Migrate
|
||||
|
||||
|
||||
APP_DIR = os.path.dirname(__file__)
|
||||
CONFIG_MODULE = os.environ.get('PANORAMIX_CONFIG', 'panoramix.config')
|
||||
|
||||
# Logging configuration
|
||||
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(name)s:%(message)s')
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(CONFIG_MODULE)
|
||||
db = SQLA(app)
|
||||
migrate = Migrate(app, db, directory=APP_DIR + "/migrations")
|
||||
|
||||
|
||||
class MyIndexView(IndexView):
|
||||
@expose('/')
|
||||
def index(self):
|
||||
return redirect('/panoramix/featured')
|
||||
|
||||
appbuilder = AppBuilder(
|
||||
app, db.session,
|
||||
base_template='panoramix/base.html',
|
||||
indexview=MyIndexView,
|
||||
security_manager_class=app.config.get("CUSTOM_SECURITY_MANAGER"))
|
||||
|
||||
sm = appbuilder.sm
|
||||
|
||||
get_session = appbuilder.get_session
|
||||
from panoramix import config, views # noqa
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
error = (
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM8OI++=~~~~~~=+?IODMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMD$~~~~~~~~~~~~~~~~~~~~~~~=$MMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMN8?:~~~~~~~~~~~~~~~~~~~~~~~~~~=+8NMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMO=~~~~~~~~~~~~~~~~~+I??~~~~~~~~~~~~~+DMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMNI~~~~~~~~~~~~~~~~~~IIIII=~~~~~~~~~~~~~~=NMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMM+=~~~~~~~~~~~~~~~~~~~=III+~~~~~~~~~~~~~~~~~?8MMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMM?~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+++=~~~~8MMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMI=~~~~~~~~~~~~~~~~~~~~~~~~~III?I~~~~~~~~,:++++++~~8MMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMN7~~~~~~~~~~~~~~~~==+=~~~~~~=IIIII~~~~~~:. ..:=++=~=MMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMO=~~~~~~~~~~~~~~~~+++=~~~~~~~~??I?I~~~~~~. ...,~~~~IMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMM~~~~~~~~~~~~~~~~~+++:,~~~~~~~~~~~?=~~~~~:. ..~~~~~OMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMM$=~~~~~~~~~~~~~~~=++:.. ..~~~~~~~~~~~~~~~~,. . . :~~~~~OMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMM~~~~~~~~~~~~~~~~+++,. .~~~~~~~~~~~~~~~.. .. . .~~~~~=OMMMMMMMMMM\n"+
|
||||
"MMMMMMMM?~~~~~~~~~~~~~~~=+~. .~~~~~~~~~~~~~~. ,MMMMM,=~~~~~~NMMMMMMMMM\n"+
|
||||
"MMMMMMMN~~~~~~~~~~~~~~~~~,. .,~~~~~~~~~~~~~.. ZMMM,+Z:~~~~~~$MMMMMMMMM\n"+
|
||||
"MMMMMM8?~~~~~~~~~~~~~~~~~.. ..~~~~~~~~~~~~~:. DMMM,+D~~~~~~~~IMMMMMMMM\n"+
|
||||
"MMMMMMI~~~~~~~~~~~~~~~~~~.. :MMMO~~~~~~~~~~~~~~~,.. ?MMMMMI~~~~~~~~~MMMMMMMM\n"+
|
||||
"MMMMMM=~~~~~~~~~~~~~~~~~~.. MMM+=M:~~~~~~~~~~~~~:. .:IM$~~~~~~~~~~~8MMMMMMM\n"+
|
||||
"MMMMMD~~~~~~~~~~~~~~~~~~~:. MMM:,M:~~~~~~~~~~~~~~~.......:~~~~~~~~~~$MMMMMMM\n"+
|
||||
"MMMMMI~~~~~~~~~~~~~~~~~~~~, MMMMMM~~~~~~~~~~~~~~~~~~,..:~~~~~~~~~~~~+MMMMMMM\n"+
|
||||
"MMMMD+~~~~~~~~~~~~~~~~~~~~~. $MMMM$~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~=MMMMMMM\n"+
|
||||
"MMMM8~~~~~~~~~~~~~~~~~~~~~~:. . .:~~~~~~,..:. .=~~~~~~~~~~~~~~~~~~~~MMMMMMM\n"+
|
||||
"MMMMO~~~~~~~~~~~~~~~~~~~~~~~:, .:~~~~~=8.. .+ . =8ZI~~~~~~~~~~~~~~~~=MMMMMMM\n"+
|
||||
"MMMMZ=~~~~~~~~~~~~~~~~~~~~~~~~:,,,:~~~~~~IZ8:. .O....888?~~~~~~~~~~~~~~~+MMMMMMM\n"+
|
||||
"MMMMO=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~?888=...I~I88888O?~~~~~~~~~~~~~~7MMMMMMM\n"+
|
||||
"MMMMO~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Z888OO88888888888O?~~~~~~~~~~~~~OMMMMMMM\n"+
|
||||
"MMMMD+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~=8888888888888888888~~~~~~~~~~~~+MMMMMMMM\n"+
|
||||
"MMMMM7~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~?8888888888888888888?~~~~~~~~~~=$MMMMMMMM\n"+
|
||||
"MMMMMD~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~=$8888888888888888888O~~~~~~~~~~8MMMMMMMMM\n"+
|
||||
"MMMMMN=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Z88888888888888888ZZ7=~~~~~~~~?MMMMMMMMMM\n"+
|
||||
"MMMMMMZ=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Z88888888Z7I===~~~~~~~~~~~~~=OMMMMMMMMMMM\n"+
|
||||
"MMMMMMN$~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~=$88888O7?=~~~~~~~~~~~~~~~~~~OMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMM?~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~I8OZ+~~~~~~~~~~~~~~~~~~~~=DMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMM8=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+$+=~~~~~~~~~~~~~~~~~~~~+MMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMD7~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~=$DMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMM?~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~=$OMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMD7=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ZMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMZ7=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~78MMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMM8OI=~~~~~~~~~~~~~~~~~~~=+?ZDNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMNDZ7?++~=~==~+?IONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM")
|
||||
|
||||
stacktrace="""
|
||||
-------------------------------------------------------------------------------------------------------
|
||||
=======================================================================================================
|
||||
-------------------------------------------------------------------------------------------------------
|
||||
___ ___ ___
|
||||
( ) ( ) ( )
|
||||
.--. | |_ .---. .--. | | ___ | |_ ___ .-. .---. .--. .--.
|
||||
/ _ \ ( __) / .-, \ / \ | | ( ) ( __) ( ) \ / .-, \ / \ / \\
|
||||
. .' `. ; | | (__) ; | | .-. ; | | ' / | | | ' .-. ; (__) ; | | .-. ; | .-. ;
|
||||
| ' | | | | ___ .'` | | |(___) | |,' / | | ___ | / (___) .'` | | |(___) | | | |
|
||||
_\_`.(___) | |( ) / .'| | | | | . '. | |( ) | | / .'| | | | | |/ |
|
||||
( ). '. | | | | | / | | | | ___ | | `. \ | | | | | | | / | | | | ___ | ' _.'
|
||||
| | `\ | | ' | | ; | ; | | '( ) | | \ \ | ' | | | | ; | ; | | '( ) | .'.-.
|
||||
; '._,' ' ' `-' ; ' `-' | ' `-' | | | \ . ' `-' ; | | ' `-' | ' `-' | ' `-' /
|
||||
'.___.' `.__. `.__.'_. `.__,' (___ ) (___) `.__. (___) `.__.'_. `.__,' `.__.'
|
||||
|
||||
-------------------------------------------------------------------------------------------------------
|
||||
=======================================================================================================
|
||||
-------------------------------------------------------------------------------------------------------
|
||||
"""
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"presets" : ["es2015", "react"]
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
node_modules/*
|
||||
vendor/*
|
||||
javascripts/dist/*
|
||||
|
|
@ -1,234 +0,0 @@
|
|||
{
|
||||
"root": true,
|
||||
|
||||
"globals": {
|
||||
"Symbol": false,
|
||||
"Map": false,
|
||||
"Set": false,
|
||||
"Reflect": false,
|
||||
},
|
||||
|
||||
"env": {
|
||||
"es6": false,
|
||||
"browser": true,
|
||||
"node": true,
|
||||
},
|
||||
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 5,
|
||||
"sourceType": "module"
|
||||
},
|
||||
|
||||
"rules": {
|
||||
"array-bracket-spacing": [2, "never", {
|
||||
"singleValue": false,
|
||||
"objectsInArrays": false,
|
||||
"arraysInArrays": false
|
||||
}],
|
||||
"array-callback-return": [2],
|
||||
"block-spacing": [2, "always"],
|
||||
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
|
||||
"callback-return": [2, ["callback"]],
|
||||
"camelcase": [0],
|
||||
"comma-dangle": [2, "never"],
|
||||
"comma-spacing": [2],
|
||||
"comma-style": [2, "last"],
|
||||
"curly": [2, "all"],
|
||||
"eqeqeq": 2,
|
||||
"func-names": [0],
|
||||
"id-length": [2, { "min": 1, "max": 25, "properties": "never" }],
|
||||
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
|
||||
"keyword-spacing": [2, {
|
||||
"before": true,
|
||||
"after": true,
|
||||
"overrides": {
|
||||
"return": { "after": true },
|
||||
"throw": { "after": true },
|
||||
"case": { "after": true }
|
||||
}
|
||||
}],
|
||||
"linebreak-style": [2, "unix"],
|
||||
"lines-around-comment": [2, {
|
||||
"beforeBlockComment": false,
|
||||
"afterBlockComment": false,
|
||||
"beforeLineComment": false,
|
||||
"allowBlockStart": true,
|
||||
"allowBlockEnd": true
|
||||
}],
|
||||
"max-depth": [2, 5],
|
||||
"max-len": [0, 80, 4],
|
||||
"max-nested-callbacks": [1, 2],
|
||||
"max-params": [1, 4],
|
||||
"new-parens": [2],
|
||||
"newline-after-var": [0],
|
||||
"no-bitwise": [0],
|
||||
"no-cond-assign": [2],
|
||||
"no-console": [2],
|
||||
"no-const-assign": [2],
|
||||
"no-constant-condition": [2],
|
||||
"no-control-regex": [2],
|
||||
"no-debugger": [2],
|
||||
"no-delete-var": [2],
|
||||
"no-dupe-args": [2],
|
||||
"no-dupe-class-members": [2],
|
||||
"no-dupe-keys": [2],
|
||||
"no-duplicate-case": [2],
|
||||
"no-else-return": [0],
|
||||
"no-empty": [2],
|
||||
"no-eq-null": [0],
|
||||
"no-eval": [2],
|
||||
"no-ex-assign": [2],
|
||||
"no-extend-native": [2],
|
||||
"no-extra-bind": [2],
|
||||
"no-extra-boolean-cast": [2],
|
||||
"no-extra-label": [2],
|
||||
"no-extra-parens": [0], // needed for clearer #math eg (a - b) / c
|
||||
"no-extra-semi": [2],
|
||||
"no-fallthrough": [2],
|
||||
"no-floating-decimal": [2],
|
||||
"no-func-assign": [2],
|
||||
"no-implied-eval": [2],
|
||||
"no-implicit-coercion": [2, {
|
||||
"boolean": false,
|
||||
"number": true,
|
||||
"string": true
|
||||
}],
|
||||
"no-implicit-globals": [2],
|
||||
"no-inline-comments": [0],
|
||||
"no-invalid-regexp": [2],
|
||||
"no-irregular-whitespace": [2],
|
||||
"no-iterator": [2],
|
||||
"no-label-var": [2],
|
||||
"no-labels": [2, { "allowLoop": false, "allowSwitch": false }],
|
||||
"no-lone-blocks": [2],
|
||||
"no-lonely-if": [2],
|
||||
"no-loop-func": [2],
|
||||
"no-magic-numbers": [0], // doesn't work well with vis cosmetic constant
|
||||
"no-mixed-requires": [1, false],
|
||||
"no-mixed-spaces-and-tabs": [2, false],
|
||||
"no-multi-spaces": [2, {
|
||||
"exceptions": {
|
||||
"ImportDeclaration": true,
|
||||
"Property": true,
|
||||
"VariableDeclarator": true
|
||||
}
|
||||
}],
|
||||
"no-multi-str": [2],
|
||||
"no-multiple-empty-lines": [2, { "max": 1, "maxEOF": 1 }],
|
||||
"no-native-reassign": [2],
|
||||
"no-negated-condition": [2],
|
||||
"no-negated-in-lhs": [2],
|
||||
"no-nested-ternary": [0],
|
||||
"no-new": [2],
|
||||
"no-new-func": [2],
|
||||
"no-new-object": [2],
|
||||
"no-new-require": [0],
|
||||
"no-new-symbol": [2],
|
||||
"no-new-wrappers": [2],
|
||||
"no-obj-calls": [2],
|
||||
"no-octal": [2],
|
||||
"no-octal-escape": [2],
|
||||
"no-path-concat": [0],
|
||||
"no-process-env": [0],
|
||||
"no-process-exit": [2],
|
||||
"no-proto": [2],
|
||||
"no-redeclare": [2],
|
||||
"no-regex-spaces": [2],
|
||||
"no-restricted-modules": [0],
|
||||
"no-restricted-imports": [0],
|
||||
"no-restricted-syntax": [2,
|
||||
"DebuggerStatement",
|
||||
"LabeledStatement",
|
||||
"WithStatement"
|
||||
],
|
||||
"no-return-assign": [2, "always"],
|
||||
"no-script-url": [2],
|
||||
"no-self-assign": [2],
|
||||
"no-self-compare": [0],
|
||||
"no-sequences": [2],
|
||||
"no-shadow-restricted-names": [2],
|
||||
"no-spaced-func": [2],
|
||||
"no-sparse-arrays": [2],
|
||||
"no-sync": [0],
|
||||
"no-ternary": [0],
|
||||
"no-this-before-super": [2],
|
||||
"no-throw-literal": [2],
|
||||
"no-trailing-spaces": [2, { "skipBlankLines": false }],
|
||||
"no-undef": [2, { "typeof": true }],
|
||||
"no-undef-init": [2],
|
||||
"no-undefined": [0],
|
||||
"no-underscore-dangle": [0], // __data__ sometimes
|
||||
"no-unexpected-multiline": [2],
|
||||
"no-unmodified-loop-condition": [2],
|
||||
"no-unneeded-ternary": [2],
|
||||
"no-unreachable": [2],
|
||||
"no-unused-expressions": [2],
|
||||
"no-unused-labels": [2],
|
||||
"no-unused-vars": [2, {
|
||||
"vars": "all",
|
||||
"args": "none", // (d, i) pattern d3 func makes difficult to enforce
|
||||
"varsIgnorePattern": "jQuery"
|
||||
}],
|
||||
"no-use-before-define": [0],
|
||||
"no-useless-call": [2],
|
||||
"no-useless-concat": [2],
|
||||
"no-useless-constructor": [2],
|
||||
"no-void": [0],
|
||||
"no-warning-comments": [0, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],
|
||||
"no-with": [2],
|
||||
"no-whitespace-before-property": [2],
|
||||
"object-curly-spacing": [2, "always"],
|
||||
"object-shorthand": [2, "never"],
|
||||
"one-var": [0],
|
||||
"one-var-declaration-per-line": [2, "initializations"],
|
||||
"operator-assignment": [0, "always"],
|
||||
"padded-blocks": [0],
|
||||
"prefer-arrow-callback": [0],
|
||||
"prefer-const": [0],
|
||||
"prefer-reflect": [0],
|
||||
"prefer-rest-params": [0],
|
||||
"prefer-spread": [0],
|
||||
"prefer-template": [0],
|
||||
"quote-props": [2, "as-needed", { "keywords": true }],
|
||||
"radix": [2],
|
||||
"require-yield": [2],
|
||||
"semi": [2],
|
||||
"semi-spacing": [2, { "before": false, "after": true }],
|
||||
"sort-vars": [0],
|
||||
"sort-imports": [0],
|
||||
"space-before-function-paren": [2, { "anonymous": "always", "named": "never" }],
|
||||
"space-before-blocks": [2, { "functions": "always", "keywords": "always" }],
|
||||
"space-in-brackets": [0, "never", {
|
||||
"singleValue": true,
|
||||
"arraysInArrays": false,
|
||||
"arraysInObjects": false,
|
||||
"objectsInArrays": true,
|
||||
"objectsInObjects": true,
|
||||
"propertyName": false
|
||||
}],
|
||||
},
|
||||
// Temporarily not enforced
|
||||
"new-cap": [2], // @TODO more tricky for the moment
|
||||
"newline-per-chained-call": [2, { "ignoreChainWithDepth": 6 }],
|
||||
"no-param-reassign": [0], // turn on once default args supported
|
||||
"no-shadow": [2, { // @TODO more tricky for the moment with eg 'data'
|
||||
"builtinGlobals": false,
|
||||
"hoist": "functions",
|
||||
"allow": ["i", "d"]
|
||||
}],
|
||||
"space-in-parens": [2, "never"],
|
||||
"space-infix-ops": [2],
|
||||
"space-unary-ops": [2, { "words": true, "nonwords": false }],
|
||||
"spaced-comment": [2, "always", { "markers": ["!"] }],
|
||||
"spaced-line-comment": [0, "always"],
|
||||
"strict": [2, "global"],
|
||||
"template-curly-spacing": [2, "never"],
|
||||
"use-isnan": [2],
|
||||
"valid-jsdoc": [0],
|
||||
"valid-typeof": [2],
|
||||
"vars-on-top": [0],
|
||||
"wrap-iife": [2],
|
||||
"wrap-regex": [2],
|
||||
"yield-star-spacing": [2, { "before": false, "after": true }],
|
||||
"yoda": [2, "never", { "exceptRange": true, "onlyEquality": false }]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 5.7 KiB |
|
|
@ -1 +0,0 @@
|
|||
require('../stylesheets/less/index.less');
|
||||
|
|
@ -1,229 +0,0 @@
|
|||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
var px = require('./modules/panoramix.js');
|
||||
var d3 = require('d3');
|
||||
require('bootstrap');
|
||||
|
||||
var ace = require('brace');
|
||||
require('brace/mode/css');
|
||||
require('brace/theme/crimson_editor');
|
||||
|
||||
require('./panoramix-select2.js');
|
||||
require('../node_modules/gridster/dist/jquery.gridster.min.css');
|
||||
require('../node_modules/gridster/dist/jquery.gridster.min.js');
|
||||
|
||||
var Dashboard = function (dashboardData) {
|
||||
var dashboard = $.extend(dashboardData, {
|
||||
filters: {},
|
||||
init: function () {
|
||||
this.initDashboardView();
|
||||
var sliceObjects = [],
|
||||
dash = this;
|
||||
dashboard.slices.forEach(function (data) {
|
||||
var slice = px.Slice(data, dash);
|
||||
$("#slice_" + data.slice_id).find('a.refresh').click(function () {
|
||||
slice.render();
|
||||
});
|
||||
sliceObjects.push(slice);
|
||||
slice.render();
|
||||
});
|
||||
this.slices = sliceObjects;
|
||||
},
|
||||
setFilter: function (slice_id, col, vals) {
|
||||
this.addFilter(slice_id, col, vals, false);
|
||||
},
|
||||
addFilter: function (slice_id, col, vals, merge) {
|
||||
if (merge === undefined) {
|
||||
merge = true;
|
||||
}
|
||||
if (!(slice_id in this.filters)) {
|
||||
this.filters[slice_id] = {};
|
||||
}
|
||||
if (!(col in this.filters[slice_id]) || !merge) {
|
||||
this.filters[slice_id][col] = vals;
|
||||
} else {
|
||||
this.filters[slice_id][col] = d3.merge([this.filters[slice_id][col], vals]);
|
||||
}
|
||||
this.refreshExcept(slice_id);
|
||||
},
|
||||
readFilters: function () {
|
||||
// Returns a list of human readable active filters
|
||||
return JSON.stringify(this.filters, null, 4);
|
||||
},
|
||||
refreshExcept: function (slice_id) {
|
||||
var immune = this.metadata.filter_immune_slices;
|
||||
if (immune) {
|
||||
this.slices.forEach(function (slice) {
|
||||
if (slice.data.slice_id !== slice_id && immune.indexOf(slice.data.slice_id) === -1) {
|
||||
slice.render();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
clearFilters: function (slice_id) {
|
||||
delete this.filters[slice_id];
|
||||
this.refreshExcept(slice_id);
|
||||
},
|
||||
removeFilter: function (slice_id, col, vals) {
|
||||
if (slice_id in this.filters) {
|
||||
if (col in this.filters[slice_id]) {
|
||||
var a = [];
|
||||
this.filters[slice_id][col].forEach(function (v) {
|
||||
if (vals.indexOf(v) < 0) {
|
||||
a.push(v);
|
||||
}
|
||||
});
|
||||
this.filters[slice_id][col] = a;
|
||||
}
|
||||
}
|
||||
this.refreshExcept(slice_id);
|
||||
},
|
||||
getSlice: function (slice_id) {
|
||||
this.slices.forEach(function (slice, i) {
|
||||
if (slice.slice_id === slice_id) {
|
||||
return slice;
|
||||
}
|
||||
});
|
||||
},
|
||||
initDashboardView: function () {
|
||||
dashboard = this;
|
||||
var gridster = $(".gridster ul").gridster({
|
||||
autogrow_cols: true,
|
||||
widget_margins: [10, 10],
|
||||
widget_base_dimensions: [100, 100],
|
||||
draggable: {
|
||||
handle: '.drag'
|
||||
},
|
||||
resize: {
|
||||
enabled: true,
|
||||
stop: function (e, ui, element) {
|
||||
var slice_data = $(element).data('slice');
|
||||
if (slice_data) {
|
||||
dashboard.getSlice(slice_data.slice_id).resize();
|
||||
}
|
||||
}
|
||||
},
|
||||
serialize_params: function (_w, wgd) {
|
||||
return {
|
||||
slice_id: $(_w).attr('slice_id'),
|
||||
col: wgd.col,
|
||||
row: wgd.row,
|
||||
size_x: wgd.size_x,
|
||||
size_y: wgd.size_y
|
||||
};
|
||||
}
|
||||
}).data('gridster');
|
||||
$("div.gridster").css('visibility', 'visible');
|
||||
$("#savedash").click(function () {
|
||||
var expanded_slices = {};
|
||||
$.each($(".slice_info"), function (i, d) {
|
||||
var widget = $(this).parents('.widget');
|
||||
var slice_description = widget.find('.slice_description');
|
||||
if (slice_description.is(":visible")) {
|
||||
expanded_slices[$(d).attr('slice_id')] = true;
|
||||
}
|
||||
});
|
||||
var data = {
|
||||
positions: gridster.serialize(),
|
||||
css: editor.getValue(),
|
||||
expanded_slices: expanded_slices
|
||||
};
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '/panoramix/save_dash/' + dashboard.id + '/',
|
||||
data: {
|
||||
data: JSON.stringify(data)
|
||||
},
|
||||
success: function () {
|
||||
alert("Saved!");
|
||||
},
|
||||
error: function () {
|
||||
alert("Error :(");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var editor = ace.edit("dash_css");
|
||||
editor.$blockScrolling = Infinity;
|
||||
|
||||
editor.setTheme("ace/theme/crimson_editor");
|
||||
editor.setOptions({
|
||||
minLines: 16,
|
||||
maxLines: Infinity,
|
||||
useWorker: false
|
||||
});
|
||||
editor.getSession().setMode("ace/mode/css");
|
||||
|
||||
$(".select2").select2({
|
||||
dropdownAutoWidth: true
|
||||
});
|
||||
$("#css_template").on("change", function () {
|
||||
var css = $(this).find('option:selected').data('css');
|
||||
editor.setValue(css);
|
||||
|
||||
$('#dash_css').val(css);
|
||||
injectCss("dashboard-template", css);
|
||||
|
||||
});
|
||||
$('#filters').click(function () {
|
||||
alert(dashboard.readFilters());
|
||||
});
|
||||
$("a.remove-chart").click(function () {
|
||||
var li = $(this).parents("li");
|
||||
gridster.remove_widget(li);
|
||||
});
|
||||
|
||||
$("li.widget").click(function (e) {
|
||||
var $this = $(this);
|
||||
var $target = $(e.target);
|
||||
|
||||
if ($target.hasClass("slice_info")) {
|
||||
$this.find(".slice_description").slideToggle(0, function () {
|
||||
$this.find('.refresh').click();
|
||||
});
|
||||
} else if ($target.hasClass("controls-toggle")) {
|
||||
$this.find(".chart-controls").toggle();
|
||||
}
|
||||
});
|
||||
|
||||
editor.on("change", function () {
|
||||
var css = editor.getValue();
|
||||
$('#dash_css').val(css);
|
||||
injectCss("dashboard-template", css);
|
||||
});
|
||||
|
||||
var css = $('.dashboard').data('css');
|
||||
injectCss("dashboard-template", css);
|
||||
|
||||
// Injects the passed css string into a style sheet with the specified className
|
||||
// If a stylesheet doesn't exist with the passed className, one will be injected into <head>
|
||||
function injectCss(className, css) {
|
||||
|
||||
var head = document.head || document.getElementsByTagName('head')[0];
|
||||
var style = document.querySelector('.' + className);
|
||||
|
||||
if (!style) {
|
||||
if (className.split(' ').length > 1) {
|
||||
throw new Error("This method only supports selections with a single class name.");
|
||||
}
|
||||
style = document.createElement('style');
|
||||
style.className = className;
|
||||
style.type = 'text/css';
|
||||
head.appendChild(style);
|
||||
}
|
||||
|
||||
if (style.styleSheet) {
|
||||
style.styleSheet.cssText = css;
|
||||
} else {
|
||||
style.innerHTML = css;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
dashboard.init();
|
||||
return dashboard;
|
||||
};
|
||||
|
||||
$(document).ready(function () {
|
||||
Dashboard($('.dashboard').data('dashboard'));
|
||||
});
|
||||
|
|
@ -1,334 +0,0 @@
|
|||
// Javascript for the explorer page
|
||||
// Init explorer view -> load vis dependencies -> read data (from dynamic html) -> render slice
|
||||
// nb: to add a new vis, you must also add a Python fn in viz.py
|
||||
//
|
||||
// js
|
||||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
var px = require('./modules/panoramix.js');
|
||||
|
||||
require('jquery-ui');
|
||||
$.widget.bridge('uitooltip', $.ui.tooltip); // Shutting down jq-ui tooltips
|
||||
require('bootstrap');
|
||||
|
||||
require('./panoramix-select2.js');
|
||||
|
||||
require('../node_modules/bootstrap-toggle/js/bootstrap-toggle.min.js');
|
||||
|
||||
// css
|
||||
require('../vendor/pygments.css');
|
||||
require('../node_modules/bootstrap-toggle/css/bootstrap-toggle.min.css');
|
||||
|
||||
var slice;
|
||||
|
||||
function prepForm() {
|
||||
var i = 1;
|
||||
// Assigning the right id to form elements in filters
|
||||
$("#filters > div").each(function () {
|
||||
$(this).attr("id", function () {
|
||||
return "flt_" + i;
|
||||
});
|
||||
$(this).find("#flt_col_0")
|
||||
.attr("id", function () {
|
||||
return "flt_col_" + i;
|
||||
})
|
||||
.attr("name", function () {
|
||||
return "flt_col_" + i;
|
||||
});
|
||||
$(this).find("#flt_op_0")
|
||||
.attr("id", function () {
|
||||
return "flt_op_" + i;
|
||||
})
|
||||
.attr("name", function () {
|
||||
return "flt_op_" + i;
|
||||
});
|
||||
$(this).find("#flt_eq_0")
|
||||
.attr("id", function () {
|
||||
return "flt_eq_" + i;
|
||||
})
|
||||
.attr("name", function () {
|
||||
return "flt_eq_" + i;
|
||||
});
|
||||
i++;
|
||||
});
|
||||
}
|
||||
|
||||
function renderSlice() {
|
||||
prepForm();
|
||||
slice.render();
|
||||
}
|
||||
|
||||
function initExploreView() {
|
||||
|
||||
function druidify() {
|
||||
$('div.alert').remove();
|
||||
history.pushState({}, document.title, slice.querystring());
|
||||
renderSlice();
|
||||
}
|
||||
|
||||
function get_collapsed_fieldsets() {
|
||||
var collapsed_fieldsets = $("#collapsed_fieldsets").val();
|
||||
|
||||
if (collapsed_fieldsets !== undefined && collapsed_fieldsets !== "") {
|
||||
collapsed_fieldsets = collapsed_fieldsets.split('||');
|
||||
} else {
|
||||
collapsed_fieldsets = [];
|
||||
}
|
||||
return collapsed_fieldsets;
|
||||
}
|
||||
|
||||
function toggle_fieldset(legend, animation) {
|
||||
var parent = legend.parent();
|
||||
var fieldset = parent.find(".legend_label").text();
|
||||
var collapsed_fieldsets = get_collapsed_fieldsets();
|
||||
var index;
|
||||
|
||||
if (parent.hasClass("collapsed")) {
|
||||
if (animation) {
|
||||
parent.find(".fieldset_content").slideDown();
|
||||
} else {
|
||||
parent.find(".fieldset_content").show();
|
||||
}
|
||||
parent.removeClass("collapsed");
|
||||
parent.find("span.collapser").text("[-]");
|
||||
|
||||
// removing from array, js is overcomplicated
|
||||
index = collapsed_fieldsets.indexOf(fieldset);
|
||||
if (index !== -1) {
|
||||
collapsed_fieldsets.splice(index, 1);
|
||||
}
|
||||
} else { // not collapsed
|
||||
if (animation) {
|
||||
parent.find(".fieldset_content").slideUp();
|
||||
} else {
|
||||
parent.find(".fieldset_content").hide();
|
||||
}
|
||||
|
||||
parent.addClass("collapsed");
|
||||
parent.find("span.collapser").text("[+]");
|
||||
index = collapsed_fieldsets.indexOf(fieldset);
|
||||
if (index === -1 && fieldset !== "" && fieldset !== undefined) {
|
||||
collapsed_fieldsets.push(fieldset);
|
||||
}
|
||||
}
|
||||
|
||||
$("#collapsed_fieldsets").val(collapsed_fieldsets.join("||"));
|
||||
}
|
||||
|
||||
$('legend').click(function () {
|
||||
toggle_fieldset($(this), true);
|
||||
});
|
||||
|
||||
function copyURLToClipboard(url) {
|
||||
var textArea = document.createElement("textarea");
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.left = '-1000px';
|
||||
textArea.value = url;
|
||||
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
var successful = document.execCommand('copy');
|
||||
if (!successful) {
|
||||
throw new Error("Not successful");
|
||||
}
|
||||
} catch (err) {
|
||||
window.alert("Sorry, your browser does not support copying. Use Ctrl / Cmd + C!");
|
||||
}
|
||||
document.body.removeChild(textArea);
|
||||
return successful;
|
||||
}
|
||||
|
||||
$('#shortner').click(function () {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '/r/shortner/',
|
||||
data: {
|
||||
data: '/' + window.location.pathname + slice.querystring()
|
||||
},
|
||||
success: function (data) {
|
||||
var close = '<a style="cursor: pointer;"><i class="fa fa-close" id="close_shortner"></i></a>';
|
||||
var copy = '<a style="cursor: pointer;"><i class="fa fa-clipboard" title="Copy to clipboard" id="copy_url"></i></a>';
|
||||
var spaces = ' ';
|
||||
var popover = data + spaces + copy + spaces + close;
|
||||
|
||||
var $shortner = $('#shortner')
|
||||
.popover({
|
||||
content: popover,
|
||||
placement: 'left',
|
||||
html: true,
|
||||
trigger: 'manual'
|
||||
})
|
||||
.popover('show');
|
||||
|
||||
$('#copy_url').tooltip().click(function () {
|
||||
var success = copyURLToClipboard(data);
|
||||
if (success) {
|
||||
$(this).attr("data-original-title", "Copied!").tooltip('fixTitle').tooltip('show');
|
||||
window.setTimeout(destroyPopover, 1200);
|
||||
}
|
||||
});
|
||||
$('#close_shortner').click(destroyPopover);
|
||||
|
||||
function destroyPopover() {
|
||||
$shortner.popover('destroy');
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
alert("Error :(");
|
||||
}
|
||||
});
|
||||
});
|
||||
$("#viz_type").change(function () {
|
||||
$("#query").submit();
|
||||
});
|
||||
|
||||
var collapsed_fieldsets = get_collapsed_fieldsets();
|
||||
for (var i = 0; i < collapsed_fieldsets.length; i++) {
|
||||
toggle_fieldset($('legend:contains("' + collapsed_fieldsets[i] + '")'), false);
|
||||
}
|
||||
|
||||
$(".select2").select2({
|
||||
dropdownAutoWidth: true
|
||||
});
|
||||
$(".select2Sortable").select2({
|
||||
dropdownAutoWidth: true
|
||||
});
|
||||
$(".select2Sortable").select2Sortable({
|
||||
bindOrder: 'sortableStop'
|
||||
});
|
||||
$("form").show();
|
||||
$('[data-toggle="tooltip"]').tooltip({
|
||||
container: 'body'
|
||||
});
|
||||
$(".ui-helper-hidden-accessible").remove(); // jQuery-ui 1.11+ creates a div for every tooltip
|
||||
|
||||
function set_filters() {
|
||||
for (var i = 1; i < 10; i++) {
|
||||
var eq = px.getParam("flt_eq_" + i);
|
||||
if (eq !== '') {
|
||||
add_filter(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
set_filters();
|
||||
|
||||
function add_filter(i) {
|
||||
var cp = $("#flt0").clone();
|
||||
$(cp).appendTo("#filters");
|
||||
$(cp).show();
|
||||
if (i !== undefined) {
|
||||
$(cp).find("#flt_eq_0").val(px.getParam("flt_eq_" + i));
|
||||
$(cp).find("#flt_op_0").val(px.getParam("flt_op_" + i));
|
||||
$(cp).find("#flt_col_0").val(px.getParam("flt_col_" + i));
|
||||
}
|
||||
$(cp).find('select').select2();
|
||||
$(cp).find('.remove').click(function () {
|
||||
$(this).parent().parent().remove();
|
||||
});
|
||||
}
|
||||
|
||||
$(window).bind("popstate", function (event) {
|
||||
// Browser back button
|
||||
var returnLocation = history.location || document.location;
|
||||
// Could do something more lightweight here, but we're not optimizing
|
||||
// for the use of the back button anyways
|
||||
returnLocation.reload();
|
||||
});
|
||||
|
||||
$("#plus").click(add_filter);
|
||||
$("#btn_save").click(function () {
|
||||
var slice_name = prompt("Name your slice!");
|
||||
if (slice_name !== "" && slice_name !== null) {
|
||||
$("#slice_name").val(slice_name);
|
||||
prepForm();
|
||||
$("#action").val("save");
|
||||
$("#query").submit();
|
||||
}
|
||||
});
|
||||
$("#btn_overwrite").click(function () {
|
||||
var flag = confirm("Overwrite slice [" + $("#slice_name").val() + "] !?");
|
||||
if (flag) {
|
||||
$("#action").val("overwrite");
|
||||
prepForm();
|
||||
$("#query").submit();
|
||||
}
|
||||
});
|
||||
|
||||
$(".druidify").click(druidify);
|
||||
|
||||
function create_choices(term, data) {
|
||||
var filtered = $(data).filter(function () {
|
||||
return this.text.localeCompare(term) === 0;
|
||||
});
|
||||
if (filtered.length === 0) {
|
||||
return {
|
||||
id: term,
|
||||
text: term
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function initSelectionToValue(element, callback) {
|
||||
callback({
|
||||
id: element.val(),
|
||||
text: element.val()
|
||||
});
|
||||
}
|
||||
|
||||
$(".select2_freeform").each(function () {
|
||||
var parent = $(this).parent();
|
||||
var name = $(this).attr('name');
|
||||
var l = [];
|
||||
var selected = '';
|
||||
for (var i = 0; i < this.options.length; i++) {
|
||||
l.push({
|
||||
id: this.options[i].value,
|
||||
text: this.options[i].text
|
||||
});
|
||||
if (this.options[i].selected) {
|
||||
selected = this.options[i].value;
|
||||
}
|
||||
}
|
||||
parent.append(
|
||||
'<input class="' + $(this).attr('class') + '" name="' + name + '" type="text" value="' + selected + '">'
|
||||
);
|
||||
$("input[name='" + name + "']").select2({
|
||||
createSearchChoice: create_choices,
|
||||
initSelection: initSelectionToValue,
|
||||
dropdownAutoWidth: true,
|
||||
multiple: false,
|
||||
data: l
|
||||
});
|
||||
$(this).remove();
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
initExploreView();
|
||||
|
||||
// Dynamically register this visualization
|
||||
var visType = window.viz_type.value;
|
||||
px.registerViz(visType);
|
||||
|
||||
var data = $('.slice').data('slice');
|
||||
slice = px.Slice(data);
|
||||
|
||||
//
|
||||
$('.slice').data('slice', slice);
|
||||
|
||||
// call vis render method, which issues ajax
|
||||
renderSlice();
|
||||
|
||||
// make checkbox inputs display as toggles
|
||||
$(':checkbox')
|
||||
.addClass('pull-right')
|
||||
.attr("data-onstyle", "default")
|
||||
.bootstrapToggle({
|
||||
size: 'mini'
|
||||
});
|
||||
|
||||
$('div.toggle').addClass('pull-right');
|
||||
slice.bindResizeToWindowResize();
|
||||
});
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
var px = require('./modules/panoramix.js');
|
||||
|
||||
require('bootstrap');
|
||||
require('datatables');
|
||||
require('../node_modules/datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css');
|
||||
|
||||
$(document).ready(function () {
|
||||
$('#dataset-table').DataTable({
|
||||
bPaginate: false,
|
||||
order: [
|
||||
[1, "asc"]
|
||||
]
|
||||
});
|
||||
$('#dataset-table_info').remove();
|
||||
//$('input[type=search]').addClass('form-control'); # TODO get search box to look nice
|
||||
$('#dataset-table').show();
|
||||
});
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
var $ = require('jquery');
|
||||
var jQuery = $;
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { Jumbotron } from 'react-bootstrap';
|
||||
|
||||
class App extends React.Component {
|
||||
render () {
|
||||
return (
|
||||
<Jumbotron>
|
||||
<h1>Panoramix</h1>
|
||||
<p>Extensible visualization tool for exploring data from any database.</p>
|
||||
</Jumbotron>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render(<App />, document.getElementById('app'));
|
||||
|
|
@ -1,300 +0,0 @@
|
|||
var $ = require('jquery');
|
||||
var jQuery = $;
|
||||
var d3 = require('d3');
|
||||
|
||||
require('../../stylesheets/panoramix.css');
|
||||
|
||||
// vis sources
|
||||
var sourceMap = {
|
||||
area: 'nvd3_vis.js',
|
||||
bar: 'nvd3_vis.js',
|
||||
bubble: 'nvd3_vis.js',
|
||||
big_number: 'big_number.js',
|
||||
compare: 'nvd3_vis.js',
|
||||
dist_bar: 'nvd3_vis.js',
|
||||
directed_force: 'directed_force.js',
|
||||
filter_box: 'filter_box.js',
|
||||
heatmap: 'heatmap.js',
|
||||
iframe: 'iframe.js',
|
||||
line: 'nvd3_vis.js',
|
||||
markup: 'markup.js',
|
||||
para: 'parallel_coordinates.js',
|
||||
pie: 'nvd3_vis.js',
|
||||
pivot_table: 'pivot_table.js',
|
||||
sankey: 'sankey.js',
|
||||
sunburst: 'sunburst.js',
|
||||
table: 'table.js',
|
||||
word_cloud: 'word_cloud.js',
|
||||
world_map: 'world_map.js'
|
||||
};
|
||||
|
||||
var color = function () {
|
||||
// Color related utility functions go in this object
|
||||
var bnbColors = [
|
||||
//rausch hackb kazan babu lima beach barol
|
||||
'#ff5a5f', '#7b0051', '#007A87', '#00d1c1', '#8ce071', '#ffb400', '#b4a76c',
|
||||
'#ff8083', '#cc0086', '#00a1b3', '#00ffeb', '#bbedab', '#ffd266', '#cbc29a',
|
||||
'#ff3339', '#ff1ab1', '#005c66', '#00b3a5', '#55d12e', '#b37e00', '#988b4e'
|
||||
];
|
||||
var spectrums = {
|
||||
blue_white_yellow: ['#00d1c1', 'white', '#ffb400'],
|
||||
fire: ['white', 'yellow', 'red', 'black'],
|
||||
white_black: ['white', 'black'],
|
||||
black_white: ['black', 'white']
|
||||
};
|
||||
var colorBnb = function () {
|
||||
// Color factory
|
||||
var seen = {};
|
||||
return function (s) {
|
||||
// next line is for dashed series that should have the same color
|
||||
s = s.replace('---', '');
|
||||
if (seen[s] === undefined) {
|
||||
seen[s] = Object.keys(seen).length;
|
||||
}
|
||||
return this.bnbColors[seen[s] % this.bnbColors.length];
|
||||
};
|
||||
};
|
||||
var colorScalerFactory = function (colors, data, accessor) {
|
||||
// Returns a linear scaler our of an array of color
|
||||
if (!Array.isArray(colors)) {
|
||||
colors = spectrums[colors];
|
||||
}
|
||||
|
||||
var ext = [0, 1];
|
||||
if (data !== undefined) {
|
||||
ext = d3.extent(data, accessor);
|
||||
}
|
||||
|
||||
var points = [];
|
||||
var chunkSize = (ext[1] - ext[0]) / colors.length;
|
||||
$.each(colors, function (i, c) {
|
||||
points.push(i * chunkSize);
|
||||
});
|
||||
return d3.scale.linear().domain(points).range(colors);
|
||||
};
|
||||
return {
|
||||
bnbColors: bnbColors,
|
||||
category21: colorBnb(),
|
||||
colorScalerFactory: colorScalerFactory
|
||||
};
|
||||
};
|
||||
|
||||
var px = (function () {
|
||||
|
||||
var visualizations = {};
|
||||
var slice;
|
||||
|
||||
function getParam(name) {
|
||||
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
|
||||
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
|
||||
results = regex.exec(location.search);
|
||||
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
|
||||
}
|
||||
|
||||
function UTC(dttm) {
|
||||
return new Date(dttm.getUTCFullYear(), dttm.getUTCMonth(), dttm.getUTCDate(), dttm.getUTCHours(), dttm.getUTCMinutes(), dttm.getUTCSeconds());
|
||||
}
|
||||
var tickMultiFormat = d3.time.format.multi([
|
||||
[".%L", function (d) {
|
||||
return d.getMilliseconds();
|
||||
}], // If there are millisections, show only them
|
||||
[":%S", function (d) {
|
||||
return d.getSeconds();
|
||||
}], // If there are seconds, show only them
|
||||
["%a %b %d, %I:%M %p", function (d) {
|
||||
return d.getMinutes() !== 0;
|
||||
}], // If there are non-zero minutes, show Date, Hour:Minute [AM/PM]
|
||||
["%a %b %d, %I %p", function (d) {
|
||||
return d.getHours() !== 0;
|
||||
}], // If there are hours that are multiples of 3, show date and AM/PM
|
||||
["%a %b %d, %Y", function (d) {
|
||||
return d.getDate() !== 1;
|
||||
}], // If not the first of the month, do "month day, year."
|
||||
["%B %Y", function (d) {
|
||||
return d.getMonth() !== 0 && d.getDate() === 1;
|
||||
}], // If the first of the month, do "month day, year."
|
||||
["%Y", function (d) {
|
||||
return true;
|
||||
}] // fall back on month, year
|
||||
]);
|
||||
|
||||
function formatDate(dttm) {
|
||||
var d = UTC(new Date(dttm));
|
||||
//d = new Date(d.getTime() - 1 * 60 * 60 * 1000);
|
||||
return tickMultiFormat(d);
|
||||
}
|
||||
|
||||
function timeFormatFactory(d3timeFormat) {
|
||||
var f = d3.time.format(d3timeFormat);
|
||||
return function (dttm) {
|
||||
var d = UTC(new Date(dttm));
|
||||
return f(d);
|
||||
};
|
||||
}
|
||||
|
||||
var Slice = function (data, dashboard) {
|
||||
var timer;
|
||||
var token = $('#' + data.token);
|
||||
var container_id = data.token + '_con';
|
||||
var selector = '#' + container_id;
|
||||
var container = $(selector);
|
||||
var slice_id = data.slice_id;
|
||||
var dttm = 0;
|
||||
var stopwatch = function () {
|
||||
dttm += 10;
|
||||
var num = dttm / 1000;
|
||||
$('#timer').text(num.toFixed(2) + " sec");
|
||||
};
|
||||
var qrystr = '';
|
||||
var always = function (data) {
|
||||
//Private f, runs after done and error
|
||||
clearInterval(timer);
|
||||
$('#timer').removeClass('btn-warning');
|
||||
};
|
||||
slice = {
|
||||
data: data,
|
||||
container: container,
|
||||
container_id: container_id,
|
||||
selector: selector,
|
||||
querystring: function () {
|
||||
var parser = document.createElement('a');
|
||||
parser.href = data.json_endpoint;
|
||||
if (dashboard !== undefined) {
|
||||
var flts = encodeURIComponent(JSON.stringify(dashboard.filters));
|
||||
qrystr = parser.search + "&extra_filters=" + flts;
|
||||
} else if ($('#query').length === 0) {
|
||||
qrystr = parser.search;
|
||||
} else {
|
||||
qrystr = '?' + $('#query').serialize();
|
||||
}
|
||||
return qrystr;
|
||||
},
|
||||
jsonEndpoint: function () {
|
||||
var parser = document.createElement('a');
|
||||
parser.href = data.json_endpoint;
|
||||
var endpoint = parser.pathname + this.querystring() + "&json=true";
|
||||
return endpoint;
|
||||
},
|
||||
done: function (data) {
|
||||
clearInterval(timer);
|
||||
token.find("img.loading").hide();
|
||||
container.show();
|
||||
if (data !== undefined) {
|
||||
$("#query_container").html(data.query);
|
||||
}
|
||||
$('#timer').removeClass('btn-warning');
|
||||
$('#timer').addClass('btn-success');
|
||||
$('span.query').removeClass('disabled');
|
||||
$('#json').click(function () {
|
||||
window.location = data.json_endpoint;
|
||||
});
|
||||
$('#standalone').click(function () {
|
||||
window.location = data.standalone_endpoint;
|
||||
});
|
||||
$('#csv').click(function () {
|
||||
window.location = data.csv_endpoint;
|
||||
});
|
||||
$('.btn-group.results span').removeAttr('disabled');
|
||||
always(data);
|
||||
},
|
||||
error: function (msg) {
|
||||
token.find("img.loading").hide();
|
||||
var err = '<div class="alert alert-danger">' + msg + '</div>';
|
||||
container.html(err);
|
||||
container.show();
|
||||
$('span.query').removeClass('disabled');
|
||||
$('#timer').addClass('btn-danger');
|
||||
always(data);
|
||||
},
|
||||
width: function () {
|
||||
return token.width();
|
||||
},
|
||||
height: function () {
|
||||
var others = 0;
|
||||
var widget = container.parents('.widget');
|
||||
var slice_description = widget.find('.slice_description');
|
||||
if (slice_description.is(":visible")) {
|
||||
others += widget.find('.slice_description').height() + 25;
|
||||
}
|
||||
others += widget.find('.chart-header').height();
|
||||
return widget.height() - others - 10;
|
||||
},
|
||||
bindResizeToWindowResize: function () {
|
||||
var resizeTimer;
|
||||
$(window).on('resize', function (e) {
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(function () {
|
||||
slice.resize();
|
||||
}, 500);
|
||||
});
|
||||
},
|
||||
render: function () {
|
||||
$('.btn-group.results span').attr('disabled', 'disabled');
|
||||
token.find("img.loading").show();
|
||||
container.hide();
|
||||
container.html('');
|
||||
dttm = 0;
|
||||
timer = setInterval(stopwatch, 10);
|
||||
$('#timer').removeClass('btn-danger btn-success');
|
||||
$('#timer').addClass('btn-warning');
|
||||
this.viz.render();
|
||||
},
|
||||
resize: function () {
|
||||
token.find("img.loading").show();
|
||||
container.hide();
|
||||
container.html('');
|
||||
this.viz.render();
|
||||
this.viz.resize();
|
||||
},
|
||||
addFilter: function (col, vals) {
|
||||
if (dashboard !== undefined) {
|
||||
dashboard.addFilter(slice_id, col, vals);
|
||||
}
|
||||
},
|
||||
setFilter: function (col, vals) {
|
||||
if (dashboard !== undefined) {
|
||||
dashboard.setFilter(slice_id, col, vals);
|
||||
}
|
||||
},
|
||||
clearFilter: function () {
|
||||
if (dashboard !== undefined) {
|
||||
delete dashboard.clearFilter(slice_id);
|
||||
}
|
||||
},
|
||||
removeFilter: function (col, vals) {
|
||||
if (dashboard !== undefined) {
|
||||
delete dashboard.removeFilter(slice_id, col, vals);
|
||||
}
|
||||
}
|
||||
};
|
||||
var visType = data.form_data.viz_type;
|
||||
px.registerViz(visType);
|
||||
slice.viz = visualizations[data.form_data.viz_type](slice);
|
||||
return slice;
|
||||
};
|
||||
|
||||
function registerViz(name) {
|
||||
var visSource = sourceMap[name];
|
||||
|
||||
if (visSource) {
|
||||
var visFactory = require('../../visualizations/' + visSource);
|
||||
if (typeof visFactory === 'function') {
|
||||
visualizations[name] = visFactory;
|
||||
}
|
||||
} else {
|
||||
throw new Error("require(" + name + ") failed.");
|
||||
}
|
||||
}
|
||||
|
||||
// Export public functions
|
||||
return {
|
||||
registerViz: registerViz,
|
||||
Slice: Slice,
|
||||
formatDate: formatDate,
|
||||
timeFormatFactory: timeFormatFactory,
|
||||
color: color(),
|
||||
getParam: getParam
|
||||
};
|
||||
})();
|
||||
|
||||
module.exports = px;
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
var d3 = require('d3');
|
||||
|
||||
/*
|
||||
Utility function that takes a d3 svg:text selection and a max width, and splits the
|
||||
text's text across multiple tspan lines such that any given line does not exceed max width
|
||||
|
||||
If text does not span multiple lines AND adjustedY is passed, will set the text to the passed val
|
||||
*/
|
||||
function wrapSvgText(text, width, adjustedY) {
|
||||
var lineHeight = 1; // ems
|
||||
|
||||
text.each(function () {
|
||||
var text = d3.select(this),
|
||||
words = text.text().split(/\s+/),
|
||||
word,
|
||||
line = [],
|
||||
lineNumber = 0,
|
||||
x = text.attr("x"),
|
||||
y = text.attr("y"),
|
||||
dy = parseFloat(text.attr("dy")),
|
||||
tspan = text.text(null)
|
||||
.append("tspan")
|
||||
.attr("x", x)
|
||||
.attr("y", y)
|
||||
.attr("dy", dy + "em");
|
||||
|
||||
var didWrap = false;
|
||||
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
word = words[i];
|
||||
line.push(word);
|
||||
tspan.text(line.join(" "));
|
||||
|
||||
if (tspan.node().getComputedTextLength() > width) {
|
||||
line.pop(); // remove word that pushes over the limit
|
||||
tspan.text(line.join(" "));
|
||||
line = [word];
|
||||
tspan = text.append("tspan")
|
||||
.attr("x", x)
|
||||
.attr("y", y)
|
||||
.attr("dy", ++lineNumber * lineHeight + dy + "em")
|
||||
.text(word);
|
||||
|
||||
didWrap = true;
|
||||
}
|
||||
}
|
||||
if (!didWrap && typeof adjustedY !== "undefined") {
|
||||
tspan.attr("y", adjustedY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
wrapSvgText: wrapSvgText
|
||||
};
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
require('../node_modules/select2/select2.css');
|
||||
require('../node_modules/select2-bootstrap-css/select2-bootstrap.min.css');
|
||||
require('../node_modules/jquery-ui/themes/base/jquery-ui.css');
|
||||
require('select2');
|
||||
require('../vendor/select2.sortable.js');
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
require('select2');
|
||||
require('datatables');
|
||||
require('bootstrap');
|
||||
|
||||
var ace = require('brace');
|
||||
require('brace/mode/sql');
|
||||
require('brace/theme/crimson_editor');
|
||||
|
||||
$(document).ready(function () {
|
||||
function getParam(name) {
|
||||
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
|
||||
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
|
||||
results = regex.exec(location.search);
|
||||
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
|
||||
}
|
||||
|
||||
function initSqlEditorView() {
|
||||
var database_id = $('#database_id').val();
|
||||
var editor = ace.edit("sql");
|
||||
editor.$blockScrolling = Infinity;
|
||||
editor.getSession().setUseWrapMode(true);
|
||||
|
||||
$('#sql').hide();
|
||||
editor.setTheme("ace/theme/crimson_editor");
|
||||
editor.setOptions({
|
||||
minLines: 16,
|
||||
maxLines: Infinity
|
||||
});
|
||||
editor.getSession().setMode("ace/mode/sql");
|
||||
editor.focus();
|
||||
$("select").select2({
|
||||
dropdownAutoWidth: true
|
||||
});
|
||||
|
||||
function showTableMetadata() {
|
||||
$(".metadata").load(
|
||||
'/panoramix/table/' + database_id + '/' + $("#dbtable").val() + '/');
|
||||
}
|
||||
$("#dbtable").on("change", showTableMetadata);
|
||||
showTableMetadata();
|
||||
$("#create_view").click(function () {
|
||||
alert("Not implemented");
|
||||
});
|
||||
$(".sqlcontent").show();
|
||||
|
||||
function selectStarOnClick() {
|
||||
$.ajax('/panoramix/select_star/' + database_id + '/' + $("#dbtable").val() + '/')
|
||||
.done(function (msg) {
|
||||
editor.setValue(msg);
|
||||
});
|
||||
}
|
||||
|
||||
$("#select_star").click(selectStarOnClick);
|
||||
|
||||
editor.setValue(getParam('sql'));
|
||||
$(window).bind("popstate", function (event) {
|
||||
// Could do something more lightweight here, but we're not optimizing
|
||||
// for the use of the back button anyways
|
||||
editor.setValue(getParam('sql'));
|
||||
$("#run").click();
|
||||
});
|
||||
$("#run").click(function () {
|
||||
$('#results').hide(0);
|
||||
$('#loading').show(0);
|
||||
history.pushState({}, document.title, '?sql=' + encodeURIComponent(editor.getValue()));
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '/panoramix/runsql/',
|
||||
data: {
|
||||
data: JSON.stringify({
|
||||
database_id: $('#database_id').val(),
|
||||
sql: editor.getSession().getValue()
|
||||
})
|
||||
},
|
||||
success: function (data) {
|
||||
$('#loading').hide(0);
|
||||
$('#results').show(0);
|
||||
$('#results').html(data);
|
||||
|
||||
$('table.sql_results').DataTable({
|
||||
paging: false,
|
||||
searching: true,
|
||||
aaSorting: []
|
||||
});
|
||||
},
|
||||
error: function (err, err2) {
|
||||
$('#loading').hide(0);
|
||||
$('#results').show(0);
|
||||
$('#results').html(err.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
initSqlEditorView();
|
||||
});
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
var px = require('./modules/panoramix.js');
|
||||
|
||||
require('bootstrap');
|
||||
|
||||
$(document).ready(function () {
|
||||
var slice;
|
||||
var data = $('.slice').data('slice');
|
||||
slice = px.Slice(data);
|
||||
slice.render();
|
||||
slice.bindResizeToWindowResize();
|
||||
});
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
{
|
||||
"name": "panoramix",
|
||||
"version": "0.1.0",
|
||||
"description": "Any database to any visualization",
|
||||
"directories": {
|
||||
"doc": "docs",
|
||||
"test": "tests"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "webpack -d --watch --colors",
|
||||
"prod": "webpack -p --colors",
|
||||
"lint": "npm run --silent lint:js",
|
||||
"lint:js": "eslint --ignore-path=.eslintignore --ext .js .; exit 0;"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/mistercrunch/panoramix.git"
|
||||
},
|
||||
"keywords": [
|
||||
"big",
|
||||
"data",
|
||||
"exploratory",
|
||||
"analysis",
|
||||
"react",
|
||||
"d3",
|
||||
"airbnb",
|
||||
"nerds",
|
||||
"database",
|
||||
"flask"
|
||||
],
|
||||
"author": "Airbnb",
|
||||
"bugs": {
|
||||
"url": "https://github.com/mistercrunch/panoramix/issues"
|
||||
},
|
||||
"homepage": "https://github.com/mistercrunch/panoramix#readme",
|
||||
"dependencies": {
|
||||
"babel-loader": "^6.2.1",
|
||||
"babel-polyfill": "^6.3.14",
|
||||
"babel-preset-es2015": "^6.3.13",
|
||||
"babel-preset-react": "^6.3.13",
|
||||
"bootstrap": "^3.3.6",
|
||||
"bootstrap-datepicker": "^1.6.0",
|
||||
"bootstrap-toggle": "^2.2.1",
|
||||
"brace": "^0.7.0",
|
||||
"css-loader": "^0.23.1",
|
||||
"d3": "^3.5.14",
|
||||
"d3-cloud": "^1.2.1",
|
||||
"d3-sankey": "^0.2.1",
|
||||
"d3-tip": "^0.6.7",
|
||||
"d3.layout.cloud": "^1.2.0",
|
||||
"datamaps": "^0.4.4",
|
||||
"datatables": "^1.10.9",
|
||||
"datatables-bootstrap3-plugin": "^0.4.0",
|
||||
"exports-loader": "^0.6.3",
|
||||
"font-awesome": "^4.5.0",
|
||||
"gridster": "^0.5.6",
|
||||
"imports-loader": "^0.6.5",
|
||||
"jquery": "^2.2.1",
|
||||
"jquery-ui": "^1.10.5",
|
||||
"less-loader": "^2.2.2",
|
||||
"nvd3": "1.8.2",
|
||||
"react": "^0.14.7",
|
||||
"react-bootstrap": "^0.28.3",
|
||||
"react-dom": "^0.14.7",
|
||||
"select2": "3.5",
|
||||
"select2-bootstrap-css": "^1.4.6",
|
||||
"style-loader": "^0.13.0",
|
||||
"topojson": "^1.6.22",
|
||||
"webpack": "^1.12.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^2.2.0",
|
||||
"file-loader": "^0.8.5",
|
||||
"url-loader": "^0.5.7"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,616 +0,0 @@
|
|||
// Paper 3.3.5
|
||||
// Bootswatch
|
||||
// -----------------------------------------------------
|
||||
|
||||
@web-font-path: "https://fonts.googleapis.com/css?family=Roboto:300,400,500,700";
|
||||
|
||||
.web-font(@path) {
|
||||
@import url("@{path}");
|
||||
}
|
||||
.web-font(@web-font-path);
|
||||
|
||||
// Navbar =====================================================================
|
||||
|
||||
.navbar {
|
||||
border: none;
|
||||
.box-shadow(0 1px 2px rgba(0,0,0,.3));
|
||||
|
||||
&-brand {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
&-inverse {
|
||||
.navbar-form {
|
||||
|
||||
input[type=text],
|
||||
input[type=password] {
|
||||
color: #fff;
|
||||
.box-shadow(inset 0 -1px 0 @navbar-inverse-link-color);
|
||||
.placeholder(@navbar-inverse-link-color);
|
||||
|
||||
&:focus {
|
||||
.box-shadow(inset 0 -2px 0 #fff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Buttons ====================================================================
|
||||
|
||||
#btn(@class,@bg) {
|
||||
.btn-@{class} {
|
||||
background-size: 200%;
|
||||
background-position: 50%;
|
||||
|
||||
&:focus {
|
||||
background-color: @bg;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active:hover {
|
||||
background-color: darken(@bg, 6%);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: darken(@bg, 12%);
|
||||
#gradient > .radial(darken(@bg, 12%) 10%, @bg 11%);
|
||||
background-size: 1000%;
|
||||
.box-shadow(2px 2px 4px rgba(0,0,0,.4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#btn(default,@btn-default-bg);
|
||||
#btn(primary,@btn-primary-bg);
|
||||
#btn(success,@btn-success-bg);
|
||||
#btn(info,@btn-info-bg);
|
||||
#btn(warning,@btn-warning-bg);
|
||||
#btn(danger,@btn-danger-bg);
|
||||
#btn(link,#fff);
|
||||
|
||||
.btn {
|
||||
text-transform: uppercase;
|
||||
border: none;
|
||||
.box-shadow(1px 1px 4px rgba(0,0,0,.4));
|
||||
.transition(all 0.4s);
|
||||
|
||||
&-link {
|
||||
border-radius: @btn-border-radius-base;
|
||||
.box-shadow(none);
|
||||
color: @btn-default-color;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
.box-shadow(none);
|
||||
color: @btn-default-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-default {
|
||||
|
||||
&.disabled {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
.btn + .btn,
|
||||
.btn + .btn-group,
|
||||
.btn-group + .btn,
|
||||
.btn-group + .btn-group {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&-vertical {
|
||||
> .btn + .btn,
|
||||
> .btn + .btn-group,
|
||||
> .btn-group + .btn,
|
||||
> .btn-group + .btn-group {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Typography =================================================================
|
||||
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
letter-spacing: .1px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 1em;
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
letter-spacing: .1px;
|
||||
}
|
||||
|
||||
a {
|
||||
.transition(all 0.2s);
|
||||
}
|
||||
|
||||
// Tables =====================================================================
|
||||
|
||||
.table-hover {
|
||||
> tbody > tr,
|
||||
> tbody > tr > th,
|
||||
> tbody > tr > td {
|
||||
.transition(all 0.2s);
|
||||
}
|
||||
}
|
||||
|
||||
// Forms ======================================================================
|
||||
|
||||
label {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
textarea,
|
||||
textarea.form-control,
|
||||
input.form-control,
|
||||
input[type=text],
|
||||
input[type=password],
|
||||
input[type=email],
|
||||
input[type=number],
|
||||
[type=text].form-control,
|
||||
[type=password].form-control,
|
||||
[type=email].form-control,
|
||||
[type=tel].form-control,
|
||||
[contenteditable].form-control {
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
-webkit-appearance: none;
|
||||
.box-shadow(inset 0 -1px 0 #ddd);
|
||||
font-size: 16px;
|
||||
|
||||
&:focus {
|
||||
.box-shadow(inset 0 -2px 0 @brand-primary);
|
||||
}
|
||||
|
||||
&[disabled],
|
||||
&[readonly] {
|
||||
.box-shadow(none);
|
||||
border-bottom: 1px dotted #ddd;
|
||||
}
|
||||
|
||||
&.input {
|
||||
&-sm {
|
||||
font-size: @font-size-small;
|
||||
}
|
||||
|
||||
&-lg {
|
||||
font-size: @font-size-large;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select,
|
||||
select.form-control {
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
padding-left: 0;
|
||||
padding-right: 0\9; // remove padding for < ie9 since default arrow can't be removed
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAAJ1BMVEVmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmaP/QSjAAAADHRSTlMAAgMJC0uWpKa6wMxMdjkoAAAANUlEQVR4AeXJyQEAERAAsNl7Hf3X6xt0QL6JpZWq30pdvdadme+0PMdzvHm8YThHcT1H7K0BtOMDniZhWOgAAAAASUVORK5CYII=);
|
||||
background-size: 13px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: right center;
|
||||
.box-shadow(inset 0 -1px 0 #ddd);
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
|
||||
&::-ms-expand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.input {
|
||||
&-sm {
|
||||
font-size: @font-size-small;
|
||||
}
|
||||
|
||||
&-lg {
|
||||
font-size: @font-size-large;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
.box-shadow(inset 0 -2px 0 @brand-primary);
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAAJ1BMVEUhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISF8S9ewAAAADHRSTlMAAgMJC0uWpKa6wMxMdjkoAAAANUlEQVR4AeXJyQEAERAAsNl7Hf3X6xt0QL6JpZWq30pdvdadme+0PMdzvHm8YThHcT1H7K0BtOMDniZhWOgAAAAASUVORK5CYII=);
|
||||
}
|
||||
|
||||
&[multiple] {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
.radio,
|
||||
.radio-inline,
|
||||
.checkbox,
|
||||
.checkbox-inline {
|
||||
label {
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
margin-left: -25px;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
.radio input[type="radio"],
|
||||
.radio-inline input[type="radio"] {
|
||||
position: relative;
|
||||
margin-top: 6px;
|
||||
margin-right: 4px;
|
||||
vertical-align: top;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
.transition(240ms);
|
||||
}
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: -3px;
|
||||
background-color: @brand-primary;
|
||||
.scale(0);
|
||||
}
|
||||
|
||||
&:after {
|
||||
position: relative;
|
||||
top: -3px;
|
||||
border: 2px solid @gray;
|
||||
}
|
||||
|
||||
&:checked:before {
|
||||
.scale(0.5);
|
||||
}
|
||||
|
||||
&:disabled:checked:before {
|
||||
background-color: @gray-light;
|
||||
}
|
||||
|
||||
&:checked:after {
|
||||
border-color: @brand-primary;
|
||||
}
|
||||
|
||||
&:disabled:after,
|
||||
&:disabled:checked:after {
|
||||
border-color: @gray-light;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="checkbox"],
|
||||
.checkbox input[type="checkbox"],
|
||||
.checkbox-inline input[type="checkbox"] {
|
||||
position: relative;
|
||||
border: none;
|
||||
margin-bottom: -4px;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:focus:after {
|
||||
border-color: @brand-primary;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-top: -2px;
|
||||
margin-right: 5px;
|
||||
border: 2px solid @gray;
|
||||
border-radius: 2px;
|
||||
.transition(240ms);
|
||||
}
|
||||
|
||||
&:checked:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 6px;
|
||||
display: table;
|
||||
width: 6px;
|
||||
height: 12px;
|
||||
border: 2px solid #fff;
|
||||
border-top-width: 0;
|
||||
border-left-width: 0;
|
||||
.rotate(45deg);
|
||||
}
|
||||
|
||||
&:checked:after {
|
||||
background-color: @brand-primary;
|
||||
border-color: @brand-primary;
|
||||
}
|
||||
|
||||
&:disabled:after {
|
||||
border-color: @gray-light;
|
||||
}
|
||||
|
||||
&:disabled:checked:after {
|
||||
background-color: @gray-light;
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.has-warning {
|
||||
input:not([type=checkbox]),
|
||||
.form-control,
|
||||
input.form-control[readonly],
|
||||
input[type=text][readonly],
|
||||
[type=text].form-control[readonly],
|
||||
input:not([type=checkbox]):focus,
|
||||
.form-control:focus {
|
||||
border-bottom: none;
|
||||
.box-shadow(inset 0 -2px 0 @brand-warning);
|
||||
}
|
||||
}
|
||||
|
||||
.has-error {
|
||||
input:not([type=checkbox]),
|
||||
.form-control,
|
||||
input.form-control[readonly],
|
||||
input[type=text][readonly],
|
||||
[type=text].form-control[readonly],
|
||||
input:not([type=checkbox]):focus,
|
||||
.form-control:focus {
|
||||
border-bottom: none;
|
||||
.box-shadow(inset 0 -2px 0 @brand-danger);
|
||||
}
|
||||
}
|
||||
|
||||
.has-success {
|
||||
input:not([type=checkbox]),
|
||||
.form-control,
|
||||
input.form-control[readonly],
|
||||
input[type=text][readonly],
|
||||
[type=text].form-control[readonly],
|
||||
input:not([type=checkbox]):focus,
|
||||
.form-control:focus {
|
||||
border-bottom: none;
|
||||
.box-shadow(inset 0 -2px 0 @brand-success);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the Bootstrap feedback styles for input addons
|
||||
.input-group-addon {
|
||||
.has-warning &, .has-error &, .has-success & {
|
||||
color: @input-color;
|
||||
border-color: @input-group-addon-border-color;
|
||||
background-color: @input-group-addon-bg;
|
||||
}
|
||||
}
|
||||
|
||||
// Navs =======================================================================
|
||||
|
||||
.nav-tabs {
|
||||
> li > a,
|
||||
> li > a:focus {
|
||||
margin-right: 0;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: @navbar-default-link-color;
|
||||
.box-shadow(inset 0 -1px 0 #ddd);
|
||||
.transition(all 0.2s);
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
.box-shadow(inset 0 -2px 0 @brand-primary);
|
||||
color: @brand-primary;
|
||||
}
|
||||
}
|
||||
|
||||
& > li.active > a,
|
||||
& > li.active > a:focus {
|
||||
border: none;
|
||||
.box-shadow(inset 0 -2px 0 @brand-primary);
|
||||
color: @brand-primary;
|
||||
|
||||
&:hover {
|
||||
border: none;
|
||||
color: @brand-primary;
|
||||
}
|
||||
}
|
||||
|
||||
& > li.disabled > a {
|
||||
.box-shadow(inset 0 -1px 0 #ddd);
|
||||
}
|
||||
|
||||
&.nav-justified {
|
||||
|
||||
& > li > a,
|
||||
& > li > a:hover,
|
||||
& > li > a:focus,
|
||||
& > .active > a,
|
||||
& > .active > a:hover,
|
||||
& > .active > a:focus {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
margin-top: 0;
|
||||
border: none;
|
||||
.box-shadow(0 1px 4px rgba(0,0,0,.3));
|
||||
}
|
||||
|
||||
// Indicators =================================================================
|
||||
|
||||
.alert {
|
||||
border: none;
|
||||
color: #fff;
|
||||
|
||||
&-success {
|
||||
background-color: @brand-success;
|
||||
}
|
||||
|
||||
&-info {
|
||||
background-color: @brand-info;
|
||||
}
|
||||
|
||||
&-warning {
|
||||
background-color: @brand-warning;
|
||||
}
|
||||
|
||||
&-danger {
|
||||
background-color: @brand-danger;
|
||||
}
|
||||
|
||||
a:not(.close),
|
||||
.alert-link {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.close {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
padding: 4px 6px 4px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: 6px;
|
||||
border-radius: 0;
|
||||
|
||||
.box-shadow(none);
|
||||
|
||||
&-bar {
|
||||
.box-shadow(none);
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
&:before {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: -1;
|
||||
background-color: lighten(@progress-bar-bg, 35%);
|
||||
}
|
||||
}
|
||||
|
||||
&-success:last-child.progress-bar:before {
|
||||
background-color: lighten(@brand-success, 35%);
|
||||
}
|
||||
|
||||
&-info:last-child.progress-bar:before {
|
||||
background-color: lighten(@brand-info, 45%);
|
||||
}
|
||||
&-warning:last-child.progress-bar:before {
|
||||
background-color: lighten(@brand-warning, 35%);
|
||||
}
|
||||
|
||||
&-danger:last-child.progress-bar:before {
|
||||
background-color: lighten(@brand-danger, 25%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Progress bars ==============================================================
|
||||
|
||||
// Containers =================================================================
|
||||
|
||||
.close {
|
||||
font-size: 34px;
|
||||
font-weight: 300;
|
||||
line-height: 24px;
|
||||
opacity: 0.6;
|
||||
.transition(all 0.2s);
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.list-group {
|
||||
|
||||
&-item {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
&-item-text {
|
||||
color: @gray-light;
|
||||
}
|
||||
}
|
||||
|
||||
.well {
|
||||
border-radius: 0;
|
||||
.box-shadow(none);
|
||||
}
|
||||
|
||||
.panel {
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
.box-shadow(0 1px 4px rgba(0,0,0,.3));
|
||||
|
||||
&-heading {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&-footer {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
.popover {
|
||||
border: none;
|
||||
.box-shadow(0 1px 4px rgba(0,0,0,.3));
|
||||
}
|
||||
|
||||
.carousel {
|
||||
&-caption {
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
// Index .less, any imports here will be included in the final css build
|
||||
|
||||
@import "~bootstrap/less/bootstrap.less";
|
||||
@import "./variables.less";
|
||||
@import "./bootswatch.less";
|
||||
|
|
@ -1,881 +0,0 @@
|
|||
// Modified from Bootswatch Paper 3.3.6
|
||||
// Variables
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
//== Colors
|
||||
//
|
||||
//## Airbnb colors
|
||||
@rausch: #ff5a5f; // coral
|
||||
@kazan: #007a87; // dark teal
|
||||
@hackberry: #7b0051; // purple
|
||||
@babu: #00d1c1; // light teal
|
||||
@lima: #8ce071; // bright green
|
||||
@beach: #ffb400; // yellow
|
||||
@ebisu: #ffaa91; // peach
|
||||
@tirol: #b4a76c; // khaki
|
||||
@foggy: #9CA299; // dark grey
|
||||
@hof: #565A5C; // light grey
|
||||
|
||||
//## Gray and brand colors for use across Bootstrap.
|
||||
|
||||
@gray-base: #000;
|
||||
@gray-darker: lighten(@gray-base, 13.5%); // #222
|
||||
@gray-dark: #212121;
|
||||
@gray: #666;
|
||||
@gray-light: #bbb;
|
||||
@gray-lighter: lighten(@gray-base, 93.5%); // #eee
|
||||
|
||||
@brand-primary: darken(@babu, 5%);
|
||||
@brand-success: darken(@lima, 15%);
|
||||
@brand-info: @beach;
|
||||
@brand-warning: @hackberry;
|
||||
@brand-danger: darken(@rausch, 5%);
|
||||
|
||||
|
||||
//== Scaffolding
|
||||
//
|
||||
//## Settings for some of the most global styles.
|
||||
|
||||
//** Background color for `<body>`.
|
||||
@body-bg: #fff;
|
||||
//** Global text color on `<body>`.
|
||||
@text-color: @gray;
|
||||
|
||||
//** Global textual link color.
|
||||
@link-color: @brand-primary;
|
||||
//** Link hover color set via `darken()` function.
|
||||
@link-hover-color: darken(@link-color, 15%);
|
||||
//** Link hover decoration.
|
||||
@link-hover-decoration: underline;
|
||||
|
||||
|
||||
//== Typography
|
||||
//
|
||||
//## Font, line-height, and color for body text, headings, and more.
|
||||
|
||||
@font-family-sans-serif: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
@font-family-serif: Georgia, "Times New Roman", Times, serif;
|
||||
//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`.
|
||||
@font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
@font-family-base: @font-family-sans-serif;
|
||||
|
||||
@font-size-base: 13px;
|
||||
@font-size-large: ceil((@font-size-base * 1.25)); // ~18px
|
||||
@font-size-small: ceil((@font-size-base * 0.85)); // ~12px
|
||||
|
||||
@font-size-h1: 56px;
|
||||
@font-size-h2: 45px;
|
||||
@font-size-h3: 34px;
|
||||
@font-size-h4: 24px;
|
||||
@font-size-h5: 20px;
|
||||
@font-size-h6: 14px;
|
||||
|
||||
//** Unit-less `line-height` for use in components like buttons.
|
||||
@line-height-base: 1.846; // 20/14
|
||||
//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
|
||||
@line-height-computed: floor((@font-size-base * @line-height-base)); // ~20px
|
||||
|
||||
//** By default, this inherits from the `<body>`.
|
||||
@headings-font-family: inherit;
|
||||
@headings-font-weight: 400;
|
||||
@headings-line-height: 1.1;
|
||||
@headings-color: #444;
|
||||
|
||||
|
||||
//== Iconography
|
||||
//
|
||||
//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
|
||||
|
||||
//** Load fonts from this directory.
|
||||
@icon-font-path: "../fonts/";
|
||||
//** File name for all font files.
|
||||
@icon-font-name: "glyphicons-halflings-regular";
|
||||
//** Element ID within SVG icon file.
|
||||
@icon-font-svg-id: "glyphicons_halflingsregular";
|
||||
|
||||
|
||||
//== Components
|
||||
//
|
||||
//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
|
||||
|
||||
@padding-base-vertical: 6px;
|
||||
@padding-base-horizontal: 16px;
|
||||
|
||||
@padding-large-vertical: 10px;
|
||||
@padding-large-horizontal: 16px;
|
||||
|
||||
@padding-small-vertical: 5px;
|
||||
@padding-small-horizontal: 10px;
|
||||
|
||||
@padding-xs-vertical: 1px;
|
||||
@padding-xs-horizontal: 5px;
|
||||
|
||||
@line-height-large: 1.3333333; // extra decimals for Win 8.1 Chrome
|
||||
@line-height-small: 1.5;
|
||||
|
||||
@border-radius-base: 3px;
|
||||
@border-radius-large: 3px;
|
||||
@border-radius-small: 3px;
|
||||
|
||||
//** Global color for active items (e.g., navs or dropdowns).
|
||||
@component-active-color: #fff;
|
||||
//** Global background color for active items (e.g., navs or dropdowns).
|
||||
@component-active-bg: @brand-primary;
|
||||
|
||||
//** Width of the `border` for generating carets that indicator dropdowns.
|
||||
@caret-width-base: 4px;
|
||||
//** Carets increase slightly in size for larger components.
|
||||
@caret-width-large: 5px;
|
||||
|
||||
|
||||
//== Tables
|
||||
//
|
||||
//## Customizes the `.table` component with basic values, each used across all table variations.
|
||||
|
||||
//** Padding for `<th>`s and `<td>`s.
|
||||
@table-cell-padding: 8px;
|
||||
//** Padding for cells in `.table-condensed`.
|
||||
@table-condensed-cell-padding: 5px;
|
||||
|
||||
//** Default background color used for all tables.
|
||||
@table-bg: transparent;
|
||||
//** Background color used for `.table-striped`.
|
||||
@table-bg-accent: #f9f9f9;
|
||||
//** Background color used for `.table-hover`.
|
||||
@table-bg-hover: @gray-lighter;
|
||||
@table-bg-active: @table-bg-hover;
|
||||
|
||||
//** Border color for table and cell borders.
|
||||
@table-border-color: #ddd;
|
||||
|
||||
|
||||
//== Buttons
|
||||
//
|
||||
//## For each of Bootstrap's buttons, define text, background and border color.
|
||||
|
||||
@btn-font-weight: normal;
|
||||
|
||||
@btn-default-color: #444;
|
||||
@btn-default-bg: #fff;
|
||||
@btn-default-border: transparent;
|
||||
|
||||
@btn-primary-color: #fff;
|
||||
@btn-primary-bg: @brand-primary;
|
||||
@btn-primary-border: transparent;
|
||||
|
||||
@btn-success-color: #fff;
|
||||
@btn-success-bg: @brand-success;
|
||||
@btn-success-border: transparent;
|
||||
|
||||
@btn-info-color: #fff;
|
||||
@btn-info-bg: @brand-info;
|
||||
@btn-info-border: transparent;
|
||||
|
||||
@btn-warning-color: #fff;
|
||||
@btn-warning-bg: @brand-warning;
|
||||
@btn-warning-border: transparent;
|
||||
|
||||
@btn-danger-color: #fff;
|
||||
@btn-danger-bg: @brand-danger;
|
||||
@btn-danger-border: transparent;
|
||||
|
||||
@btn-link-disabled-color: @gray-light;
|
||||
|
||||
// Allows for customizing button radius independently from global border radius
|
||||
@btn-border-radius-base: @border-radius-base;
|
||||
@btn-border-radius-large: @border-radius-large;
|
||||
@btn-border-radius-small: @border-radius-small;
|
||||
|
||||
|
||||
//== Forms
|
||||
//
|
||||
//##
|
||||
|
||||
//** `<input>` background color
|
||||
@input-bg: transparent;
|
||||
//** `<input disabled>` background color
|
||||
@input-bg-disabled: transparent;
|
||||
|
||||
//** Text color for `<input>`s
|
||||
@input-color: @gray;
|
||||
//** `<input>` border color
|
||||
@input-border: transparent;
|
||||
|
||||
// TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4
|
||||
//** Default `.form-control` border radius
|
||||
// This has no effect on `<select>`s in some browsers, due to the limited stylability of `<select>`s in CSS.
|
||||
@input-border-radius: @border-radius-base;
|
||||
//** Large `.form-control` border radius
|
||||
@input-border-radius-large: @border-radius-large;
|
||||
//** Small `.form-control` border radius
|
||||
@input-border-radius-small: @border-radius-small;
|
||||
|
||||
//** Border color for inputs on focus
|
||||
@input-border-focus: #66afe9;
|
||||
|
||||
//** Placeholder text color
|
||||
@input-color-placeholder: @gray-light;
|
||||
|
||||
//** Default `.form-control` height
|
||||
@input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2);
|
||||
//** Large `.form-control` height
|
||||
@input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);
|
||||
//** Small `.form-control` height
|
||||
@input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);
|
||||
|
||||
//** `.form-group` margin
|
||||
@form-group-margin-bottom: 15px;
|
||||
|
||||
@legend-color: @gray-dark;
|
||||
@legend-border-color: #e5e5e5;
|
||||
|
||||
//** Background color for textual input addons
|
||||
@input-group-addon-bg: transparent;
|
||||
//** Border color for textual input addons
|
||||
@input-group-addon-border-color: @input-border;
|
||||
|
||||
//** Disabled cursor for form controls and buttons.
|
||||
@cursor-disabled: not-allowed;
|
||||
|
||||
|
||||
//== Dropdowns
|
||||
//
|
||||
//## Dropdown menu container and contents.
|
||||
|
||||
//** Background for the dropdown menu.
|
||||
@dropdown-bg: #fff;
|
||||
//** Dropdown menu `border-color`.
|
||||
@dropdown-border: rgba(0,0,0,.15);
|
||||
//** Dropdown menu `border-color` **for IE8**.
|
||||
@dropdown-fallback-border: #ccc;
|
||||
//** Divider color for between dropdown items.
|
||||
@dropdown-divider-bg: #e5e5e5;
|
||||
|
||||
//** Dropdown link text color.
|
||||
@dropdown-link-color: @text-color;
|
||||
//** Hover color for dropdown links.
|
||||
@dropdown-link-hover-color: darken(@gray-dark, 5%);
|
||||
//** Hover background for dropdown links.
|
||||
@dropdown-link-hover-bg: @gray-lighter;
|
||||
|
||||
//** Active dropdown menu item text color.
|
||||
@dropdown-link-active-color: @component-active-color;
|
||||
//** Active dropdown menu item background color.
|
||||
@dropdown-link-active-bg: @component-active-bg;
|
||||
|
||||
//** Disabled dropdown menu item background color.
|
||||
@dropdown-link-disabled-color: @gray-light;
|
||||
|
||||
//** Text color for headers within dropdown menus.
|
||||
@dropdown-header-color: @gray-light;
|
||||
|
||||
//** Deprecated `@dropdown-caret-color` as of v3.1.0
|
||||
@dropdown-caret-color: @gray-light;
|
||||
|
||||
|
||||
//-- Z-index master list
|
||||
//
|
||||
// Warning: Avoid customizing these values. They're used for a bird's eye view
|
||||
// of components dependent on the z-axis and are designed to all work together.
|
||||
//
|
||||
// Note: These variables are not generated into the Customizer.
|
||||
|
||||
@zindex-navbar: 1000;
|
||||
@zindex-dropdown: 1000;
|
||||
@zindex-popover: 1060;
|
||||
@zindex-tooltip: 1070;
|
||||
@zindex-navbar-fixed: 1030;
|
||||
@zindex-modal-background: 1040;
|
||||
@zindex-modal: 1050;
|
||||
|
||||
|
||||
//== Media queries breakpoints
|
||||
//
|
||||
//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
|
||||
|
||||
// Extra small screen / phone
|
||||
//** Deprecated `@screen-xs` as of v3.0.1
|
||||
@screen-xs: 480px;
|
||||
//** Deprecated `@screen-xs-min` as of v3.2.0
|
||||
@screen-xs-min: @screen-xs;
|
||||
//** Deprecated `@screen-phone` as of v3.0.1
|
||||
@screen-phone: @screen-xs-min;
|
||||
|
||||
// Small screen / tablet
|
||||
//** Deprecated `@screen-sm` as of v3.0.1
|
||||
@screen-sm: 768px;
|
||||
@screen-sm-min: @screen-sm;
|
||||
//** Deprecated `@screen-tablet` as of v3.0.1
|
||||
@screen-tablet: @screen-sm-min;
|
||||
|
||||
// Medium screen / desktop
|
||||
//** Deprecated `@screen-md` as of v3.0.1
|
||||
@screen-md: 992px;
|
||||
@screen-md-min: @screen-md;
|
||||
//** Deprecated `@screen-desktop` as of v3.0.1
|
||||
@screen-desktop: @screen-md-min;
|
||||
|
||||
// Large screen / wide desktop
|
||||
//** Deprecated `@screen-lg` as of v3.0.1
|
||||
@screen-lg: 1200px;
|
||||
@screen-lg-min: @screen-lg;
|
||||
//** Deprecated `@screen-lg-desktop` as of v3.0.1
|
||||
@screen-lg-desktop: @screen-lg-min;
|
||||
|
||||
// So media queries don't overlap when required, provide a maximum
|
||||
@screen-xs-max: (@screen-sm-min - 1);
|
||||
@screen-sm-max: (@screen-md-min - 1);
|
||||
@screen-md-max: (@screen-lg-min - 1);
|
||||
|
||||
|
||||
//== Grid system
|
||||
//
|
||||
//## Define your custom responsive grid.
|
||||
|
||||
//** Number of columns in the grid.
|
||||
@grid-columns: 12;
|
||||
//** Padding between columns. Gets divided in half for the left and right.
|
||||
@grid-gutter-width: 30px;
|
||||
// Navbar collapse
|
||||
//** Point at which the navbar becomes uncollapsed.
|
||||
@grid-float-breakpoint: @screen-sm-min;
|
||||
//** Point at which the navbar begins collapsing.
|
||||
@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
|
||||
|
||||
|
||||
//== Container sizes
|
||||
//
|
||||
//## Define the maximum width of `.container` for different screen sizes.
|
||||
|
||||
// Small screen / tablet
|
||||
@container-tablet: (720px + @grid-gutter-width);
|
||||
//** For `@screen-sm-min` and up.
|
||||
@container-sm: @container-tablet;
|
||||
|
||||
// Medium screen / desktop
|
||||
@container-desktop: (940px + @grid-gutter-width);
|
||||
//** For `@screen-md-min` and up.
|
||||
@container-md: @container-desktop;
|
||||
|
||||
// Large screen / wide desktop
|
||||
@container-large-desktop: (1140px + @grid-gutter-width);
|
||||
//** For `@screen-lg-min` and up.
|
||||
@container-lg: @container-large-desktop;
|
||||
|
||||
|
||||
//== Navbar
|
||||
//
|
||||
//##
|
||||
|
||||
// Basics of a navbar
|
||||
@navbar-height: 64px;
|
||||
@navbar-margin-bottom: @line-height-computed;
|
||||
@navbar-border-radius: @border-radius-base;
|
||||
@navbar-padding-horizontal: floor((@grid-gutter-width / 2));
|
||||
@navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2);
|
||||
@navbar-collapse-max-height: 340px;
|
||||
|
||||
@navbar-default-color: @gray-light;
|
||||
@navbar-default-bg: #fff;
|
||||
@navbar-default-border: transparent;
|
||||
|
||||
// Navbar links
|
||||
@navbar-default-link-color: @gray;
|
||||
@navbar-default-link-hover-color: @gray-dark;
|
||||
@navbar-default-link-hover-bg: transparent;
|
||||
@navbar-default-link-active-color: @gray-dark;
|
||||
@navbar-default-link-active-bg: darken(@navbar-default-bg, 6.5%);
|
||||
@navbar-default-link-disabled-color: #ccc;
|
||||
@navbar-default-link-disabled-bg: transparent;
|
||||
|
||||
// Navbar brand label
|
||||
@navbar-default-brand-color: @navbar-default-link-color;
|
||||
@navbar-default-brand-hover-color: @navbar-default-link-hover-color;
|
||||
@navbar-default-brand-hover-bg: transparent;
|
||||
|
||||
// Navbar toggle
|
||||
@navbar-default-toggle-hover-bg: transparent;
|
||||
@navbar-default-toggle-icon-bar-bg: rgba(0,0,0,0.5);
|
||||
@navbar-default-toggle-border-color: transparent;
|
||||
|
||||
|
||||
//=== Inverted navbar
|
||||
// Reset inverted navbar basics
|
||||
@navbar-inverse-color: @gray-light;
|
||||
@navbar-inverse-bg: @brand-primary;
|
||||
@navbar-inverse-border: transparent;
|
||||
|
||||
// Inverted navbar links
|
||||
@navbar-inverse-link-color: lighten(@brand-primary, 30%);
|
||||
@navbar-inverse-link-hover-color: #fff;
|
||||
@navbar-inverse-link-hover-bg: transparent;
|
||||
@navbar-inverse-link-active-color: @navbar-inverse-link-hover-color;
|
||||
@navbar-inverse-link-active-bg: darken(@navbar-inverse-bg, 10%);
|
||||
@navbar-inverse-link-disabled-color: #444;
|
||||
@navbar-inverse-link-disabled-bg: transparent;
|
||||
|
||||
// Inverted navbar brand label
|
||||
@navbar-inverse-brand-color: @navbar-inverse-link-color;
|
||||
@navbar-inverse-brand-hover-color: #fff;
|
||||
@navbar-inverse-brand-hover-bg: transparent;
|
||||
|
||||
// Inverted navbar toggle\
|
||||
@navbar-inverse-toggle-hover-bg: transparent;
|
||||
@navbar-inverse-toggle-icon-bar-bg: rgba(0,0,0,0.5);
|
||||
@navbar-inverse-toggle-border-color: transparent;
|
||||
|
||||
|
||||
//== Navs
|
||||
//
|
||||
//##
|
||||
|
||||
//=== Shared nav styles
|
||||
@nav-link-padding: 10px 15px;
|
||||
@nav-link-hover-bg: @gray-lighter;
|
||||
|
||||
@nav-disabled-link-color: @gray-light;
|
||||
@nav-disabled-link-hover-color: @gray-light;
|
||||
|
||||
//== Tabs
|
||||
@nav-tabs-border-color: transparent;
|
||||
|
||||
@nav-tabs-link-hover-border-color: @gray-lighter;
|
||||
|
||||
@nav-tabs-active-link-hover-bg: transparent;
|
||||
@nav-tabs-active-link-hover-color: @gray;
|
||||
@nav-tabs-active-link-hover-border-color: transparent;
|
||||
|
||||
@nav-tabs-justified-link-border-color: @nav-tabs-border-color;
|
||||
@nav-tabs-justified-active-link-border-color: @body-bg;
|
||||
|
||||
//== Pills
|
||||
@nav-pills-border-radius: @border-radius-base;
|
||||
@nav-pills-active-link-hover-bg: @component-active-bg;
|
||||
@nav-pills-active-link-hover-color: @component-active-color;
|
||||
|
||||
|
||||
//== Pagination
|
||||
//
|
||||
//##
|
||||
|
||||
@pagination-color: @link-color;
|
||||
@pagination-bg: #fff;
|
||||
@pagination-border: #ddd;
|
||||
|
||||
@pagination-hover-color: @link-hover-color;
|
||||
@pagination-hover-bg: @gray-lighter;
|
||||
@pagination-hover-border: #ddd;
|
||||
|
||||
@pagination-active-color: #fff;
|
||||
@pagination-active-bg: @brand-primary;
|
||||
@pagination-active-border: @brand-primary;
|
||||
|
||||
@pagination-disabled-color: @gray-light;
|
||||
@pagination-disabled-bg: #fff;
|
||||
@pagination-disabled-border: #ddd;
|
||||
|
||||
|
||||
//== Pager
|
||||
//
|
||||
//##
|
||||
|
||||
@pager-bg: @pagination-bg;
|
||||
@pager-border: @pagination-border;
|
||||
@pager-border-radius: 15px;
|
||||
|
||||
@pager-hover-bg: @pagination-hover-bg;
|
||||
|
||||
@pager-active-bg: @pagination-active-bg;
|
||||
@pager-active-color: @pagination-active-color;
|
||||
|
||||
@pager-disabled-color: @pagination-disabled-color;
|
||||
|
||||
|
||||
//== Jumbotron
|
||||
//
|
||||
//##
|
||||
|
||||
@jumbotron-padding: 30px;
|
||||
@jumbotron-color: inherit;
|
||||
@jumbotron-bg: #f9f9f9;
|
||||
@jumbotron-heading-color: @headings-color;
|
||||
@jumbotron-font-size: ceil((@font-size-base * 1.5));
|
||||
@jumbotron-heading-font-size: ceil((@font-size-base * 4.5));
|
||||
|
||||
|
||||
//== Form states and alerts
|
||||
//
|
||||
//## Define colors for form feedback states and, by default, alerts.
|
||||
|
||||
@state-success-text: @brand-success;
|
||||
@state-success-bg: #dff0d8;
|
||||
@state-success-border: darken(spin(@state-success-bg, -10), 5%);
|
||||
|
||||
@state-info-text: @brand-info;
|
||||
@state-info-bg: #e1bee7;
|
||||
@state-info-border: darken(spin(@state-info-bg, -10), 7%);
|
||||
|
||||
@state-warning-text: @brand-warning;
|
||||
@state-warning-bg: #ffe0b2;
|
||||
@state-warning-border: darken(spin(@state-warning-bg, -10), 5%);
|
||||
|
||||
@state-danger-text: @brand-danger;
|
||||
@state-danger-bg: #f9bdbb;
|
||||
@state-danger-border: darken(spin(@state-danger-bg, -10), 5%);
|
||||
|
||||
|
||||
//== Tooltips
|
||||
//
|
||||
//##
|
||||
|
||||
//** Tooltip max width
|
||||
@tooltip-max-width: 200px;
|
||||
//** Tooltip text color
|
||||
@tooltip-color: #fff;
|
||||
//** Tooltip background color
|
||||
@tooltip-bg: #727272;
|
||||
@tooltip-opacity: .9;
|
||||
|
||||
//** Tooltip arrow width
|
||||
@tooltip-arrow-width: 5px;
|
||||
//** Tooltip arrow color
|
||||
@tooltip-arrow-color: @tooltip-bg;
|
||||
|
||||
|
||||
//== Popovers
|
||||
//
|
||||
//##
|
||||
|
||||
//** Popover body background color
|
||||
@popover-bg: #fff;
|
||||
//** Popover maximum width
|
||||
@popover-max-width: 276px;
|
||||
//** Popover border color
|
||||
@popover-border-color: transparent;
|
||||
//** Popover fallback border color
|
||||
@popover-fallback-border-color: transparent;
|
||||
|
||||
//** Popover title background color
|
||||
@popover-title-bg: darken(@popover-bg, 3%);
|
||||
|
||||
//** Popover arrow width
|
||||
@popover-arrow-width: 10px;
|
||||
//** Popover arrow color
|
||||
@popover-arrow-color: @popover-bg;
|
||||
|
||||
//** Popover outer arrow width
|
||||
@popover-arrow-outer-width: (@popover-arrow-width + 1);
|
||||
//** Popover outer arrow color
|
||||
@popover-arrow-outer-color: fadein(@popover-border-color, 7.5%);
|
||||
//** Popover outer arrow fallback color
|
||||
@popover-arrow-outer-fallback-color: darken(@popover-fallback-border-color, 20%);
|
||||
|
||||
|
||||
//== Labels
|
||||
//
|
||||
//##
|
||||
|
||||
//** Default label background color
|
||||
@label-default-bg: @gray-light;
|
||||
//** Primary label background color
|
||||
@label-primary-bg: @brand-primary;
|
||||
//** Success label background color
|
||||
@label-success-bg: @brand-success;
|
||||
//** Info label background color
|
||||
@label-info-bg: @brand-info;
|
||||
//** Warning label background color
|
||||
@label-warning-bg: @brand-warning;
|
||||
//** Danger label background color
|
||||
@label-danger-bg: @brand-danger;
|
||||
|
||||
//** Default label text color
|
||||
@label-color: #fff;
|
||||
//** Default text color of a linked label
|
||||
@label-link-hover-color: #fff;
|
||||
|
||||
|
||||
//== Modals
|
||||
//
|
||||
//##
|
||||
|
||||
//** Padding applied to the modal body
|
||||
@modal-inner-padding: 15px;
|
||||
|
||||
//** Padding applied to the modal title
|
||||
@modal-title-padding: 15px;
|
||||
//** Modal title line-height
|
||||
@modal-title-line-height: @line-height-base;
|
||||
|
||||
//** Background color of modal content area
|
||||
@modal-content-bg: #fff;
|
||||
//** Modal content border color
|
||||
@modal-content-border-color: transparent;
|
||||
//** Modal content border color **for IE8**
|
||||
@modal-content-fallback-border-color: #999;
|
||||
|
||||
//** Modal backdrop background color
|
||||
@modal-backdrop-bg: #000;
|
||||
//** Modal backdrop opacity
|
||||
@modal-backdrop-opacity: .5;
|
||||
//** Modal header border color
|
||||
@modal-header-border-color: transparent;
|
||||
//** Modal footer border color
|
||||
@modal-footer-border-color: @modal-header-border-color;
|
||||
|
||||
@modal-lg: 900px;
|
||||
@modal-md: 600px;
|
||||
@modal-sm: 300px;
|
||||
|
||||
|
||||
//== Alerts
|
||||
//
|
||||
//## Define alert colors, border radius, and padding.
|
||||
|
||||
@alert-padding: 15px;
|
||||
@alert-border-radius: @border-radius-base;
|
||||
@alert-link-font-weight: bold;
|
||||
|
||||
@alert-success-bg: @state-success-bg;
|
||||
@alert-success-text: @state-success-text;
|
||||
@alert-success-border: @state-success-border;
|
||||
|
||||
@alert-info-bg: @state-info-bg;
|
||||
@alert-info-text: @state-info-text;
|
||||
@alert-info-border: @state-info-border;
|
||||
|
||||
@alert-warning-bg: @state-warning-bg;
|
||||
@alert-warning-text: @state-warning-text;
|
||||
@alert-warning-border: @state-warning-border;
|
||||
|
||||
@alert-danger-bg: @state-danger-bg;
|
||||
@alert-danger-text: @state-danger-text;
|
||||
@alert-danger-border: @state-danger-border;
|
||||
|
||||
|
||||
//== Progress bars
|
||||
//
|
||||
//##
|
||||
|
||||
//** Background color of the whole progress component
|
||||
@progress-bg: #f5f5f5;
|
||||
//** Progress bar text color
|
||||
@progress-bar-color: #fff;
|
||||
//** Variable for setting rounded corners on progress bar.
|
||||
@progress-border-radius: @border-radius-base;
|
||||
|
||||
//** Default progress bar color
|
||||
@progress-bar-bg: @brand-primary;
|
||||
//** Success progress bar color
|
||||
@progress-bar-success-bg: @brand-success;
|
||||
//** Warning progress bar color
|
||||
@progress-bar-warning-bg: @brand-warning;
|
||||
//** Danger progress bar color
|
||||
@progress-bar-danger-bg: @brand-danger;
|
||||
//** Info progress bar color
|
||||
@progress-bar-info-bg: @brand-info;
|
||||
|
||||
|
||||
//== List group
|
||||
//
|
||||
//##
|
||||
|
||||
//** Background color on `.list-group-item`
|
||||
@list-group-bg: #fff;
|
||||
//** `.list-group-item` border color
|
||||
@list-group-border: #ddd;
|
||||
//** List group border radius
|
||||
@list-group-border-radius: @border-radius-base;
|
||||
|
||||
//** Background color of single list items on hover
|
||||
@list-group-hover-bg: #f5f5f5;
|
||||
//** Text color of active list items
|
||||
@list-group-active-color: @component-active-color;
|
||||
//** Background color of active list items
|
||||
@list-group-active-bg: @component-active-bg;
|
||||
//** Border color of active list elements
|
||||
@list-group-active-border: @list-group-active-bg;
|
||||
//** Text color for content within active list items
|
||||
@list-group-active-text-color: lighten(@list-group-active-bg, 40%);
|
||||
|
||||
//** Text color of disabled list items
|
||||
@list-group-disabled-color: @gray-light;
|
||||
//** Background color of disabled list items
|
||||
@list-group-disabled-bg: @gray-lighter;
|
||||
//** Text color for content within disabled list items
|
||||
@list-group-disabled-text-color: @list-group-disabled-color;
|
||||
|
||||
@list-group-link-color: #555;
|
||||
@list-group-link-hover-color: @list-group-link-color;
|
||||
@list-group-link-heading-color: #333;
|
||||
|
||||
|
||||
//== Panels
|
||||
//
|
||||
//##
|
||||
|
||||
@panel-bg: #fff;
|
||||
@panel-body-padding: 15px;
|
||||
@panel-heading-padding: 10px 15px;
|
||||
@panel-footer-padding: @panel-heading-padding;
|
||||
@panel-border-radius: @border-radius-base;
|
||||
|
||||
//** Border color for elements within panels
|
||||
@panel-inner-border: #ddd;
|
||||
@panel-footer-bg: #f5f5f5;
|
||||
|
||||
@panel-default-text: @gray-dark;
|
||||
@panel-default-border: #ddd;
|
||||
@panel-default-heading-bg: #f5f5f5;
|
||||
|
||||
@panel-primary-text: #fff;
|
||||
@panel-primary-border: @brand-primary;
|
||||
@panel-primary-heading-bg: @brand-primary;
|
||||
|
||||
@panel-success-text: #fff;
|
||||
@panel-success-border: @state-success-border;
|
||||
@panel-success-heading-bg: @brand-success;
|
||||
|
||||
@panel-info-text: #fff;
|
||||
@panel-info-border: @state-info-border;
|
||||
@panel-info-heading-bg: @brand-info;
|
||||
|
||||
@panel-warning-text: #fff;
|
||||
@panel-warning-border: @state-warning-border;
|
||||
@panel-warning-heading-bg: @brand-warning;
|
||||
|
||||
@panel-danger-text: #fff;
|
||||
@panel-danger-border: @state-danger-border;
|
||||
@panel-danger-heading-bg: @brand-danger;
|
||||
|
||||
|
||||
//== Thumbnails
|
||||
//
|
||||
//##
|
||||
|
||||
//** Padding around the thumbnail image
|
||||
@thumbnail-padding: 4px;
|
||||
//** Thumbnail background color
|
||||
@thumbnail-bg: @body-bg;
|
||||
//** Thumbnail border color
|
||||
@thumbnail-border: #ddd;
|
||||
//** Thumbnail border radius
|
||||
@thumbnail-border-radius: @border-radius-base;
|
||||
|
||||
//** Custom text color for thumbnail captions
|
||||
@thumbnail-caption-color: @text-color;
|
||||
//** Padding around the thumbnail caption
|
||||
@thumbnail-caption-padding: 9px;
|
||||
|
||||
|
||||
//== Wells
|
||||
//
|
||||
//##
|
||||
|
||||
@well-bg: #f9f9f9;
|
||||
@well-border: transparent;
|
||||
|
||||
|
||||
//== Badges
|
||||
//
|
||||
//##
|
||||
|
||||
@badge-color: #fff;
|
||||
//** Linked badge text color on hover
|
||||
@badge-link-hover-color: #fff;
|
||||
@badge-bg: @gray-light;
|
||||
|
||||
//** Badge text color in active nav link
|
||||
@badge-active-color: @link-color;
|
||||
//** Badge background color in active nav link
|
||||
@badge-active-bg: #fff;
|
||||
|
||||
@badge-font-weight: normal;
|
||||
@badge-line-height: 1;
|
||||
@badge-border-radius: 10px;
|
||||
|
||||
|
||||
//== Breadcrumbs
|
||||
//
|
||||
//##
|
||||
|
||||
@breadcrumb-padding-vertical: 8px;
|
||||
@breadcrumb-padding-horizontal: 15px;
|
||||
//** Breadcrumb background color
|
||||
@breadcrumb-bg: #f5f5f5;
|
||||
//** Breadcrumb text color
|
||||
@breadcrumb-color: #ccc;
|
||||
//** Text color of current page in the breadcrumb
|
||||
@breadcrumb-active-color: @gray-light;
|
||||
//** Textual separator for between breadcrumb elements
|
||||
@breadcrumb-separator: "/";
|
||||
|
||||
|
||||
//== Carousel
|
||||
//
|
||||
//##
|
||||
|
||||
@carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6);
|
||||
|
||||
@carousel-control-color: #fff;
|
||||
@carousel-control-width: 15%;
|
||||
@carousel-control-opacity: .5;
|
||||
@carousel-control-font-size: 20px;
|
||||
|
||||
@carousel-indicator-active-bg: #fff;
|
||||
@carousel-indicator-border-color: #fff;
|
||||
|
||||
@carousel-caption-color: #fff;
|
||||
|
||||
|
||||
//== Close
|
||||
//
|
||||
//##
|
||||
|
||||
@close-font-weight: normal;
|
||||
@close-color: #000;
|
||||
@close-text-shadow: none;
|
||||
|
||||
|
||||
//== Code
|
||||
//
|
||||
//##
|
||||
|
||||
@code-color: #c7254e;
|
||||
@code-bg: #f9f2f4;
|
||||
|
||||
@kbd-color: #fff;
|
||||
@kbd-bg: #333;
|
||||
|
||||
@pre-bg: #f5f5f5;
|
||||
@pre-color: @gray-dark;
|
||||
@pre-border-color: #ccc;
|
||||
@pre-scrollable-max-height: 340px;
|
||||
|
||||
|
||||
//== Type
|
||||
//
|
||||
//##
|
||||
|
||||
//** Horizontal offset for forms and lists.
|
||||
@component-offset-horizontal: 180px;
|
||||
//** Text muted color
|
||||
@text-muted: @gray-light;
|
||||
//** Abbreviations and acronyms border color
|
||||
@abbr-border-color: @gray-light;
|
||||
//** Headings small color
|
||||
@headings-small-color: @gray-light;
|
||||
//** Blockquote small color
|
||||
@blockquote-small-color: @gray-light;
|
||||
//** Blockquote font size
|
||||
@blockquote-font-size: (@font-size-base * 1.25);
|
||||
//** Blockquote border color
|
||||
@blockquote-border-color: @gray-lighter;
|
||||
//** Page header border color
|
||||
@page-header-border-color: @gray-lighter;
|
||||
//** Width of horizontal description list titles
|
||||
@dl-horizontal-offset: @component-offset-horizontal;
|
||||
//** Point at which .dl-horizontal becomes horizontal
|
||||
@dl-horizontal-breakpoint: @grid-float-breakpoint;
|
||||
//** Horizontal line color.
|
||||
@hr-border: @gray-lighter;
|
||||
|
|
@ -1,244 +0,0 @@
|
|||
body {
|
||||
margin: 0px !important;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
z-index: 1100;
|
||||
}
|
||||
|
||||
input.form-control {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.col-left-fixed {
|
||||
width:350px;
|
||||
position: absolute;
|
||||
float: left;
|
||||
}
|
||||
.col-offset {
|
||||
margin-left: 365px;
|
||||
}
|
||||
|
||||
.slice_description{
|
||||
padding: 8px;
|
||||
margin: 5px;
|
||||
border: 1px solid #DDD;
|
||||
background-color: #F8F8F8;
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.slice_info{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.padded {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.intable-longtext{
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.container-fluid {
|
||||
text-align: left;
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
float: right;
|
||||
}
|
||||
form div {
|
||||
padding-top: 1px;
|
||||
}
|
||||
.navbar-brand a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.header span{
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
#timer {
|
||||
width: 80px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.notbtn {
|
||||
cursor: default;
|
||||
}
|
||||
hr {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
span.title-block {
|
||||
background-color: #EEE;
|
||||
border-radius: 4px;
|
||||
padding: 6px 12px;
|
||||
margin: 0px 10px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
fieldset.fs-style {
|
||||
font-family: Verdana, Arial, sans-serif;
|
||||
font-size: small;
|
||||
font-weight: normal;
|
||||
border: 1px solid #CCC;
|
||||
background-color: #F4F4F4;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
margin: 0px 0px 10px 0px;
|
||||
}
|
||||
legend.legend-style {
|
||||
font-size: 14px;
|
||||
padding: 0px 6px;
|
||||
cursor: pointer;
|
||||
margin: 0px;
|
||||
color: #444;
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
.nvtooltip {
|
||||
//position: relative !important;
|
||||
z-index: 888;
|
||||
}
|
||||
.nvtooltip table td{
|
||||
font-size: 11px !important;
|
||||
}
|
||||
legend {
|
||||
width: auto;
|
||||
border-bottom: 0px;
|
||||
}
|
||||
.navbar {
|
||||
-webkit-box-shadow: 0px 3px 3px #AAA;
|
||||
-moz-box-shadow: 0px 3px 3px #AAA;
|
||||
box-shadow: 0px 3px 3px #AAA;
|
||||
z-index: 999;
|
||||
}
|
||||
.panel.panel-primary {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.index .carousel img {
|
||||
max-height: 500px;
|
||||
}
|
||||
.index .carousel {
|
||||
overflow: hidden;
|
||||
height: 500px;
|
||||
}
|
||||
.index .carousel-caption h1 {
|
||||
font-size: 80px;
|
||||
}
|
||||
.index .carousel-caption p {
|
||||
font-size: 20px;
|
||||
}
|
||||
.index div.carousel-caption{
|
||||
background: rgba(0,0,0,0.5);
|
||||
border-radius: 20px;
|
||||
top: 150px;
|
||||
bottom: auto !important;
|
||||
}
|
||||
.index .carousel-inner > .item > img {
|
||||
margin: 0 auto;
|
||||
}
|
||||
.index {
|
||||
margin: -20px;
|
||||
}
|
||||
.index .carousel-indicators li {
|
||||
background-color: #AAA;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.index .carousel-indicators .active {
|
||||
background-color: #000;
|
||||
border: 5px solid black;
|
||||
}
|
||||
|
||||
.datasource form div.form-control {
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
.datasource form input.form-control {
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
.datasource .tooltip-inner {
|
||||
max-width: 350px;
|
||||
}
|
||||
img.loading {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.dashboard a i {
|
||||
cursor: pointer;
|
||||
}
|
||||
.dashboard i.drag {
|
||||
cursor: move !important;
|
||||
}
|
||||
.dashboard .gridster .preview-holder {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
background-color: #AAA;
|
||||
border-color: #AAA;
|
||||
opacity: 0.3;
|
||||
}
|
||||
.gridster li.widget{
|
||||
list-style-type: none;
|
||||
border-radius: 0;
|
||||
margin: 5px;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 2px 1px 5px -2px #aaa;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
}
|
||||
.dashboard .gridster .dragging,
|
||||
.dashboard .gridster .resizing {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.dashboard img.loading {
|
||||
width: 20px;
|
||||
margin: 5px;
|
||||
}
|
||||
.dashboard .title {
|
||||
text-align: center;
|
||||
}
|
||||
.dashboard .slice_title {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
padding: 5px;
|
||||
}
|
||||
.dashboard div.slice_content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dashboard div.nvtooltip {
|
||||
z-index: 888; /* this lets tool tips go on top of other slices */
|
||||
}
|
||||
|
||||
div.header {
|
||||
font-weight: bold;
|
||||
}
|
||||
li.widget:hover {
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
li.widget .chart-header {
|
||||
padding: 5px;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
li.widget .chart-header a {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
li.widget .chart-controls {
|
||||
display: none;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
li.widget .slice_container {
|
||||
overflow: auto;
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
.parcoords svg, .parcoords canvas {
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
}
|
||||
.parcoords > canvas {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.parcoords text.label {
|
||||
font: 100%;
|
||||
font-size: 12px;
|
||||
cursor: drag;
|
||||
}
|
||||
|
||||
.parcoords rect.background {
|
||||
fill: transparent;
|
||||
}
|
||||
.parcoords rect.background:hover {
|
||||
fill: rgba(120,120,120,0.2);
|
||||
}
|
||||
.parcoords .resize rect {
|
||||
fill: rgba(0,0,0,0.1);
|
||||
}
|
||||
.parcoords rect.extent {
|
||||
fill: rgba(255,255,255,0.25);
|
||||
stroke: rgba(0,0,0,0.6);
|
||||
}
|
||||
.parcoords .axis line, .parcoords .axis path {
|
||||
fill: none;
|
||||
stroke: #222;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
.parcoords canvas {
|
||||
opacity: 1;
|
||||
-moz-transition: opacity 0.3s;
|
||||
-webkit-transition: opacity 0.3s;
|
||||
-o-transition: opacity 0.3s;
|
||||
}
|
||||
.parcoords canvas.faded {
|
||||
opacity: 0.25;
|
||||
}
|
||||
.parcoords {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/* data table styles */
|
||||
.parcoords .row, .parcoords .header {
|
||||
clear: left; font-size: 12px; line-height: 18px; height: 18px;
|
||||
margin: 0px;
|
||||
}
|
||||
.parcoords .row:nth-child(odd) {
|
||||
background: rgba(0,0,0,0.05);
|
||||
}
|
||||
.parcoords .header {
|
||||
font-weight: bold;
|
||||
}
|
||||
.parcoords .cell {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
width: 100px; height: 18px;
|
||||
}
|
||||
.parcoords .col-0 {
|
||||
width: 180px;
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
// from http://bl.ocks.org/3687826
|
||||
module.exports = function(config) {
|
||||
var columns = [];
|
||||
|
||||
var dg = function(selection) {
|
||||
if (columns.length == 0) columns = d3.keys(selection.data()[0][0]);
|
||||
|
||||
// header
|
||||
selection.selectAll(".header")
|
||||
.data([true])
|
||||
.enter().append("div")
|
||||
.attr("class", "header")
|
||||
|
||||
var header = selection.select(".header")
|
||||
.selectAll(".cell")
|
||||
.data(columns);
|
||||
|
||||
header.enter().append("div")
|
||||
.attr("class", function(d,i) { return "col-" + i; })
|
||||
.classed("cell", true)
|
||||
|
||||
selection.selectAll(".header .cell")
|
||||
.text(function(d) { return d; });
|
||||
|
||||
header.exit().remove();
|
||||
|
||||
// rows
|
||||
var rows = selection.selectAll(".row")
|
||||
.data(function(d) { return d; })
|
||||
|
||||
rows.enter().append("div")
|
||||
.attr("class", "row")
|
||||
|
||||
rows.exit().remove();
|
||||
|
||||
var cells = selection.selectAll(".row").selectAll(".cell")
|
||||
.data(function(d) { return columns.map(function(col){return d[col];}) })
|
||||
|
||||
// cells
|
||||
cells.enter().append("div")
|
||||
.attr("class", function(d,i) { return "col-" + i; })
|
||||
.classed("cell", true)
|
||||
|
||||
cells.exit().remove();
|
||||
|
||||
selection.selectAll(".cell")
|
||||
.text(function(d) { return d; });
|
||||
|
||||
return dg;
|
||||
};
|
||||
|
||||
dg.columns = function(_) {
|
||||
if (!arguments.length) return columns;
|
||||
columns = _;
|
||||
return this;
|
||||
};
|
||||
|
||||
return dg;
|
||||
};
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
.codehilite .hll { background-color: #ffffcc }
|
||||
.codehilite { background: #f8f8f8; }
|
||||
.codehilite .c { color: #408080; font-style: italic } /* Comment */
|
||||
.codehilite .err { border: 1px solid #FF0000 } /* Error */
|
||||
.codehilite .k { color: #008000; font-weight: bold } /* Keyword */
|
||||
.codehilite .o { color: #666666 } /* Operator */
|
||||
.codehilite .cm { color: #408080; font-style: italic } /* Comment.Multiline */
|
||||
.codehilite .cp { color: #BC7A00 } /* Comment.Preproc */
|
||||
.codehilite .c1 { color: #408080; font-style: italic } /* Comment.Single */
|
||||
.codehilite .cs { color: #408080; font-style: italic } /* Comment.Special */
|
||||
.codehilite .gd { color: #A00000 } /* Generic.Deleted */
|
||||
.codehilite .ge { font-style: italic } /* Generic.Emph */
|
||||
.codehilite .gr { color: #FF0000 } /* Generic.Error */
|
||||
.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||
.codehilite .gi { color: #00A000 } /* Generic.Inserted */
|
||||
.codehilite .go { color: #808080 } /* Generic.Output */
|
||||
.codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
|
||||
.codehilite .gs { font-weight: bold } /* Generic.Strong */
|
||||
.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||
.codehilite .gt { color: #0040D0 } /* Generic.Traceback */
|
||||
.codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
|
||||
.codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
|
||||
.codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
|
||||
.codehilite .kp { color: #008000 } /* Keyword.Pseudo */
|
||||
.codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
|
||||
.codehilite .kt { color: #B00040 } /* Keyword.Type */
|
||||
.codehilite .m { color: #666666 } /* Literal.Number */
|
||||
.codehilite .s { color: #BA2121 } /* Literal.String */
|
||||
.codehilite .na { color: #7D9029 } /* Name.Attribute */
|
||||
.codehilite .nb { color: #008000 } /* Name.Builtin */
|
||||
.codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */
|
||||
.codehilite .no { color: #880000 } /* Name.Constant */
|
||||
.codehilite .nd { color: #AA22FF } /* Name.Decorator */
|
||||
.codehilite .ni { color: #999999; font-weight: bold } /* Name.Entity */
|
||||
.codehilite .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
|
||||
.codehilite .nf { color: #0000FF } /* Name.Function */
|
||||
.codehilite .nl { color: #A0A000 } /* Name.Label */
|
||||
.codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
|
||||
.codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */
|
||||
.codehilite .nv { color: #19177C } /* Name.Variable */
|
||||
.codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
|
||||
.codehilite .w { color: #bbbbbb } /* Text.Whitespace */
|
||||
.codehilite .mf { color: #666666 } /* Literal.Number.Float */
|
||||
.codehilite .mh { color: #666666 } /* Literal.Number.Hex */
|
||||
.codehilite .mi { color: #666666 } /* Literal.Number.Integer */
|
||||
.codehilite .mo { color: #666666 } /* Literal.Number.Oct */
|
||||
.codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */
|
||||
.codehilite .sc { color: #BA2121 } /* Literal.String.Char */
|
||||
.codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
|
||||
.codehilite .s2 { color: #BA2121 } /* Literal.String.Double */
|
||||
.codehilite .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
|
||||
.codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */
|
||||
.codehilite .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
|
||||
.codehilite .sx { color: #008000 } /* Literal.String.Other */
|
||||
.codehilite .sr { color: #BB6688 } /* Literal.String.Regex */
|
||||
.codehilite .s1 { color: #BA2121 } /* Literal.String.Single */
|
||||
.codehilite .ss { color: #19177C } /* Literal.String.Symbol */
|
||||
.codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */
|
||||
.codehilite .vc { color: #19177C } /* Name.Variable.Class */
|
||||
.codehilite .vg { color: #19177C } /* Name.Variable.Global */
|
||||
.codehilite .vi { color: #19177C } /* Name.Variable.Instance */
|
||||
.codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
/**
|
||||
* jQuery Select2 Sortable
|
||||
* - enable select2 to be sortable via normal select element
|
||||
*
|
||||
* author : Vafour
|
||||
* modified : Kevin Provance (kprovance)
|
||||
* inspired by : jQuery Chosen Sortable (https://github.com/mrhenry/jquery-chosen-sortable)
|
||||
* License : GPL
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
$.fn.extend({
|
||||
select2SortableOrder: function () {
|
||||
var $this = this.filter('[multiple]');
|
||||
|
||||
$this.each(function () {
|
||||
var $select = $(this);
|
||||
|
||||
// skip elements not select2-ed
|
||||
if (typeof ($select.data('select2')) !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
var $select2 = $select.siblings('.select2-container');
|
||||
var sorted;
|
||||
|
||||
// Opt group names
|
||||
var optArr = [];
|
||||
|
||||
$select.find('optgroup').each(function(idx, val) {
|
||||
optArr.push (val);
|
||||
});
|
||||
|
||||
$select.find('option').each(function(idx, val) {
|
||||
var groupName = $(this).parent('optgroup').prop('label');
|
||||
var optVal = this;
|
||||
|
||||
if (groupName === undefined) {
|
||||
if (this.value !== '' && !this.selected) {
|
||||
optArr.push (optVal);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
sorted = $($select2.find('.select2-choices li[class!="select2-search-field"]').map(function () {
|
||||
if (!this) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var id = $(this).data('select2Data').id;
|
||||
|
||||
return $select.find('option[value="' + id + '"]')[0];
|
||||
}));
|
||||
|
||||
sorted.push.apply(sorted, optArr);
|
||||
|
||||
$select.children().remove();
|
||||
$select.append(sorted);
|
||||
});
|
||||
|
||||
return $this;
|
||||
},
|
||||
|
||||
select2Sortable: function () {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
var $this = this.filter('[multiple]'),
|
||||
validMethods = ['destroy'];
|
||||
|
||||
if (args.length === 0 || typeof (args[0]) === 'object') {
|
||||
var defaultOptions = {
|
||||
bindOrder: 'formSubmit', // or sortableStop
|
||||
sortableOptions: {
|
||||
placeholder: 'ui-state-highlight',
|
||||
items: 'li:not(.select2-search-field)',
|
||||
tolerance: 'pointer'
|
||||
}
|
||||
};
|
||||
|
||||
var options = $.extend(defaultOptions, args[0]);
|
||||
|
||||
// Init select2 only if not already initialized to prevent select2 configuration loss
|
||||
if (typeof ($this.data('select2')) !== 'object') {
|
||||
$this.select2();
|
||||
}
|
||||
|
||||
$this.each(function () {
|
||||
var $select = $(this)
|
||||
var $select2choices = $select.siblings('.select2-container').find('.select2-choices');
|
||||
|
||||
// Init jQuery UI Sortable
|
||||
$select2choices.sortable(options.sortableOptions);
|
||||
|
||||
switch (options.bindOrder) {
|
||||
case 'sortableStop':
|
||||
// apply options ordering in sortstop event
|
||||
$select2choices.on("sortstop.select2sortable", function (event, ui) {
|
||||
$select.select2SortableOrder();
|
||||
});
|
||||
|
||||
$select.on('change', function (e) {
|
||||
$(this).select2SortableOrder();
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
// apply options ordering in form submit
|
||||
$select.closest('form').unbind('submit.select2sortable').on('submit.select2sortable', function () {
|
||||
$select.select2SortableOrder();
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (typeof (args[0] === 'string')) {
|
||||
if ($.inArray(args[0], validMethods) == -1) {
|
||||
throw "Unknown method: " + args[0];
|
||||
}
|
||||
|
||||
if (args[0] === 'destroy') {
|
||||
$this.select2SortableDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
},
|
||||
|
||||
select2SortableDestroy: function () {
|
||||
var $this = this.filter('[multiple]');
|
||||
$this.each(function () {
|
||||
var $select = $(this)
|
||||
var $select2choices = $select.parent().find('.select2-choices');
|
||||
|
||||
// unbind form submit event
|
||||
$select.closest('form').unbind('submit.select2sortable');
|
||||
|
||||
// unbind sortstop event
|
||||
$select2choices.unbind("sortstop.select2sortable");
|
||||
|
||||
// destroy select2Sortable
|
||||
$select2choices.sortable('destroy');
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
});
|
||||
}(jQuery));
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
.big_number g.axis text {
|
||||
font-size: 10px;
|
||||
font-weight: normal;
|
||||
color: gray;
|
||||
fill: gray;
|
||||
text-anchor: middle;
|
||||
alignment-baseline: middle;
|
||||
font-weight: none;
|
||||
}
|
||||
|
||||
.big_number text.big {
|
||||
stroke: black;
|
||||
text-anchor: middle;
|
||||
fill: black;
|
||||
}
|
||||
|
||||
.big_number g.tick line {
|
||||
stroke-width: 1px;
|
||||
stroke: grey;
|
||||
}
|
||||
|
||||
.big_number .domain {
|
||||
fill: none;
|
||||
stroke: black;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
// JS
|
||||
var d3 = window.d3 || require('d3');
|
||||
|
||||
// CSS
|
||||
require('./big_number.css');
|
||||
|
||||
var px = require('../javascripts/modules/panoramix.js');
|
||||
|
||||
function bigNumberVis(slice) {
|
||||
var div = d3.select(slice.selector);
|
||||
|
||||
function render() {
|
||||
d3.json(slice.jsonEndpoint(), function (error, payload) {
|
||||
//Define the percentage bounds that define color from red to green
|
||||
if (error !== null) {
|
||||
slice.error(error.responseText);
|
||||
return '';
|
||||
}
|
||||
var fd = payload.form_data;
|
||||
var json = payload.data;
|
||||
var color_range = [-1, 1];
|
||||
|
||||
var f = d3.format(fd.y_axis_format);
|
||||
var fp = d3.format('+.1%');
|
||||
var width = slice.width();
|
||||
var height = slice.height();
|
||||
var svg = div.append('svg');
|
||||
svg.attr("width", width);
|
||||
svg.attr("height", height);
|
||||
var data = json.data;
|
||||
var compare_suffix = ' ' + json.compare_suffix;
|
||||
var v_compare = null;
|
||||
var v = data[data.length - 1][1];
|
||||
if (json.compare_lag > 0) {
|
||||
var pos = data.length - (json.compare_lag + 1);
|
||||
if (pos >= 0) {
|
||||
v_compare = (v / data[pos][1]) - 1;
|
||||
}
|
||||
}
|
||||
var date_ext = d3.extent(data, function (d) {
|
||||
return d[0];
|
||||
});
|
||||
var value_ext = d3.extent(data, function (d) {
|
||||
return d[1];
|
||||
});
|
||||
|
||||
var margin = 20;
|
||||
var scale_x = d3.time.scale.utc().domain(date_ext).range([margin, width - margin]);
|
||||
var scale_y = d3.scale.linear().domain(value_ext).range([height - (margin), margin]);
|
||||
var colorRange = [d3.hsl(0, 1, 0.3), d3.hsl(120, 1, 0.3)];
|
||||
var scale_color = d3.scale
|
||||
.linear().domain(color_range)
|
||||
.interpolate(d3.interpolateHsl)
|
||||
.range(colorRange).clamp(true);
|
||||
var line = d3.svg.line()
|
||||
.x(function (d) {
|
||||
return scale_x(d[0]);
|
||||
})
|
||||
.y(function (d) {
|
||||
return scale_y(d[1]);
|
||||
})
|
||||
.interpolate("basis");
|
||||
|
||||
//Drawing trend line
|
||||
var g = svg.append('g');
|
||||
|
||||
g.append('path')
|
||||
.attr('d', function (d) {
|
||||
return line(data);
|
||||
})
|
||||
.attr('stroke-width', 5)
|
||||
.attr('opacity', 0.5)
|
||||
.attr('fill', "none")
|
||||
.attr('stroke-linecap', "round")
|
||||
.attr('stroke', "grey");
|
||||
|
||||
g = svg.append('g')
|
||||
.attr('class', 'digits')
|
||||
.attr('opacity', 1);
|
||||
|
||||
var y = height / 2;
|
||||
if (v_compare !== null) {
|
||||
y = (height / 8) * 3;
|
||||
}
|
||||
|
||||
//Printing big number
|
||||
g.append('text')
|
||||
.attr('x', width / 2)
|
||||
.attr('y', y)
|
||||
.attr('class', 'big')
|
||||
.attr('alignment-baseline', 'middle')
|
||||
.attr('id', 'bigNumber')
|
||||
.style('font-weight', 'bold')
|
||||
.style('cursor', 'pointer')
|
||||
.text(f(v))
|
||||
.style('font-size', d3.min([height, width]) / 3.5)
|
||||
.attr('fill', 'white');
|
||||
|
||||
var c = scale_color(v_compare);
|
||||
|
||||
//Printing compare %
|
||||
if (v_compare !== null) {
|
||||
g.append('text')
|
||||
.attr('x', width / 2)
|
||||
.attr('y', (height / 16) * 12)
|
||||
.text(fp(v_compare) + compare_suffix)
|
||||
.style('font-size', d3.min([height, width]) / 8)
|
||||
.style('text-anchor', 'middle')
|
||||
.attr('fill', c)
|
||||
.attr('stroke', c);
|
||||
}
|
||||
|
||||
var g_axis = svg.append('g').attr('class', 'axis').attr('opacity', 0);
|
||||
g = g_axis.append('g');
|
||||
var x_axis = d3.svg.axis()
|
||||
.scale(scale_x)
|
||||
.orient('bottom')
|
||||
.ticks(4)
|
||||
.tickFormat(px.formatDate);
|
||||
g.call(x_axis);
|
||||
g.attr('transform', 'translate(0,' + (height - margin) + ')');
|
||||
|
||||
g = g_axis.append('g').attr('transform', 'translate(' + (width - margin) + ',0)');
|
||||
var y_axis = d3.svg.axis()
|
||||
.scale(scale_y)
|
||||
.orient('left')
|
||||
.tickFormat(d3.format(fd.y_axis_format))
|
||||
.tickValues(value_ext);
|
||||
g.call(y_axis);
|
||||
g.selectAll('text')
|
||||
.style('text-anchor', 'end')
|
||||
.attr('y', '-7')
|
||||
.attr('x', '-4');
|
||||
|
||||
g.selectAll("text")
|
||||
.style('font-size', '10px');
|
||||
|
||||
div.on('mouseover', function (d) {
|
||||
var div = d3.select(this);
|
||||
div.select('path').transition().duration(500).attr('opacity', 1)
|
||||
.style('stroke-width', '2px');
|
||||
div.select('g.digits').transition().duration(500).attr('opacity', 0.1);
|
||||
div.select('g.axis').transition().duration(500).attr('opacity', 1);
|
||||
})
|
||||
.on('mouseout', function (d) {
|
||||
var div = d3.select(this);
|
||||
div.select('path').transition().duration(500).attr('opacity', 0.5)
|
||||
.style('stroke-width', '5px');
|
||||
div.select('g.digits').transition().duration(500).attr('opacity', 1);
|
||||
div.select('g.axis').transition().duration(500).attr('opacity', 0);
|
||||
});
|
||||
slice.done(payload);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
render: render,
|
||||
resize: render
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = bigNumberVis;
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
.directed_force path.link {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
.directed_force circle {
|
||||
fill: #ccc;
|
||||
stroke: #000;
|
||||
stroke-width: 1.5px;
|
||||
stroke-opacity: 1;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.directed_force text {
|
||||
fill: #000;
|
||||
font: 10px sans-serif;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
|
@ -1,175 +0,0 @@
|
|||
// JS
|
||||
var d3 = window.d3 || require('d3');
|
||||
|
||||
// CSS
|
||||
require('./directed_force.css');
|
||||
|
||||
/* Modified from http://bl.ocks.org/d3noob/5141278 */
|
||||
function directedForceVis(slice) {
|
||||
var div = d3.select(slice.selector);
|
||||
var link_length = slice.data.form_data.link_length || 200;
|
||||
var charge = slice.data.form_data.charge || -500;
|
||||
|
||||
var render = function () {
|
||||
var width = slice.width();
|
||||
var height = slice.height() - 25;
|
||||
d3.json(slice.jsonEndpoint(), function (error, json) {
|
||||
|
||||
if (error !== null) {
|
||||
slice.error(error.responseText);
|
||||
return '';
|
||||
}
|
||||
var links = json.data;
|
||||
var nodes = {};
|
||||
// Compute the distinct nodes from the links.
|
||||
links.forEach(function (link) {
|
||||
link.source = nodes[link.source] || (nodes[link.source] = {
|
||||
name: link.source
|
||||
});
|
||||
link.target = nodes[link.target] || (nodes[link.target] = {
|
||||
name: link.target
|
||||
});
|
||||
link.value = Number(link.value);
|
||||
|
||||
var target_name = link.target.name;
|
||||
var source_name = link.source.name;
|
||||
|
||||
if (nodes[target_name].total === undefined) {
|
||||
nodes[target_name].total = link.value;
|
||||
}
|
||||
if (nodes[source_name].total === undefined) {
|
||||
nodes[source_name].total = 0;
|
||||
}
|
||||
if (nodes[target_name].max === undefined) {
|
||||
nodes[target_name].max = 0;
|
||||
}
|
||||
if (link.value > nodes[target_name].max) {
|
||||
nodes[target_name].max = link.value;
|
||||
}
|
||||
if (nodes[target_name].min === undefined) {
|
||||
nodes[target_name].min = 0;
|
||||
}
|
||||
if (link.value > nodes[target_name].min) {
|
||||
nodes[target_name].min = link.value;
|
||||
}
|
||||
|
||||
nodes[target_name].total += link.value;
|
||||
});
|
||||
|
||||
var force = d3.layout.force()
|
||||
.nodes(d3.values(nodes))
|
||||
.links(links)
|
||||
.size([width, height])
|
||||
.linkDistance(link_length)
|
||||
.charge(charge)
|
||||
.on("tick", tick)
|
||||
.start();
|
||||
|
||||
var svg = div.append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height);
|
||||
|
||||
// build the arrow.
|
||||
svg.append("svg:defs").selectAll("marker")
|
||||
.data(["end"]) // Different link/path types can be defined here
|
||||
.enter().append("svg:marker") // This section adds in the arrows
|
||||
.attr("id", String)
|
||||
.attr("viewBox", "0 -5 10 10")
|
||||
.attr("refX", 15)
|
||||
.attr("refY", -1.5)
|
||||
.attr("markerWidth", 6)
|
||||
.attr("markerHeight", 6)
|
||||
.attr("orient", "auto")
|
||||
.append("svg:path")
|
||||
.attr("d", "M0,-5L10,0L0,5");
|
||||
|
||||
var edgeScale = d3.scale.linear()
|
||||
.range([0.1, 0.5]);
|
||||
// add the links and the arrows
|
||||
var path = svg.append("svg:g").selectAll("path")
|
||||
.data(force.links())
|
||||
.enter().append("svg:path")
|
||||
.attr("class", "link")
|
||||
.style("opacity", function (d) {
|
||||
return edgeScale(d.value / d.target.max);
|
||||
})
|
||||
.attr("marker-end", "url(#end)");
|
||||
|
||||
// define the nodes
|
||||
var node = svg.selectAll(".node")
|
||||
.data(force.nodes())
|
||||
.enter().append("g")
|
||||
.attr("class", "node")
|
||||
.on("mouseenter", function (d) {
|
||||
d3.select(this)
|
||||
.select("circle")
|
||||
.transition()
|
||||
.style('stroke-width', 5);
|
||||
|
||||
d3.select(this)
|
||||
.select("text")
|
||||
.transition()
|
||||
.style('font-size', 25);
|
||||
})
|
||||
.on("mouseleave", function (d) {
|
||||
d3.select(this)
|
||||
.select("circle")
|
||||
.transition()
|
||||
.style('stroke-width', 1.5);
|
||||
d3.select(this)
|
||||
.select("text")
|
||||
.transition()
|
||||
.style('font-size', 12);
|
||||
})
|
||||
.call(force.drag);
|
||||
|
||||
// add the nodes
|
||||
var ext = d3.extent(d3.values(nodes), function (d) {
|
||||
return Math.sqrt(d.total);
|
||||
});
|
||||
var circleScale = d3.scale.linear()
|
||||
.domain(ext)
|
||||
.range([3, 30]);
|
||||
|
||||
node.append("circle")
|
||||
.attr("r", function (d) {
|
||||
return circleScale(Math.sqrt(d.total));
|
||||
});
|
||||
|
||||
// add the text
|
||||
node.append("text")
|
||||
.attr("x", 6)
|
||||
.attr("dy", ".35em")
|
||||
.text(function (d) {
|
||||
return d.name;
|
||||
});
|
||||
|
||||
// add the curvy lines
|
||||
function tick() {
|
||||
path.attr("d", function (d) {
|
||||
var dx = d.target.x - d.source.x,
|
||||
dy = d.target.y - d.source.y,
|
||||
dr = Math.sqrt(dx * dx + dy * dy);
|
||||
return "M" +
|
||||
d.source.x + "," +
|
||||
d.source.y + "A" +
|
||||
dr + "," + dr + " 0 0,1 " +
|
||||
d.target.x + "," +
|
||||
d.target.y;
|
||||
});
|
||||
|
||||
node.attr("transform", function (d) {
|
||||
return "translate(" + d.x + "," + d.y + ")";
|
||||
});
|
||||
}
|
||||
|
||||
slice.done(json);
|
||||
});
|
||||
};
|
||||
return {
|
||||
render: render,
|
||||
resize: render
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = directedForceVis;
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.select2-highlighted > .filter_box {
|
||||
background-color: transparent;
|
||||
border: 1px dashed black;
|
||||
}
|
||||
|
||||
.dashboard .filter_box .slice_container > div {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
// JS
|
||||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
var d3 = window.d3 || require('d3');
|
||||
|
||||
// CSS
|
||||
require('./filter_box.css');
|
||||
require('../javascripts/panoramix-select2.js');
|
||||
|
||||
function filterBox(slice) {
|
||||
var filtersObj = {};
|
||||
var d3token = d3.select(slice.selector);
|
||||
|
||||
var fltChanged = function () {
|
||||
var val = $(this).val();
|
||||
var vals = [];
|
||||
if (val !== '') {
|
||||
vals = val.split(',');
|
||||
}
|
||||
slice.setFilter($(this).attr('name'), vals);
|
||||
};
|
||||
|
||||
var refresh = function () {
|
||||
d3token.selectAll("*").remove();
|
||||
var container = d3token
|
||||
.append('div')
|
||||
.classed('padded', true);
|
||||
|
||||
$.getJSON(slice.jsonEndpoint(), function (payload) {
|
||||
var maxes = {};
|
||||
|
||||
for (var filter in payload.data) {
|
||||
var data = payload.data[filter];
|
||||
maxes[filter] = d3.max(data, function (d) {
|
||||
return d.metric;
|
||||
});
|
||||
var id = 'fltbox__' + filter;
|
||||
|
||||
var div = container.append('div');
|
||||
|
||||
div.append("label").text(filter);
|
||||
|
||||
div.append('div')
|
||||
.attr('name', filter)
|
||||
.classed('form-control', true)
|
||||
.attr('multiple', '')
|
||||
.attr('id', id);
|
||||
|
||||
filtersObj[filter] = $('#' + id).select2({
|
||||
placeholder: "Select [" + filter + ']',
|
||||
containment: 'parent',
|
||||
dropdownAutoWidth: true,
|
||||
data: data,
|
||||
multiple: true,
|
||||
formatResult: select2Formatter
|
||||
})
|
||||
.on('change', fltChanged);
|
||||
}
|
||||
slice.done();
|
||||
|
||||
function select2Formatter(result, container /*, query, escapeMarkup*/) {
|
||||
var perc = Math.round((result.metric / maxes[result.filter]) * 100);
|
||||
var style = 'padding: 2px 5px;';
|
||||
style += "background-image: ";
|
||||
style += "linear-gradient(to right, lightgrey, lightgrey " + perc + "%, rgba(0,0,0,0) " + perc + "%";
|
||||
|
||||
$(container).attr('style', 'padding: 0px; background: white;');
|
||||
$(container).addClass('filter_box');
|
||||
return '<div style="' + style + '"><span>' + result.text + '</span></div>';
|
||||
}
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
slice.error(xhr.responseText);
|
||||
});
|
||||
};
|
||||
return {
|
||||
render: refresh,
|
||||
resize: refresh
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = filterBox;
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
.heatmap .axis text {
|
||||
font: 10px sans-serif;
|
||||
}
|
||||
|
||||
.heatmap .axis path,
|
||||
.heatmap .axis line {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.heatmap svg {
|
||||
}
|
||||
|
||||
.heatmap canvas, .heatmap img {
|
||||
image-rendering: optimizeSpeed; /* Older versions of FF */
|
||||
image-rendering: -moz-crisp-edges; /* FF 6.0+ */
|
||||
image-rendering: -webkit-optimize-contrast; /* Safari */
|
||||
image-rendering: -o-crisp-edges; /* OS X & Windows Opera (12.02+) */
|
||||
image-rendering: pixelated; /* Awesome future-browsers */
|
||||
-ms-interpolation-mode: nearest-neighbor; /* IE */
|
||||
}
|
||||
|
||||
/* from d3-tip */
|
||||
.d3-tip {
|
||||
line-height: 1;
|
||||
font-weight: bold;
|
||||
padding: 12px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Creates a small triangle extender for the tooltip */
|
||||
.d3-tip:after {
|
||||
box-sizing: border-box;
|
||||
display: inline;
|
||||
font-size: 10px;
|
||||
width: 100%;
|
||||
line-height: 1;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Northward tooltips */
|
||||
.d3-tip.n:after {
|
||||
content: "\25BC";
|
||||
margin: -1px 0 0 0;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Eastward tooltips */
|
||||
.d3-tip.e:after {
|
||||
content: "\25C0";
|
||||
margin: -4px 0 0 0;
|
||||
top: 50%;
|
||||
left: -8px;
|
||||
}
|
||||
|
||||
/* Southward tooltips */
|
||||
.d3-tip.s:after {
|
||||
content: "\25B2";
|
||||
margin: 0 0 1px 0;
|
||||
top: -8px;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Westward tooltips */
|
||||
.d3-tip.w:after {
|
||||
content: "\25B6";
|
||||
margin: -4px 0 0 -1px;
|
||||
top: 50%;
|
||||
left: 100%;
|
||||
}
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
// JS
|
||||
var $ = window.$ || require('jquery');
|
||||
var px = window.px || require('../javascripts/modules/panoramix.js');
|
||||
var d3 = require('d3');
|
||||
|
||||
d3.tip = require('d3-tip'); //using window.d3 doesn't capture events properly bc of multiple instances
|
||||
|
||||
// CSS
|
||||
require('./heatmap.css');
|
||||
|
||||
// Inspired from http://bl.ocks.org/mbostock/3074470
|
||||
// https://jsfiddle.net/cyril123/h0reyumq/
|
||||
function heatmapVis(slice) {
|
||||
var margins = {
|
||||
t: 10,
|
||||
r: 10,
|
||||
b: 50,
|
||||
l: 60
|
||||
};
|
||||
|
||||
function refresh() {
|
||||
var width = slice.width();
|
||||
var height = slice.height();
|
||||
var hmWidth = width - (margins.l + margins.r);
|
||||
var hmHeight = height - (margins.b + margins.t);
|
||||
var fp = d3.format('.3p');
|
||||
d3.json(slice.jsonEndpoint(), function (error, payload) {
|
||||
var matrix = {};
|
||||
if (error) {
|
||||
slice.error(error.responseText);
|
||||
return '';
|
||||
}
|
||||
var fd = payload.form_data;
|
||||
var data = payload.data;
|
||||
|
||||
function ordScale(k, rangeBands, reverse) {
|
||||
if (reverse === undefined) {
|
||||
reverse = false;
|
||||
}
|
||||
var domain = {};
|
||||
$.each(data, function (i, d) {
|
||||
domain[d[k]] = true;
|
||||
});
|
||||
domain = Object.keys(domain).sort(function (a, b) {
|
||||
return b - a;
|
||||
});
|
||||
if (reverse) {
|
||||
domain.reverse();
|
||||
}
|
||||
if (rangeBands === undefined) {
|
||||
return d3.scale.ordinal().domain(domain).range(d3.range(domain.length));
|
||||
} else {
|
||||
return d3.scale.ordinal().domain(domain).rangeBands(rangeBands);
|
||||
}
|
||||
}
|
||||
var xScale = ordScale('x');
|
||||
var yScale = ordScale('y', undefined, true);
|
||||
var xRbScale = ordScale('x', [0, hmWidth]);
|
||||
var yRbScale = ordScale('y', [hmHeight, 0]);
|
||||
var X = 0,
|
||||
Y = 1;
|
||||
var heatmapDim = [xRbScale.domain().length, yRbScale.domain().length];
|
||||
|
||||
var color = px.color.colorScalerFactory(fd.linear_color_scheme);
|
||||
|
||||
var scale = [
|
||||
d3.scale.linear()
|
||||
.domain([0, heatmapDim[X]])
|
||||
.range([0, hmWidth]),
|
||||
d3.scale.linear()
|
||||
.domain([0, heatmapDim[Y]])
|
||||
.range([0, hmHeight])
|
||||
];
|
||||
|
||||
var container = d3.select(slice.selector)
|
||||
.style("left", "0px")
|
||||
.style("position", "relative")
|
||||
.style("top", "0px");
|
||||
|
||||
var canvas = container.append("canvas")
|
||||
.attr("width", heatmapDim[X])
|
||||
.attr("height", heatmapDim[Y])
|
||||
.style("width", hmWidth + "px")
|
||||
.style("height", hmHeight + "px")
|
||||
.style("image-rendering", fd.canvas_image_rendering)
|
||||
.style("left", margins.l + "px")
|
||||
.style("top", margins.t + "px")
|
||||
.style("position", "absolute");
|
||||
|
||||
var svg = container.append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.style("left", "0px")
|
||||
.style("top", "0px")
|
||||
.style("position", "absolute");
|
||||
|
||||
var rect = svg.append('g')
|
||||
.attr("transform", "translate(" + margins.l + "," + margins.t + ")")
|
||||
.append('rect')
|
||||
.style('fill-opacity', 0)
|
||||
.attr('stroke', 'black')
|
||||
.attr("width", hmWidth)
|
||||
.attr("height", hmHeight);
|
||||
|
||||
var tip = d3.tip()
|
||||
.attr('class', 'd3-tip')
|
||||
.offset(function () {
|
||||
var k = d3.mouse(this);
|
||||
var x = k[0] - (hmWidth / 2);
|
||||
return [k[1] - 20, x];
|
||||
})
|
||||
.html(function (d) {
|
||||
var k = d3.mouse(this);
|
||||
var m = Math.floor(scale[0].invert(k[0]));
|
||||
var n = Math.floor(scale[1].invert(k[1]));
|
||||
if (m in matrix && n in matrix[m]) {
|
||||
var obj = matrix[m][n];
|
||||
var s = "";
|
||||
s += "<div><b>" + fd.all_columns_x + ": </b>" + obj.x + "<div>";
|
||||
s += "<div><b>" + fd.all_columns_y + ": </b>" + obj.y + "<div>";
|
||||
s += "<div><b>" + fd.metric + ": </b>" + obj.v + "<div>";
|
||||
s += "<div><b>%: </b>" + fp(obj.perc) + "<div>";
|
||||
return s;
|
||||
}
|
||||
});
|
||||
|
||||
rect.call(tip);
|
||||
|
||||
var xAxis = d3.svg.axis()
|
||||
.scale(xRbScale)
|
||||
.tickValues(xRbScale.domain().filter(
|
||||
function (d, i) {
|
||||
return !(i % (parseInt(fd.xscale_interval, 10)));
|
||||
}))
|
||||
.orient("bottom");
|
||||
var yAxis = d3.svg.axis()
|
||||
.scale(yRbScale)
|
||||
.tickValues(yRbScale.domain().filter(
|
||||
function (d, i) {
|
||||
return !(i % (parseInt(fd.yscale_interval, 10)));
|
||||
}))
|
||||
.orient("left");
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "x axis")
|
||||
.attr("transform", "translate(" + margins.l + "," + (margins.t + hmHeight) + ")")
|
||||
.call(xAxis)
|
||||
.selectAll("text")
|
||||
.style("text-anchor", "end")
|
||||
.attr("transform", "rotate(-45)")
|
||||
.style("font-weight", "bold");
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "y axis")
|
||||
.attr("transform", "translate(" + margins.l + ", 0)")
|
||||
.call(yAxis);
|
||||
|
||||
rect.on('mousemove', tip.show);
|
||||
rect.on('mouseout', tip.hide);
|
||||
|
||||
var context = canvas.node().getContext("2d");
|
||||
context.imageSmoothingEnabled = false;
|
||||
createImageObj();
|
||||
|
||||
// Compute the pixel colors; scaled by CSS.
|
||||
function createImageObj() {
|
||||
var imageObj = new Image();
|
||||
var image = context.createImageData(heatmapDim[0], heatmapDim[1]);
|
||||
var pixs = {};
|
||||
$.each(data, function (i, d) {
|
||||
var c = d3.rgb(color(d.perc));
|
||||
var x = xScale(d.x);
|
||||
var y = yScale(d.y);
|
||||
pixs[x + (y * xScale.domain().length)] = c;
|
||||
if (matrix[x] === undefined) {
|
||||
matrix[x] = {};
|
||||
}
|
||||
if (matrix[x][y] === undefined) {
|
||||
matrix[x][y] = d;
|
||||
}
|
||||
});
|
||||
|
||||
var p = -1;
|
||||
for (var i = 0; i < heatmapDim[0] * heatmapDim[1]; i++) {
|
||||
var c = pixs[i];
|
||||
var alpha = 255;
|
||||
if (c === undefined) {
|
||||
c = d3.rgb('#F00');
|
||||
alpha = 0;
|
||||
}
|
||||
image.data[++p] = c.r;
|
||||
image.data[++p] = c.g;
|
||||
image.data[++p] = c.b;
|
||||
image.data[++p] = alpha;
|
||||
}
|
||||
context.putImageData(image, 0, 0);
|
||||
imageObj.src = canvas.node().toDataURL();
|
||||
}
|
||||
slice.done();
|
||||
|
||||
});
|
||||
}
|
||||
return {
|
||||
render: refresh,
|
||||
resize: refresh
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = heatmapVis;
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
var $ = window.$ || require('jquery');
|
||||
|
||||
function iframeWidget(slice) {
|
||||
|
||||
function refresh() {
|
||||
$('#code').attr('rows', '15');
|
||||
$.getJSON(slice.jsonEndpoint(), function (payload) {
|
||||
slice.container.html('<iframe style="width:100%;"></iframe>');
|
||||
var iframe = slice.container.find('iframe');
|
||||
iframe.css('height', slice.height());
|
||||
iframe.attr('src', payload.form_data.url);
|
||||
slice.done();
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
slice.error(xhr.responseText);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
render: refresh,
|
||||
resize: refresh
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = iframeWidget;
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
var $ = window.$ || require('jquery');
|
||||
|
||||
function markupWidget(slice) {
|
||||
|
||||
function refresh() {
|
||||
$('#code').attr('rows', '15');
|
||||
|
||||
$.getJSON(slice.jsonEndpoint(), function (payload) {
|
||||
slice.container.html(payload.data.html);
|
||||
slice.done();
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
slice.error(xhr.responseText);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
render: refresh,
|
||||
resize: refresh
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = markupWidget;
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
g.dashed path {
|
||||
stroke-dasharray: 5, 5;
|
||||
}
|
||||
|
||||
.nvtooltip tr.highlight td {
|
||||
font-weight: bold;
|
||||
font-size: 15px !important;
|
||||
}
|
||||
|
|
@ -1,208 +0,0 @@
|
|||
// JS
|
||||
var $ = window.$ || require('jquery');
|
||||
var d3 = window.d3 || require('d3');
|
||||
var px = window.px || require('../javascripts/modules/panoramix.js');
|
||||
var nv = require('nvd3');
|
||||
|
||||
// CSS
|
||||
require('../node_modules/nvd3/build/nv.d3.min.css');
|
||||
require('./nvd3_vis.css');
|
||||
|
||||
function nvd3Vis(slice) {
|
||||
var chart;
|
||||
|
||||
var render = function () {
|
||||
$.getJSON(slice.jsonEndpoint(), function (payload) {
|
||||
var fd = payload.form_data;
|
||||
var viz_type = fd.viz_type;
|
||||
|
||||
var f = d3.format('.3s');
|
||||
var colorKey = 'key';
|
||||
|
||||
nv.addGraph(function () {
|
||||
switch (viz_type) {
|
||||
case 'line':
|
||||
if (fd.show_brush) {
|
||||
chart = nv.models.lineWithFocusChart();
|
||||
chart.lines2.xScale(d3.time.scale.utc());
|
||||
chart.x2Axis
|
||||
.showMaxMin(fd.x_axis_showminmax)
|
||||
.staggerLabels(true);
|
||||
} else {
|
||||
chart = nv.models.lineChart();
|
||||
}
|
||||
// To alter the tooltip header
|
||||
// chart.interactiveLayer.tooltip.headerFormatter(function(){return '';});
|
||||
chart.xScale(d3.time.scale.utc());
|
||||
chart.interpolate(fd.line_interpolation);
|
||||
chart.xAxis
|
||||
.showMaxMin(fd.x_axis_showminmax)
|
||||
.staggerLabels(true);
|
||||
break;
|
||||
|
||||
case 'bar':
|
||||
chart = nv.models.multiBarChart()
|
||||
.showControls(true)
|
||||
.groupSpacing(0.1);
|
||||
|
||||
chart.xAxis
|
||||
.showMaxMin(false)
|
||||
.staggerLabels(true);
|
||||
|
||||
chart.stacked(fd.bar_stacked);
|
||||
break;
|
||||
|
||||
case 'dist_bar':
|
||||
chart = nv.models.multiBarChart()
|
||||
.showControls(true) //Allow user to switch between 'Grouped' and 'Stacked' mode.
|
||||
.reduceXTicks(false)
|
||||
.rotateLabels(45)
|
||||
.groupSpacing(0.1); //Distance between each group of bars.
|
||||
|
||||
chart.xAxis
|
||||
.showMaxMin(false);
|
||||
|
||||
chart.stacked(fd.bar_stacked);
|
||||
break;
|
||||
|
||||
case 'pie':
|
||||
chart = nv.models.pieChart();
|
||||
colorKey = 'x';
|
||||
chart.valueFormat(f);
|
||||
if (fd.donut) {
|
||||
chart.donut(true);
|
||||
chart.labelsOutside(true);
|
||||
}
|
||||
chart.labelsOutside(true);
|
||||
chart.cornerRadius(true);
|
||||
break;
|
||||
|
||||
case 'column':
|
||||
chart = nv.models.multiBarChart()
|
||||
.reduceXTicks(false)
|
||||
.rotateLabels(45);
|
||||
break;
|
||||
|
||||
case 'compare':
|
||||
chart = nv.models.cumulativeLineChart();
|
||||
chart.xScale(d3.time.scale.utc());
|
||||
chart.xAxis
|
||||
.showMaxMin(false)
|
||||
.staggerLabels(true);
|
||||
break;
|
||||
|
||||
case 'bubble':
|
||||
var row = function (col1, col2) {
|
||||
return "<tr><td>" + col1 + "</td><td>" + col2 + "</td></tr>";
|
||||
};
|
||||
chart = nv.models.scatterChart();
|
||||
chart.showDistX(true);
|
||||
chart.showDistY(true);
|
||||
chart.tooltip.contentGenerator(function (obj) {
|
||||
var p = obj.point;
|
||||
var s = "<table>";
|
||||
s += '<tr><td style="color:' + p.color + ';"><strong>' + p[fd.entity] + '</strong> (' + p.group + ')</td></tr>';
|
||||
s += row(fd.x, f(p.x));
|
||||
s += row(fd.y, f(p.y));
|
||||
s += row(fd.size, f(p.size));
|
||||
s += "</table>";
|
||||
return s;
|
||||
});
|
||||
chart.pointRange([5, fd.max_bubble_size * fd.max_bubble_size]);
|
||||
break;
|
||||
|
||||
case 'area':
|
||||
chart = nv.models.stackedAreaChart();
|
||||
chart.style(fd.stacked_style);
|
||||
chart.xScale(d3.time.scale.utc());
|
||||
chart.xAxis
|
||||
.showMaxMin(false)
|
||||
.staggerLabels(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("Unrecognized visualization for nvd3" + viz_type);
|
||||
}
|
||||
|
||||
if ("showLegend" in chart && typeof fd.show_legend !== 'undefined') {
|
||||
chart.showLegend(fd.show_legend);
|
||||
}
|
||||
|
||||
var height = slice.height();
|
||||
height -= 15; // accounting for the staggered xAxis
|
||||
|
||||
if (chart.hasOwnProperty("x2Axis")) {
|
||||
height += 30;
|
||||
}
|
||||
chart.height(height);
|
||||
slice.container.css('height', height + 'px');
|
||||
|
||||
if ((viz_type === "line" || viz_type === "area") && fd.rich_tooltip) {
|
||||
chart.useInteractiveGuideline(true);
|
||||
}
|
||||
if (fd.y_axis_zero) {
|
||||
chart.forceY([0, 1]);
|
||||
} else if (fd.y_log_scale) {
|
||||
chart.yScale(d3.scale.log());
|
||||
}
|
||||
if (fd.x_log_scale) {
|
||||
chart.xScale(d3.scale.log());
|
||||
}
|
||||
if (viz_type === 'bubble') {
|
||||
chart.xAxis.tickFormat(d3.format('.3s'));
|
||||
} else if (fd.x_axis_format === 'smart_date') {
|
||||
chart.xAxis.tickFormat(px.formatDate);
|
||||
} else if (fd.x_axis_format !== undefined) {
|
||||
chart.xAxis.tickFormat(px.timeFormatFactory(fd.x_axis_format));
|
||||
}
|
||||
if (chart.yAxis !== undefined) {
|
||||
chart.yAxis.tickFormat(d3.format('.3s'));
|
||||
}
|
||||
|
||||
if (fd.contribution || fd.num_period_compare || viz_type === 'compare') {
|
||||
chart.yAxis.tickFormat(d3.format('.3p'));
|
||||
if (chart.y2Axis !== undefined) {
|
||||
chart.y2Axis.tickFormat(d3.format('.3p'));
|
||||
}
|
||||
} else if (fd.y_axis_format) {
|
||||
chart.yAxis.tickFormat(d3.format(fd.y_axis_format));
|
||||
|
||||
if (chart.y2Axis !== undefined) {
|
||||
chart.y2Axis.tickFormat(d3.format(fd.y_axis_format));
|
||||
}
|
||||
}
|
||||
|
||||
chart.color(function (d, i) {
|
||||
return px.color.category21(d[colorKey]);
|
||||
});
|
||||
|
||||
d3.select(slice.selector).html('');
|
||||
d3.select(slice.selector).append("svg")
|
||||
.datum(payload.data)
|
||||
.transition().duration(500)
|
||||
.attr('height', height)
|
||||
.call(chart);
|
||||
|
||||
return chart;
|
||||
});
|
||||
|
||||
slice.done(payload);
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
slice.error(xhr.responseText);
|
||||
});
|
||||
};
|
||||
|
||||
var update = function () {
|
||||
if (chart && chart.update) {
|
||||
chart.update();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
render: render,
|
||||
resize: update
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = nvd3Vis;
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
// JS
|
||||
var $ = window.$ || require('jquery');
|
||||
var d3 = window.d3 || require('d3');
|
||||
d3.parcoords = require('../vendor/parallel_coordinates/d3.parcoords.js');
|
||||
d3.divgrid = require('../vendor/parallel_coordinates/divgrid.js');
|
||||
|
||||
// CSS
|
||||
require('../vendor/parallel_coordinates/d3.parcoords.css');
|
||||
|
||||
function parallelCoordVis(slice) {
|
||||
|
||||
function refresh() {
|
||||
$('#code').attr('rows', '15');
|
||||
$.getJSON(slice.jsonEndpoint(), function (payload) {
|
||||
var data = payload.data;
|
||||
var fd = payload.form_data;
|
||||
var ext = d3.extent(data, function (d) {
|
||||
return d[fd.secondary_metric];
|
||||
});
|
||||
ext = [ext[0], (ext[1] - ext[0]) / 2, ext[1]];
|
||||
var cScale = d3.scale.linear()
|
||||
.domain(ext)
|
||||
.range(['red', 'grey', 'blue'])
|
||||
.interpolate(d3.interpolateLab);
|
||||
|
||||
var color = function (d) {
|
||||
return cScale(d[fd.secondary_metric]);
|
||||
};
|
||||
var container = d3.select(slice.selector);
|
||||
var eff_height = fd.show_datatable ? (slice.height() / 2) : slice.height();
|
||||
|
||||
container.append('div')
|
||||
.attr('id', 'parcoords_' + slice.container_id)
|
||||
.style('height', eff_height + 'px')
|
||||
.classed("parcoords", true);
|
||||
|
||||
var parcoords = d3.parcoords()('#parcoords_' + slice.container_id)
|
||||
.width(slice.width())
|
||||
.color(color)
|
||||
.alpha(0.5)
|
||||
.composite("darken")
|
||||
.height(eff_height)
|
||||
.data(payload.data)
|
||||
.render()
|
||||
.createAxes()
|
||||
.shadows()
|
||||
.reorderable()
|
||||
.brushMode("1D-axes");
|
||||
|
||||
if (fd.show_datatable) {
|
||||
// create data table, row hover highlighting
|
||||
var grid = d3.divgrid();
|
||||
container.append("div")
|
||||
.datum(data.slice(0, 10))
|
||||
.attr('id', "grid")
|
||||
.call(grid)
|
||||
.classed("parcoords", true)
|
||||
.selectAll(".row")
|
||||
.on({
|
||||
mouseover: function (d) {
|
||||
parcoords.highlight([d]);
|
||||
},
|
||||
mouseout: parcoords.unhighlight
|
||||
});
|
||||
// update data table on brush event
|
||||
parcoords.on("brush", function (d) {
|
||||
d3.select("#grid")
|
||||
.datum(d.slice(0, 10))
|
||||
.call(grid)
|
||||
.selectAll(".row")
|
||||
.on({
|
||||
mouseover: function (d) {
|
||||
parcoords.highlight([d]);
|
||||
},
|
||||
mouseout: parcoords.unhighlight
|
||||
});
|
||||
});
|
||||
}
|
||||
slice.done();
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
slice.error(xhr.responseText);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
render: refresh,
|
||||
resize: refresh
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = parallelCoordVis;
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
.gridster .widget.pivot_table {
|
||||
overflow: auto !important;
|
||||
}
|
||||
|
||||
.table tr>th {
|
||||
padding: 1px 5px !important;
|
||||
font-size: small !important;
|
||||
}
|
||||
|
||||
.table tr>td {
|
||||
padding: 1px 5px !important;
|
||||
font-size: small !important;
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
|
||||
require('datatables');
|
||||
require('./pivot_table.css');
|
||||
require('../node_modules/datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css');
|
||||
|
||||
module.exports = function (slice) {
|
||||
var container = slice.container;
|
||||
var form_data = slice.data.form_data;
|
||||
|
||||
function refresh() {
|
||||
$.getJSON(slice.jsonEndpoint(), function (json) {
|
||||
container.html(json.data);
|
||||
if (form_data.groupby.length === 1) {
|
||||
var table = container.find('table').DataTable({
|
||||
paging: false,
|
||||
searching: false
|
||||
});
|
||||
table.column('-1').order('desc').draw();
|
||||
}
|
||||
slice.done(json);
|
||||
}).fail(function (xhr) {
|
||||
slice.error(xhr.responseText);
|
||||
});
|
||||
}
|
||||
return {
|
||||
render: refresh,
|
||||
resize: refresh
|
||||
};
|
||||
};
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
.sankey .node rect {
|
||||
cursor: move;
|
||||
fill-opacity: .9;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.sankey .node text {
|
||||
pointer-events: none;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
}
|
||||
|
||||
.sankey .link {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
stroke-opacity: .2;
|
||||
}
|
||||
|
||||
.sankey .link:hover {
|
||||
stroke-opacity: .5;
|
||||
}
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
// CSS
|
||||
require('./sankey.css');
|
||||
// JS
|
||||
var px = window.px || require('../javascripts/modules/panoramix.js');
|
||||
var d3 = window.d3 || require('d3');
|
||||
d3.sankey = require('d3-sankey').sankey;
|
||||
|
||||
function sankeyVis(slice) {
|
||||
var div = d3.select(slice.selector);
|
||||
|
||||
var render = function () {
|
||||
var margin = {
|
||||
top: 5,
|
||||
right: 5,
|
||||
bottom: 5,
|
||||
left: 5
|
||||
};
|
||||
var width = slice.width() - margin.left - margin.right;
|
||||
var height = slice.height() - margin.top - margin.bottom;
|
||||
|
||||
var formatNumber = d3.format(",.0f"),
|
||||
format = function (d) {
|
||||
return formatNumber(d) + " TWh";
|
||||
};
|
||||
|
||||
var svg = div.append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
|
||||
var sankey = d3.sankey()
|
||||
.nodeWidth(15)
|
||||
.nodePadding(10)
|
||||
.size([width, height]);
|
||||
|
||||
var path = sankey.link();
|
||||
|
||||
d3.json(slice.jsonEndpoint(), function (error, json) {
|
||||
if (error !== null) {
|
||||
slice.error(error.responseText);
|
||||
return '';
|
||||
}
|
||||
var links = json.data;
|
||||
var nodes = {};
|
||||
// Compute the distinct nodes from the links.
|
||||
links.forEach(function (link) {
|
||||
link.source = nodes[link.source] || (nodes[link.source] = { name: link.source });
|
||||
link.target = nodes[link.target] || (nodes[link.target] = { name: link.target });
|
||||
link.value = Number(link.value);
|
||||
});
|
||||
nodes = d3.values(nodes);
|
||||
|
||||
sankey
|
||||
.nodes(nodes)
|
||||
.links(links)
|
||||
.layout(32);
|
||||
|
||||
var link = svg.append("g").selectAll(".link")
|
||||
.data(links)
|
||||
.enter().append("path")
|
||||
.attr("class", "link")
|
||||
.attr("d", path)
|
||||
.style("stroke-width", function (d) {
|
||||
return Math.max(1, d.dy);
|
||||
})
|
||||
.sort(function (a, b) {
|
||||
return b.dy - a.dy;
|
||||
});
|
||||
|
||||
link.append("title")
|
||||
.text(function (d) {
|
||||
return d.source.name + " → " + d.target.name + "\n" + format(d.value);
|
||||
});
|
||||
|
||||
var node = svg.append("g").selectAll(".node")
|
||||
.data(nodes)
|
||||
.enter().append("g")
|
||||
.attr("class", "node")
|
||||
.attr("transform", function (d) {
|
||||
return "translate(" + d.x + "," + d.y + ")";
|
||||
})
|
||||
.call(d3.behavior.drag()
|
||||
.origin(function (d) {
|
||||
return d;
|
||||
})
|
||||
.on("dragstart", function () {
|
||||
this.parentNode.appendChild(this);
|
||||
})
|
||||
.on("drag", dragmove));
|
||||
|
||||
node.append("rect")
|
||||
.attr("height", function (d) {
|
||||
return d.dy;
|
||||
})
|
||||
.attr("width", sankey.nodeWidth())
|
||||
.style("fill", function (d) {
|
||||
d.color = px.color.category21(d.name.replace(/ .*/, ""));
|
||||
return d.color;
|
||||
})
|
||||
.style("stroke", function (d) {
|
||||
return d3.rgb(d.color).darker(2);
|
||||
})
|
||||
.append("title")
|
||||
.text(function (d) {
|
||||
return d.name + "\n" + format(d.value);
|
||||
});
|
||||
|
||||
node.append("text")
|
||||
.attr("x", -6)
|
||||
.attr("y", function (d) {
|
||||
return d.dy / 2;
|
||||
})
|
||||
.attr("dy", ".35em")
|
||||
.attr("text-anchor", "end")
|
||||
.attr("transform", null)
|
||||
.text(function (d) {
|
||||
return d.name;
|
||||
})
|
||||
.filter(function (d) {
|
||||
return d.x < width / 2;
|
||||
})
|
||||
.attr("x", 6 + sankey.nodeWidth())
|
||||
.attr("text-anchor", "start");
|
||||
|
||||
function dragmove(d) {
|
||||
d3.select(this).attr("transform", "translate(" + d.x + "," + (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ")");
|
||||
sankey.relayout();
|
||||
link.attr("d", path);
|
||||
}
|
||||
slice.done(json);
|
||||
});
|
||||
};
|
||||
return {
|
||||
render: render,
|
||||
resize: render
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = sankeyVis;
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
.sunburst text {
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
.sunburst path {
|
||||
stroke: #333;
|
||||
stroke-width: 0.5px;
|
||||
}
|
||||
.sunburst .center-label {
|
||||
text-anchor: middle;
|
||||
fill: #000;
|
||||
pointer-events: none;
|
||||
}
|
||||
.sunburst .path-percent {
|
||||
font-size: 4em;
|
||||
}
|
||||
.sunburst .path-metrics {
|
||||
font-size: 1.75em;
|
||||
}
|
||||
.sunburst .path-ratio {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.sunburst .breadcrumbs text {
|
||||
font-weight: 600;
|
||||
font-size: 1.2em;
|
||||
text-anchor: middle;
|
||||
fill: #000;
|
||||
}
|
||||
|
||||
/* dashboard specific */
|
||||
.dashboard .sunburst text {
|
||||
font-size: 1em;
|
||||
}
|
||||
.dashboard .sunburst .path-percent {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
.dashboard .sunburst .path-metrics {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
|
@ -1,359 +0,0 @@
|
|||
var d3 = window.d3 || require('d3');
|
||||
var px = require('../javascripts/modules/panoramix.js');
|
||||
var wrapSvgText = require('../javascripts/modules/utils.js').wrapSvgText;
|
||||
|
||||
require('./sunburst.css');
|
||||
|
||||
// Modified from http://bl.ocks.org/kerryrodden/7090426
|
||||
function sunburstVis(slice) {
|
||||
var container = d3.select(slice.selector);
|
||||
|
||||
var render = function () {
|
||||
// vars with shared scope within this function
|
||||
var margin = { top: 10, right: 5, bottom: 10, left: 5 };
|
||||
var containerWidth = slice.width();
|
||||
var containerHeight = slice.height();
|
||||
var breadcrumbHeight = containerHeight * 0.085;
|
||||
var visWidth = containerWidth - margin.left - margin.right;
|
||||
var visHeight = containerHeight - margin.top - margin.bottom - breadcrumbHeight;
|
||||
var radius = Math.min(visWidth, visHeight) / 2;
|
||||
var colorByCategory = true; // color by category if primary/secondary metrics match
|
||||
|
||||
var maxBreadcrumbs, breadcrumbDims, // set based on data
|
||||
totalSize, // total size of all segments; set after loading the data.
|
||||
colorScale,
|
||||
breadcrumbs, vis, arcs, gMiddleText; // dom handles
|
||||
|
||||
// Helper + path gen functions
|
||||
var partition = d3.layout.partition()
|
||||
.size([2 * Math.PI, radius * radius])
|
||||
.value(function (d) { return d.m1; });
|
||||
|
||||
var arc = d3.svg.arc()
|
||||
.startAngle(function (d) {
|
||||
return d.x;
|
||||
})
|
||||
.endAngle(function (d) {
|
||||
return d.x + d.dx;
|
||||
})
|
||||
.innerRadius(function (d) {
|
||||
return Math.sqrt(d.y);
|
||||
})
|
||||
.outerRadius(function (d) {
|
||||
return Math.sqrt(d.y + d.dy);
|
||||
});
|
||||
|
||||
var f = d3.format(".3s");
|
||||
var fp = d3.format(".3p");
|
||||
|
||||
container.select("svg").remove();
|
||||
|
||||
var svg = container.append("svg:svg")
|
||||
.attr("width", containerWidth)
|
||||
.attr("height", containerHeight);
|
||||
|
||||
d3.json(slice.jsonEndpoint(), function (error, rawData) {
|
||||
if (error !== null) {
|
||||
slice.error(error.responseText);
|
||||
return '';
|
||||
}
|
||||
|
||||
createBreadcrumbs(rawData);
|
||||
createVisualization(rawData);
|
||||
|
||||
slice.done(rawData);
|
||||
});
|
||||
|
||||
function createBreadcrumbs(rawData) {
|
||||
var firstRowData = rawData.data[0];
|
||||
maxBreadcrumbs = (firstRowData.length - 2) + 1; // -2 bc row contains 2x metrics, +extra for %label and buffer
|
||||
|
||||
breadcrumbDims = {
|
||||
width: visWidth / maxBreadcrumbs,
|
||||
height: breadcrumbHeight *0.8, // more margin
|
||||
spacing: 3,
|
||||
tipTailWidth: 10
|
||||
};
|
||||
|
||||
breadcrumbs = svg.append("svg:g")
|
||||
.attr("class", "breadcrumbs")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
|
||||
breadcrumbs.append("svg:text")
|
||||
.attr("class", "end-label");
|
||||
}
|
||||
|
||||
// Main function to draw and set up the visualization, once we have the data.
|
||||
function createVisualization(rawData) {
|
||||
var tree = buildHierarchy(rawData.data);
|
||||
|
||||
vis = svg.append("svg:g")
|
||||
.attr("class", "sunburst-vis")
|
||||
.attr("transform", "translate(" + (margin.left + (visWidth / 2)) + "," + (margin.top + breadcrumbHeight + (visHeight / 2)) + ")")
|
||||
.on("mouseleave", mouseleave);
|
||||
|
||||
arcs = vis.append("svg:g")
|
||||
.attr("id", "arcs");
|
||||
|
||||
gMiddleText = vis.append("svg:g")
|
||||
.attr("class", "center-label");
|
||||
|
||||
// Bounding circle underneath the sunburst, to make it easier to detect
|
||||
// when the mouse leaves the parent g.
|
||||
arcs.append("svg:circle")
|
||||
.attr("r", radius)
|
||||
.style("opacity", 0);
|
||||
|
||||
// For efficiency, filter nodes to keep only those large enough to see.
|
||||
var nodes = partition.nodes(tree)
|
||||
.filter(function (d) {
|
||||
return (d.dx > 0.005); // 0.005 radians = 0.29 degrees
|
||||
});
|
||||
|
||||
var ext;
|
||||
|
||||
if (rawData.form_data.metric !== rawData.form_data.secondary_metric) {
|
||||
colorByCategory = false;
|
||||
|
||||
ext = d3.extent(nodes, function (d) {
|
||||
return d.m2 / d.m1;
|
||||
});
|
||||
|
||||
colorScale = d3.scale.linear()
|
||||
.domain([ext[0], ext[0] + ((ext[1] - ext[0]) / 2), ext[1]])
|
||||
.range(["#00D1C1", "white", "#FFB400"]);
|
||||
}
|
||||
|
||||
var path = arcs.data([tree]).selectAll("path")
|
||||
.data(nodes)
|
||||
.enter().append("svg:path")
|
||||
.attr("display", function (d) {
|
||||
return d.depth ? null : "none";
|
||||
})
|
||||
.attr("d", arc)
|
||||
.attr("fill-rule", "evenodd")
|
||||
.style("fill", function (d) {
|
||||
return colorByCategory ? px.color.category21(d.name) : colorScale(d.m2 / d.m1);
|
||||
})
|
||||
.style("opacity", 1)
|
||||
.on("mouseenter", mouseenter);
|
||||
|
||||
// Get total size of the tree = value of root node from partition.
|
||||
totalSize = path.node().__data__.value;
|
||||
}
|
||||
|
||||
// Fade all but the current sequence, and show it in the breadcrumb trail.
|
||||
function mouseenter(d) {
|
||||
|
||||
var percentage = (d.m1 / totalSize).toPrecision(3);
|
||||
var percentageString = fp(percentage);
|
||||
var metricsMatch = Math.abs(d.m1 - d.m2) < 0.000001;
|
||||
|
||||
gMiddleText.selectAll("*").remove();
|
||||
|
||||
gMiddleText.append("text")
|
||||
.attr("class", "path-percent")
|
||||
.attr("y", "-10")
|
||||
.text(percentageString);
|
||||
|
||||
gMiddleText.append("text")
|
||||
.attr("class", "path-metrics")
|
||||
.attr("y", "25")
|
||||
.text("m1: " + f(d.m1) + (metricsMatch ? "" : ", m2: " + f(d.m2)));
|
||||
|
||||
gMiddleText.append("text")
|
||||
.attr("class", "path-ratio")
|
||||
.attr("y", "50")
|
||||
.text("m2/m1: " + fp(d.m2 / d.m1));
|
||||
|
||||
var sequenceArray = getAncestors(d);
|
||||
|
||||
// Reset and fade all the segments.
|
||||
arcs.selectAll("path")
|
||||
.style("stroke-width", null)
|
||||
.style("stroke", null)
|
||||
.style("opacity", 0.3);
|
||||
|
||||
// Then highlight only those that are an ancestor of the current segment.
|
||||
arcs.selectAll("path")
|
||||
.filter(function (node) {
|
||||
return (sequenceArray.indexOf(node) >= 0);
|
||||
})
|
||||
.style("opacity", 1)
|
||||
.style("stroke-width", "2px")
|
||||
.style("stroke", "#000");
|
||||
|
||||
updateBreadcrumbs(sequenceArray, percentageString);
|
||||
}
|
||||
|
||||
// Restore everything to full opacity when moving off the visualization.
|
||||
function mouseleave(d) {
|
||||
|
||||
// Hide the breadcrumb trail
|
||||
breadcrumbs.style("visibility", "hidden");
|
||||
|
||||
gMiddleText.selectAll("*").remove();
|
||||
|
||||
// Deactivate all segments during transition.
|
||||
arcs.selectAll("path").on("mouseenter", null);
|
||||
//gMiddleText.selectAll("*").remove();
|
||||
|
||||
// Transition each segment to full opacity and then reactivate it.
|
||||
arcs.selectAll("path")
|
||||
.transition()
|
||||
.duration(200)
|
||||
.style("opacity", 1)
|
||||
.style("stroke", null)
|
||||
.style("stroke-width", null)
|
||||
.each("end", function () {
|
||||
d3.select(this).on("mouseenter", mouseenter);
|
||||
});
|
||||
}
|
||||
|
||||
// Given a node in a partition layout, return an array of all of its ancestor
|
||||
// nodes, highest first, but excluding the root.
|
||||
function getAncestors(node) {
|
||||
var path = [];
|
||||
var current = node;
|
||||
while (current.parent) {
|
||||
path.unshift(current);
|
||||
current = current.parent;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
// Generate a string that describes the points of a breadcrumb polygon.
|
||||
function breadcrumbPoints(d, i) {
|
||||
var points = [];
|
||||
points.push("0,0");
|
||||
points.push(breadcrumbDims.width + ",0");
|
||||
points.push(breadcrumbDims.width + breadcrumbDims.tipTailWidth + "," + (breadcrumbDims.height / 2));
|
||||
points.push(breadcrumbDims.width+ "," + breadcrumbDims.height);
|
||||
points.push("0," + breadcrumbDims.height);
|
||||
if (i > 0) { // Leftmost breadcrumb; don't include 6th vertex.
|
||||
points.push(breadcrumbDims.tipTailWidth + "," + (breadcrumbDims.height / 2));
|
||||
}
|
||||
return points.join(" ");
|
||||
}
|
||||
|
||||
function updateBreadcrumbs(sequenceArray, percentageString) {
|
||||
var g = breadcrumbs.selectAll("g")
|
||||
.data(sequenceArray, function (d) {
|
||||
return d.name + d.depth;
|
||||
});
|
||||
|
||||
// Add breadcrumb and label for entering nodes.
|
||||
var entering = g.enter().append("svg:g");
|
||||
|
||||
entering.append("svg:polygon")
|
||||
.attr("points", breadcrumbPoints)
|
||||
.style("fill", function (d) {
|
||||
return colorByCategory ? px.color.category21(d.name) : colorScale(d.m2 / d.m1);
|
||||
});
|
||||
|
||||
entering.append("svg:text")
|
||||
.attr("x", (breadcrumbDims.width + breadcrumbDims.tipTailWidth) / 2)
|
||||
.attr("y", breadcrumbDims.height / 4)
|
||||
.attr("dy", "0.35em")
|
||||
.attr("class", "step-label")
|
||||
.text(function (d) { return d.name; })
|
||||
.call(wrapSvgText, breadcrumbDims.width, breadcrumbDims.height / 2);
|
||||
|
||||
// Set position for entering and updating nodes.
|
||||
g.attr("transform", function (d, i) {
|
||||
return "translate(" + i * (breadcrumbDims.width + breadcrumbDims.spacing) + ", 0)";
|
||||
});
|
||||
|
||||
// Remove exiting nodes.
|
||||
g.exit().remove();
|
||||
|
||||
// Now move and update the percentage at the end.
|
||||
breadcrumbs.select(".end-label")
|
||||
.attr("x", (sequenceArray.length + 0.5) * (breadcrumbDims.width + breadcrumbDims.spacing))
|
||||
.attr("y", breadcrumbDims.height / 2)
|
||||
.attr("dy", "0.35em")
|
||||
.text(percentageString);
|
||||
|
||||
// Make the breadcrumb trail visible, if it's hidden.
|
||||
breadcrumbs.style("visibility", null);
|
||||
}
|
||||
|
||||
function buildHierarchy(rows) {
|
||||
var root = {
|
||||
name: "root",
|
||||
children: []
|
||||
};
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var row = rows[i];
|
||||
var m1 = Number(row[row.length - 2]);
|
||||
var m2 = Number(row[row.length - 1]);
|
||||
var levels = row.slice(0, row.length - 2);
|
||||
if (isNaN(m1)) { // e.g. if this is a header row
|
||||
continue;
|
||||
}
|
||||
var currentNode = root;
|
||||
for (var j = 0; j < levels.length; j++) {
|
||||
var children = currentNode.children;
|
||||
var nodeName = levels[j];
|
||||
// If the next node has the name "0", it will
|
||||
var isLeafNode = (j >= levels.length - 1) || levels[j+1] === 0;
|
||||
var childNode;
|
||||
|
||||
if (!isLeafNode) {
|
||||
// Not yet at the end of the sequence; move down the tree.
|
||||
var foundChild = false;
|
||||
for (var k = 0; k < children.length; k++) {
|
||||
if (children[k].name === nodeName) {
|
||||
childNode = children[k];
|
||||
foundChild = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If we don't already have a child node for this branch, create it.
|
||||
if (!foundChild) {
|
||||
childNode = {
|
||||
name: nodeName,
|
||||
children: []
|
||||
};
|
||||
children.push(childNode);
|
||||
}
|
||||
currentNode = childNode;
|
||||
} else if (nodeName !== 0) {
|
||||
// Reached the end of the sequence; create a leaf node.
|
||||
childNode = {
|
||||
name: nodeName,
|
||||
m1: m1,
|
||||
m2: m2
|
||||
};
|
||||
children.push(childNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function recurse(node) {
|
||||
if (node.children) {
|
||||
var sums;
|
||||
var m1 = 0;
|
||||
var m2 = 0;
|
||||
for (var i = 0; i < node.children.length; i++) {
|
||||
sums = recurse(node.children[i]);
|
||||
m1 += sums[0];
|
||||
m2 += sums[1];
|
||||
}
|
||||
node.m1 = m1;
|
||||
node.m2 = m2;
|
||||
}
|
||||
return [node.m1, node.m2];
|
||||
}
|
||||
recurse(root);
|
||||
return root;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
render: render,
|
||||
resize: render
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = sunburstVis;
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
.gridster .widget.table {
|
||||
overflow: auto !important;
|
||||
}
|
||||
|
||||
.widget.table td.filtered {
|
||||
background-color: #005a63;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.table tr>th {
|
||||
padding: 1px 5px !important;
|
||||
font-size: small !important;
|
||||
}
|
||||
|
||||
.table tr>td {
|
||||
padding: 1px 5px !important;
|
||||
font-size: small !important;
|
||||
}
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
var d3 = require('d3');
|
||||
|
||||
require('./table.css');
|
||||
require('datatables');
|
||||
require('../node_modules/datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css');
|
||||
|
||||
function tableVis(slice) {
|
||||
var data = slice.data;
|
||||
var form_data = data.form_data;
|
||||
var f = d3.format('.3s');
|
||||
var fC = d3.format('0,000');
|
||||
|
||||
function refresh() {
|
||||
$.getJSON(slice.jsonEndpoint(), onSuccess).fail(onError);
|
||||
|
||||
function onError(xhr) {
|
||||
slice.error(xhr.responseText);
|
||||
}
|
||||
|
||||
function onSuccess(json) {
|
||||
var data = json.data;
|
||||
var metrics = json.form_data.metrics;
|
||||
|
||||
function col(c) {
|
||||
var arr = [];
|
||||
for (var i = 0; i < data.records.length; i++) {
|
||||
arr.push(json.data.records[i][c]);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
var maxes = {};
|
||||
for (var i = 0; i < metrics.length; i++) {
|
||||
maxes[metrics[i]] = d3.max(col(metrics[i]));
|
||||
}
|
||||
|
||||
var table = d3.select(slice.selector).append('table')
|
||||
.classed('dataframe dataframe table table-striped table-bordered table-condensed table-hover dataTable no-footer', true);
|
||||
|
||||
table.append('thead').append('tr')
|
||||
.selectAll('th')
|
||||
.data(data.columns).enter()
|
||||
.append('th')
|
||||
.text(function (d) {
|
||||
return d;
|
||||
});
|
||||
|
||||
table.append('tbody')
|
||||
.selectAll('tr')
|
||||
.data(data.records).enter()
|
||||
.append('tr')
|
||||
.selectAll('td')
|
||||
.data(function (row, i) {
|
||||
return data.columns.map(function (c) {
|
||||
return {
|
||||
col: c,
|
||||
val: row[c],
|
||||
isMetric: metrics.indexOf(c) >= 0
|
||||
};
|
||||
});
|
||||
}).enter()
|
||||
.append('td')
|
||||
.style('background-image', function (d) {
|
||||
if (d.isMetric) {
|
||||
var perc = Math.round((d.val / maxes[d.col]) * 100);
|
||||
return "linear-gradient(to right, lightgrey, lightgrey " + perc + "%, rgba(0,0,0,0) " + perc + "%";
|
||||
}
|
||||
})
|
||||
.attr('title', function (d) {
|
||||
if (!isNaN(d.val)) {
|
||||
return fC(d.val);
|
||||
}
|
||||
})
|
||||
.attr('data-sort', function (d) {
|
||||
if (d.isMetric) {
|
||||
return d.val;
|
||||
}
|
||||
})
|
||||
.on("click", function (d) {
|
||||
if (!d.isMetric) {
|
||||
var td = d3.select(this);
|
||||
if (td.classed('filtered')) {
|
||||
slice.removeFilter(d.col, [d.val]);
|
||||
d3.select(this).classed('filtered', false);
|
||||
} else {
|
||||
d3.select(this).classed('filtered', true);
|
||||
slice.addFilter(d.col, [d.val]);
|
||||
}
|
||||
}
|
||||
})
|
||||
.style("cursor", function (d) {
|
||||
if (!d.isMetric) {
|
||||
return 'pointer';
|
||||
}
|
||||
})
|
||||
.html(function (d) {
|
||||
if (d.isMetric) {
|
||||
return f(d.val);
|
||||
} else {
|
||||
return d.val;
|
||||
}
|
||||
});
|
||||
var datatable = slice.container.find('.dataTable').DataTable({
|
||||
paging: false,
|
||||
searching: form_data.include_search
|
||||
});
|
||||
// Sorting table by main column
|
||||
if (form_data.metrics.length > 0) {
|
||||
var main_metric = form_data.metrics[0];
|
||||
datatable.column(data.columns.indexOf(main_metric)).order('desc').draw();
|
||||
}
|
||||
slice.done(json);
|
||||
slice.container.parents('.widget').find('.tooltip').remove();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
render: refresh,
|
||||
resize: function () {}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = tableVis;
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
var px = window.px || require('../javascripts/modules/panoramix.js');
|
||||
var d3 = window.d3 || require('d3');
|
||||
var cloudLayout = require('d3-cloud');
|
||||
|
||||
function wordCloudChart(slice) {
|
||||
var chart = d3.select(slice.selector);
|
||||
|
||||
function refresh() {
|
||||
d3.json(slice.jsonEndpoint(), function (error, json) {
|
||||
if (error !== null) {
|
||||
slice.error(error.responseText);
|
||||
return '';
|
||||
}
|
||||
var data = json.data;
|
||||
var range = [
|
||||
json.form_data.size_from,
|
||||
json.form_data.size_to
|
||||
];
|
||||
var rotation = json.form_data.rotation;
|
||||
var f_rotation;
|
||||
if (rotation === "square") {
|
||||
f_rotation = function () {
|
||||
return ~~(Math.random() * 2) * 90;
|
||||
};
|
||||
} else if (rotation === "flat") {
|
||||
f_rotation = function () {
|
||||
return 0;
|
||||
};
|
||||
} else {
|
||||
f_rotation = function () {
|
||||
return (~~(Math.random() * 6) - 3) * 30;
|
||||
};
|
||||
}
|
||||
var size = [slice.width(), slice.height()];
|
||||
|
||||
var scale = d3.scale.linear()
|
||||
.range(range)
|
||||
.domain(d3.extent(data, function (d) {
|
||||
return d.size;
|
||||
}));
|
||||
|
||||
var layout = cloudLayout()
|
||||
.size(size)
|
||||
.words(data)
|
||||
.padding(5)
|
||||
.rotate(f_rotation)
|
||||
.font("serif")
|
||||
.fontSize(function (d) {
|
||||
return scale(d.size);
|
||||
})
|
||||
.on("end", draw);
|
||||
|
||||
layout.start();
|
||||
|
||||
function draw(words) {
|
||||
chart.selectAll("*").remove();
|
||||
|
||||
chart.append("svg")
|
||||
.attr("width", layout.size()[0])
|
||||
.attr("height", layout.size()[1])
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + layout.size()[0] / 2 + "," + layout.size()[1] / 2 + ")")
|
||||
.selectAll("text")
|
||||
.data(words)
|
||||
.enter().append("text")
|
||||
.style("font-size", function (d) {
|
||||
return d.size + "px";
|
||||
})
|
||||
.style("font-family", "Impact")
|
||||
.style("fill", function (d) {
|
||||
return px.color.category21(d.text);
|
||||
})
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("transform", function (d) {
|
||||
return "translate(" + [d.x, d.y] + ") rotate(" + d.rotate + ")";
|
||||
})
|
||||
.text(function (d) {
|
||||
return d.text;
|
||||
});
|
||||
}
|
||||
slice.done(data);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
render: refresh,
|
||||
resize: refresh
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = wordCloudChart;
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
.world_map svg {
|
||||
background-color: #feffff;
|
||||
}
|
||||
|
||||
.world_map {
|
||||
position: relative;
|
||||
}
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
// JS
|
||||
var d3 = window.d3 || require('d3');
|
||||
//var Datamap = require('../vendor/datamaps/datamaps.all.js');
|
||||
var Datamap = require('datamaps');
|
||||
|
||||
// CSS
|
||||
require('./world_map.css');
|
||||
|
||||
function worldMapChart(slice) {
|
||||
var render = function () {
|
||||
var container = slice.container;
|
||||
var div = d3.select(slice.selector);
|
||||
|
||||
container.css('height', slice.height());
|
||||
|
||||
d3.json(slice.jsonEndpoint(), function (error, json) {
|
||||
var fd = json.form_data;
|
||||
|
||||
if (error !== null) {
|
||||
slice.error(error.responseText);
|
||||
return '';
|
||||
}
|
||||
var ext = d3.extent(json.data, function (d) {
|
||||
return d.m1;
|
||||
});
|
||||
var extRadius = d3.extent(json.data, function (d) {
|
||||
return d.m2;
|
||||
});
|
||||
var radiusScale = d3.scale.linear()
|
||||
.domain([extRadius[0], extRadius[1]])
|
||||
.range([1, fd.max_bubble_size]);
|
||||
|
||||
json.data.forEach(function (d) {
|
||||
d.radius = radiusScale(d.m2);
|
||||
});
|
||||
|
||||
var colorScale = d3.scale.linear()
|
||||
.domain([ext[0], ext[1]])
|
||||
.range(["#FFF", "black"]);
|
||||
|
||||
var d = {};
|
||||
for (var i = 0; i < json.data.length; i++) {
|
||||
var country = json.data[i];
|
||||
country.fillColor = colorScale(country.m1);
|
||||
d[country.country] = country;
|
||||
}
|
||||
|
||||
var f = d3.format('.3s');
|
||||
|
||||
container.show();
|
||||
|
||||
var map = new Datamap({
|
||||
element: slice.container.get(0),
|
||||
data: json.data,
|
||||
fills: {
|
||||
defaultFill: '#ddd'
|
||||
},
|
||||
geographyConfig: {
|
||||
popupOnHover: true,
|
||||
highlightOnHover: true,
|
||||
borderWidth: 1,
|
||||
borderColor: '#fff',
|
||||
highlightBorderColor: '#fff',
|
||||
highlightFillColor: '#005a63',
|
||||
highlightBorderWidth: 1,
|
||||
popupTemplate: function (geo, data) {
|
||||
return '<div class="hoverinfo"><strong>' + data.name + '</strong><br>' + f(data.m1) + '</div>';
|
||||
}
|
||||
},
|
||||
bubblesConfig: {
|
||||
borderWidth: 1,
|
||||
borderOpacity: 1,
|
||||
borderColor: '#005a63',
|
||||
popupOnHover: true,
|
||||
radius: null,
|
||||
popupTemplate: function (geo, data) {
|
||||
return '<div class="hoverinfo"><strong>' + data.name + '</strong><br>' + f(data.m2) + '</div>';
|
||||
},
|
||||
fillOpacity: 0.5,
|
||||
animate: true,
|
||||
highlightOnHover: true,
|
||||
highlightFillColor: '#005a63',
|
||||
highlightBorderColor: 'black',
|
||||
highlightBorderWidth: 2,
|
||||
highlightBorderOpacity: 1,
|
||||
highlightFillOpacity: 0.85,
|
||||
exitDelay: 100,
|
||||
key: JSON.stringify
|
||||
}
|
||||
});
|
||||
|
||||
map.updateChoropleth(d);
|
||||
|
||||
if (fd.show_bubbles) {
|
||||
map.bubbles(json.data);
|
||||
div.selectAll("circle.datamaps-bubble").style('fill', '#005a63');
|
||||
}
|
||||
|
||||
slice.done(json);
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
render: render,
|
||||
resize: render
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = worldMapChart;
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
var path = require('path');
|
||||
var APP_DIR = path.resolve(__dirname, './'); // input
|
||||
var BUILD_DIR = path.resolve(__dirname, './javascripts/dist'); // output
|
||||
|
||||
var config = {
|
||||
// for now generate one compiled js file per entry point / html page
|
||||
entry: {
|
||||
'css-theme': APP_DIR + '/javascripts/css-theme.js',
|
||||
dashboard: APP_DIR + '/javascripts/dashboard.js',
|
||||
explore: APP_DIR + '/javascripts/explore.js',
|
||||
featured: APP_DIR + '/javascripts/featured.js',
|
||||
sql: APP_DIR + '/javascripts/sql.js',
|
||||
standalone: APP_DIR + '/javascripts/standalone.js'
|
||||
},
|
||||
output: {
|
||||
path: BUILD_DIR,
|
||||
filename: '[name].entry.js'
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.jsx?/,
|
||||
include: APP_DIR,
|
||||
exclude: APP_DIR + '/node_modules',
|
||||
loader: 'babel'
|
||||
},
|
||||
/* for require('*.css') */
|
||||
{
|
||||
test: /\.css$/,
|
||||
include: APP_DIR,
|
||||
loader: "style-loader!css-loader"
|
||||
},
|
||||
/* for css linking images */
|
||||
{ test: /\.png$/, loader: "url-loader?limit=100000" },
|
||||
{ test: /\.jpg$/, loader: "file-loader" },
|
||||
{ test: /\.gif$/, loader: "file-loader" },
|
||||
/* for font-awesome */
|
||||
{ test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&minetype=application/font-woff" },
|
||||
{ test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "file-loader" },
|
||||
/* for require('*.less') */
|
||||
{
|
||||
test: /\.less$/,
|
||||
include: APP_DIR,
|
||||
loader: "style!css!less"
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: []
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from subprocess import Popen
|
||||
|
||||
from flask.ext.script import Manager
|
||||
from panoramix import app
|
||||
from flask.ext.migrate import MigrateCommand
|
||||
import panoramix
|
||||
from panoramix import db
|
||||
from panoramix import data, utils
|
||||
|
||||
config = app.config
|
||||
|
||||
manager = Manager(app)
|
||||
manager.add_command('db', MigrateCommand)
|
||||
|
||||
|
||||
@manager.option(
|
||||
'-d', '--debug', action='store_true',
|
||||
help="Start the web server in debug mode")
|
||||
@manager.option(
|
||||
'-p', '--port', default=config.get("PANORAMIX_WEBSERVER_PORT"),
|
||||
help="Specify the port on which to run the web server")
|
||||
@manager.option(
|
||||
'-w', '--workers', default=config.get("PANORAMIX_WORKERS", 16),
|
||||
help="Number of gunicorn web server workers to fire up")
|
||||
@manager.option(
|
||||
'-t', '--timeout', default=config.get("PANORAMIX_WEBSERVER_TIMEOUT"),
|
||||
help="Specify the timeout (seconds) for the gunicorn web server")
|
||||
def runserver(debug, port, timeout, workers):
|
||||
"""Starts a Panoramix web server"""
|
||||
debug = debug or config.get("DEBUG")
|
||||
if debug:
|
||||
app.run(
|
||||
host='0.0.0.0',
|
||||
port=int(port),
|
||||
debug=True)
|
||||
else:
|
||||
cmd = (
|
||||
"gunicorn "
|
||||
"-w {workers} "
|
||||
"--timeout {timeout} "
|
||||
"-b 0.0.0.0:{port} "
|
||||
"panoramix:app").format(**locals())
|
||||
print("Starting server with command: " + cmd)
|
||||
Popen(cmd, shell=True).wait()
|
||||
|
||||
@manager.command
|
||||
def init():
|
||||
"""Inits the Panoramix application"""
|
||||
utils.init(panoramix)
|
||||
|
||||
@manager.option(
|
||||
'-s', '--sample', action='store_true',
|
||||
help="Only load 1000 rows (faster, used for testing)")
|
||||
def load_examples(sample):
|
||||
"""Loads a set of Slices and Dashboards and a supporting dataset """
|
||||
print("Loading examples into {}".format(db))
|
||||
|
||||
data.load_css_templates()
|
||||
|
||||
print("Loading [World Bank's Health Nutrition and Population Stats]")
|
||||
data.load_world_bank_health_n_pop()
|
||||
|
||||
print("Loading [Birth names]")
|
||||
data.load_birth_names()
|
||||
|
||||
@manager.command
|
||||
def refresh_druid():
|
||||
"""Refresh all druid datasources"""
|
||||
session = db.session()
|
||||
from panoramix import models
|
||||
for cluster in session.query(models.DruidCluster).all():
|
||||
try:
|
||||
cluster.refresh_datasources()
|
||||
except Exception as e:
|
||||
print(
|
||||
"Error while processing cluster '{}'\n{}".format(
|
||||
cluster, str(e)))
|
||||
logging.exception(e)
|
||||
cluster.metadata_last_refreshed = datetime.now()
|
||||
print(
|
||||
"Refreshed metadata from cluster "
|
||||
"[" + cluster.cluster_name + "]")
|
||||
session.commit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
manager.run()
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
"""
|
||||
All configuration in this file can be overridden by providing a local_config
|
||||
in your PYTHONPATH.
|
||||
|
||||
There' a ``from local_config import *`` at the end of this file.
|
||||
"""
|
||||
import os
|
||||
from flask_appbuilder.security.manager import AUTH_DB
|
||||
# from flask_appbuilder.security.manager import (
|
||||
# AUTH_OID, AUTH_REMOTE_USER, AUTH_DB, AUTH_LDAP, AUTH_OAUTH)
|
||||
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
from dateutil import tz
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Panoramix specifix config
|
||||
# ---------------------------------------------------------
|
||||
ROW_LIMIT = 50000
|
||||
WEBSERVER_THREADS = 8
|
||||
|
||||
PANORAMIX_WEBSERVER_PORT = 8088
|
||||
PANORAMIX_WEBSERVER_TIMEOUT = 60
|
||||
|
||||
CUSTOM_SECURITY_MANAGER = None
|
||||
# ---------------------------------------------------------
|
||||
|
||||
# Your App secret key
|
||||
SECRET_KEY = '\2\1thisismyscretkey\1\2\e\y\y\h' # noqa
|
||||
|
||||
# The SQLAlchemy connection string.
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:////tmp/panoramix.db'
|
||||
# SQLALCHEMY_DATABASE_URI = 'mysql://myapp@localhost/myapp'
|
||||
# SQLALCHEMY_DATABASE_URI = 'postgresql://root:password@localhost/myapp'
|
||||
|
||||
# Flask-WTF flag for CSRF
|
||||
CSRF_ENABLED = True
|
||||
|
||||
# Whether to run the web server in debug mode or not
|
||||
DEBUG = True
|
||||
|
||||
# Whether to show the stacktrace on 500 error
|
||||
SHOW_STACKTRACE = True
|
||||
|
||||
# ------------------------------
|
||||
# GLOBALS FOR APP Builder
|
||||
# ------------------------------
|
||||
# Uncomment to setup Your App name
|
||||
APP_NAME = "Panoramix"
|
||||
|
||||
# Uncomment to setup Setup an App icon
|
||||
# APP_ICON = "/static/img/something.png"
|
||||
|
||||
# Druid query timezone
|
||||
# tz.tzutc() : Using utc timezone
|
||||
# tz.tzlocal() : Using local timezone
|
||||
# other tz can be overridden by providing a local_config
|
||||
DRUID_TZ = tz.tzutc()
|
||||
|
||||
# ----------------------------------------------------
|
||||
# AUTHENTICATION CONFIG
|
||||
# ----------------------------------------------------
|
||||
# The authentication type
|
||||
# AUTH_OID : Is for OpenID
|
||||
# AUTH_DB : Is for database (username/password()
|
||||
# AUTH_LDAP : Is for LDAP
|
||||
# AUTH_REMOTE_USER : Is for using REMOTE_USER from web server
|
||||
AUTH_TYPE = AUTH_DB
|
||||
|
||||
# Uncomment to setup Full admin role name
|
||||
# AUTH_ROLE_ADMIN = 'Admin'
|
||||
|
||||
# Uncomment to setup Public role name, no authentication needed
|
||||
# AUTH_ROLE_PUBLIC = 'Public'
|
||||
|
||||
# Will allow user self registration
|
||||
# AUTH_USER_REGISTRATION = True
|
||||
|
||||
# The default user self registration role
|
||||
# AUTH_USER_REGISTRATION_ROLE = "Public"
|
||||
|
||||
# When using LDAP Auth, setup the ldap server
|
||||
# AUTH_LDAP_SERVER = "ldap://ldapserver.new"
|
||||
|
||||
# Uncomment to setup OpenID providers example for OpenID authentication
|
||||
# OPENID_PROVIDERS = [
|
||||
# { 'name': 'Yahoo', 'url': 'https://me.yahoo.com' },
|
||||
# { 'name': 'AOL', 'url': 'http://openid.aol.com/<username>' },
|
||||
# { 'name': 'Flickr', 'url': 'http://www.flickr.com/<username>' },
|
||||
# { 'name': 'MyOpenID', 'url': 'https://www.myopenid.com' }]
|
||||
# ---------------------------------------------------
|
||||
# Babel config for translations
|
||||
# ---------------------------------------------------
|
||||
# Setup default language
|
||||
BABEL_DEFAULT_LOCALE = 'en'
|
||||
# Your application default translation path
|
||||
BABEL_DEFAULT_FOLDER = 'translations'
|
||||
# The allowed translation for you app
|
||||
LANGUAGES = {
|
||||
'en': {'flag': 'us', 'name': 'English'},
|
||||
}
|
||||
# ---------------------------------------------------
|
||||
# Image and file configuration
|
||||
# ---------------------------------------------------
|
||||
# The file upload folder, when using models with files
|
||||
UPLOAD_FOLDER = BASE_DIR + '/app/static/uploads/'
|
||||
|
||||
# The image upload folder, when using models with images
|
||||
IMG_UPLOAD_FOLDER = BASE_DIR + '/app/static/uploads/'
|
||||
|
||||
# The image upload url, when using models with images
|
||||
IMG_UPLOAD_URL = '/static/uploads/'
|
||||
# Setup image size default is (300, 200, True)
|
||||
# IMG_SIZE = (300, 200, True)
|
||||
|
||||
try:
|
||||
from panoramix_config import * # noqa
|
||||
except Exception:
|
||||
pass
|
||||
|
|
@ -1,624 +0,0 @@
|
|||
"""Loads datasets, dashboards and slices in a new panoramix instance"""
|
||||
|
||||
import gzip
|
||||
import json
|
||||
import os
|
||||
import textwrap
|
||||
|
||||
import pandas as pd
|
||||
from sqlalchemy import String, DateTime
|
||||
|
||||
from panoramix import app, db, models, utils
|
||||
|
||||
# Shortcuts
|
||||
DB = models.Database
|
||||
Slice = models.Slice
|
||||
TBL = models.SqlaTable
|
||||
Dash = models.Dashboard
|
||||
|
||||
config = app.config
|
||||
|
||||
DATA_FOLDER = os.path.join(config.get("BASE_DIR"), 'data')
|
||||
|
||||
|
||||
def get_or_create_db(session):
|
||||
print("Creating database reference")
|
||||
dbobj = session.query(DB).filter_by(database_name='main').first()
|
||||
if not dbobj:
|
||||
dbobj = DB(database_name="main")
|
||||
print(config.get("SQLALCHEMY_DATABASE_URI"))
|
||||
dbobj.sqlalchemy_uri = config.get("SQLALCHEMY_DATABASE_URI")
|
||||
session.add(dbobj)
|
||||
session.commit()
|
||||
return dbobj
|
||||
|
||||
|
||||
def merge_slice(slc):
|
||||
o = db.session.query(Slice).filter_by(slice_name=slc.slice_name).first()
|
||||
if o:
|
||||
db.session.delete(o)
|
||||
db.session.add(slc)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def get_slice_json(defaults, **kwargs):
|
||||
d = defaults.copy()
|
||||
d.update(kwargs)
|
||||
return json.dumps(d, indent=4, sort_keys=True)
|
||||
|
||||
|
||||
def load_world_bank_health_n_pop():
|
||||
"""Loads the world bank health dataset, slices and a dashboard"""
|
||||
tbl_name = 'wb_health_population'
|
||||
with gzip.open(os.path.join(DATA_FOLDER, 'countries.json.gz')) as f:
|
||||
pdf = pd.read_json(f)
|
||||
pdf.columns = [col.replace('.', '_') for col in pdf.columns]
|
||||
pdf.year = pd.to_datetime(pdf.year)
|
||||
pdf.to_sql(
|
||||
tbl_name,
|
||||
db.engine,
|
||||
if_exists='replace',
|
||||
chunksize=500,
|
||||
dtype={
|
||||
'year': DateTime(),
|
||||
'country_code': String(3),
|
||||
'country_name': String(255),
|
||||
'region': String(255),
|
||||
},
|
||||
index=False)
|
||||
|
||||
print("Creating table [wb_health_population] reference")
|
||||
tbl = db.session.query(TBL).filter_by(table_name=tbl_name).first()
|
||||
if not tbl:
|
||||
tbl = TBL(table_name=tbl_name)
|
||||
tbl.description = utils.readfile(os.path.join(DATA_FOLDER, 'countries.md'))
|
||||
tbl.main_dttm_col = 'year'
|
||||
tbl.is_featured = True
|
||||
tbl.database = get_or_create_db(db.session)
|
||||
db.session.merge(tbl)
|
||||
db.session.commit()
|
||||
tbl.fetch_metadata()
|
||||
|
||||
defaults = {
|
||||
"compare_lag": "10",
|
||||
"compare_suffix": "o10Y",
|
||||
"datasource_id": "1",
|
||||
"datasource_name": "birth_names",
|
||||
"datasource_type": "table",
|
||||
"limit": "25",
|
||||
"granularity": "year",
|
||||
"groupby": [],
|
||||
"metric": 'sum__SP_POP_TOTL',
|
||||
"metrics": ["sum__SP_POP_TOTL"],
|
||||
"row_limit": config.get("ROW_LIMIT"),
|
||||
"since": "2014-01-01",
|
||||
"until": "2014-01-01",
|
||||
"where": "",
|
||||
"markup_type": "markdown",
|
||||
"country_fieldtype": "cca3",
|
||||
"secondary_metric": "sum__SP_POP_TOTL",
|
||||
"entity": "country_code",
|
||||
"show_bubbles": "y",
|
||||
}
|
||||
|
||||
print("Creating slices")
|
||||
slices = [
|
||||
Slice(
|
||||
slice_name="Region Filter",
|
||||
viz_type='filter_box',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
viz_type='filter_box',
|
||||
groupby=['region'],
|
||||
)),
|
||||
Slice(
|
||||
slice_name="World's Population",
|
||||
viz_type='big_number',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
since='2000',
|
||||
viz_type='big_number',
|
||||
compare_lag="10",
|
||||
metric='sum__SP_POP_TOTL',
|
||||
compare_suffix="over 10Y")),
|
||||
Slice(
|
||||
slice_name="Most Populated Countries",
|
||||
viz_type='table',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
viz_type='table',
|
||||
metrics=["sum__SP_POP_TOTL"],
|
||||
groupby=['country_name'])),
|
||||
Slice(
|
||||
slice_name="Growth Rate",
|
||||
viz_type='line',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
viz_type='line',
|
||||
since="1960-01-01",
|
||||
metrics=["sum__SP_POP_TOTL"],
|
||||
num_period_compare="10",
|
||||
groupby=['country_name'])),
|
||||
Slice(
|
||||
slice_name="% Rural",
|
||||
viz_type='world_map',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
viz_type='world_map',
|
||||
metric= "sum__SP_RUR_TOTL_ZS",
|
||||
num_period_compare="10",)),
|
||||
Slice(
|
||||
slice_name="Life Expexctancy VS Rural %",
|
||||
viz_type='bubble',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
viz_type='bubble',
|
||||
since= "2011-01-01",
|
||||
until= "2011-01-01",
|
||||
series="region",
|
||||
limit="0",
|
||||
entity="country_name",
|
||||
x="sum__SP_RUR_TOTL_ZS",
|
||||
y="sum__SP_DYN_LE00_IN",
|
||||
size="sum__SP_POP_TOTL",
|
||||
max_bubble_size="50",
|
||||
flt_col_1="country_code",
|
||||
flt_op_1= "not in",
|
||||
flt_eq_1="TCA,MNP,DMA,MHL,MCO,SXM,CYM,TUV,IMY,KNA,ASM,ADO,AMA,PLW",
|
||||
num_period_compare="10",)),
|
||||
Slice(
|
||||
slice_name="Rural Breakdown",
|
||||
viz_type='sunburst',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
viz_type='sunburst',
|
||||
groupby=["region", "country_name"],
|
||||
secondary_metric="sum__SP_RUR_TOTL",
|
||||
since= "2011-01-01",
|
||||
until= "2011-01-01",)),
|
||||
Slice(
|
||||
slice_name="World's Pop Growth",
|
||||
viz_type='area',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
since="1960-01-01",
|
||||
until="now",
|
||||
viz_type='area',
|
||||
groupby=["region"],)),
|
||||
]
|
||||
for slc in slices:
|
||||
merge_slice(slc)
|
||||
|
||||
print("Creating a World's Health Bank dashboard")
|
||||
dash_name = "World's Health Bank Dashboard"
|
||||
dash = db.session.query(Dash).filter_by(dashboard_title=dash_name).first()
|
||||
|
||||
if dash:
|
||||
db.session.delete(dash)
|
||||
js = """\
|
||||
[
|
||||
{
|
||||
"size_y": 1,
|
||||
"size_x": 3,
|
||||
"col": 1,
|
||||
"slice_id": "269",
|
||||
"row": 1
|
||||
},
|
||||
{
|
||||
"size_y": 3,
|
||||
"size_x": 3,
|
||||
"col": 1,
|
||||
"slice_id": "270",
|
||||
"row": 2
|
||||
},
|
||||
{
|
||||
"size_y": 7,
|
||||
"size_x": 3,
|
||||
"col": 10,
|
||||
"slice_id": "271",
|
||||
"row": 1
|
||||
},
|
||||
{
|
||||
"size_y": 3,
|
||||
"size_x": 6,
|
||||
"col": 1,
|
||||
"slice_id": "272",
|
||||
"row": 5
|
||||
},
|
||||
{
|
||||
"size_y": 4,
|
||||
"size_x": 6,
|
||||
"col": 4,
|
||||
"slice_id": "273",
|
||||
"row": 1
|
||||
},
|
||||
{
|
||||
"size_y": 4,
|
||||
"size_x": 6,
|
||||
"col": 7,
|
||||
"slice_id": "274",
|
||||
"row": 8
|
||||
},
|
||||
{
|
||||
"size_y": 3,
|
||||
"size_x": 3,
|
||||
"col": 7,
|
||||
"slice_id": "275",
|
||||
"row": 5
|
||||
},
|
||||
{
|
||||
"size_y": 4,
|
||||
"size_x": 6,
|
||||
"col": 1,
|
||||
"slice_id": "276",
|
||||
"row": 8
|
||||
}
|
||||
]
|
||||
"""
|
||||
l = json.loads(js)
|
||||
for i, pos in enumerate(l):
|
||||
pos['slice_id'] = str(slices[i].id)
|
||||
dash = Dash(
|
||||
dashboard_title=dash_name,
|
||||
position_json=json.dumps(l, indent=4),
|
||||
slug="world_health",
|
||||
)
|
||||
for s in slices:
|
||||
dash.slices.append(s)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def load_css_templates():
|
||||
"""Loads 2 css templates to demonstrate the feature"""
|
||||
print('Creating default CSS templates')
|
||||
CSS = models.CssTemplate
|
||||
|
||||
obj = db.session.query(CSS).filter_by(template_name='Flat').first()
|
||||
if not obj:
|
||||
obj = CSS(template_name="Flat")
|
||||
css = textwrap.dedent("""\
|
||||
.gridster li.widget {
|
||||
transition: background-color 0.5s ease;
|
||||
background-color: #FAFAFA;
|
||||
border: 1px solid #CCC;
|
||||
overflow: hidden;
|
||||
box-shadow: none;
|
||||
border-radius: 0px;
|
||||
}
|
||||
.gridster li.widget:hover {
|
||||
border: 1px solid #000;
|
||||
background-color: #EAEAEA;
|
||||
}
|
||||
.navbar {
|
||||
transition: opacity 0.5s ease;
|
||||
opacity: 0.05;
|
||||
}
|
||||
.navbar:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
.chart-header .header{
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
}
|
||||
/*
|
||||
var bnbColors = [
|
||||
//rausch hackb kazan babu lima beach tirol
|
||||
'#ff5a5f', '#7b0051', '#007A87', '#00d1c1', '#8ce071', '#ffb400', '#b4a76c',
|
||||
'#ff8083', '#cc0086', '#00a1b3', '#00ffeb', '#bbedab', '#ffd266', '#cbc29a',
|
||||
'#ff3339', '#ff1ab1', '#005c66', '#00b3a5', '#55d12e', '#b37e00', '#988b4e',
|
||||
];
|
||||
*/
|
||||
""")
|
||||
obj.css = css
|
||||
db.session.merge(obj)
|
||||
db.session.commit()
|
||||
|
||||
obj = (
|
||||
db.session.query(CSS).filter_by(template_name='Courier Black').first())
|
||||
if not obj:
|
||||
obj = CSS(template_name="Courier Black")
|
||||
css = textwrap.dedent("""\
|
||||
.gridster li.widget {
|
||||
transition: background-color 0.5s ease;
|
||||
background-color: #EEE;
|
||||
border: 2px solid #444;
|
||||
overflow: hidden;
|
||||
border-radius: 15px;
|
||||
box-shadow: none;
|
||||
}
|
||||
h2 {
|
||||
color: white;
|
||||
font-size: 52px;
|
||||
}
|
||||
.navbar {
|
||||
box-shadow: none;
|
||||
}
|
||||
.gridster li.widget:hover {
|
||||
border: 2px solid #000;
|
||||
background-color: #EAEAEA;
|
||||
}
|
||||
.navbar {
|
||||
transition: opacity 0.5s ease;
|
||||
opacity: 0.05;
|
||||
}
|
||||
.navbar:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
.chart-header .header{
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
}
|
||||
.nvd3 text {
|
||||
font-size: 12px;
|
||||
font-family: inherit;
|
||||
}
|
||||
body{
|
||||
background: #000;
|
||||
font-family: Courier, Monaco, monospace;;
|
||||
}
|
||||
/*
|
||||
var bnbColors = [
|
||||
//rausch hackb kazan babu lima beach tirol
|
||||
'#ff5a5f', '#7b0051', '#007A87', '#00d1c1', '#8ce071', '#ffb400', '#b4a76c',
|
||||
'#ff8083', '#cc0086', '#00a1b3', '#00ffeb', '#bbedab', '#ffd266', '#cbc29a',
|
||||
'#ff3339', '#ff1ab1', '#005c66', '#00b3a5', '#55d12e', '#b37e00', '#988b4e',
|
||||
];
|
||||
*/
|
||||
""")
|
||||
obj.css = css
|
||||
db.session.merge(obj)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def load_birth_names():
|
||||
with gzip.open(os.path.join(DATA_FOLDER, 'birth_names.json.gz')) as f:
|
||||
pdf = pd.read_json(f)
|
||||
pdf.ds = pd.to_datetime(pdf.ds, unit='ms')
|
||||
pdf.to_sql(
|
||||
'birth_names',
|
||||
db.engine,
|
||||
if_exists='replace',
|
||||
chunksize=500,
|
||||
dtype={
|
||||
'ds': DateTime,
|
||||
'gender': String(16),
|
||||
'state': String(10),
|
||||
'name': String(255),
|
||||
},
|
||||
index=False)
|
||||
l = []
|
||||
print("Done loading table!")
|
||||
print("-" * 80)
|
||||
|
||||
print("Creating table reference")
|
||||
obj = db.session.query(TBL).filter_by(table_name='birth_names').first()
|
||||
if not obj:
|
||||
obj = TBL(table_name = 'birth_names')
|
||||
obj.main_dttm_col = 'ds'
|
||||
obj.database = get_or_create_db(db.session)
|
||||
obj.is_featured = True
|
||||
db.session.merge(obj)
|
||||
db.session.commit()
|
||||
obj.fetch_metadata()
|
||||
tbl = obj
|
||||
|
||||
defaults = {
|
||||
"compare_lag": "10",
|
||||
"compare_suffix": "o10Y",
|
||||
"datasource_id": "1",
|
||||
"datasource_name": "birth_names",
|
||||
"datasource_type": "table",
|
||||
"flt_op_1": "in",
|
||||
"limit": "25",
|
||||
"granularity": "ds",
|
||||
"groupby": [],
|
||||
"metric": 'sum__num',
|
||||
"metrics": ["sum__num"],
|
||||
"row_limit": config.get("ROW_LIMIT"),
|
||||
"since": "100 years ago",
|
||||
"until": "now",
|
||||
"viz_type": "table",
|
||||
"where": "",
|
||||
"markup_type": "markdown",
|
||||
}
|
||||
|
||||
print("Creating some slices")
|
||||
slices = [
|
||||
Slice(
|
||||
slice_name="Girls",
|
||||
viz_type='table',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
groupby=['name'],
|
||||
flt_col_1='gender',
|
||||
flt_eq_1="girl", row_limit=50)),
|
||||
Slice(
|
||||
slice_name="Boys",
|
||||
viz_type='table',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
groupby=['name'],
|
||||
flt_col_1='gender',
|
||||
flt_eq_1="boy",
|
||||
row_limit=50)),
|
||||
Slice(
|
||||
slice_name="Participants",
|
||||
viz_type='big_number',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
viz_type="big_number", granularity="ds",
|
||||
compare_lag="5", compare_suffix="over 5Y")),
|
||||
Slice(
|
||||
slice_name="Genders",
|
||||
viz_type='pie',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
viz_type="pie", groupby=['gender'])),
|
||||
Slice(
|
||||
slice_name="Genders by State",
|
||||
viz_type='dist_bar',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
flt_eq_1="other", viz_type="dist_bar",
|
||||
metrics=['sum__sum_girls', 'sum__sum_boys'],
|
||||
groupby=['state'], flt_op_1='not in', flt_col_1='state')),
|
||||
Slice(
|
||||
slice_name="Trends",
|
||||
viz_type='line',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
viz_type="line", groupby=['name'],
|
||||
granularity='ds', rich_tooltip='y', show_legend='y')),
|
||||
Slice(
|
||||
slice_name="Title",
|
||||
viz_type='markup',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
viz_type="markup", markup_type="html",
|
||||
code="""\
|
||||
<div style="text-align:center">
|
||||
<h1>Birth Names Dashboard</h1>
|
||||
<p>
|
||||
The source dataset came from
|
||||
<a href="https://github.com/hadley/babynames">[here]</a>
|
||||
</p>
|
||||
<img src="http://monblog.system-linux.net/image/tux/baby-tux_overlord59-tux.png">
|
||||
</div>
|
||||
"""
|
||||
)),
|
||||
Slice(
|
||||
slice_name="Name Cloud",
|
||||
viz_type='word_cloud',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
viz_type="word_cloud", size_from="10",
|
||||
series='name', size_to="70", rotation="square",
|
||||
limit='100')),
|
||||
Slice(
|
||||
slice_name="Pivot Table",
|
||||
viz_type='pivot_table',
|
||||
datasource_type='table',
|
||||
table=tbl,
|
||||
params=get_slice_json(
|
||||
defaults,
|
||||
viz_type="pivot_table", metrics=['sum__num'],
|
||||
groupby=['name'], columns=['state'])),
|
||||
]
|
||||
for slc in slices:
|
||||
merge_slice(slc)
|
||||
|
||||
print("Creating a dashboard")
|
||||
dash = db.session.query(Dash).filter_by(dashboard_title="Births").first()
|
||||
|
||||
if dash:
|
||||
db.session.delete(dash)
|
||||
js = """
|
||||
[
|
||||
{
|
||||
"size_y": 4,
|
||||
"size_x": 2,
|
||||
"col": 8,
|
||||
"slice_id": "85",
|
||||
"row": 7
|
||||
},
|
||||
{
|
||||
"size_y": 4,
|
||||
"size_x": 2,
|
||||
"col": 10,
|
||||
"slice_id": "86",
|
||||
"row": 7
|
||||
},
|
||||
{
|
||||
"size_y": 2,
|
||||
"size_x": 2,
|
||||
"col": 1,
|
||||
"slice_id": "87",
|
||||
"row": 1
|
||||
},
|
||||
{
|
||||
"size_y": 2,
|
||||
"size_x": 2,
|
||||
"col": 3,
|
||||
"slice_id": "88",
|
||||
"row": 1
|
||||
},
|
||||
{
|
||||
"size_y": 3,
|
||||
"size_x": 7,
|
||||
"col": 5,
|
||||
"slice_id": "89",
|
||||
"row": 4
|
||||
},
|
||||
{
|
||||
"size_y": 4,
|
||||
"size_x": 7,
|
||||
"col": 1,
|
||||
"slice_id": "90",
|
||||
"row": 7
|
||||
},
|
||||
{
|
||||
"size_y": 3,
|
||||
"size_x": 3,
|
||||
"col": 9,
|
||||
"slice_id": "91",
|
||||
"row": 1
|
||||
},
|
||||
{
|
||||
"size_y": 3,
|
||||
"size_x": 4,
|
||||
"col": 5,
|
||||
"slice_id": "92",
|
||||
"row": 1
|
||||
},
|
||||
{
|
||||
"size_y": 4,
|
||||
"size_x": 4,
|
||||
"col": 1,
|
||||
"slice_id": "93",
|
||||
"row": 3
|
||||
}
|
||||
]
|
||||
"""
|
||||
l = json.loads(js)
|
||||
for i, pos in enumerate(l):
|
||||
pos['slice_id'] = str(slices[i].id)
|
||||
dash = Dash(
|
||||
dashboard_title="Births",
|
||||
position_json=json.dumps(l, indent=4),
|
||||
slug="births",
|
||||
)
|
||||
for s in slices:
|
||||
dash.slices.append(s)
|
||||
db.session.commit()
|
||||
|
|
@ -1,355 +0,0 @@
|
|||
This data was download from the
|
||||
[World's Health Organization's website](http://data.worldbank.org/data-catalog/health-nutrition-and-population-statistics)
|
||||
|
||||
Here's the script that was used to massage the data:
|
||||
|
||||
DIR = ""
|
||||
df_country = pd.read_csv(DIR + '/HNP_Country.csv')
|
||||
df_country.columns = ['country_code'] + list(df_country.columns[1:])
|
||||
df_country = df_country[['country_code', 'Region']]
|
||||
df_country.columns = ['country_code', 'region']
|
||||
|
||||
df = pd.read_csv(DIR + '/HNP_Data.csv')
|
||||
del df['Unnamed: 60']
|
||||
df.columns = ['country_name', 'country_code'] + list(df.columns[2:])
|
||||
ndf = df.merge(df_country, how='inner')
|
||||
|
||||
dims = ('country_name', 'country_code', 'region')
|
||||
vv = [str(i) for i in range(1960, 2015)]
|
||||
mdf = pd.melt(ndf, id_vars=dims + ('Indicator Code',), value_vars=vv)
|
||||
mdf['year'] = mdf.variable + '-01-01'
|
||||
dims = dims + ('year',)
|
||||
|
||||
pdf = mdf.pivot_table(values='value', columns='Indicator Code', index=dims)
|
||||
pdf = pdf.reset_index()
|
||||
pdf.to_csv(DIR + '/countries.csv')
|
||||
pdf.to_json(DIR + '/countries.json', orient='records')
|
||||
|
||||
Here's the description of the metrics available:
|
||||
|
||||
Series | Code Indicator Name
|
||||
--- | ---
|
||||
NY.GNP.PCAP.CD | GNI per capita, Atlas method (current US$)
|
||||
SE.ADT.1524.LT.FM.ZS | Literacy rate, youth (ages 15-24), gender parity index (GPI)
|
||||
SE.ADT.1524.LT.MA.ZS | Literacy rate, youth male (% of males ages 15-24)
|
||||
SE.ADT.1524.LT.ZS | Literacy rate, youth total (% of people ages 15-24)
|
||||
SE.ADT.LITR.FE.ZS | Literacy rate, adult female (% of females ages 15 and above)
|
||||
SE.ADT.LITR.MA.ZS | Literacy rate, adult male (% of males ages 15 and above)
|
||||
SE.ADT.LITR.ZS | Literacy rate, adult total (% of people ages 15 and above)
|
||||
SE.ENR.ORPH | Ratio of school attendance of orphans to school attendance of non-orphans ages 10-14
|
||||
SE.PRM.CMPT.FE.ZS | Primary completion rate, female (% of relevant age group)
|
||||
SE.PRM.CMPT.MA.ZS | Primary completion rate, male (% of relevant age group)
|
||||
SE.PRM.CMPT.ZS | Primary completion rate, total (% of relevant age group)
|
||||
SE.PRM.ENRR | School enrollment, primary (% gross)
|
||||
SE.PRM.ENRR.FE | School enrollment, primary, female (% gross)
|
||||
SE.PRM.ENRR.MA | School enrollment, primary, male (% gross)
|
||||
SE.PRM.NENR | School enrollment, primary (% net)
|
||||
SE.PRM.NENR.FE | School enrollment, primary, female (% net)
|
||||
SE.PRM.NENR.MA | School enrollment, primary, male (% net)
|
||||
SE.SEC.ENRR | School enrollment, secondary (% gross)
|
||||
SE.SEC.ENRR.FE | School enrollment, secondary, female (% gross)
|
||||
SE.SEC.ENRR.MA | School enrollment, secondary, male (% gross)
|
||||
SE.SEC.NENR | School enrollment, secondary (% net)
|
||||
SE.SEC.NENR.FE | School enrollment, secondary, female (% net)
|
||||
SE.SEC.NENR.MA | School enrollment, secondary, male (% net)
|
||||
SE.TER.ENRR | School enrollment, tertiary (% gross)
|
||||
SE.TER.ENRR.FE | School enrollment, tertiary, female (% gross)
|
||||
SE.XPD.TOTL.GD.ZS | Government expenditure on education, total (% of GDP)
|
||||
SH.ANM.CHLD.ZS | Prevalence of anemia among children (% of children under 5)
|
||||
SH.ANM.NPRG.ZS | Prevalence of anemia among non-pregnant women (% of women ages 15-49)
|
||||
SH.CON.1524.FE.ZS | Condom use, population ages 15-24, female (% of females ages 15-24)
|
||||
SH.CON.1524.MA.ZS | Condom use, population ages 15-24, male (% of males ages 15-24)
|
||||
SH.CON.AIDS.FE.ZS | Condom use at last high-risk sex, adult female (% ages 15-49)
|
||||
SH.CON.AIDS.MA.ZS | Condom use at last high-risk sex, adult male (% ages 15-49)
|
||||
SH.DTH.COMM.ZS | Cause of death, by communicable diseases and maternal, prenatal and nutrition conditions (% of total)
|
||||
SH.DTH.IMRT | Number of infant deaths
|
||||
SH.DTH.INJR.ZS | Cause of death, by injury (% of total)
|
||||
SH.DTH.MORT | Number of under-five deaths
|
||||
SH.DTH.NCOM.ZS | Cause of death, by non-communicable diseases (% of total)
|
||||
SH.DTH.NMRT | Number of neonatal deaths
|
||||
SH.DYN.AIDS | Adults (ages 15+) living with HIV
|
||||
SH.DYN.AIDS.DH | AIDS estimated deaths (UNAIDS estimates)
|
||||
SH.DYN.AIDS.FE.ZS | Women's share of population ages 15+ living with HIV (%)
|
||||
SH.DYN.AIDS.ZS | Prevalence of HIV, total (% of population ages 15-49)
|
||||
SH.DYN.MORT | Mortality rate, under-5 (per 1,000 live births)
|
||||
SH.DYN.MORT.FE | Mortality rate, under-5, female (per 1,000 live births)
|
||||
SH.DYN.MORT.MA | Mortality rate, under-5, male (per 1,000 live births)
|
||||
SH.DYN.NMRT | Mortality rate, neonatal (per 1,000 live births)
|
||||
SH.FPL.SATI.ZS | Met need for contraception (% of married women ages 15-49)
|
||||
SH.H2O.SAFE.RU.ZS | Improved water source, rural (% of rural population with access)
|
||||
SH.H2O.SAFE.UR.ZS | Improved water source, urban (% of urban population with access)
|
||||
SH.H2O.SAFE.ZS | Improved water source (% of population with access)
|
||||
SH.HIV.0014 | Children (0-14) living with HIV
|
||||
SH.HIV.1524.FE.ZS | Prevalence of HIV, female (% ages 15-24)
|
||||
SH.HIV.1524.KW.FE.ZS | Comprehensive correct knowledge of HIV/AIDS, ages 15-24, female (2 prevent ways and reject 3 misconceptions)
|
||||
SH.HIV.1524.KW.MA.ZS | Comprehensive correct knowledge of HIV/AIDS, ages 15-24, male (2 prevent ways and reject 3 misconceptions)
|
||||
SH.HIV.1524.MA.ZS | Prevalence of HIV, male (% ages 15-24)
|
||||
SH.HIV.ARTC.ZS | Antiretroviral therapy coverage (% of people living with HIV)
|
||||
SH.HIV.KNOW.FE.ZS | % of females ages 15-49 having comprehensive correct knowledge about HIV (2 prevent ways and reject 3 misconceptions)
|
||||
SH.HIV.KNOW.MA.ZS | % of males ages 15-49 having comprehensive correct knowledge about HIV (2 prevent ways and reject 3 misconceptions)
|
||||
SH.HIV.ORPH | Children orphaned by HIV/AIDS
|
||||
SH.HIV.TOTL | Adults (ages 15+) and children (0-14 years) living with HIV
|
||||
SH.IMM.HEPB | Immunization, HepB3 (% of one-year-old children)
|
||||
SH.IMM.HIB3 | Immunization, Hib3 (% of children ages 12-23 months)
|
||||
SH.IMM.IBCG | Immunization, BCG (% of one-year-old children)
|
||||
SH.IMM.IDPT | Immunization, DPT (% of children ages 12-23 months)
|
||||
SH.IMM.MEAS | Immunization, measles (% of children ages 12-23 months)
|
||||
SH.IMM.POL3 | Immunization, Pol3 (% of one-year-old children)
|
||||
SH.MED.BEDS.ZS | Hospital beds (per 1,000 people)
|
||||
SH.MED.CMHW.P3 | Community health workers (per 1,000 people)
|
||||
SH.MED.NUMW.P3 | Nurses and midwives (per 1,000 people)
|
||||
SH.MED.PHYS.ZS | Physicians (per 1,000 people)
|
||||
SH.MLR.NETS.ZS | Use of insecticide-treated bed nets (% of under-5 population)
|
||||
SH.MLR.PREG.ZS | Use of any antimalarial drug (% of pregnant women)
|
||||
SH.MLR.SPF2.ZS | Use of Intermittent Preventive Treatment of malaria, 2+ doses of SP/Fansidar (% of pregnant women)
|
||||
SH.MLR.TRET.ZS | Children with fever receiving antimalarial drugs (% of children under age 5 with fever)
|
||||
SH.MMR.DTHS | Number of maternal deaths
|
||||
SH.MMR.LEVE | Number of weeks of maternity leave
|
||||
SH.MMR.RISK | Lifetime risk of maternal death (1 in: rate varies by country)
|
||||
SH.MMR.RISK.ZS | Lifetime risk of maternal death (%)
|
||||
SH.MMR.WAGE.ZS | Maternal leave benefits (% of wages paid in covered period)
|
||||
SH.PRG.ANEM | Prevalence of anemia among pregnant women (%)
|
||||
SH.PRG.ARTC.ZS | Antiretroviral therapy coverage (% of pregnant women living with HIV)
|
||||
SH.PRG.SYPH.ZS | Prevalence of syphilis (% of women attending antenatal care)
|
||||
SH.PRV.SMOK.FE | Smoking prevalence, females (% of adults)
|
||||
SH.PRV.SMOK.MA | Smoking prevalence, males (% of adults)
|
||||
SH.STA.ACSN | Improved sanitation facilities (% of population with access)
|
||||
SH.STA.ACSN.RU | Improved sanitation facilities, rural (% of rural population with access)
|
||||
SH.STA.ACSN.UR | Improved sanitation facilities, urban (% of urban population with access)
|
||||
SH.STA.ANV4.ZS | Pregnant women receiving prenatal care of at least four visits (% of pregnant women)
|
||||
SH.STA.ANVC.ZS | Pregnant women receiving prenatal care (%)
|
||||
SH.STA.ARIC.ZS | ARI treatment (% of children under 5 taken to a health provider)
|
||||
SH.STA.BFED.ZS | Exclusive breastfeeding (% of children under 6 months)
|
||||
SH.STA.BRTC.ZS | Births attended by skilled health staff (% of total)
|
||||
SH.STA.BRTW.ZS | Low-birthweight babies (% of births)
|
||||
SH.STA.DIAB.ZS | Diabetes prevalence (% of population ages 20 to 79)
|
||||
SH.STA.IYCF.ZS | Infant and young child feeding practices, all 3 IYCF (% children ages 6-23 months)
|
||||
SH.STA.MALN.FE.ZS | Prevalence of underweight, weight for age, female (% of children under 5)
|
||||
SH.STA.MALN.MA.ZS | Prevalence of underweight, weight for age, male (% of children under 5)
|
||||
SH.STA.MALN.ZS | Prevalence of underweight, weight for age (% of children under 5)
|
||||
SH.STA.MALR | Malaria cases reported
|
||||
SH.STA.MMRT | Maternal mortality ratio (modeled estimate, per 100,000 live births)
|
||||
SH.STA.MMRT.NE | Maternal mortality ratio (national estimate, per 100,000 live births)
|
||||
SH.STA.ORCF.ZS | Diarrhea treatment (% of children under 5 receiving oral rehydration and continued feeding)
|
||||
SH.STA.ORTH | Diarrhea treatment (% of children under 5 who received ORS packet)
|
||||
SH.STA.OW15.FE.ZS | Prevalence of overweight, female (% of female adults)
|
||||
SH.STA.OW15.MA.ZS | Prevalence of overweight, male (% of male adults)
|
||||
SH.STA.OW15.ZS | Prevalence of overweight (% of adults)
|
||||
SH.STA.OWGH.FE.ZS | Prevalence of overweight, weight for height, female (% of children under 5)
|
||||
SH.STA.OWGH.MA.ZS | Prevalence of overweight, weight for height, male (% of children under 5)
|
||||
SH.STA.OWGH.ZS | Prevalence of overweight, weight for height (% of children under 5)
|
||||
SH.STA.PNVC.ZS | Postnatal care coverage (% mothers)
|
||||
SH.STA.STNT.FE.ZS | Prevalence of stunting, height for age, female (% of children under 5)
|
||||
SH.STA.STNT.MA.ZS | Prevalence of stunting, height for age, male (% of children under 5)
|
||||
SH.STA.STNT.ZS | Prevalence of stunting, height for age (% of children under 5)
|
||||
SH.STA.WAST.FE.ZS | Prevalence of wasting, weight for height, female (% of children under 5)
|
||||
SH.STA.WAST.MA.ZS | Prevalence of wasting, weight for height, male (% of children under 5)
|
||||
SH.STA.WAST.ZS | Prevalence of wasting, weight for height (% of children under 5)
|
||||
SH.SVR.WAST.FE.ZS | Prevalence of severe wasting, weight for height, female (% of children under 5)
|
||||
SH.SVR.WAST.MA.ZS | Prevalence of severe wasting, weight for height, male (% of children under 5)
|
||||
SH.SVR.WAST.ZS | Prevalence of severe wasting, weight for height (% of children under 5)
|
||||
SH.TBS.CURE.ZS | Tuberculosis treatment success rate (% of new cases)
|
||||
SH.TBS.DTEC.ZS | Tuberculosis case detection rate (%, all forms)
|
||||
SH.TBS.INCD | Incidence of tuberculosis (per 100,000 people)
|
||||
SH.TBS.MORT | Tuberculosis death rate (per 100,000 people)
|
||||
SH.TBS.PREV | Prevalence of tuberculosis (per 100,000 population)
|
||||
SH.VAC.TTNS.ZS | Newborns protected against tetanus (%)
|
||||
SH.XPD.EXTR.ZS | External resources for health (% of total expenditure on health)
|
||||
SH.XPD.OOPC.TO.ZS | Out-of-pocket health expenditure (% of total expenditure on health)
|
||||
SH.XPD.OOPC.ZS | Out-of-pocket health expenditure (% of private expenditure on health)
|
||||
SH.XPD.PCAP | Health expenditure per capita (current US$)
|
||||
SH.XPD.PCAP.PP.KD | Health expenditure per capita, PPP (constant 2011 international $)
|
||||
SH.XPD.PRIV | Health expenditure, private (% of total health expenditure)
|
||||
SH.XPD.PRIV.ZS | Health expenditure, private (% of GDP)
|
||||
SH.XPD.PUBL | Health expenditure, public (% of total health expenditure)
|
||||
SH.XPD.PUBL.GX.ZS | Health expenditure, public (% of government expenditure)
|
||||
SH.XPD.PUBL.ZS | Health expenditure, public (% of GDP)
|
||||
SH.XPD.TOTL.CD | Health expenditure, total (current US$)
|
||||
SH.XPD.TOTL.ZS | Health expenditure, total (% of GDP)
|
||||
SI.POV.NAHC | Poverty headcount ratio at national poverty lines (% of population)
|
||||
SI.POV.RUHC | Rural poverty headcount ratio at national poverty lines (% of rural population)
|
||||
SI.POV.URHC | Urban poverty headcount ratio at national poverty lines (% of urban population)
|
||||
SL.EMP.INSV.FE.ZS | Share of women in wage employment in the nonagricultural sector (% of total nonagricultural employment)
|
||||
SL.TLF.TOTL.FE.ZS | Labor force, female (% of total labor force)
|
||||
SL.TLF.TOTL.IN | Labor force, total
|
||||
SL.UEM.TOTL.FE.ZS | Unemployment, female (% of female labor force) (modeled ILO estimate)
|
||||
SL.UEM.TOTL.MA.ZS | Unemployment, male (% of male labor force) (modeled ILO estimate)
|
||||
SL.UEM.TOTL.ZS | Unemployment, total (% of total labor force) (modeled ILO estimate)
|
||||
SM.POP.NETM | Net migration
|
||||
SN.ITK.DEFC | Number of people who are undernourished
|
||||
SN.ITK.DEFC.ZS | Prevalence of undernourishment (% of population)
|
||||
SN.ITK.SALT.ZS | Consumption of iodized salt (% of households)
|
||||
SN.ITK.VITA.ZS | Vitamin A supplementation coverage rate (% of children ages 6-59 months)
|
||||
SP.ADO.TFRT | Adolescent fertility rate (births per 1,000 women ages 15-19)
|
||||
SP.DYN.AMRT.FE | Mortality rate, adult, female (per 1,000 female adults)
|
||||
SP.DYN.AMRT.MA | Mortality rate, adult, male (per 1,000 male adults)
|
||||
SP.DYN.CBRT.IN | Birth rate, crude (per 1,000 people)
|
||||
SP.DYN.CDRT.IN | Death rate, crude (per 1,000 people)
|
||||
SP.DYN.CONU.ZS | Contraceptive prevalence (% of women ages 15-49)
|
||||
SP.DYN.IMRT.FE.IN | Mortality rate, infant, female (per 1,000 live births)
|
||||
SP.DYN.IMRT.IN | Mortality rate, infant (per 1,000 live births)
|
||||
SP.DYN.IMRT.MA.IN | Mortality rate, infant, male (per 1,000 live births)
|
||||
SP.DYN.LE00.FE.IN | Life expectancy at birth, female (years)
|
||||
SP.DYN.LE00.IN | Life expectancy at birth, total (years)
|
||||
SP.DYN.LE00.MA.IN | Life expectancy at birth, male (years)
|
||||
SP.DYN.SMAM.FE | Mean age at first marriage, female
|
||||
SP.DYN.SMAM.MA | Mean age at first marriage, male
|
||||
SP.DYN.TFRT.IN | Fertility rate, total (births per woman)
|
||||
SP.DYN.TO65.FE.ZS | Survival to age 65, female (% of cohort)
|
||||
SP.DYN.TO65.MA.ZS | Survival to age 65, male (% of cohort)
|
||||
SP.DYN.WFRT | Wanted fertility rate (births per woman)
|
||||
SP.HOU.FEMA.ZS | Female headed households (% of households with a female head)
|
||||
SP.MTR.1519.ZS | Teenage mothers (% of women ages 15-19 who have had children or are currently pregnant)
|
||||
SP.POP.0004.FE | Population ages 0-4, female
|
||||
SP.POP.0004.FE.5Y | Population ages 0-4, female (% of female population)
|
||||
SP.POP.0004.MA | Population ages 0-4, male
|
||||
SP.POP.0004.MA.5Y | Population ages 0-4, male (% of male population)
|
||||
SP.POP.0014.FE.ZS | Population ages 0-14, female (% of total)
|
||||
SP.POP.0014.MA.ZS | Population ages 0-14, male (% of total)
|
||||
SP.POP.0014.TO | Population ages 0-14, total
|
||||
SP.POP.0014.TO.ZS | Population ages 0-14 (% of total)
|
||||
SP.POP.0509.FE | Population ages 5-9, female
|
||||
SP.POP.0509.FE.5Y | Population ages 5-9, female (% of female population)
|
||||
SP.POP.0509.MA | Population ages 5-9, male
|
||||
SP.POP.0509.MA.5Y | Population ages 5-9, male (% of male population)
|
||||
SP.POP.1014.FE | Population ages 10-14, female
|
||||
SP.POP.1014.FE.5Y | Population ages 10-14, female (% of female population)
|
||||
SP.POP.1014.MA | Population ages 10-14, male
|
||||
SP.POP.1014.MA.5Y | Population ages 10-14, male (% of male population)
|
||||
SP.POP.1519.FE | Population ages 15-19, female
|
||||
SP.POP.1519.FE.5Y | Population ages 15-19, female (% of female population)
|
||||
SP.POP.1519.MA | Population ages 15-19, male
|
||||
SP.POP.1519.MA.5Y | Population ages 15-19, male (% of male population)
|
||||
SP.POP.1564.FE.ZS | Population ages 15-64, female (% of total)
|
||||
SP.POP.1564.MA.ZS | Population ages 15-64, male (% of total)
|
||||
SP.POP.1564.TO | Population ages 15-64, total
|
||||
SP.POP.1564.TO.ZS | Population ages 15-64 (% of total)
|
||||
SP.POP.2024.FE | Population ages 20-24, female
|
||||
SP.POP.2024.FE.5Y | Population ages 20-24, female (% of female population)
|
||||
SP.POP.2024.MA | Population ages 20-24, male
|
||||
SP.POP.2024.MA.5Y | Population ages 20-24, male (% of male population)
|
||||
SP.POP.2529.FE | Population ages 25-29, female
|
||||
SP.POP.2529.FE.5Y | Population ages 25-29, female (% of female population)
|
||||
SP.POP.2529.MA | Population ages 25-29, male
|
||||
SP.POP.2529.MA.5Y | Population ages 25-29, male (% of male population)
|
||||
SP.POP.3034.FE | Population ages 30-34, female
|
||||
SP.POP.3034.FE.5Y | Population ages 30-34, female (% of female population)
|
||||
SP.POP.3034.MA | Population ages 30-34, male
|
||||
SP.POP.3034.MA.5Y | Population ages 30-34, male (% of male population)
|
||||
SP.POP.3539.FE | Population ages 35-39, female
|
||||
SP.POP.3539.FE.5Y | Population ages 35-39, female (% of female population)
|
||||
SP.POP.3539.MA | Population ages 35-39, male
|
||||
SP.POP.3539.MA.5Y | Population ages 35-39, male (% of male population)
|
||||
SP.POP.4044.FE | Population ages 40-44, female
|
||||
SP.POP.4044.FE.5Y | Population ages 40-44, female (% of female population)
|
||||
SP.POP.4044.MA | Population ages 40-44, male
|
||||
SP.POP.4044.MA.5Y | Population ages 40-44, male (% of male population)
|
||||
SP.POP.4549.FE | Population ages 45-49, female
|
||||
SP.POP.4549.FE.5Y | Population ages 45-49, female (% of female population)
|
||||
SP.POP.4549.MA | Population ages 45-49, male
|
||||
SP.POP.4549.MA.5Y | Population ages 45-49, male (% of male population)
|
||||
SP.POP.5054.FE | Population ages 50-54, female
|
||||
SP.POP.5054.FE.5Y | Population ages 50-54, female (% of female population)
|
||||
SP.POP.5054.MA | Population ages 50-54, male
|
||||
SP.POP.5054.MA.5Y | Population ages 50-54, male (% of male population)
|
||||
SP.POP.5559.FE | Population ages 55-59, female
|
||||
SP.POP.5559.FE.5Y | Population ages 55-59, female (% of female population)
|
||||
SP.POP.5559.MA | Population ages 55-59, male
|
||||
SP.POP.5559.MA.5Y | Population ages 55-59, male (% of male population)
|
||||
SP.POP.6064.FE | Population ages 60-64, female
|
||||
SP.POP.6064.FE.5Y | Population ages 60-64, female (% of female population)
|
||||
SP.POP.6064.MA | Population ages 60-64, male
|
||||
SP.POP.6064.MA.5Y | Population ages 60-64, male (% of male population)
|
||||
SP.POP.6569.FE | Population ages 65-69, female
|
||||
SP.POP.6569.FE.5Y | Population ages 65-69, female (% of female population)
|
||||
SP.POP.6569.MA | Population ages 65-69, male
|
||||
SP.POP.6569.MA.5Y | Population ages 65-69, male (% of male population)
|
||||
SP.POP.65UP.FE.ZS | Population ages 65 and above, female (% of total)
|
||||
SP.POP.65UP.MA.ZS | Population ages 65 and above, male (% of total)
|
||||
SP.POP.65UP.TO | Population ages 65 and above, total
|
||||
SP.POP.65UP.TO.ZS | Population ages 65 and above (% of total)
|
||||
SP.POP.7074.FE | Population ages 70-74, female
|
||||
SP.POP.7074.FE.5Y | Population ages 70-74, female (% of female population)
|
||||
SP.POP.7074.MA | Population ages 70-74, male
|
||||
SP.POP.7074.MA.5Y | Population ages 70-74, male (% of male population)
|
||||
SP.POP.7579.FE | Population ages 75-79, female
|
||||
SP.POP.7579.FE.5Y | Population ages 75-79, female (% of female population)
|
||||
SP.POP.7579.MA | Population ages 75-79, male
|
||||
SP.POP.7579.MA.5Y | Population ages 75-79, male (% of male population)
|
||||
SP.POP.80UP.FE | Population ages 80 and above, female
|
||||
SP.POP.80UP.FE.5Y | Population ages 80 and above, female (% of female population)
|
||||
SP.POP.80UP.MA | Population ages 80 and above, male
|
||||
SP.POP.80UP.MA.5Y | Population ages 80 and above, male (% of male population)
|
||||
SP.POP.AG00.FE.IN | Age population, age 0, female, interpolated
|
||||
SP.POP.AG00.MA.IN | Age population, age 0, male, interpolated
|
||||
SP.POP.AG01.FE.IN | Age population, age 01, female, interpolated
|
||||
SP.POP.AG01.MA.IN | Age population, age 01, male, interpolated
|
||||
SP.POP.AG02.FE.IN | Age population, age 02, female, interpolated
|
||||
SP.POP.AG02.MA.IN | Age population, age 02, male, interpolated
|
||||
SP.POP.AG03.FE.IN | Age population, age 03, female, interpolated
|
||||
SP.POP.AG03.MA.IN | Age population, age 03, male, interpolated
|
||||
SP.POP.AG04.FE.IN | Age population, age 04, female, interpolated
|
||||
SP.POP.AG04.MA.IN | Age population, age 04, male, interpolated
|
||||
SP.POP.AG05.FE.IN | Age population, age 05, female, interpolated
|
||||
SP.POP.AG05.MA.IN | Age population, age 05, male, interpolated
|
||||
SP.POP.AG06.FE.IN | Age population, age 06, female, interpolated
|
||||
SP.POP.AG06.MA.IN | Age population, age 06, male, interpolated
|
||||
SP.POP.AG07.FE.IN | Age population, age 07, female, interpolated
|
||||
SP.POP.AG07.MA.IN | Age population, age 07, male, interpolated
|
||||
SP.POP.AG08.FE.IN | Age population, age 08, female, interpolated
|
||||
SP.POP.AG08.MA.IN | Age population, age 08, male, interpolated
|
||||
SP.POP.AG09.FE.IN | Age population, age 09, female, interpolated
|
||||
SP.POP.AG09.MA.IN | Age population, age 09, male, interpolated
|
||||
SP.POP.AG10.FE.IN | Age population, age 10, female, interpolated
|
||||
SP.POP.AG10.MA.IN | Age population, age 10, male
|
||||
SP.POP.AG11.FE.IN | Age population, age 11, female, interpolated
|
||||
SP.POP.AG11.MA.IN | Age population, age 11, male
|
||||
SP.POP.AG12.FE.IN | Age population, age 12, female, interpolated
|
||||
SP.POP.AG12.MA.IN | Age population, age 12, male
|
||||
SP.POP.AG13.FE.IN | Age population, age 13, female, interpolated
|
||||
SP.POP.AG13.MA.IN | Age population, age 13, male
|
||||
SP.POP.AG14.FE.IN | Age population, age 14, female, interpolated
|
||||
SP.POP.AG14.MA.IN | Age population, age 14, male
|
||||
SP.POP.AG15.FE.IN | Age population, age 15, female, interpolated
|
||||
SP.POP.AG15.MA.IN | Age population, age 15, male, interpolated
|
||||
SP.POP.AG16.FE.IN | Age population, age 16, female, interpolated
|
||||
SP.POP.AG16.MA.IN | Age population, age 16, male, interpolated
|
||||
SP.POP.AG17.FE.IN | Age population, age 17, female, interpolated
|
||||
SP.POP.AG17.MA.IN | Age population, age 17, male, interpolated
|
||||
SP.POP.AG18.FE.IN | Age population, age 18, female, interpolated
|
||||
SP.POP.AG18.MA.IN | Age population, age 18, male, interpolated
|
||||
SP.POP.AG19.FE.IN | Age population, age 19, female, interpolated
|
||||
SP.POP.AG19.MA.IN | Age population, age 19, male, interpolated
|
||||
SP.POP.AG20.FE.IN | Age population, age 20, female, interpolated
|
||||
SP.POP.AG20.MA.IN | Age population, age 20, male, interpolated
|
||||
SP.POP.AG21.FE.IN | Age population, age 21, female, interpolated
|
||||
SP.POP.AG21.MA.IN | Age population, age 21, male, interpolated
|
||||
SP.POP.AG22.FE.IN | Age population, age 22, female, interpolated
|
||||
SP.POP.AG22.MA.IN | Age population, age 22, male, interpolated
|
||||
SP.POP.AG23.FE.IN | Age population, age 23, female, interpolated
|
||||
SP.POP.AG23.MA.IN | Age population, age 23, male, interpolated
|
||||
SP.POP.AG24.FE.IN | Age population, age 24, female, interpolated
|
||||
SP.POP.AG24.MA.IN | Age population, age 24, male, interpolated
|
||||
SP.POP.AG25.FE.IN | Age population, age 25, female, interpolated
|
||||
SP.POP.AG25.MA.IN | Age population, age 25, male, interpolated
|
||||
SP.POP.BRTH.MF | Sex ratio at birth (male births per female births)
|
||||
SP.POP.DPND | Age dependency ratio (% of working-age population)
|
||||
SP.POP.DPND.OL | Age dependency ratio, old (% of working-age population)
|
||||
SP.POP.DPND.YG | Age dependency ratio, young (% of working-age population)
|
||||
SP.POP.GROW | Population growth (annual %)
|
||||
SP.POP.TOTL | Population, total
|
||||
SP.POP.TOTL.FE.IN | Population, female
|
||||
SP.POP.TOTL.FE.ZS | Population, female (% of total)
|
||||
SP.POP.TOTL.MA.IN | Population, male
|
||||
SP.POP.TOTL.MA.ZS | Population, male (% of total)
|
||||
SP.REG.BRTH.RU.ZS | Completeness of birth registration, rural (%)
|
||||
SP.REG.BRTH.UR.ZS | Completeness of birth registration, urban (%)
|
||||
SP.REG.BRTH.ZS | Completeness of birth registration (%)
|
||||
SP.REG.DTHS.ZS | Completeness of death registration with cause-of-death information (%)
|
||||
SP.RUR.TOTL | Rural population
|
||||
SP.RUR.TOTL.ZG | Rural population growth (annual %)
|
||||
SP.RUR.TOTL.ZS | Rural population (% of total population)
|
||||
SP.URB.GROW | Urban population growth (annual %)
|
||||
SP.URB.TOTL | Urban population
|
||||
SP.URB.TOTL.IN.ZS | Urban population (% of total)
|
||||
SP.UWT.TFRT | Unmet need for contraception (% of married women ages 15-49)
|
||||
|
|
@ -1,588 +0,0 @@
|
|||
from wtforms import (
|
||||
Form, SelectMultipleField, SelectField, TextField, TextAreaField,
|
||||
BooleanField, IntegerField, HiddenField)
|
||||
from wtforms import validators, widgets
|
||||
from copy import copy
|
||||
from panoramix import app
|
||||
from collections import OrderedDict
|
||||
config = app.config
|
||||
|
||||
|
||||
class BetterBooleanField(BooleanField):
|
||||
|
||||
"""
|
||||
Fixes behavior of html forms omitting non checked <input>
|
||||
(which doesn't distinguish False from NULL/missing )
|
||||
If value is unchecked, this hidden <input> fills in False value
|
||||
"""
|
||||
|
||||
def __call__(self, **kwargs):
|
||||
html = super(BetterBooleanField, self).__call__(**kwargs)
|
||||
html += u'<input type="hidden" name="{}" value="false">'.format(self.name)
|
||||
return widgets.HTMLString(html)
|
||||
|
||||
|
||||
class SelectMultipleSortableField(SelectMultipleField):
|
||||
|
||||
"""Works along with select2sortable to preserves the sort order"""
|
||||
|
||||
def iter_choices(self):
|
||||
d = OrderedDict()
|
||||
for value, label in self.choices:
|
||||
selected = self.data is not None and self.coerce(value) in self.data
|
||||
d[value] = (value, label, selected)
|
||||
if self.data:
|
||||
for value in self.data:
|
||||
if value:
|
||||
yield d.pop(value)
|
||||
while d:
|
||||
yield d.pop(d.keys()[0])
|
||||
|
||||
|
||||
class FreeFormSelect(widgets.Select):
|
||||
|
||||
"""A WTF widget that allows for free form entry"""
|
||||
|
||||
def __call__(self, field, **kwargs):
|
||||
kwargs.setdefault('id', field.id)
|
||||
if self.multiple:
|
||||
kwargs['multiple'] = True
|
||||
html = ['<select %s>' % widgets.html_params(name=field.name, **kwargs)]
|
||||
found = False
|
||||
for val, label, selected in field.iter_choices():
|
||||
html.append(self.render_option(val, label, selected))
|
||||
if field.data and val == field.data:
|
||||
found = True
|
||||
if not found:
|
||||
html.insert(1, self.render_option(field.data, field.data, True))
|
||||
html.append('</select>')
|
||||
return widgets.HTMLString(''.join(html))
|
||||
|
||||
|
||||
class FreeFormSelectField(SelectField):
|
||||
|
||||
""" A WTF SelectField that allows for free form input """
|
||||
|
||||
widget = FreeFormSelect()
|
||||
def pre_validate(self, form):
|
||||
return
|
||||
|
||||
|
||||
class OmgWtForm(Form):
|
||||
|
||||
"""Panoramixification of the WTForm Form object"""
|
||||
|
||||
fieldsets = {}
|
||||
css_classes = dict()
|
||||
|
||||
def get_field(self, fieldname):
|
||||
return getattr(self, fieldname)
|
||||
|
||||
def field_css_classes(self, fieldname):
|
||||
if fieldname in self.css_classes:
|
||||
return " ".join(self.css_classes[fieldname])
|
||||
return ""
|
||||
|
||||
|
||||
class FormFactory(object):
|
||||
"""Used to create the forms in the explore view dynamically"""
|
||||
series_limits = [0, 5, 10, 25, 50, 100, 500]
|
||||
fieltype_class = {
|
||||
SelectField: 'select2',
|
||||
SelectMultipleField: 'select2',
|
||||
FreeFormSelectField: 'select2_freeform',
|
||||
SelectMultipleSortableField: 'select2Sortable',
|
||||
}
|
||||
|
||||
def __init__(self, viz):
|
||||
self.viz = viz
|
||||
from panoramix.viz import viz_types
|
||||
viz = self.viz
|
||||
datasource = viz.datasource
|
||||
default_metric = datasource.metrics_combo[0][0]
|
||||
default_groupby = datasource.groupby_column_names[0]
|
||||
group_by_choices = [(s, s) for s in datasource.groupby_column_names]
|
||||
# Pool of all the fields that can be used in Panoramix
|
||||
self.field_dict = {
|
||||
'viz_type': SelectField(
|
||||
'Viz',
|
||||
default='table',
|
||||
choices=[(k, v.verbose_name) for k, v in viz_types.items()],
|
||||
description="The type of visualization to display"),
|
||||
'metrics': SelectMultipleSortableField(
|
||||
'Metrics', choices=datasource.metrics_combo,
|
||||
default=[default_metric],
|
||||
description="One or many metrics to display"),
|
||||
'metric': SelectField(
|
||||
'Metric', choices=datasource.metrics_combo,
|
||||
default=default_metric,
|
||||
description="Chose the metric"),
|
||||
'stacked_style': SelectField(
|
||||
'Chart Style', choices=self.choicify(
|
||||
['stack', 'stream', 'expand']),
|
||||
default='stack',
|
||||
description=""),
|
||||
'linear_color_scheme': SelectField(
|
||||
'Color Scheme', choices=self.choicify([
|
||||
'fire', 'blue_white_yellow', 'white_black',
|
||||
'black_white']),
|
||||
default='fire',
|
||||
description=""),
|
||||
'normalize_across': SelectField(
|
||||
'Normalize Across', choices=self.choicify([
|
||||
'heatmap', 'x', 'y']),
|
||||
default='heatmap',
|
||||
description=(
|
||||
"Color will be rendered based on a ratio "
|
||||
"of the cell against the sum of across this "
|
||||
"criteria")),
|
||||
'canvas_image_rendering': SelectField(
|
||||
'Rendering', choices=(
|
||||
('pixelated', 'pixelated (Sharp)'),
|
||||
('auto', 'auto (Smooth)'),
|
||||
),
|
||||
default='pixelated',
|
||||
description=(
|
||||
"image-rendering CSS attribute of the canvas object that "
|
||||
"defines how the browser scales up the image")),
|
||||
'xscale_interval': SelectField(
|
||||
'XScale Interval', choices=self.choicify(range(1, 50)),
|
||||
default='1',
|
||||
description=(
|
||||
"Number of step to take between ticks when "
|
||||
"printing the x scale")),
|
||||
'yscale_interval': SelectField(
|
||||
'YScale Interval', choices=self.choicify(range(1, 50)),
|
||||
default='1',
|
||||
description=(
|
||||
"Number of step to take between ticks when "
|
||||
"printing the y scale")),
|
||||
'bar_stacked': BetterBooleanField(
|
||||
'Stacked Bars',
|
||||
default=False,
|
||||
description=""),
|
||||
'secondary_metric': SelectField(
|
||||
'Color Metric', choices=datasource.metrics_combo,
|
||||
default=default_metric,
|
||||
description="A metric to use for color"),
|
||||
'country_fieldtype': SelectField(
|
||||
'Country Field Type',
|
||||
default='cca2',
|
||||
choices=(
|
||||
('name', 'Full name'),
|
||||
('cioc', 'code International Olympic Committee (cioc)'),
|
||||
('cca2', 'code ISO 3166-1 alpha-2 (cca2)'),
|
||||
('cca3', 'code ISO 3166-1 alpha-3 (cca3)'),
|
||||
),
|
||||
description=(
|
||||
"The country code standard that Panoramix should expect "
|
||||
"to find in the [country] column")),
|
||||
'groupby': SelectMultipleSortableField(
|
||||
'Group by',
|
||||
choices=self.choicify(datasource.groupby_column_names),
|
||||
description="One or many fields to group by"),
|
||||
'columns': SelectMultipleSortableField(
|
||||
'Columns',
|
||||
choices=self.choicify(datasource.groupby_column_names),
|
||||
description="One or many fields to pivot as columns"),
|
||||
'all_columns': SelectMultipleSortableField(
|
||||
'Columns',
|
||||
choices=self.choicify(datasource.column_names),
|
||||
description="Columns to display"),
|
||||
'all_columns_x': SelectField(
|
||||
'X',
|
||||
choices=self.choicify(datasource.column_names),
|
||||
description="Columns to display"),
|
||||
'all_columns_y': SelectField(
|
||||
'Y',
|
||||
choices=self.choicify(datasource.column_names),
|
||||
description="Columns to display"),
|
||||
'granularity': FreeFormSelectField(
|
||||
'Time Granularity', default="one day",
|
||||
choices=self.choicify([
|
||||
'all',
|
||||
'5 seconds',
|
||||
'30 seconds',
|
||||
'1 minute',
|
||||
'5 minutes',
|
||||
'1 hour',
|
||||
'6 hour',
|
||||
'1 day',
|
||||
'7 days',
|
||||
]),
|
||||
description=(
|
||||
"The time granularity for the visualization. Note that you "
|
||||
"can type and use simple natural language as in '10 seconds', "
|
||||
"'1 day' or '56 weeks'")),
|
||||
'link_length': FreeFormSelectField(
|
||||
'Link Length', default="200",
|
||||
choices=self.choicify([
|
||||
'10',
|
||||
'25',
|
||||
'50',
|
||||
'75',
|
||||
'100',
|
||||
'150',
|
||||
'200',
|
||||
'250',
|
||||
]),
|
||||
description="Link length in the force layout"),
|
||||
'charge': FreeFormSelectField(
|
||||
'Charge', default="-500",
|
||||
choices=self.choicify([
|
||||
'-50',
|
||||
'-75',
|
||||
'-100',
|
||||
'-150',
|
||||
'-200',
|
||||
'-250',
|
||||
'-500',
|
||||
'-1000',
|
||||
'-2500',
|
||||
'-5000',
|
||||
]),
|
||||
description="Charge in the force layout"),
|
||||
'granularity_sqla': SelectField(
|
||||
'Time Column',
|
||||
default=datasource.main_dttm_col or datasource.any_dttm_col,
|
||||
choices=self.choicify(datasource.dttm_cols),
|
||||
description=(
|
||||
"The time column for the visualization. Note that you "
|
||||
"can define arbitrary expression that return a DATETIME "
|
||||
"column in the table editor. Also note that the "
|
||||
"filter bellow is applied against this column or "
|
||||
"expression")),
|
||||
'resample_rule': FreeFormSelectField(
|
||||
'Resample Rule', default='',
|
||||
choices=self.choicify(('1T', '1H', '1D', '7D', '1M', '1AS')),
|
||||
description=("Pandas resample rule")),
|
||||
'resample_how': FreeFormSelectField(
|
||||
'Resample How', default='',
|
||||
choices=self.choicify(('', 'mean', 'sum', 'median')),
|
||||
description=("Pandas resample how")),
|
||||
'resample_fillmethod': FreeFormSelectField(
|
||||
'Resample Fill Method', default='',
|
||||
choices=self.choicify(('', 'ffill', 'bfill')),
|
||||
description=("Pandas resample fill method")),
|
||||
'since': FreeFormSelectField(
|
||||
'Since', default="7 days ago",
|
||||
choices=self.choicify([
|
||||
'1 hour ago',
|
||||
'12 hours ago',
|
||||
'1 day ago',
|
||||
'7 days ago',
|
||||
'28 days ago',
|
||||
'90 days ago',
|
||||
'1 year ago'
|
||||
]),
|
||||
description=(
|
||||
"Timestamp from filter. This supports free form typing and "
|
||||
"natural language as in '1 day ago', '28 days' or '3 years'")),
|
||||
'until': FreeFormSelectField('Until', default="now",
|
||||
choices=self.choicify([
|
||||
'now',
|
||||
'1 day ago',
|
||||
'7 days ago',
|
||||
'28 days ago',
|
||||
'90 days ago',
|
||||
'1 year ago'])
|
||||
),
|
||||
'max_bubble_size': FreeFormSelectField(
|
||||
'Max Bubble Size', default="25",
|
||||
choices=self.choicify([
|
||||
'5',
|
||||
'10',
|
||||
'15',
|
||||
'25',
|
||||
'50',
|
||||
'75',
|
||||
'100',
|
||||
])
|
||||
),
|
||||
'row_limit':
|
||||
FreeFormSelectField(
|
||||
'Row limit',
|
||||
default=config.get("ROW_LIMIT"),
|
||||
choices=self.choicify(
|
||||
[10, 50, 100, 250, 500, 1000, 5000, 10000, 50000])),
|
||||
'limit':
|
||||
FreeFormSelectField(
|
||||
'Series limit',
|
||||
choices=self.choicify(self.series_limits),
|
||||
default=50,
|
||||
description=(
|
||||
"Limits the number of time series that get displayed")),
|
||||
'rolling_type': SelectField(
|
||||
'Rolling',
|
||||
default='None',
|
||||
choices=[(s, s) for s in ['None', 'mean', 'sum', 'std', 'cumsum']],
|
||||
description=(
|
||||
"Defines a rolling window function to apply, works along "
|
||||
"with the [Periods] text box")),
|
||||
'rolling_periods': IntegerField(
|
||||
'Periods',
|
||||
validators=[validators.optional()],
|
||||
description=(
|
||||
"Defines the size of the rolling window function, "
|
||||
"relative to the time granularity selected")),
|
||||
'series': SelectField(
|
||||
'Series', choices=group_by_choices,
|
||||
default=default_groupby,
|
||||
description=(
|
||||
"Defines the grouping of entities. "
|
||||
"Each serie is shown as a specific color on the chart and "
|
||||
"has a legend toggle")),
|
||||
'entity': SelectField('Entity', choices=group_by_choices,
|
||||
default=default_groupby,
|
||||
description="This define the element to be plotted on the chart"),
|
||||
'x': SelectField(
|
||||
'X Axis', choices=datasource.metrics_combo,
|
||||
default=default_metric,
|
||||
description="Metric assigned to the [X] axis"),
|
||||
'y': SelectField('Y Axis', choices=datasource.metrics_combo,
|
||||
default=default_metric,
|
||||
description="Metric assigned to the [Y] axis"),
|
||||
'size': SelectField(
|
||||
'Bubble Size',
|
||||
default=default_metric,
|
||||
choices=datasource.metrics_combo),
|
||||
'url': TextField(
|
||||
'URL', default='www.airbnb.com',),
|
||||
'where': TextField(
|
||||
'Custom WHERE clause', default='',
|
||||
description=(
|
||||
"The text in this box gets included in your query's WHERE "
|
||||
"clause, as an AND to other criteria. You can include "
|
||||
"complex expression, parenthesis and anything else "
|
||||
"supported by the backend it is directed towards.")),
|
||||
'having': TextField('Custom HAVING clause', default='',
|
||||
description=(
|
||||
"The text in this box gets included in your query's HAVING"
|
||||
" clause, as an AND to other criteria. You can include "
|
||||
"complex expression, parenthesis and anything else "
|
||||
"supported by the backend it is directed towards.")),
|
||||
'compare_lag': TextField('Comparison Period Lag',
|
||||
description=(
|
||||
"Based on granularity, number of time periods to "
|
||||
"compare against")),
|
||||
'compare_suffix': TextField('Comparison suffix',
|
||||
description="Suffix to apply after the percentage display"),
|
||||
'x_axis_format': FreeFormSelectField('X axis format',
|
||||
default='smart_date',
|
||||
choices=[
|
||||
('smart_date', 'Adaptative formating'),
|
||||
("%m/%d/%Y", '"%m/%d/%Y" | 01/14/2019'),
|
||||
("%Y-%m-%d", '"%Y-%m-%d" | 2019-01-14'),
|
||||
("%Y-%m-%d %H:%M:%S",
|
||||
'"%Y-%m-%d %H:%M:%S" | 2019-01-14 01:32:10'),
|
||||
("%H:%M:%S", '"%H:%M:%S" | 01:32:10'),
|
||||
],
|
||||
description="D3 format syntax for y axis "
|
||||
"https://github.com/mbostock/\n"
|
||||
"d3/wiki/Formatting"),
|
||||
'y_axis_format': FreeFormSelectField('Y axis format',
|
||||
default='.3s',
|
||||
choices=[
|
||||
('.3s', '".3s" | 12.3k'),
|
||||
('.3%', '".3%" | 1234543.210%'),
|
||||
('.4r', '".4r" | 12350'),
|
||||
('.3f', '".3f" | 12345.432'),
|
||||
('+,', '"+," | +12,345.4321'),
|
||||
('$,.2f', '"$,.2f" | $12,345.43'),
|
||||
],
|
||||
description="D3 format syntax for y axis "
|
||||
"https://github.com/mbostock/\n"
|
||||
"d3/wiki/Formatting"),
|
||||
'markup_type': SelectField(
|
||||
"Markup Type",
|
||||
choices=self.choicify(['markdown', 'html']),
|
||||
default="markdown",
|
||||
description="Pick your favorite markup language"),
|
||||
'rotation': SelectField(
|
||||
"Rotation",
|
||||
choices=[(s, s) for s in ['random', 'flat', 'square']],
|
||||
default="random",
|
||||
description="Rotation to apply to words in the cloud"),
|
||||
'line_interpolation': SelectField(
|
||||
"Line Style",
|
||||
choices=self.choicify([
|
||||
'linear', 'basis', 'cardinal', 'monotone',
|
||||
'step-before', 'step-after']),
|
||||
default='linear',
|
||||
description="Line interpolation as defined by d3.js"),
|
||||
'code': TextAreaField(
|
||||
"Code", description="Put your code here", default=''),
|
||||
'pandas_aggfunc': SelectField(
|
||||
"Aggregation function",
|
||||
choices=self.choicify([
|
||||
'sum', 'mean', 'min', 'max', 'median', 'stdev', 'var']),
|
||||
default='sum',
|
||||
description=(
|
||||
"Aggregate function to apply when pivoting and "
|
||||
"computing the total rows and columns")),
|
||||
'size_from': TextField(
|
||||
"Font Size From",
|
||||
default="20",
|
||||
description="Font size for the smallest value in the list"),
|
||||
'size_to': TextField(
|
||||
"Font Size To",
|
||||
default="150",
|
||||
description="Font size for the biggest value in the list"),
|
||||
'show_brush': BetterBooleanField(
|
||||
"Range Filter", default=False,
|
||||
description=(
|
||||
"Whether to display the time range interactive selector")),
|
||||
'show_datatable': BetterBooleanField(
|
||||
"Data Table", default=False,
|
||||
description="Whether to display the interactive data table"),
|
||||
'include_search': BetterBooleanField(
|
||||
"Search Box", default=False,
|
||||
description=(
|
||||
"Whether to include a client side search box")),
|
||||
'show_bubbles': BetterBooleanField(
|
||||
"Show Bubbles", default=False,
|
||||
description=(
|
||||
"Whether to display bubbles on top of countries")),
|
||||
'show_legend': BetterBooleanField(
|
||||
"Legend", default=True,
|
||||
description="Whether to display the legend (toggles)"),
|
||||
'x_axis_showminmax': BetterBooleanField(
|
||||
"X bounds", default=True,
|
||||
description=(
|
||||
"Whether to display the min and max values of the X axis")),
|
||||
'rich_tooltip': BetterBooleanField(
|
||||
"Rich Tooltip", default=True,
|
||||
description=(
|
||||
"The rich tooltip shows a list of all series for that"
|
||||
" point in time")),
|
||||
'y_axis_zero': BetterBooleanField(
|
||||
"Y Axis Zero", default=False,
|
||||
description=(
|
||||
"Force the Y axis to start at 0 instead of the minimum "
|
||||
"value")),
|
||||
'y_log_scale': BetterBooleanField(
|
||||
"Y Log", default=False,
|
||||
description="Use a log scale for the Y axis"),
|
||||
'x_log_scale': BetterBooleanField(
|
||||
"X Log", default=False,
|
||||
description="Use a log scale for the X axis"),
|
||||
'donut': BetterBooleanField(
|
||||
"Donut", default=False,
|
||||
description="Do you want a donut or a pie?"),
|
||||
'contribution': BetterBooleanField(
|
||||
"Contribution", default=False,
|
||||
description="Compute the contribution to the total"),
|
||||
'num_period_compare': IntegerField(
|
||||
"Period Ratio", default=None,
|
||||
validators=[validators.optional()],
|
||||
description=(
|
||||
"[integer] Number of period to compare against, "
|
||||
"this is relative to the granularity selected")),
|
||||
'time_compare': TextField(
|
||||
"Time Shift",
|
||||
default="",
|
||||
description=(
|
||||
"Overlay a timeseries from a "
|
||||
"relative time period. Expects relative time delta "
|
||||
"in natural language (example: 24 hours, 7 days, "
|
||||
"56 weeks, 365 days")),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def choicify(l):
|
||||
return [("{}".format(obj), "{}".format(obj)) for obj in l]
|
||||
|
||||
def get_form(self):
|
||||
"""Returns a form object based on the viz/datasource/context"""
|
||||
viz = self.viz
|
||||
field_css_classes = {}
|
||||
for name, obj in self.field_dict.items():
|
||||
field_css_classes[name] = ['form-control']
|
||||
s = self.fieltype_class.get(obj.field_class)
|
||||
if s:
|
||||
field_css_classes[name] += [s]
|
||||
|
||||
for field in ('show_brush', 'show_legend', 'rich_tooltip'):
|
||||
field_css_classes[field] += ['input-sm']
|
||||
|
||||
class QueryForm(OmgWtForm):
|
||||
fieldsets = copy(viz.fieldsets)
|
||||
css_classes = field_css_classes
|
||||
standalone = HiddenField()
|
||||
async = HiddenField()
|
||||
extra_filters = HiddenField()
|
||||
json = HiddenField()
|
||||
slice_id = HiddenField()
|
||||
slice_name = HiddenField()
|
||||
previous_viz_type = HiddenField(default=viz.viz_type)
|
||||
collapsed_fieldsets = HiddenField()
|
||||
viz_type = self.field_dict.get('viz_type')
|
||||
|
||||
filter_cols = viz.datasource.filterable_column_names or ['']
|
||||
for i in range(10):
|
||||
setattr(QueryForm, 'flt_col_' + str(i), SelectField(
|
||||
'Filter 1',
|
||||
default=filter_cols[0],
|
||||
choices=self.choicify(filter_cols)))
|
||||
setattr(QueryForm, 'flt_op_' + str(i), SelectField(
|
||||
'Filter 1',
|
||||
default='in',
|
||||
choices=self.choicify(['in', 'not in'])))
|
||||
setattr(
|
||||
QueryForm, 'flt_eq_' + str(i),
|
||||
TextField("Super", default=''))
|
||||
|
||||
for field in viz.flat_form_fields():
|
||||
setattr(QueryForm, field, self.field_dict[field])
|
||||
|
||||
def add_to_form(attrs):
|
||||
for attr in attrs:
|
||||
setattr(QueryForm, attr, self.field_dict[attr])
|
||||
|
||||
# datasource type specific form elements
|
||||
if viz.datasource.__class__.__name__ == 'SqlaTable':
|
||||
QueryForm.fieldsets += ({
|
||||
'label': 'SQL',
|
||||
'fields': ['where', 'having'],
|
||||
'description': (
|
||||
"This section exposes ways to include snippets of "
|
||||
"SQL in your query"),
|
||||
},)
|
||||
add_to_form(('where', 'having'))
|
||||
grains = viz.datasource.database.grains()
|
||||
|
||||
if not viz.datasource.any_dttm_col:
|
||||
return QueryForm
|
||||
if grains:
|
||||
time_fields = ('granularity_sqla', 'time_grain_sqla')
|
||||
self.field_dict['time_grain_sqla'] = SelectField(
|
||||
'Time Grain',
|
||||
choices=self.choicify((grain.name for grain in grains)),
|
||||
default="Time Column",
|
||||
description=(
|
||||
"The time granularity for the visualization. This "
|
||||
"applies a date transformation to alter "
|
||||
"your time column and defines a new time granularity."
|
||||
"The options here are defined on a per database "
|
||||
"engine basis in the Panoramix source code"))
|
||||
add_to_form(time_fields)
|
||||
field_css_classes['time_grain_sqla'] = ['form-control', 'select2']
|
||||
field_css_classes['granularity_sqla'] = ['form-control', 'select2']
|
||||
else:
|
||||
time_fields = 'granularity_sqla'
|
||||
add_to_form((time_fields, ))
|
||||
else:
|
||||
time_fields = 'granularity'
|
||||
add_to_form(('granularity',))
|
||||
field_css_classes['granularity'] = ['form-control', 'select2']
|
||||
add_to_form(('since', 'until'))
|
||||
|
||||
QueryForm.fieldsets = ({
|
||||
'label': 'Time',
|
||||
'fields': (
|
||||
time_fields,
|
||||
('since', 'until'),
|
||||
),
|
||||
'description': "Time related form attributes",
|
||||
},) + tuple(QueryForm.fieldsets)
|
||||
return QueryForm
|
||||
|
|
@ -1 +0,0 @@
|
|||
Generic single-database configuration.
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
# A generic, single database configuration.
|
||||
|
||||
[alembic]
|
||||
# template used to generate migration files
|
||||
# file_template = %%(rev)s_%%(slug)s
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# revision_environment = false
|
||||
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARN
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
from __future__ import with_statement
|
||||
from alembic import context
|
||||
from sqlalchemy import engine_from_config, pool
|
||||
from logging.config import fileConfig
|
||||
import logging
|
||||
from flask.ext.appbuilder import Base
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
fileConfig(config.config_file_name)
|
||||
logger = logging.getLogger('alembic.env')
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# from myapp import mymodel
|
||||
from flask import current_app
|
||||
config.set_main_option('sqlalchemy.url',
|
||||
current_app.config.get('SQLALCHEMY_DATABASE_URI'))
|
||||
target_metadata = Base.metadata
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# my_important_option = config.get_main_option("my_important_option")
|
||||
# ... etc.
|
||||
|
||||
|
||||
def run_migrations_offline():
|
||||
"""Run migrations in 'offline' mode.
|
||||
|
||||
This configures the context with just a URL
|
||||
and not an Engine, though an Engine is acceptable
|
||||
here as well. By skipping the Engine creation
|
||||
we don't even need a DBAPI to be available.
|
||||
|
||||
Calls to context.execute() here emit the given string to the
|
||||
script output.
|
||||
|
||||
"""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(url=url)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def run_migrations_online():
|
||||
"""Run migrations in 'online' mode.
|
||||
|
||||
In this scenario we need to create an Engine
|
||||
and associate a connection with the context.
|
||||
|
||||
"""
|
||||
|
||||
# this callback is used to prevent an auto-migration from being generated
|
||||
# when there are no changes to the schema
|
||||
# reference: http://alembic.readthedocs.org/en/latest/cookbook.html
|
||||
def process_revision_directives(context, revision, directives):
|
||||
if getattr(config.cmd_opts, 'autogenerate', False):
|
||||
script = directives[0]
|
||||
if script.upgrade_ops.is_empty():
|
||||
directives[:] = []
|
||||
logger.info('No changes in schema detected.')
|
||||
|
||||
engine = engine_from_config(config.get_section(config.config_ini_section),
|
||||
prefix='sqlalchemy.',
|
||||
poolclass=pool.NullPool)
|
||||
|
||||
connection = engine.connect()
|
||||
context.configure(connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
#compare_type=True,
|
||||
process_revision_directives=process_revision_directives,
|
||||
**current_app.extensions['migrate'].configure_args)
|
||||
|
||||
try:
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
finally:
|
||||
connection.close()
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
run_migrations_online()
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = ${repr(up_revision)}
|
||||
down_revision = ${repr(down_revision)}
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
def upgrade():
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade():
|
||||
${downgrades if downgrades else "pass"}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
"""is_featured
|
||||
|
||||
Revision ID: 12d55656cbca
|
||||
Revises: 55179c7f25c7
|
||||
Create Date: 2015-12-14 13:37:17.374852
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '12d55656cbca'
|
||||
down_revision = '55179c7f25c7'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('tables', sa.Column('is_featured', sa.Boolean(), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('tables', 'is_featured')
|
||||
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
"""making audit nullable
|
||||
|
||||
Revision ID: 18e88e1cc004
|
||||
Revises: 430039611635
|
||||
Create Date: 2016-03-13 21:30:24.833107
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '18e88e1cc004'
|
||||
down_revision = '430039611635'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
try:
|
||||
op.alter_column(
|
||||
'clusters', 'changed_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column(
|
||||
'clusters', 'created_on',
|
||||
existing_type=sa.DATETIME(), nullable=True)
|
||||
op.drop_constraint(None, 'columns', type_='foreignkey')
|
||||
op.drop_constraint(None, 'columns', type_='foreignkey')
|
||||
op.drop_column('columns', 'created_on')
|
||||
op.drop_column('columns', 'created_by_fk')
|
||||
op.drop_column('columns', 'changed_on')
|
||||
op.drop_column('columns', 'changed_by_fk')
|
||||
op.alter_column('css_templates', 'changed_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('css_templates', 'created_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('dashboards', 'changed_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('dashboards', 'created_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.create_unique_constraint(None, 'dashboards', ['slug'])
|
||||
op.alter_column('datasources', 'changed_by_fk',
|
||||
existing_type=sa.INTEGER(),
|
||||
nullable=True)
|
||||
op.alter_column('datasources', 'changed_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('datasources', 'created_by_fk',
|
||||
existing_type=sa.INTEGER(),
|
||||
nullable=True)
|
||||
op.alter_column('datasources', 'created_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('dbs', 'changed_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('dbs', 'created_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('slices', 'changed_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('slices', 'created_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('sql_metrics', 'changed_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('sql_metrics', 'created_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('table_columns', 'changed_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('table_columns', 'created_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('tables', 'changed_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('tables', 'created_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('url', 'changed_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
op.alter_column('url', 'created_on',
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True)
|
||||
### end Alembic commands ###
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
"""adding slug to dash
|
||||
|
||||
Revision ID: 1a48a5411020
|
||||
Revises: 289ce07647b
|
||||
Create Date: 2015-12-04 09:42:16.973264
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '1a48a5411020'
|
||||
down_revision = '289ce07647b'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
def upgrade():
|
||||
op.add_column('dashboards', sa.Column('slug', sa.String(length=255), nullable=True))
|
||||
try:
|
||||
op.create_unique_constraint('idx_unique_slug', 'dashboards', ['slug'])
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_constraint(None, 'dashboards', type_='unique')
|
||||
op.drop_column('dashboards', 'slug')
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 1e2841a4128
|
||||
Revises: 5a7bad26f2a7
|
||||
Create Date: 2015-10-05 22:11:00.537054
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '1e2841a4128'
|
||||
down_revision = '5a7bad26f2a7'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
def upgrade():
|
||||
op.add_column('table_columns', sa.Column('expression', sa.Text(), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('table_columns', 'expression')
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
"""user_id
|
||||
|
||||
Revision ID: 2591d77e9831
|
||||
Revises: 12d55656cbca
|
||||
Create Date: 2015-12-15 17:02:45.128709
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2591d77e9831'
|
||||
down_revision = '12d55656cbca'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
with op.batch_alter_table('tables') as batch_op:
|
||||
batch_op.add_column(sa.Column('user_id', sa.Integer()))
|
||||
batch_op.create_foreign_key('user_id', 'ab_user', ['user_id'], ['id'])
|
||||
|
||||
|
||||
def downgrade():
|
||||
with op.batch_alter_table('tables') as batch_op:
|
||||
batch_op.drop_constraint('user_id', type_='foreignkey')
|
||||
batch_op.drop_column('user_id')
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
"""Add encrypted password field
|
||||
|
||||
Revision ID: 289ce07647b
|
||||
Revises: 2929af7925ed
|
||||
Create Date: 2015-11-21 11:18:00.650587
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '289ce07647b'
|
||||
down_revision = '2929af7925ed'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy_utils.types.encrypted import EncryptedType
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column(
|
||||
'dbs',
|
||||
sa.Column(
|
||||
'password',
|
||||
EncryptedType(sa.String(1024)),
|
||||
nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('dbs', 'password')
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
"""TZ offsets in data sources
|
||||
|
||||
Revision ID: 2929af7925ed
|
||||
Revises: 1e2841a4128
|
||||
Create Date: 2015-10-19 20:54:00.565633
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2929af7925ed'
|
||||
down_revision = '1e2841a4128'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
def upgrade():
|
||||
op.add_column('datasources', sa.Column('offset', sa.Integer(), nullable=True))
|
||||
op.add_column('tables', sa.Column('offset', sa.Integer(), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('tables', 'offset')
|
||||
op.drop_column('datasources', 'offset')
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
"""adding log model
|
||||
|
||||
Revision ID: 315b3f4da9b0
|
||||
Revises: 1a48a5411020
|
||||
Create Date: 2015-12-04 11:16:58.226984
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '315b3f4da9b0'
|
||||
down_revision = '1a48a5411020'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table('logs',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('action', sa.String(length=512), nullable=True),
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.Column('json', sa.Text(), nullable=True),
|
||||
sa.Column('dttm', sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['ab_user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('logs')
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
"""log more
|
||||
|
||||
Revision ID: 430039611635
|
||||
Revises: d827694c7555
|
||||
Create Date: 2016-02-10 08:47:28.950891
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '430039611635'
|
||||
down_revision = 'd827694c7555'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
def upgrade():
|
||||
op.add_column('logs', sa.Column('dashboard_id', sa.Integer(), nullable=True))
|
||||
op.add_column('logs', sa.Column('slice_id', sa.Integer(), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('logs', 'slice_id')
|
||||
op.drop_column('logs', 'dashboard_id')
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 43df8de3a5f4
|
||||
Revises: 7dbf98566af7
|
||||
Create Date: 2016-01-18 23:43:16.073483
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '43df8de3a5f4'
|
||||
down_revision = '7dbf98566af7'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('dashboards', sa.Column('json_metadata', sa.Text(), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('dashboards', 'json_metadata')
|
||||
|
|
@ -1,215 +0,0 @@
|
|||
"""Init
|
||||
|
||||
Revision ID: 4e6a06bad7a8
|
||||
Revises: None
|
||||
Create Date: 2015-09-21 17:30:38.442998
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4e6a06bad7a8'
|
||||
down_revision = None
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('clusters',
|
||||
sa.Column('created_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('changed_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('cluster_name', sa.String(length=250), nullable=True),
|
||||
sa.Column('coordinator_host', sa.String(length=256), nullable=True),
|
||||
sa.Column('coordinator_port', sa.Integer(), nullable=True),
|
||||
sa.Column('coordinator_endpoint', sa.String(length=256), nullable=True),
|
||||
sa.Column('broker_host', sa.String(length=256), nullable=True),
|
||||
sa.Column('broker_port', sa.Integer(), nullable=True),
|
||||
sa.Column('broker_endpoint', sa.String(length=256), nullable=True),
|
||||
sa.Column('metadata_last_refreshed', sa.DateTime(), nullable=True),
|
||||
sa.Column('created_by_fk', sa.Integer(), nullable=True),
|
||||
sa.Column('changed_by_fk', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['changed_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['created_by_fk'], ['ab_user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('cluster_name')
|
||||
)
|
||||
op.create_table('dashboards',
|
||||
sa.Column('created_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('changed_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('dashboard_title', sa.String(length=500), nullable=True),
|
||||
sa.Column('position_json', sa.Text(), nullable=True),
|
||||
sa.Column('created_by_fk', sa.Integer(), nullable=True),
|
||||
sa.Column('changed_by_fk', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['changed_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['created_by_fk'], ['ab_user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('dbs',
|
||||
sa.Column('created_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('changed_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('database_name', sa.String(length=250), nullable=True),
|
||||
sa.Column('sqlalchemy_uri', sa.String(length=1024), nullable=True),
|
||||
sa.Column('created_by_fk', sa.Integer(), nullable=True),
|
||||
sa.Column('changed_by_fk', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['changed_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['created_by_fk'], ['ab_user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('database_name')
|
||||
)
|
||||
op.create_table('datasources',
|
||||
sa.Column('created_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('changed_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('datasource_name', sa.String(length=250), nullable=True),
|
||||
sa.Column('is_featured', sa.Boolean(), nullable=True),
|
||||
sa.Column('is_hidden', sa.Boolean(), nullable=True),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('default_endpoint', sa.Text(), nullable=True),
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.Column('cluster_name', sa.String(length=250), nullable=True),
|
||||
sa.Column('changed_by_fk', sa.Integer(), nullable=False),
|
||||
sa.Column('created_by_fk', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['changed_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['cluster_name'], ['clusters.cluster_name'], ),
|
||||
sa.ForeignKeyConstraint(['created_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['ab_user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('datasource_name')
|
||||
)
|
||||
op.create_table('tables',
|
||||
sa.Column('created_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('changed_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('table_name', sa.String(length=250), nullable=True),
|
||||
sa.Column('main_dttm_col', sa.String(length=250), nullable=True),
|
||||
sa.Column('default_endpoint', sa.Text(), nullable=True),
|
||||
sa.Column('database_id', sa.Integer(), nullable=False),
|
||||
sa.Column('created_by_fk', sa.Integer(), nullable=True),
|
||||
sa.Column('changed_by_fk', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['changed_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['created_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['database_id'], ['dbs.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('table_name')
|
||||
)
|
||||
op.create_table('columns',
|
||||
sa.Column('created_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('changed_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('datasource_name', sa.String(length=250), nullable=True),
|
||||
sa.Column('column_name', sa.String(length=256), nullable=True),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=True),
|
||||
sa.Column('type', sa.String(length=32), nullable=True),
|
||||
sa.Column('groupby', sa.Boolean(), nullable=True),
|
||||
sa.Column('count_distinct', sa.Boolean(), nullable=True),
|
||||
sa.Column('sum', sa.Boolean(), nullable=True),
|
||||
sa.Column('max', sa.Boolean(), nullable=True),
|
||||
sa.Column('min', sa.Boolean(), nullable=True),
|
||||
sa.Column('filterable', sa.Boolean(), nullable=True),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('created_by_fk', sa.Integer(), nullable=True),
|
||||
sa.Column('changed_by_fk', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['changed_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['created_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['datasource_name'], ['datasources.datasource_name'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('metrics',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('metric_name', sa.String(length=512), nullable=True),
|
||||
sa.Column('verbose_name', sa.String(length=1024), nullable=True),
|
||||
sa.Column('metric_type', sa.String(length=32), nullable=True),
|
||||
sa.Column('datasource_name', sa.String(length=250), nullable=True),
|
||||
sa.Column('json', sa.Text(), nullable=True),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['datasource_name'], ['datasources.datasource_name'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('slices',
|
||||
sa.Column('created_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('changed_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('slice_name', sa.String(length=250), nullable=True),
|
||||
sa.Column('druid_datasource_id', sa.Integer(), nullable=True),
|
||||
sa.Column('table_id', sa.Integer(), nullable=True),
|
||||
sa.Column('datasource_type', sa.String(length=200), nullable=True),
|
||||
sa.Column('datasource_name', sa.String(length=2000), nullable=True),
|
||||
sa.Column('viz_type', sa.String(length=250), nullable=True),
|
||||
sa.Column('params', sa.Text(), nullable=True),
|
||||
sa.Column('created_by_fk', sa.Integer(), nullable=True),
|
||||
sa.Column('changed_by_fk', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['changed_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['created_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['druid_datasource_id'], ['datasources.id'], ),
|
||||
sa.ForeignKeyConstraint(['table_id'], ['tables.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('sql_metrics',
|
||||
sa.Column('created_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('changed_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('metric_name', sa.String(length=512), nullable=True),
|
||||
sa.Column('verbose_name', sa.String(length=1024), nullable=True),
|
||||
sa.Column('metric_type', sa.String(length=32), nullable=True),
|
||||
sa.Column('table_id', sa.Integer(), nullable=True),
|
||||
sa.Column('expression', sa.Text(), nullable=True),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('created_by_fk', sa.Integer(), nullable=True),
|
||||
sa.Column('changed_by_fk', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['changed_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['created_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['table_id'], ['tables.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('table_columns',
|
||||
sa.Column('created_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('changed_on', sa.DateTime(), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('table_id', sa.Integer(), nullable=True),
|
||||
sa.Column('column_name', sa.String(length=256), nullable=True),
|
||||
sa.Column('is_dttm', sa.Boolean(), nullable=True),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=True),
|
||||
sa.Column('type', sa.String(length=32), nullable=True),
|
||||
sa.Column('groupby', sa.Boolean(), nullable=True),
|
||||
sa.Column('count_distinct', sa.Boolean(), nullable=True),
|
||||
sa.Column('sum', sa.Boolean(), nullable=True),
|
||||
sa.Column('max', sa.Boolean(), nullable=True),
|
||||
sa.Column('min', sa.Boolean(), nullable=True),
|
||||
sa.Column('filterable', sa.Boolean(), nullable=True),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('created_by_fk', sa.Integer(), nullable=True),
|
||||
sa.Column('changed_by_fk', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['changed_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['created_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['table_id'], ['tables.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('dashboard_slices',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('dashboard_id', sa.Integer(), nullable=True),
|
||||
sa.Column('slice_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['dashboard_id'], ['dashboards.id'], ),
|
||||
sa.ForeignKeyConstraint(['slice_id'], ['slices.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('dashboard_slices')
|
||||
op.drop_table('table_columns')
|
||||
op.drop_table('sql_metrics')
|
||||
op.drop_table('slices')
|
||||
op.drop_table('metrics')
|
||||
op.drop_table('columns')
|
||||
op.drop_table('tables')
|
||||
op.drop_table('datasources')
|
||||
op.drop_table('dbs')
|
||||
op.drop_table('dashboards')
|
||||
op.drop_table('clusters')
|
||||
### end Alembic commands ###
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
"""sqla_descr
|
||||
|
||||
Revision ID: 55179c7f25c7
|
||||
Revises: 315b3f4da9b0
|
||||
Create Date: 2015-12-13 08:38:43.704145
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '55179c7f25c7'
|
||||
down_revision = '315b3f4da9b0'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('tables', sa.Column('description', sa.Text(), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('tables', 'description')
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 5a7bad26f2a7
|
||||
Revises: 4e6a06bad7a8
|
||||
Create Date: 2015-10-05 10:32:15.850753
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '5a7bad26f2a7'
|
||||
down_revision = '4e6a06bad7a8'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('dashboards', sa.Column('css', sa.Text(), nullable=True))
|
||||
op.add_column('dashboards', sa.Column('description', sa.Text(), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('dashboards', 'description')
|
||||
op.drop_column('dashboards', 'css')
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 7dbf98566af7
|
||||
Revises: 8e80a26a31db
|
||||
Create Date: 2016-01-17 22:00:23.640788
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '7dbf98566af7'
|
||||
down_revision = '8e80a26a31db'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
def upgrade():
|
||||
op.add_column('slices', sa.Column('description', sa.Text(), nullable=True))
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('slices', 'description')
|
||||