Compare commits

..

630 Commits
0.2.1 ... 0.8.4

Author SHA1 Message Date
Maxime Beauchemin
21d1c0a1b5 0.8.4 2016-03-23 22:26:16 -07:00
Maxime Beauchemin
3118d1b302 Merge pull request #193 from airbnb/favstar
Adding favorites for Slices and Dashboards
2016-03-23 22:25:12 -07:00
Maxime Beauchemin
2362b5a157 Tunning tooltips 2016-03-23 22:23:29 -07:00
Maxime Beauchemin
7ede732892 Adding favorites for Slices and Dashboards 2016-03-23 22:15:23 -07:00
Maxime Beauchemin
5b10b19ed7 Fixing the docs 2016-03-23 21:41:37 -07:00
Maxime Beauchemin
14f298b385 dashboard bug fixes and better page titles 2016-03-23 21:27:45 -07:00
Maxime Beauchemin
89da51f8d7 Pointing landscape badge to the right branch 2016-03-23 16:16:20 -07:00
Maxime Beauchemin
d5487c6b25 0.8.3 2016-03-23 14:11:37 -07:00
Maxime Beauchemin
a244f3aafb Fixing build issues with preventive rm /Users/maxime_beauchemin/.npm/*.lock 2016-03-23 14:11:37 -07:00
Maxime Beauchemin
9d3bf7763c Fixing build issues with preventive rm /Users/maxime_beauchemin/.npm/*.lock 2016-03-23 11:14:29 -07:00
Maxime Beauchemin
b0f10a90ce Merge pull request #188 from airbnb/caching
Introducing a caching layer!
2016-03-22 22:55:35 -07:00
Maxime Beauchemin
417749f880 Explore mode should only expose cache on first load 2016-03-22 22:09:02 -07:00
Maxime Beauchemin
fded04a51d Hack to get the force refresh in the explore view 2016-03-22 18:11:10 -07:00
Maxime Beauchemin
d8192eca0a Introducing a caching layer 2016-03-22 18:11:10 -07:00
Maxime Beauchemin
2d3edf3a2e js lint breaks the build 2016-03-22 08:16:23 -07:00
Maxime Beauchemin
0890d22899 Merge pull request #191 from airbnb/week-grain
Add week ending and week start to grain
2016-03-21 15:56:36 -07:00
Siddharth
bad3128df9 Update grain label name 2016-03-21 15:41:49 -07:00
Siddharth
48c8a90247 Fix multi-line statement 2016-03-21 14:27:03 -07:00
Siddharth
a9ba47fe12 Add week ending and week start to grain 2016-03-21 13:45:14 -07:00
Maxime Beauchemin
f09f3d7c4b Merge pull request #190 from airbnb/vnum
Cranking up version numbers
2016-03-19 21:46:03 -07:00
Maxime Beauchemin
e17d78e196 Cranking up version numbers 2016-03-19 21:33:39 -07:00
Maxime Beauchemin
9450c12552 note about the previous name in README 2016-03-19 16:09:08 -07:00
Maxime Beauchemin
f01b41827e Adding reqs badge 2016-03-19 16:03:15 -07:00
Maxime Beauchemin
b5f4d3b515 Adding reqs badge 2016-03-19 10:30:54 -07:00
Maxime Beauchemin
28e5a87cd0 pypi badges 2016-03-19 10:16:59 -07:00
Maxime Beauchemin
10c48f04cd [renaming] 2016-03-19 10:07:18 -07:00
Maxime Beauchemin
f79aca1796 rename - 2nd phase 2016-03-18 08:52:50 -07:00
Maxime Beauchemin
be6b2fe556 [panoramix] -> [dashed] 2016-03-17 23:44:58 -07:00
Chris Williams
8f4f5b126a Merge pull request #184 from mistercrunch/chris/sunburst-fixes
sunburst improvements
2016-03-17 17:54:10 -07:00
Maxime Beauchemin
2b1dcf0d2e [hotfix] granularity fix 2016-03-17 14:54:26 -07:00
Maxime Beauchemin
987a1afbb7 [hotfix] granularity fix 2016-03-17 14:45:21 -07:00
Maxime Beauchemin
f453932589 [hotfix] druid granularity missing 2016-03-17 14:37:14 -07:00
Chris Williams
3dc9996e96 hide breadcrumbs on mouseout 2016-03-17 14:10:15 -07:00
Chris Williams
3197c4b3f9 add utility function for wrapping svg text, apply to sunburst breadcrumbs, and make colors more readable for sunburst. 2016-03-17 14:02:54 -07:00
Maxime Beauchemin
f0b2f985b4 Merge pull request #186 from mistercrunch/docstrings
Adding docstrings !
2016-03-16 22:40:50 -07:00
Maxime Beauchemin
1c9c154f0f More docstrings 2016-03-16 22:26:17 -07:00
Maxime Beauchemin
ebf55bf20d Adding docstrings ! 2016-03-16 20:40:50 -07:00
Maxime Beauchemin
3461538fed [hotfix] appbuilder modal z-index fix 2016-03-16 17:27:56 -07:00
Maxime Beauchemin
7cdb23f585 [hotfix] grouping by grain, filtering on raw expression 2016-03-16 17:04:36 -07:00
Maxime Beauchemin
73daf2f8d1 [hotfix] casting dates for presto grain functions 2016-03-16 16:42:35 -07:00
Maxime Beauchemin
ae1e66f09b Merge pull request #181 from mistercrunch/time_grain
Dynamic time granularity on any datetime column
2016-03-16 16:11:42 -07:00
Chris Williams
06080945b6 fix misc sunburst bugs from refactor. 2016-03-16 15:52:52 -07:00
Maxime Beauchemin
61ab06d640 Removing unicode from country data 2016-03-16 15:49:01 -07:00
Chris Williams
0c045a9e52 trim '0' groups from sunburst paths to show null groups. add ability to map color to sunburst groupby groups. add fancier breadcrumbs to sunburst. 2016-03-16 15:24:30 -07:00
Maxime Beauchemin
27fb810dd7 Dynamic time granularity on any datetime column 2016-03-16 15:12:16 -07:00
Chris Williams
2aa0e0dce0 Merge pull request #182 from mistercrunch/chris/modal-and-dashboard-css-fixes
more css fixes
2016-03-16 09:11:47 -07:00
Maxime Beauchemin
417b5a5e09 Adding linting note to CONTRIBUTING.md 2016-03-16 08:23:59 -07:00
Maxime Beauchemin
7c5e660bd1 Merge pull request #178 from mistercrunch/fix_audit
Allowing all extra fields in AuditMixin to be nullable
2016-03-16 08:15:12 -07:00
Chris Williams
1a58b6d441 fix overflow scroll bars on all dashboard charts. make dashboard chart control toggle interaction nicer. make sure user-stylesheets are applied last. remove ace css editor error/warning parsing. make filters look nicer in dashboards. fix some linting. 2016-03-15 11:52:26 -07:00
Maxime Beauchemin
1ab89631b9 Adding caching to TODO 2016-03-13 23:04:43 -07:00
Chris Williams
e1eb236cf4 Merge pull request #175 from mistercrunch/chris/css-fixes
refactor dashboard chart html, make several css improvements.
2016-03-13 23:03:35 -07:00
Maxime Beauchemin
95b7b9779b Allowing all extra fields in AuditMixin to be nullable 2016-03-13 21:36:40 -07:00
Chris Williams
b0fa4bc924 remove unused css 2016-03-11 12:29:41 -08:00
Chris Williams
49590e28e3 refactor dashboard chart html, make several css improvements. 2016-03-11 12:22:41 -08:00
Maxime Beauchemin
fb6a9977f7 Merge pull request #172 from mistercrunch/packaging
Fixing the python and js packaging
2016-03-10 22:33:09 -05:00
Maxime Beauchemin
9e4b38b5e6 Adjusting css for display not to flicker on dashboard load 2016-03-10 08:46:20 -05:00
Maxime Beauchemin
e2cd14d320 Fixing the python and js packaging 2016-03-10 01:35:20 -05:00
Maxime Beauchemin
370c5af425 [hotfix] fix utils.markdown function chokes on None 2016-03-09 07:34:00 -05:00
Maxime Beauchemin
e53a1cc75b Adding a TODO entry for viz selector 2016-03-08 16:53:54 -05:00
Maxime Beauchemin
91833ce16f Merge pull request #171 from mistercrunch/fix_filter
Fixing multiple refresh bug in filter_box
2016-03-08 07:29:14 -05:00
Maxime Beauchemin
829271417e Fixing bug in filter_box 2016-03-07 23:03:30 -05:00
Maxime Beauchemin
55d4ac45fa Merge pull request #169 from mistercrunch/select2-style
Fixing the look of select2 components
2016-03-07 13:46:38 -05:00
Maxime Beauchemin
d6db3a9cf1 semicolon 2016-03-07 13:46:19 -05:00
Maxime Beauchemin
c0e4aca7e4 Using dash - instead of _ on filename, making input form-control white 2016-03-06 17:48:50 -05:00
Maxime Beauchemin
9855a60013 Fixing the look of select2 components 2016-03-06 14:48:05 -05:00
Maxime Beauchemin
a53dbfcfaa Merge pull request #168 from mistercrunch/travis_npm
Getting travis to build the npm related stuff
2016-03-06 08:07:37 -05:00
Maxime Beauchemin
b2d72fbfe1 Tweaks 2016-03-05 23:43:17 -05:00
Maxime Beauchemin
78e01ab0a1 Caching node_modules 2016-03-05 23:01:39 -05:00
Maxime Beauchemin
454bb10f5a Getting travis to build the npm related stuff 2016-03-05 22:22:36 -05:00
Maxime Beauchemin
ee025b3331 Fixing REST api and unsortable columns 2016-03-05 21:50:23 -05:00
Chris Williams
1e27f03b4d Merge pull request #166 from mistercrunch/chris/css
make css theme customization easier by using less for bootstrap themes
2016-03-05 12:45:59 -08:00
Chris Williams
9095cd3bca update contributing.md to explain updated LESS/CSS customization 2016-03-05 12:41:22 -08:00
Maxime Beauchemin
bfa6d13b49 Merge pull request #163 from mistercrunch/css
Shipping with CSS templates out of the box
2016-03-04 23:38:42 -05:00
Chris Williams
d3f7bbd3f1 remove pre-compiled bootstrap themes, use npm/wepack to compile from less variables instead. also fix a few css style bugs. 2016-03-04 18:20:49 -08:00
Maxime Beauchemin
570b9f09f6 Merge pull request #164 from mistercrunch/docs
Improving the docs
2016-03-04 18:58:12 -05:00
Maxime Beauchemin
e39f7db0a3 Merge pull request #165 from mistercrunch/window_resize
Fixing window resize for explore and standalone
2016-03-04 18:57:44 -05:00
Maxime Beauchemin
9de19b169f setResizeOnWindowResize -> bindResizeToWindowResize 2016-03-04 18:56:06 -05:00
Maxime Beauchemin
722c16a6e5 Fixing window resize for explore and standalone 2016-03-04 14:47:50 -05:00
Maxime Beauchemin
5486e5cac8 barol -> tirol 2016-03-04 13:47:04 -05:00
Chris Williams
1782d8f278 Merge pull request #161 from mistercrunch/chris/eslint
Add linting to package.json, do all of the linting.
2016-03-04 10:24:06 -08:00
Maxime Beauchemin
0a8ab6c499 Shipping with a CSS template out of the box 2016-03-04 00:44:55 -05:00
Maxime Beauchemin
a7e75a2bc5 [hotfix] cosmetic fitting for nvd3 line chart overflowing 2016-03-03 22:30:22 -05:00
Maxime Beauchemin
486fb8bfb4 [hotfix] fixing the examples 2016-03-03 22:30:22 -05:00
Maxime Beauchemin
bd296720b8 Improving the docs 2016-03-02 12:19:11 -08:00
Chris Williams
68f8937a2e lint standalone.js 2016-03-02 12:03:32 -08:00
Maxime Beauchemin
8703fa9460 Merge pull request #160 from mistercrunch/fix_dashed
Fixing the dashed line when using time compare
2016-03-02 11:47:50 -08:00
Chris Williams
673f741aa6 Add linting to package.json, run 'npm run lint' to run (will try to add commit hook later). Do all of the linting... 2016-03-02 11:32:27 -08:00
Maxime Beauchemin
b61ddd0009 semicolon 2016-03-02 11:12:55 -08:00
Maxime Beauchemin
e1e1746967 Fixing the dashed line when using time compare 2016-03-02 11:12:55 -08:00
Maxime Beauchemin
b6a2521722 hotfix - forgot to add standalone.js 2016-03-02 10:57:36 -08:00
Maxime Beauchemin
309a6dcb1d Merge pull request #159 from mistercrunch/standalone
Fixing the standalone mode
2016-03-02 00:28:50 -08:00
Maxime Beauchemin
51797ca9a5 Fixing the standalone mode 2016-03-01 18:22:41 -08:00
Maxime Beauchemin
2872c6262d Merge pull request #158 from mistercrunch/refactor
Refactor
2016-03-01 17:09:52 -08:00
Maxime Beauchemin
b44d48c724 Using vim-jsbeautify 2016-03-01 15:56:31 -08:00
Maxime Beauchemin
a68d7428c3 Javascript refactor 2016-03-01 15:51:06 -08:00
Maxime Beauchemin
e434cbe268 Better filtering 2016-03-01 15:50:09 -08:00
Maxime Beauchemin
367ca336cc Merge pull request #154 from mistercrunch/fix_dates
Digging into leap year bug and improvming tests
2016-03-01 14:35:10 -08:00
Maxime Beauchemin
4fe89a3811 Pinning parsedatetime to 2.0 2016-03-01 14:23:54 -08:00
Maxime Beauchemin
8c525b7b8f Hotfix nvd3's secondary xaxis uses the wrong formating 2016-03-01 13:44:06 -08:00
Chris Williams
b36b1ef05a Merge pull request #157 from mistercrunch/chris/copy-short-url
add button to auto-copy short URLs in /explore page
2016-03-01 12:10:11 -08:00
Chris Williams
88f9442297 add button to auto-copy short URLs in /explore page 2016-03-01 11:40:13 -08:00
Maxime Beauchemin
32cd9f5e73 Updating TODO.md 2016-03-01 07:47:13 -08:00
Maxime Beauchemin
eccf9c2ac7 [hotfix] saving as new bugfix 2016-02-29 22:36:44 -08:00
Maxime Beauchemin
8b0f2afc0a Merge pull request #149 from mistercrunch/immune_to_filter
Allowing to make certain widgets immune to filter
2016-02-29 20:06:39 -08:00
Maxime Beauchemin
be4a7a4077 Linting 2016-02-29 18:10:33 -08:00
Maxime Beauchemin
69cfcabb72 Making Dashboard look better 2016-02-29 16:55:59 -08:00
Maxime Beauchemin
3a38c601e0 Fixing tooltips 2016-02-29 15:55:54 -08:00
Maxime Beauchemin
22327e204f Transfering all the json in one point 2016-02-29 15:55:54 -08:00
Maxime Beauchemin
bd06f995ee Allowing to make certain widgets immune to filter 2016-02-29 15:55:54 -08:00
Maxime Beauchemin
9f809e9f8b hotfix, readme pointing to the right landscape branch 2016-02-29 14:59:49 -08:00
Maxime Beauchemin
34709c8846 Digging into leap year bug and improvming tests 2016-02-29 14:55:21 -08:00
Maxime Beauchemin
bb46887668 Merge pull request #151 from mistercrunch/lint
Linting
2016-02-29 14:35:56 -08:00
Maxime Beauchemin
74b32ac993 Merge pull request #153 from tay/patch-1
Improve README
2016-02-29 12:30:18 -08:00
Fiona Tay
ca124f271b Improve README
Fix typo
Link to the actual Contributing file
2016-02-29 12:04:39 -08:00
Maxime Beauchemin
0a842598df More linting 2016-02-29 00:32:52 -08:00
Maxime Beauchemin
cb46a61728 Linting 2016-02-29 00:04:37 -08:00
Maxime Beauchemin
8bf629c0b2 [hotfix] fixing explore view 2016-02-28 15:32:25 -08:00
Maxime Beauchemin
63b4f56c6a Merge pull request #139 from mistercrunch/chris/npm-ify2
NPMification & Reactification
2016-02-28 15:19:04 -08:00
Maxime Beauchemin
d40664bca7 Merge pull request #147 from mistercrunch/npm
Tackling Featured Datasets
2016-02-28 15:14:43 -08:00
Maxime Beauchemin
bc4182060a Rolling back to previous theme until we polish bootswarch.paper 2016-02-28 08:12:00 -08:00
Maxime Beauchemin
58c7b8c436 Removing js_files and css_files entries from viz.py 2016-02-27 09:02:39 -08:00
Maxime Beauchemin
c304cc493a Moving content of assets/readme.md to contributing.md 2016-02-27 07:47:56 -08:00
Maxime Beauchemin
4f4c67ce15 Moving html assets back to flask template folder 2016-02-27 07:33:06 -08:00
Maxime Beauchemin
c929b2a9c9 npmify datamaps dependency 2016-02-27 07:27:51 -08:00
Maxime Beauchemin
fd0e680cf0 Cleaning up assets/vendors 2016-02-26 18:35:59 -08:00
Maxime Beauchemin
70e1aa37e3 Adding missing fonts 2016-02-25 22:38:48 -08:00
Maxime Beauchemin
e4e7b911c5 Fixing dashboard.html 2016-02-25 21:15:03 -08:00
Maxime Beauchemin
d8409c1e0a Fixing table and pivot_table 2016-02-25 17:44:23 -08:00
Maxime Beauchemin
8dcd5e0628 Tackling Featured Datasets 2016-02-24 17:44:22 -08:00
Maxime Beauchemin
d2bb9aaf96 Merge pull request #148 from tay/patch-1
Fix typo
2016-02-24 17:38:52 -08:00
Fiona Tay
7c96a2eddf Fix typo 2016-02-24 17:19:44 -08:00
Chris Williams
71b11117d7 Merge pull request #145 from mistercrunch/npm
Moving files around ... yay!
2016-02-24 16:35:40 -08:00
Maxime Beauchemin
e70542d743 NPMify sql.html 2016-02-24 16:22:32 -08:00
Maxime Beauchemin
171301b6cf Moving files around 2016-02-24 15:12:51 -08:00
Chris Williams
3e834603fc Remove base.entry.js, declare all JS dependencies in explore using npm. Pin older select2 version to enable select2Sortable. 2016-02-24 13:30:42 -08:00
Maxime Beauchemin
71073d8f83 Merge pull request #142 from mistercrunch/npm
A few cosmetic fixes (nvd3 tooltips, buttons, tables)
2016-02-24 11:47:21 -08:00
Maxime Beauchemin
df8ea5287b Adjustments based on comment 2016-02-24 11:43:21 -08:00
Maxime Beauchemin
9552d09b85 A few cosmetic fixes (nvd3 tooltips, buttons, tables) 2016-02-24 11:30:01 -08:00
Maxime Beauchemin
dbb9f66202 Merge pull request #141 from mistercrunch/npm
A simple base template for npm
2016-02-24 09:48:24 -08:00
Maxime Beauchemin
d582efe76b Fixing 2 links 2016-02-24 09:45:30 -08:00
Maxime Beauchemin
f5e0ed7239 A simple base template for npm 2016-02-23 17:48:13 -08:00
Chris Williams
79e6fc986d huge npm refactor of all visualizations. table views are broken due to broken shimming for jquery/jquery-ui, dashboards are broken due to refactor of explore views. 2016-02-23 17:00:08 -08:00
Maxime Beauchemin
523b6d815a Merge pull request #140 from dayzzz/segment_selection
use the latest segment to extract metadata
2016-02-22 22:27:07 -08:00
Hongbo Zeng
5c38fc731a use the latest segment to extract metadata 2016-02-22 16:44:29 -08:00
Chris Williams
af34549377 partial refactor of explore page, visualizations not managed with webpack yet. 2016-02-19 15:12:25 -08:00
Chris Williams
0eb3dcd81f Update readme.md 2016-02-19 12:07:19 -08:00
Chris Williams
f40c024fda Add npm package.json and setup webpack to transpile ES6/JSX and compile JS files for frontend refactor. See readme.md in assets/ for npm setup instructions and visit the panoramix home page for a React sandbox. 2016-02-19 11:39:50 -08:00
Maxime Beauchemin
20bd4ca0eb Merge pull request #136 from mistercrunch/barbar
Improved the bar char to allow for dimensional breakdowns
2016-02-15 20:26:12 -08:00
Maxime Beauchemin
7e60b14448 Trying to fix a unicode bug with presto 2016-02-15 20:25:23 -08:00
Maxime Beauchemin
e42d1a1fc5 Improved the bar char to allow for dimensional breakdowns 2016-02-15 20:10:33 -08:00
Maxime Beauchemin
6f4397c7d7 Fixing druid metadata import 2016-02-12 15:47:23 -08:00
Maxime Beauchemin
1f41ce3a7d CLI adding refresh_druid command 2016-02-12 15:25:42 -08:00
Maxime Beauchemin
d970e896ad bugfix - Cluster -> DruidCluster 2016-02-12 00:49:43 -08:00
Maxime Beauchemin
5686dc0865 Bugfix Column -> DruidColumn 2016-02-12 00:45:09 -08:00
Maxime Beauchemin
80651a5dc2 More logging 2016-02-12 00:32:15 -08:00
Maxime Beauchemin
2c4396882e More verbose druid metadata refresh 2016-02-12 00:01:25 -08:00
Maxime Beauchemin
ba28fbbd6b Fixing link to cluster view 2016-02-11 17:58:12 -08:00
Maxime Beauchemin
4acf1865f8 Merge pull request #134 from mistercrunch/fix_init
Fixing the roles auto maintenance
2016-02-11 16:10:39 -08:00
Maxime Beauchemin
53a0f81985 Fixing the roles auto maintenance 2016-02-11 15:54:51 -08:00
Maxime Beauchemin
baac8c44a5 Desperatly changing view name to avoid proxy odd bug 2016-02-11 13:23:46 -08:00
Maxime Beauchemin
88b8f73489 Not specifying flask-login version, letting flask-appbuilder do it 2016-02-11 12:54:08 -08:00
Maxime Beauchemin
e02702d320 Pinning appbuilder to 1.6 2016-02-11 10:59:49 -08:00
Maxime Beauchemin
e39d6dbc3d Hack to log dashboard_ids when slugs are used 2016-02-10 14:12:38 -08:00
Maxime Beauchemin
07d52eda48 Merge pull request #132 from mistercrunch/fix_legend
[nvd3] fixing the legend toggle bug
2016-02-10 12:27:46 -08:00
Maxime Beauchemin
c7ecb331e4 [nvd3] fixing the legend toggle bug 2016-02-10 12:22:05 -08:00
Maxime Beauchemin
ec0566860a Fixing legend toggle in nvd3 charts 2016-02-10 11:34:46 -08:00
Maxime Beauchemin
e759dfaf0a Merge pull request #131 from mistercrunch/moretests
More tests using doctests!
2016-02-10 11:26:17 -08:00
Maxime Beauchemin
e039547762 More tests using doctests 2016-02-10 11:20:13 -08:00
Maxime Beauchemin
7b0c045a42 Merge pull request #130 from mistercrunch/log_more
Logging more
2016-02-10 11:13:35 -08:00
Maxime Beauchemin
5e9096f275 Merge pull request #129 from mistercrunch/rename_druid_classes
Renaming Classes related to Druid
2016-02-10 08:54:20 -08:00
Maxime Beauchemin
b1f88a53ad Logging more 2016-02-10 08:52:49 -08:00
Maxime Beauchemin
32442aa6b7 Adding Metric class as well 2016-02-10 07:18:59 -08:00
Maxime Beauchemin
b18d117852 Renaming Classes related to Druid 2016-02-10 06:56:35 -08:00
Maxime Beauchemin
460f6cbed5 Updated TODO and color bugfix 2016-02-04 17:01:40 -08:00
Maxime Beauchemin
61b3a85d7a improving the sql view 2016-02-04 00:26:20 -08:00
Maxime Beauchemin
98ba32399e Polish 2016-02-03 23:11:06 -08:00
Maxime Beauchemin
b10487cf39 Fixing the overflow for tables 2016-02-03 21:51:10 -08:00
Maxime Beauchemin
72cf50a8c3 Merge pull request #127 from mistercrunch/sql
SQL editor, eventually will be tied to a flow to create views
2016-02-03 21:36:48 -08:00
Maxime Beauchemin
3b9b81f93e Getting the back button to work 2016-02-03 21:36:21 -08:00
Maxime Beauchemin
9c47415d62 Error handling and table links 2016-02-03 21:36:21 -08:00
Maxime Beauchemin
50d7d0fb5b Making progress 2016-02-03 21:36:21 -08:00
Maxime Beauchemin
e8ae49d181 An airpal like interface 2016-02-03 21:35:57 -08:00
Maxime Beauchemin
354ef9b4bb Polishing the CSS template feature 2016-02-03 21:13:43 -08:00
Maxime Beauchemin
d276be8d50 Moving CSS templates link to Sources 2016-02-03 20:19:54 -08:00
Maxime Beauchemin
cf5d290a12 Merge pull request #128 from mistercrunch/css_templates
Allowing definition of css templates
2016-02-03 18:09:21 -08:00
Maxime Beauchemin
dbbedc3ba4 Allowing definition of css templates 2016-02-03 18:04:28 -08:00
Maxime Beauchemin
9838fbb107 Fixing bad merge 2016-01-29 10:32:23 -08:00
Maxime Beauchemin
95aa6e00fa [fix] can't back out with back button 2016-01-29 10:30:43 -08:00
Maxime Beauchemin
5156d9f2dd Merge pull request #126 from mistercrunch/heatmap
New viz: Heatmap!
2016-01-28 16:45:32 -08:00
Maxime Beauchemin
4e6e20daa4 Allowing for normalizing across x or y 2016-01-28 13:11:45 -08:00
Maxime Beauchemin
44b43a352a Adding vid to README.md 2016-01-27 14:04:27 -08:00
Maxime Beauchemin
0714dc62d0 Added smoothening option 2016-01-27 09:45:02 -08:00
Maxime Beauchemin
40b28d0afa Allowing different color schemes 2016-01-27 08:50:39 -08:00
Maxime Beauchemin
09c77242fc Working without zoom 2016-01-26 23:17:58 -08:00
Maxime Beauchemin
0f58c609d0 Heatmap 2016-01-26 12:57:12 -08:00
Maxime Beauchemin
a2f14b3787 getting something to show 2016-01-24 22:52:27 -08:00
Maxime Beauchemin
b91460db4f [bugfix] sunburst text shows behind arcs 2016-01-24 17:44:44 -08:00
Maxime Beauchemin
1b661a1ee8 Merge pull request #125 from mistercrunch/colors_perfect
Consistent colors rendered client side
2016-01-24 14:35:00 -08:00
Maxime Beauchemin
a3c2ee0e1d Consistent colors rendered client side 2016-01-24 13:56:20 -08:00
Maxime Beauchemin
e6920f8066 CRUD views improvements 2016-01-24 10:09:26 -08:00
Maxime Beauchemin
0ca59bcc21 Curating list of fields 2016-01-23 10:15:56 -08:00
Maxime Beauchemin
9fb708a74a Merge pull request #124 from mistercrunch/colors
A more cohesive color strategy
2016-01-23 10:03:16 -08:00
Maxime Beauchemin
32e4b9f2bc A more cohesive color strategy 2016-01-23 09:59:16 -08:00
Maxime Beauchemin
012a651586 v0.7.0 2016-01-23 07:16:45 -08:00
Maxime Beauchemin
b472cad5d1 Merge pull request #123 from mistercrunch/color_factory
Adding a color factory
2016-01-22 15:37:47 -08:00
Maxime Beauchemin
2df3cfd774 Adding a color factory 2016-01-22 15:26:02 -08:00
Maxime Beauchemin
2f6e971463 Merge pull request #122 from mistercrunch/para
Adding Parallel coordinates viz
2016-01-21 13:40:58 -08:00
Maxime Beauchemin
73989f41ac Adding Parallel coordinates viz 2016-01-21 08:35:20 -08:00
Maxime Beauchemin
2885227050 Autodocs 2016-01-20 18:09:31 -08:00
Maxime Beauchemin
52e6e97262 Merge pull request #121 from mistercrunch/iframe
Iframe
2016-01-19 12:59:14 -08:00
Maxime Beauchemin
5f880682c0 Fixing a bug 2016-01-19 12:58:44 -08:00
Maxime Beauchemin
5f9f95b717 Adding a basic ifram viz 2016-01-19 00:39:25 -08:00
Maxime Beauchemin
ef612ed66c Merge pull request #120 from mistercrunch/sliceinfo
Slice information can be displayed in dashboard
2016-01-19 00:37:48 -08:00
Maxime Beauchemin
110be3551a bugfix in explore view 2016-01-19 00:11:54 -08:00
Maxime Beauchemin
8d40717a92 Adding description for new field in crud 2016-01-19 00:06:23 -08:00
Maxime Beauchemin
e40f5bc6d6 Displaying markdown slice info on dash view 2016-01-19 00:02:46 -08:00
Maxime Beauchemin
ba1dfcb845 Refactor + timeformating options 2016-01-16 14:01:05 -08:00
Maxime Beauchemin
b594b8a93b BigNumber tweaks 2016-01-16 07:17:26 -08:00
Maxime Beauchemin
f1127a1e47 Merge pull request #117 from mistercrunch/refact
Doing some refactoring
2016-01-16 06:52:44 -08:00
Maxime Beauchemin
085430cb53 Doing some refactoring 2016-01-16 06:45:49 -08:00
Maxime Beauchemin
85963900ae Merge pull request #115 from mistercrunch/y_format
Providing options for Y axis number formating
2016-01-15 23:28:46 -08:00
Maxime Beauchemin
d9e4c75122 Parameterizing the number of gunicorn workers 2016-01-15 12:23:28 -08:00
Maxime Beauchemin
976b7772f0 Weeding some useless lines 2016-01-15 10:13:08 -08:00
Maxime Beauchemin
02e519ab61 Fixing the bug where filters don't apply on load 2016-01-15 10:05:29 -08:00
Maxime Beauchemin
5910bf007a Providing options for Y axis number formating 2016-01-14 15:31:23 -08:00
Maxime Beauchemin
69a385bade [hotfix] date casting 2016-01-14 09:54:19 -08:00
Maxime Beauchemin
67ea040d1f Merge pull request #112 from mistercrunch/url_shortner
Adding an URL shortner
2016-01-14 09:50:25 -08:00
Maxime Beauchemin
d97788b87a Merge pull request #113 from mistercrunch/check
Prettier checkboxes
2016-01-14 09:50:00 -08:00
Maxime Beauchemin
ba30dc4c22 Merge pull request #111 from mistercrunch/load_ex_dash
Loading another example amazing dash
2016-01-14 09:49:01 -08:00
Maxime Beauchemin
df797c0ae0 Prettier checkboxes 2016-01-13 22:16:24 -08:00
Maxime Beauchemin
f4ef1c02a2 Adding an URL shortner 2016-01-13 21:44:27 -08:00
Maxime Beauchemin
a3727fc091 Making sqlite work 2016-01-13 18:16:01 -08:00
Maxime Beauchemin
ef5511cccf Getting stuff to load 2016-01-13 17:19:12 -08:00
Maxime Beauchemin
a38a8d476e Loading another example amazing dash 2016-01-13 17:05:11 -08:00
Maxime Beauchemin
d726bd0e05 NVD3 form optinos for stacked/expanded/grouped ... 2016-01-13 13:43:39 -08:00
Maxime Beauchemin
7982dee834 Updating the CONTRIBUTING.md 2016-01-13 09:03:27 -08:00
Maxime Beauchemin
4f7a5f33bb Refactoring 2016-01-13 08:50:55 -08:00
Maxime Beauchemin
b6b8d046c8 Tunning timeseries settings 2016-01-12 14:57:34 -08:00
Maxime Beauchemin
089a8c677c [hotfix] no data 2016-01-12 12:06:34 -08:00
Maxime Beauchemin
f434f3c756 Raising earlier on no data 2016-01-12 12:02:26 -08:00
Maxime Beauchemin
ffe9a68712 Adding a picture for who dataset 2016-01-12 09:47:15 -08:00
Maxime Beauchemin
2b01340bb6 Allowing TableViz to show time granularity 2016-01-12 09:34:17 -08:00
Maxime Beauchemin
0e0ae7bc92 Increasing page_size to 500 in crud 2016-01-12 08:50:17 -08:00
Maxime Beauchemin
4941565a41 Making TableViz support a time_series 2016-01-12 08:48:28 -08:00
Maxime Beauchemin
299ad09576 [bugfix] columns dupplicated in Druid df 2016-01-12 08:43:36 -08:00
Maxime Beauchemin
552a3094df Getting the markdown to do what it should 2016-01-11 23:18:43 -08:00
Maxime Beauchemin
2b0c550a0b Improvments to load_examples 2016-01-11 20:53:20 -08:00
Maxime Beauchemin
2b69912c46 Minor bug fixes 2016-01-11 16:01:54 -08:00
Maxime Beauchemin
ceb0dc1985 Touchups on field list 2016-01-11 15:38:17 -08:00
Maxime Beauchemin
ed96f9b92f Fixing the word bad assumptions about field ordering 2016-01-11 15:29:18 -08:00
Maxime Beauchemin
4aa70d0f43 Ordering columns in the Druid pandas df 2016-01-11 15:15:11 -08:00
Maxime Beauchemin
6c92fa471d some error handling for WordCloud 2016-01-11 14:42:45 -08:00
Maxime Beauchemin
3efaec8983 Minor fix + TODO alterations 2016-01-11 14:34:18 -08:00
Maxime Beauchemin
2dbad2d124 Merge pull request #109 from mistercrunch/hash_change
Getting browser history to work on the explore view
2016-01-11 11:46:26 -08:00
Maxime Beauchemin
f0cdf4318d Getting browser history to work on the explore view 2016-01-11 11:42:46 -08:00
Maxime Beauchemin
ac8a78e388 Merge pull request #108 from bradmbaker/brad_baker_final_fix_for_tooltip
pulling to the front on hover
2016-01-11 11:07:41 -08:00
BradBaker
7fb845f38d pulling to the front on hover 2016-01-11 10:11:50 -08:00
Maxime Beauchemin
af5528519d Made json csv and standalone button more dynamic 2016-01-11 09:27:00 -08:00
Maxime Beauchemin
4c8b275f88 Minor tweaks 2016-01-10 20:53:57 -08:00
Maxime Beauchemin
a4c79648a9 Minor cosmetic changes 2016-01-10 09:57:45 -08:00
Maxime Beauchemin
fc038a6e78 JS bugfixes 2016-01-09 21:14:21 -08:00
Maxime Beauchemin
c9d42176f5 Merge pull request #104 from bradmbaker/brad_baker_fixing_tooltips_v2
simplifying tooltip code
2016-01-09 08:29:05 -08:00
Maxime Beauchemin
baf759b3ca Merge pull request #105 from bradmbaker/brad_baker_adding_stagger_to_all_date_charts
adding stagger for all charts that have a date axis
2016-01-08 18:10:15 -08:00
BradBaker
99aa6f71e2 adding stagger for all charts that have a date axis 2016-01-08 16:20:22 -08:00
BradBaker
e4f45ac285 simplifying tooltip code 2016-01-08 16:09:45 -08:00
Maxime Beauchemin
e36ebfe640 Merge pull request #102 from bradmbaker/brad_baker_adding_two_x_axis_rescaling_to_nvd3_file
Fix for 2-axis charts where it shrinks them a little bit
2016-01-08 14:27:20 -08:00
bradmbaker
7e827da3b1 fixing indents 2016-01-08 14:26:49 -08:00
Maxime Beauchemin
897d519b1b Merge pull request #101 from gitter-badger/gitter-badge
Add a Gitter chat badge to README.md
2016-01-08 13:11:57 -08:00
BradBaker
98809cb3f4 Fix for 2-axis charts where it shrinks them a little bit 2016-01-08 12:12:26 -08:00
Maxime Beauchemin
eafd438323 Merge pull request #100 from bradmbaker/patch-1
Update tooltips with new classes
2016-01-06 16:50:57 -08:00
Maxime Beauchemin
09b8ed5548 Adding link to Gitter channel in README 2016-01-06 16:47:53 -08:00
The Gitter Badger
05b391cf1c Add Gitter badge 2016-01-07 00:46:40 +00:00
Maxime Beauchemin
9bc2f08b10 Improving the Test Connection button 2016-01-06 16:02:50 -08:00
bradmbaker
a04cf2706f Update tooltips with new classes
The li classes changed, so I'm updating so that the tooltip overflow works. I also changed a variable name to be correct.
2016-01-06 15:49:49 -08:00
Maxime Beauchemin
27ceb150c3 Fixing jumpy clock number 2016-01-06 14:41:05 -08:00
Maxime Beauchemin
3fa3ce1a12 Fixing resampling options 2016-01-06 14:27:10 -08:00
Maxime Beauchemin
51e9cb3d3e Making all nvd3 timeseries have the full fieldset 2016-01-06 14:21:03 -08:00
Maxime Beauchemin
ee73b6fd12 Changing the time range selector default to False 2016-01-06 14:13:34 -08:00
Maxime Beauchemin
cd1258dfc0 Merge pull request #99 from mistercrunch/resample
Time resampling as in Pandas
2016-01-06 14:07:34 -08:00
Maxime Beauchemin
3216fa2140 Allowing line breaks in fieldsets 2016-01-06 14:02:58 -08:00
Maxime Beauchemin
1b7725b303 Merge pull request #98 from bradmbaker/patch-1
Change Scaling to Operate on SVG instead of Div
2016-01-06 13:58:10 -08:00
Maxime Beauchemin
ade917582f Adding basic resampling capabilities from pandas 2016-01-06 13:52:09 -08:00
bradmbaker
9959d266f0 Change Scaling to Operate on SVG instead of Div 2016-01-06 13:49:18 -08:00
Maxime Beauchemin
4f12e78a64 Fixed left panel in explore view 2016-01-06 13:32:47 -08:00
Maxime Beauchemin
3a3e7c8964 A much more clear TODO.md 2015-12-29 13:47:21 -08:00
Maxime Beauchemin
3f292e6308 Merge pull request #96 from mistercrunch/filter_box
Adding a filter box widget
2015-12-29 08:26:50 -08:00
Maxime Beauchemin
37cbf61480 Adding markdown support for table descriptions 2015-12-27 21:06:16 -08:00
Maxime Beauchemin
6df3e67bdb Fixing the filters 2015-12-27 14:21:10 -08:00
Maxime Beauchemin
ef14c21281 Adding volume indicators on filter box 2015-12-27 09:46:07 -08:00
Maxime Beauchemin
5ad4f38c49 Fixing images in docs 2015-12-26 14:04:46 -08:00
Maxime Beauchemin
07df0f109f Adding a filter box widget 2015-12-26 12:50:46 -08:00
Maxime Beauchemin
fa0d6c6776 Merge pull request #95 from mistercrunch/docs2
Working on docs
2015-12-25 21:49:06 -08:00
Maxime Beauchemin
e96494e0cf Working on docs 2015-12-25 21:48:09 -08:00
Maxime Beauchemin
0a9600d994 Minor improvements on the bubble plot 2015-12-24 22:57:21 -08:00
Maxime Beauchemin
9d0de6c8e7 Merge pull request #94 from mistercrunch/context
Massive js refactor + Dashboard filters
2015-12-24 11:57:36 -08:00
Maxime Beauchemin
010fbc3693 Fixed standalone view 2015-12-24 09:24:44 -08:00
Maxime Beauchemin
dfee55df1a Fixing save ops 2015-12-24 09:17:38 -08:00
Maxime Beauchemin
9427cd6216 Fixing date processing 2015-12-24 08:41:35 -08:00
Maxime Beauchemin
e7ccbefe87 Achieving ASYNC explore 2015-12-23 13:35:20 -08:00
Maxime Beauchemin
527bc14162 TODO 2015-12-23 12:45:18 -08:00
Maxime Beauchemin
b88f2825ea Improvements to bubble chart 2015-12-23 07:53:21 -08:00
Maxime Beauchemin
c3bec3e35b refactoring the imports 2015-12-22 22:54:54 -08:00
Maxime Beauchemin
a5c1358229 Fixing py3 error 2015-12-22 20:46:19 -08:00
Maxime Beauchemin
0f892c6a4d Fixing sqlite error msg 2015-12-22 20:35:35 -08:00
Maxime Beauchemin
afae89239d Adding another example file 2015-12-22 15:56:18 -08:00
Maxime Beauchemin
8682196eb8 Getting filters to a reasonable place 2015-12-22 15:52:15 -08:00
Maxime Beauchemin
e0586ec666 Working state 2015-12-22 15:35:06 -08:00
Maxime Beauchemin
4e21fb4285 Adding support for quoted field names 2015-12-22 08:12:47 -08:00
Maxime Beauchemin
6a28ad3f4e Adding standalone mode 2015-12-21 08:17:02 -08:00
Maxime Beauchemin
fffb0a7a80 Getting rid of the template heavy approach 2015-12-21 00:05:50 -08:00
Maxime Beauchemin
458703d9c8 Getting the dashboard filters to work 2015-12-20 21:49:59 -08:00
Maxime Beauchemin
b1399b97a9 Progress 2015-12-20 15:04:16 -08:00
Maxime Beauchemin
48f5fcfcc7 Some major refactoring 2015-12-20 13:54:40 -08:00
Maxime Beauchemin
4b481e938a Getting in a working state 2015-12-20 08:33:08 -08:00
Maxime Beauchemin
973f1a6235 removing table-info from featured datasets 2015-12-19 07:40:24 -08:00
Maxime Beauchemin
1893e90e34 Merge pull request #93 from mistercrunch/controller
Controller
2015-12-18 16:52:16 -08:00
Maxime Beauchemin
c1080638ae Passing a ctrl to the js 2015-12-18 15:45:11 -08:00
Maxime Beauchemin
5435f53702 Passing a controller object to widget 2015-12-18 15:09:14 -08:00
Maxime Beauchemin
47bb6b61ef Merge pull request #92 from mistercrunch/nogroupby
Allowing not to group by on table view
2015-12-18 15:07:44 -08:00
Maxime Beauchemin
19eb177fe3 Changing resize action to do nothing 2015-12-18 14:49:18 -08:00
Maxime Beauchemin
bd75a58f24 Re writting table viz 2015-12-18 14:42:44 -08:00
Maxime Beauchemin
55c4433b0b Allowing to not group by on table view 2015-12-18 12:06:01 -08:00
Maxime Beauchemin
1377c37b7b Merge pull request #91 from mistercrunch/exports
Exports
2015-12-17 20:37:09 -08:00
Maxime Beauchemin
d933810cae updating TODO 2015-12-17 18:15:19 -08:00
Maxime Beauchemin
1cd63c3e4a Adding a unit test 2015-12-17 18:14:40 -08:00
Maxime Beauchemin
5f6236c80b Export to csv or json 2015-12-17 18:14:40 -08:00
Maxime Beauchemin
31dd5bf1b3 Merge pull request #90 from mistercrunch/docs
A basic squeleton for the docs
2015-12-17 18:14:03 -08:00
Maxime Beauchemin
880c7397e1 A basic squeleton for the docs 2015-12-17 16:26:36 -08:00
Maxime Beauchemin
d2ae6ceed6 Merge pull request #89 from mistercrunch/featured_datasets
Featured datasets
2015-12-17 15:52:55 -08:00
Michelle Thomas
79e7818ba8 Changes to the layout of the featured datasets page 2015-12-17 15:43:28 -08:00
Maxime Beauchemin
2a30908328 Merge branch 'master' of github.com:mistercrunch/panoramix 2015-12-17 13:49:47 -08:00
Maxime Beauchemin
4e3d284785 Restoring error bubbling 2015-12-17 13:49:36 -08:00
Michelle Thomas
e5256ef6da Fixing alignment of a hanging indent for code quality 2015-12-17 11:35:18 -08:00
Michelle Thomas
f24e58abf2 Fixes to the downgrade migration for user_id in sqlatables 2015-12-17 11:28:06 -08:00
Maxime Beauchemin
b04c4223c5 Merge pull request #87 from bradmbaker/brad_adding_js_to_allow_overflow_to_actually_work
fixing a few bugs with tool tip overflow
2015-12-17 11:13:46 -08:00
Maxime Beauchemin
047e142b43 Merge pull request #88 from mistercrunch/map
World Map viz with bubbles
2015-12-17 11:12:09 -08:00
Maxime Beauchemin
b3edc077dc Touchups 2015-12-16 20:57:19 -08:00
Maxime Beauchemin
48c1481caa World Map with bubbles 2015-12-16 19:22:19 -08:00
Michelle Thomas
6f948f0783 Changing DataTables to default to show all and sort by database 2015-12-16 17:58:16 -08:00
Michelle Thomas
6bbcbe524a Fixing the button on the featured datasets page 2015-12-16 16:34:51 -08:00
Michelle Thomas
6155ff8de6 Adding owner to sqlatables and changing some of the formatting using DataTables 2015-12-16 15:35:59 -08:00
Michelle Thomas
30df7be258 Adding a view for featured datasets 2015-12-16 15:35:59 -08:00
BradBaker
e0f8f2cdca fixing a few bugs with tool tip overflow 2015-12-16 11:54:34 -08:00
Maxime Beauchemin
dcd36780ef Merge pull request #86 from bradmbaker/brad_adding_new_and_improved_date_formats
adjusting date formats
2015-12-16 11:45:53 -08:00
Maxime Beauchemin
d6b6433d52 Better list view for druid datasurces 2015-12-16 11:29:28 -08:00
BradBaker
e2220ded7f adjusting date formats 2015-12-15 18:00:41 -08:00
Maxime Beauchemin
cf0734b8cd Merge pull request #85 from mistercrunch/sankey
Adding sankey diagrams
2015-12-15 16:05:50 -08:00
Maxime Beauchemin
c09ff2b7eb Getting the query to show 2015-12-15 16:05:35 -08:00
Maxime Beauchemin
0b0a095163 Adding sankey diagrams 2015-12-15 15:59:58 -08:00
Maxime Beauchemin
eb6890f742 Namespacing sunburst CSS 2015-12-15 14:31:43 -08:00
Maxime Beauchemin
c2736b34cd Making row_limit freeform 2015-12-15 14:16:40 -08:00
Maxime Beauchemin
d4588e00ea Merge pull request #84 from mistercrunch/force
Adding directed force layout viz
2015-12-15 14:03:55 -08:00
Maxime Beauchemin
c563057099 Adding directed force layout viz 2015-12-15 13:59:31 -08:00
Maxime Beauchemin
f1ceac996c Merge pull request #83 from mistercrunch/js
Big JS refactor
2015-12-15 13:59:06 -08:00
Maxime Beauchemin
51b273b5b8 Big JS refactor 2015-12-14 22:18:26 -08:00
Maxime Beauchemin
f8963e0b30 Merge pull request #82 from bradmbaker/letting_tool_tips_overflow
letting tooltips in the dashboard overflow
2015-12-14 16:25:53 -08:00
BradBaker
65be15cc8d letting tooltips in the dashboard overflow 2015-12-14 13:46:05 -08:00
Maxime Beauchemin
c6d007cc8b Style 2015-12-14 10:21:46 -08:00
Maxime Beauchemin
eb13ce1840 Cleaning up the html indentation on explore.html 2015-12-14 08:48:37 -08:00
Maxime Beauchemin
6b069c6741 Cosmetics on dropdowns 2015-12-14 08:21:49 -08:00
Maxime Beauchemin
5f6a7423b8 Merge pull request #81 from mistercrunch/layout
Slightly better layout for explore page
2015-12-14 08:10:23 -08:00
Maxime Beauchemin
d50a768df9 Fix td align 2015-12-14 08:09:20 -08:00
Maxime Beauchemin
387bbb8b24 Merge pull request #80 from mistercrunch/checkboxes
Checkboxes everywhere
2015-12-13 17:46:35 -08:00
Maxime Beauchemin
0d6720ad37 Checkboxes everywhere 2015-12-13 13:29:13 -08:00
Maxime Beauchemin
c97b0b6026 Slightly better layout for explore page 2015-12-13 10:03:06 -08:00
Maxime Beauchemin
06c12e7cd9 Merge pull request #79 from mistercrunch/freeform_dropd
Cleanup around multiple select fields
2015-12-13 07:55:15 -08:00
Maxime Beauchemin
5701696c79 py3 compatibility 2015-12-12 19:17:09 -08:00
Maxime Beauchemin
f4e4c529a0 Cleanup around multiple select fields 2015-12-12 18:55:56 -08:00
Maxime Beauchemin
08fd4d1258 0.6.1 2015-12-12 09:44:56 -08:00
Maxime Beauchemin
18a2670f41 [bugfix] sqlite ALTER issue 2015-12-12 09:43:23 -08:00
Maxime Beauchemin
65c1c42bc8 Align dashboard title 2015-12-11 14:33:19 -08:00
Maxime Beauchemin
1fe15496ce Formatting change_on column in list views 2015-12-11 12:17:23 -08:00
Maxime Beauchemin
b628713685 Adding a link section to readme 2015-12-11 08:51:13 -08:00
Maxime Beauchemin
59c1ee3c03 fix migrations 2015-12-10 18:04:43 -08:00
Maxime Beauchemin
a4065e4be6 v0.6.0 2015-12-10 17:17:28 -08:00
Maxime Beauchemin
d3c0a86e67 Merge pull request #77 from mistercrunch/tooltips
Better tooltips and more ways to integrate them easily
2015-12-10 11:08:06 -08:00
Maxime Beauchemin
fc93f36339 Better tooltips and more ways to integrate them easily 2015-12-10 08:26:01 -08:00
Maxime Beauchemin
c64473c49e [sunburst] limit -> row_limit 2015-12-09 22:37:49 -08:00
Maxime Beauchemin
c3f96074b9 Insuring column order 2015-12-09 18:11:45 -08:00
Maxime Beauchemin
16859117fe Merge pull request #76 from mistercrunch/form_overrides
Introducing form overrides for label and tooltips
2015-12-09 15:33:55 -08:00
Maxime Beauchemin
d476dd2b8a Introducing form overrides for label and tooltips 2015-12-09 15:19:43 -08:00
Maxime Beauchemin
9c0dd5bf3c Merge pull request #75 from mistercrunch/sunburst
New viz: sunbursts
2015-12-09 14:44:13 -08:00
Maxime Beauchemin
f5b4a0290e New viz: sunbursts 2015-12-09 14:37:18 -08:00
Maxime Beauchemin
d526711f1e Changing default sort order for databases 2015-12-08 15:09:58 -08:00
Maxime Beauchemin
2833bd3825 bugfix - empty slug field 2015-12-08 14:15:48 -08:00
Maxime Beauchemin
021654cdf1 Merge pull request #74 from mistercrunch/fieldsets
Introducing fieldsets
2015-12-07 23:28:57 -08:00
Maxime Beauchemin
a32712d9cb Cleaning up 2015-12-07 23:24:43 -08:00
Maxime Beauchemin
b666508085 Introducing field sets 2015-12-07 22:25:01 -08:00
Maxime Beauchemin
6f2c953a9e Merge pull request #73 from mistercrunch/theme
Airflowlike theme
2015-12-04 15:05:10 -08:00
Maxime Beauchemin
1a62d5f74f Bugfix pivot table with no groupby 2015-12-04 14:24:36 -08:00
Maxime Beauchemin
04c0c05c01 Airflowlike theme 2015-12-04 14:16:31 -08:00
Maxime Beauchemin
6dc21c1e78 Merge pull request #72 from mistercrunch/log
Logging slice and dash views
2015-12-04 11:55:59 -08:00
Maxime Beauchemin
f882ff13ff Logging slice and dash views 2015-12-04 11:52:32 -08:00
Maxime Beauchemin
aed2f46413 Merge pull request #70 from mistercrunch/dash_slug
Adding url slug support for dashboard model
2015-12-04 10:30:28 -08:00
Maxime Beauchemin
a0e967e3b9 Merge pull request #71 from mistercrunch/showminmax
Add option to show minmax on x axis
2015-12-04 10:30:12 -08:00
Maxime Beauchemin
66089a8948 Adding migration file 2015-12-04 10:13:16 -08:00
Maxime Beauchemin
7ac0615d83 Merge pull request #69 from mistercrunch/save_as
Allowing for [Save AS] and [Overwrite]
2015-12-04 10:10:59 -08:00
Maxime Beauchemin
63b27bbece Add option to show minmax on x axis 2015-12-04 10:10:13 -08:00
Maxime Beauchemin
2743b2412f Adding url slug support for dashboard model 2015-12-04 10:02:16 -08:00
Maxime Beauchemin
b9d7253a48 Save AS and Overwrite 2015-12-04 09:30:43 -08:00
Maxime Beauchemin
618e117374 Merge pull request #68 from mistercrunch/cumsum
Adding cumsum to rolling functions
2015-11-30 15:07:05 -08:00
Maxime Beauchemin
03e13cb957 Adding cumsum to rolling functions 2015-11-30 14:58:15 -08:00
Maxime Beauchemin
525bd509cb Merge pull request #67 from mistercrunch/fix_debug_dup_call
Fix debug mode calls get_json twice
2015-11-30 13:48:11 -08:00
Maxime Beauchemin
2002f5e369 Fix debug mode calls get_json twice 2015-11-30 12:36:59 -08:00
Maxime Beauchemin
852e3c6667 Merge pull request #66 from mistercrunch/pivot_table
Adding a PivotTableViz
2015-11-26 07:10:22 -08:00
Maxime Beauchemin
7cd3696dd2 Adding missing jquery-ui images 2015-11-24 12:30:37 -08:00
Maxime Beauchemin
139e594cce Bugfix, wrong css reference 2015-11-24 12:21:36 -08:00
Maxime Beauchemin
b0f7b3c595 A little bit more explicit error handling 2015-11-24 12:09:30 -08:00
Maxime Beauchemin
8b0b13eedc Adding a PivotTableViz 2015-11-24 11:54:43 -08:00
Maxime Beauchemin
5968344613 Merge pull request #65 from mistercrunch/custom_having
Adding custom HAVING clause
2015-11-24 11:51:33 -08:00
Maxime Beauchemin
16c81280e6 Merge pull request #64 from mistercrunch/sort_select2
Preserving the ordering in selectmultiple
2015-11-24 11:51:28 -08:00
Maxime Beauchemin
4819be2fb2 Adding groupby to select2Sortable 2015-11-24 11:46:44 -08:00
Maxime Beauchemin
3cc5db5bbf Adding custom HAVING clause 2015-11-24 11:37:20 -08:00
Maxime Beauchemin
c8faeed5b3 Preserving the ordering in selectmultiple 2015-11-24 10:52:32 -08:00
Maxime Beauchemin
de52449c2a Removing dups in color array 2015-11-23 16:42:30 -08:00
Maxime Beauchemin
2e83bf3850 Merge pull request #63 from mistercrunch/hide_pass
Encrypting the passwords out of connection strings
2015-11-23 08:45:13 -08:00
Maxime Beauchemin
85227912b3 Encrypting the passwords out of connection strings 2015-11-21 12:35:49 -08:00
Maxime Beauchemin
4645c69c85 Merge pull request #61 from mistercrunch/better-boolean-field
BetterBooleanField to fix html omitting non-checked <input>
2015-11-14 18:50:06 -08:00
Patrick Leo Tardif
f592aa1012 Add BetterBooleanField 2015-11-13 17:16:34 -08:00
Maxime Beauchemin
d8c79cd26e bugfix for error msg not surfaced 2015-11-11 12:18:20 -08:00
Maxime Beauchemin
184262844a Fixing resize for nvd3 widgets 2015-11-10 17:01:04 -08:00
Maxime Beauchemin
9d65f8d866 Merge pull request #60 from NiharikaRay/fix_markup_widget
Fix Markup Widget bug
2015-11-10 12:26:33 -08:00
Niharika Ray
54e45babe7 Fix Markup Widget bug 2015-11-10 12:22:51 -08:00
Maxime Beauchemin
3c5867cafb Merge pull request #59 from patrickleotardif/y_axis_format
Adding y-axis format option
2015-11-10 11:59:19 -08:00
Patrick Leo Tardif
93c660f2c7 make y axis format one col width only 2015-11-10 11:33:35 -08:00
Patrick Leo Tardif
4233d34678 handle y2axis for y axis format 2015-11-10 11:33:05 -08:00
Patrick Leo Tardif
6b13d78f69 verbose explanation and link for d3 format" 2015-11-10 11:32:42 -08:00
Patrick Leo Tardif
38a4157ac6 Adding y-axis format option 2015-11-09 18:30:55 -08:00
Maxime Beauchemin
774ca9fa2f Merge pull request #58 from mistercrunch/min_periods
Setting min_periods to 1 for rolling windows
2015-11-08 09:13:17 -08:00
Maxime Beauchemin
d75c5c8da8 Setting min_periods to 1 for rolling windows 2015-11-08 09:10:45 -08:00
Maxime Beauchemin
a18cf2d26b Adding audit info to a few list views 2015-11-04 17:06:17 -08:00
Maxime Beauchemin
0889947394 Clarifying time filters, small refactor 2015-11-03 09:52:19 -08:00
Maxime Beauchemin
4e6aa4c5b5 Improvments around date handling for nvd3 charts 2015-10-31 15:34:23 -07:00
Krishna Puttaswamy
ed19b8e7eb Merge pull request #56 from mistercrunch/kp_enable_sort_order
adding sort order of the slices on changed_on field
2015-10-27 11:40:22 -07:00
Krishna Puttaswamy
6ecea9123c adding sort order of the slices on changed_on field 2015-10-27 11:36:25 -07:00
Maxime Beauchemin
5057593ec9 v0.5.3 2015-10-24 08:02:16 -07:00
Maxime Beauchemin
ec805d3bd8 pinning flask-sqlalchemy==2.0 2015-10-24 07:54:03 -07:00
Maxime Beauchemin
774f205d24 pinning sqla 2015-10-24 07:49:46 -07:00
Maxime Beauchemin
c07770e52a v0.5.2 2015-10-23 18:06:01 -07:00
Maxime Beauchemin
0ec69a4ac5 druid bugfix 2015-10-20 16:40:12 -07:00
Maxime
30513116f9 Using mysqlclient instead of pymysql 2015-10-20 22:26:55 +00:00
Maxime Beauchemin
c3903eff4f Adding forgotten template 2015-10-20 14:11:21 -07:00
Maxime Beauchemin
ac79265dcb Merge pull request #53 from mistercrunch/py3
Py3
2015-10-20 13:46:57 -07:00
Maxime Beauchemin
4d8966376f Aiming at python 3.4 - test 2015-10-20 13:19:13 -07:00
Maxime Beauchemin
a58d184711 Removing references to basestring 2015-10-20 13:00:42 -07:00
Maxime Beauchemin
fc76e87653 Trying mode='rt' 2015-10-20 12:50:12 -07:00
Maxime Beauchemin
e3e3d0b1f5 py3 compat changes 2015-10-20 12:50:11 -07:00
Maxime Beauchemin
5a967031c8 Adding python 3.5 to build matrix 2015-10-20 12:49:02 -07:00
Maxime Beauchemin
1ffb9a0fc6 Reverting to old deps notation as server is on 2.7.3 2015-10-20 12:42:19 -07:00
Maxime Beauchemin
2ddebc1a4c Pinning flask version 2015-10-20 12:21:44 -07:00
Maxime Beauchemin
8b67fa1d54 Merge pull request #51 from mistercrunch/offset
Adding timezone offset as a datasource param
2015-10-19 23:53:11 -07:00
Maxime Beauchemin
cdb32a1149 Merge pull request #52 from mistercrunch/travis_speedup
Speed up travis builds with wheels
2015-10-19 23:43:46 -07:00
Maxime Beauchemin
bdd5b08787 Cleaning up reqs versions 2015-10-19 23:34:48 -07:00
Maxime Beauchemin
ea18aad3b2 Speed up travis builds with wheels 2015-10-19 22:54:53 -07:00
Maxime Beauchemin
a7fca30010 Tweaks 2015-10-19 22:15:53 -07:00
Maxime Beauchemin
06e57bf890 Adding timezone offset as a datasource param 2015-10-19 21:43:14 -07:00
Maxime Beauchemin
8aa97f7e08 Better defaults for Druid endpoints and better error handling 2015-10-16 07:41:52 -07:00
Maxime Beauchemin
9d61ee2323 Bugfix for time series bar chart 2015-10-15 21:08:52 -07:00
Maxime Beauchemin
960596228e +entry in TODO 2015-10-14 08:39:18 -07:00
Maxime Beauchemin
4837968ea4 Merge pull request #48 from mistercrunch/timeout
Allowing to specify the gunicorn timeout in CLI and config
2015-10-13 16:09:24 -07:00
Maxime Beauchemin
0a1eb64da0 Bumping timeout to 60 2015-10-13 16:06:37 -07:00
Maxime Beauchemin
686069d283 Allowing to specify the gunicorn timeout in CLI and config 2015-10-13 15:50:12 -07:00
Maxime Beauchemin
7793e243ad Note about python version support 2015-10-13 09:41:27 -07:00
Maxime Beauchemin
37629fb548 v0.5.1 2015-10-13 08:05:40 -07:00
Maxime Beauchemin
8cd27db893 Stop attribution of datasource_access to gamma on init 2015-10-13 07:55:45 -07:00
Maxime Beauchemin
b25393be08 Adding a note to run init script in installation process 2015-10-13 07:51:56 -07:00
Maxime Beauchemin
179622da77 Bugfix around empty compare_lag 2015-10-12 21:49:28 -07:00
Maxime Beauchemin
7137d7597e Adding missing installation step 2015-10-12 21:33:28 -07:00
Maxime Beauchemin
51d7225f06 v0.5.0 2015-10-12 18:09:37 -07:00
Maxime Beauchemin
85f3857285 Removing raise statement used for testing 2015-10-12 18:07:13 -07:00
Maxime Beauchemin
7a4eaf41b2 Adding error handler that shows the stacktrace 2015-10-12 18:05:46 -07:00
Maxime Beauchemin
e5aa49fb08 Descriptions for confusing columns 2015-10-12 08:17:06 -07:00
Maxime Beauchemin
93b76727d4 Removing done items from TODO 2015-10-11 23:00:41 -07:00
Maxime Beauchemin
76d34af5f8 Merge pull request #46 from mistercrunch/time_grain
Allowing to change the "Time Column" on SqlA
2015-10-11 21:30:19 -07:00
Maxime Beauchemin
f0d84f634f Forcing pandas versino 2015-10-11 21:24:26 -07:00
Maxime Beauchemin
cb92cac776 Allowing to define multiple time columns for time series on Sqla 2015-10-12 03:56:11 +00:00
Maxime
b84ede1aef Bugfix druid datasource onsave 2015-10-09 00:02:27 +00:00
Adrian Kuhn
b034bf9c5f Merge pull request #45 from mistercrunch/px_render_refresh
Bootstrapping widgets from javascript initializer.
2015-10-08 11:22:27 -07:00
Adrian Kuhn
694f82ec65 Boostrapping widgets from javascript initializer (RFC). 2015-10-07 16:02:42 -07:00
Maxime Beauchemin
467963533b Button Druidify\!->Slice\! 2015-10-06 21:12:59 -07:00
Maxime Beauchemin
cf53cfc183 Merge pull request #43 from mistercrunch/expr
Supporting arbitrary expressions
2015-10-06 21:08:32 -07:00
Maxime Beauchemin
6db1b97a0e Supporting arbitrary expressions 2015-10-06 12:06:02 -07:00
Maxime Beauchemin
36c7406786 Upgrading nvd3 to dev version fixes a bug around classed series 2015-10-05 21:32:22 -07:00
Maxime Beauchemin
0b87ea6e9b Bugfix around specific datasource access permissions 2015-10-05 17:43:58 -07:00
Maxime Beauchemin
1494b26229 Merge pull request #42 from mistercrunch/dash_css
Adding ability to style a dashboard with CSS
2015-10-05 12:03:47 -07:00
Maxime Beauchemin
bd32170b36 Adding ability to style a dashboard with CSS 2015-10-05 11:52:13 -07:00
Maxime Beauchemin
a0332823ff Changing default for time_compare to empty 2015-10-05 09:46:49 -07:00
Maxime Beauchemin
096f6977c4 Bugfix, missing WHERE CLAUSE field in form 2015-10-05 09:27:33 -07:00
Maxime Beauchemin
d810a9d5ad Adding perm property to Datasource model 2015-10-05 08:29:23 -07:00
Maxime Beauchemin
cd55ea7aab Merge pull request #41 from mistercrunch/cosmetico
Cleaning up the static folder
2015-10-04 23:18:39 -07:00
Maxime Beauchemin
2bb299225a Cleaning up the static folder 2015-10-04 19:36:38 -07:00
Maxime Beauchemin
c9b7c5daa8 Adjusting width of dashboard view controls 2015-10-04 16:54:17 -07:00
Maxime Beauchemin
5f62a1fe02 Adding css file for viz_nvd3 2015-10-04 16:44:54 -07:00
Maxime Beauchemin
a32cf0c152 Merge pull request #35 from mistercrunch/roles
A first draft on default security roles
2015-10-04 16:30:13 -07:00
Maxime Beauchemin
6ea178eced Perms are kept from getting duped, created on save 2015-10-04 16:23:19 -07:00
Maxime Beauchemin
b4c8d7a81e Cosmetics 2015-10-04 16:23:19 -07:00
Maxime Beauchemin
459048abe0 Bugfix 2015-10-04 16:23:19 -07:00
Maxime Beauchemin
71cf515d0d Getting this thing to work 2015-10-04 16:23:19 -07:00
Maxime Beauchemin
e3cdf5bb2c Progress 2015-10-04 16:23:19 -07:00
Maxime Beauchemin
45b5950019 Definining a Gamma role 2015-10-04 16:23:19 -07:00
Maxime Beauchemin
c2049f837c Automate Panoramix role creation 2015-10-04 16:23:19 -07:00
Maxime Beauchemin
8eb0c0a07b Merge pull request #40 from mistercrunch/time_compare
Introducing time comparison
2015-10-04 16:20:55 -07:00
Maxime Beauchemin
ede6768cbe Small improvements to dashboard controls 2015-10-04 07:59:21 -07:00
Maxime Beauchemin
959418cc4e Making sure to alert only once 2015-10-04 07:36:27 -07:00
Maxime Beauchemin
f83d93327f Updating TODO 2015-10-04 07:14:30 -07:00
Maxime Beauchemin
2eb8c1be8d Introducing time comparison 2015-10-03 23:33:30 -07:00
Maxime Beauchemin
46ee9d2c13 Improved free form date parsing 2015-10-02 22:23:14 -07:00
Maxime Beauchemin
bc8f9d2e1a Specifying the fields for DistributionBarViz as it was showing donut 2015-10-02 16:24:15 -07:00
Maxime Beauchemin
e3e5798ed4 Merge pull request #39 from mistercrunch/interpolation
Adding interpolation choice for line charts
2015-10-02 16:22:07 -07:00
Maxime Beauchemin
e1900fcfe9 Bugfix, distributino bar chart shows NaN on xAxis 2015-10-02 15:31:10 -07:00
Maxime Beauchemin
774b1ef9d9 removing console.log 2015-10-02 15:09:22 -07:00
Maxime Beauchemin
bb535a373d Adding an option for line_interpolation in line chart 2015-10-02 15:07:09 -07:00
Maxime Beauchemin
3f9c83828a Removing leftover print statetments 2015-10-01 23:35:05 -07:00
Maxime Beauchemin
33a45772ab py3 2015-10-01 23:22:20 -07:00
Maxime Beauchemin
8e790facbf Merge pull request #38 from mistercrunch/px_javascript_css_files_2
Extract css rules and scripts into separate files.
2015-10-01 23:15:08 -07:00
Adrian Kuhn
4a256b1dc4 Reformat javascript code. 2015-10-01 18:08:36 -07:00
Adrian Kuhn
ad1ebaa6da Extract scripts into javascript file. 2015-10-01 18:05:58 -07:00
Adrian Kuhn
367c2562e1 Extract css rules into stylesheet file. 2015-10-01 18:05:58 -07:00
Maxime Beauchemin
f3c6414ec2 Merge pull request #37 from mistercrunch/viz_type
Viz type
2015-10-01 17:49:10 -07:00
Maxime Beauchemin
ee6cc157f6 Removing chart_kind artefact 2015-10-01 17:47:52 -07:00
Maxime Beauchemin
f3e3415712 Re-adding images, mistakenly did mv instead of git mv 2015-10-01 10:18:38 -07:00
Maxime Beauchemin
c4ff8d1760 Moving static jpg/pngs to static/img 2015-09-30 23:21:09 -07:00
Maxime Beauchemin
d3c420ea01 Adding hint to not consider a bakced up function for coverage accounting 2015-09-30 18:06:38 -07:00
Maxime Beauchemin
f36ea7aa50 Aligingin stackedAreaChart with area 2015-09-30 18:00:06 -07:00
Maxime Beauchemin
04f914fb5c Viz type clarifications 2015-09-30 17:54:50 -07:00
Maxime Beauchemin
601e30aa37 Changing 'since' default to 7 days 2015-09-30 17:43:25 -07:00
Maxime Beauchemin
fa08d353e8 Bug fixes 2015-09-30 17:37:07 -07:00
Adrian Kuhn
96753cfd09 Merge pull request #36 from mistercrunch/px_javascript_css_files
Extract widget javascript to separate files.
2015-09-30 15:07:28 -07:00
Adrian Kuhn
ad2cc10788 Fix unit tests for viz without json endpoint. 2015-09-30 14:52:53 -07:00
Adrian Kuhn
83d7bf3a1a Extract nvd3 javascript to separate file. 2015-09-30 14:27:18 -07:00
Adrian Kuhn
669a0a4e2c Extract bignumber javascript to separate file. 2015-09-30 13:20:34 -07:00
Adrian Kuhn
b75c89b81f Extract word cloud javascript to separate file. 2015-09-30 13:20:34 -07:00
Maxime Beauchemin
7172be9705 Bugfix for nvd3 charts 2015-09-30 13:20:13 -07:00
Maxime Beauchemin
c9e4239025 Moving button to the right 2015-09-30 11:09:06 -07:00
Maxime Beauchemin
ca28d9c524 Penguins under construction 2015-09-30 09:38:43 -07:00
Maxime Beauchemin
2243623430 Temporary hack to see the query for NVD3 vizs 2015-09-30 08:59:19 -07:00
Maxime Beauchemin
5cf5019ff1 Done fixing form continuity 2015-09-29 22:13:29 -07:00
Maxime Beauchemin
74e5a3e868 Mostly working 2015-09-29 21:41:49 -07:00
Maxime Beauchemin
f3f807d042 Forms 2015-09-28 22:38:57 -07:00
Maxime Beauchemin
c3f0597c07 Small improvments to tests 2015-09-28 21:31:04 -07:00
Maxime Beauchemin
4a9987affa Bugfix around using an Int in a groupby 2015-09-28 17:57:26 -07:00
Maxime
4d62adb618 Removing last highcharts file standing 2015-09-29 00:38:57 +00:00
Maxime Beauchemin
0033c927cc Adding missing dependency 2015-09-28 16:13:55 -07:00
Maxime Beauchemin
af98972624 Cleaning up previous hack 2015-09-28 15:28:59 -07:00
Maxime Beauchemin
391b31e350 Forgot the .coveralls.yml file 2015-09-28 14:03:38 -07:00
Maxime Beauchemin
ac2446cf09 Adding coverage badge 2015-09-28 14:02:37 -07:00
Maxime Beauchemin
930dad4eb0 Further CI integration 2015-09-28 13:58:47 -07:00
Maxime Beauchemin
db272f831b Adding markdown dep 2015-09-28 12:46:05 -07:00
Maxime Beauchemin
e473a828e7 Adding flask-testing to list of deps 2015-09-28 12:38:44 -07:00
Maxime Beauchemin
50adfdc662 Explicit flask dependency 2015-09-28 12:28:58 -07:00
Maxime Beauchemin
80228ac293 Adapting command 2015-09-28 12:06:53 -07:00
Maxime Beauchemin
a2a0545323 Integrating with travis-ci 2015-09-28 11:51:20 -07:00
Maxime Beauchemin
2e29c39f3c Merge pull request #34 from mistercrunch/nvd3_2
Ripping out Highcharts.
2015-09-28 10:33:56 -07:00
Maxime Beauchemin
a674fca26d Better form handling 2015-09-28 10:32:56 -07:00
Maxime Beauchemin
721113db0d About to rip out args 2015-09-28 09:24:53 -07:00
Maxime Beauchemin
625bd9dd82 Period ratio compare 2015-09-28 08:41:39 -07:00
Maxime Beauchemin
7143972341 Adding contribution to total 2015-09-28 00:51:39 -07:00
Maxime Beauchemin
4cd7c3d098 Introducing compare 2015-09-27 18:21:50 -07:00
Maxime Beauchemin
495c2dfeca Ripping highcharts out 2015-09-27 18:09:58 -07:00
Maxime Beauchemin
bf8ce1716a nvd3 Stacked Area chart 2015-09-27 08:52:26 -07:00
Maxime Beauchemin
e64367149e v0.4.0 2015-09-26 21:39:45 -07:00
Maxime Beauchemin
4c78b4f34e Merge pull request #33 from mistercrunch/nvd3
Adding nvd3 support
2015-09-26 21:15:39 -07:00
Maxime Beauchemin
89e92904e1 Adding nvd3 support 2015-09-26 21:14:44 -07:00
Maxime Beauchemin
703b6f612b Forgot to add some files for unit tests 2015-09-26 15:56:08 -07:00
Maxime Beauchemin
f1a64c0988 Allowing 2 widgets to overflow 2015-09-25 16:52:15 -07:00
Maxime Beauchemin
8d4c3ea381 Increasing test coverage to 68% 2015-09-25 16:42:56 -07:00
Maxime Beauchemin
aea304d814 Making sum metrics for numeric columns 2015-09-25 16:18:31 -07:00
Maxime Beauchemin
1d6d821008 Changing default to 100 items per page 2015-09-25 16:15:13 -07:00
Maxime Beauchemin
82bf5437cd More screenshots 2015-09-25 15:50:40 -07:00
Maxime Beauchemin
2d23feed1f Merge pull request #32 from mistercrunch/tests
Adding a foundation for unit tests
2015-09-25 15:44:36 -07:00
Maxime Beauchemin
c4b24cb9cc Adding a foundation for unit tests 2015-09-25 15:43:50 -07:00
Maxime Beauchemin
f95e84442f Trying POST instead of GET for testconn 2015-09-25 08:18:27 -07:00
Maxime Beauchemin
83e37f66fa Adding fields to list_columns of DatabaseView 2015-09-23 22:55:21 -07:00
Maxime Beauchemin
74ae4ee704 ADding help text around sqlalchemy URI 2015-09-23 21:28:01 -07:00
Maxime Beauchemin
9fab75bb1f Merge pull request #31 from mistercrunch/test_connection
Adding a button to test connections
2015-09-23 21:23:06 -07:00
Maxime Beauchemin
e854798730 Adding a button to test connections 2015-09-23 21:21:48 -07:00
Maxime Beauchemin
caafb1ef4c Changing title 2015-09-23 20:17:20 -07:00
Maxime Beauchemin
63d55c9f93 Polish 2015-09-23 16:43:55 -07:00
Maxime Beauchemin
71274ae609 Beautifying the index page 2015-09-23 14:34:14 -07:00
Maxime Beauchemin
e126ab4eb8 Improvements 2015-09-23 10:40:50 -07:00
Maxime Beauchemin
f395c83bae Improving the main page 2015-09-23 08:33:46 -07:00
Maxime Beauchemin
591c90d0c0 Merge pull request #30 from mistercrunch/word_cloud
Word cloud widget!
2015-09-22 23:44:25 -07:00
Maxime Beauchemin
2cbc2e36e3 Adding a word cloud widget 2015-09-22 23:40:58 -07:00
Maxime Beauchemin
a28d9d4bb1 Bugfix 2015-09-22 23:11:59 -07:00
Maxime Beauchemin
11c291b9a1 Merge pull request #29 from mistercrunch/markup_widgets
Adding support for markup (html/markdown) widgets
2015-09-22 22:58:57 -07:00
Maxime Beauchemin
24882884b8 Forgot to git add viz_markup.html 2015-09-22 18:00:38 -07:00
Maxime Beauchemin
f5e355a26f Forgot to git add viz_markup.html 2015-09-22 17:58:44 -07:00
Maxime Beauchemin
e4a5f34f71 Uncommenting try block 2015-09-22 16:53:06 -07:00
Maxime Beauchemin
449441fed5 Adding support for markup (html/markdown) widgets 2015-09-22 16:34:11 -07:00
Maxime Beauchemin
3d8fbaa966 Improvments to the dashboard view 2015-09-22 11:55:40 -07:00
Maxime Beauchemin
ff29f905c9 Loading examples with a CLI subcommand 2015-09-22 09:52:41 -07:00
Maxime Beauchemin
4d1d3ad0f6 Minor bugfix 2015-09-21 15:49:34 -07:00
Maxime Beauchemin
e1b3c7e63b Bugfixes+ better docs 2015-09-21 13:48:02 -07:00
Maxime Beauchemin
2cbe25c6b5 Changing an icon 2015-09-20 21:56:23 -07:00
Maxime Beauchemin
c09dca514d Adding a BigNumber widget 2015-09-20 13:50:07 -07:00
Maxime Beauchemin
72ec6ae570 Cosmetics 2015-09-19 13:52:47 -07:00
Maxime Beauchemin
256193ce9f Merge pull request #28 from noddi/bugfix/default_sqlite_path
Fix default Sqlite path.
2015-09-19 07:30:53 -07:00
Bartosz Ługowski
6c8f268587 Fix default Sqlite path in README. 2015-09-19 11:40:16 +02:00
Bartosz Ługowski
8ac35bd610 Fix default Sqlite path. 2015-09-19 11:21:49 +02:00
Maxime Beauchemin
e0d6d20993 Adding a command to load a sample dataset 2015-09-18 23:48:41 -07:00
Maxime Beauchemin
4edbbd350d More bug squashing 2015-09-18 17:27:46 -07:00
Maxime Beauchemin
6f1fa5152a Merge pull request #27 from mistercrunch/dash
More refactor and bugfixes
2015-09-18 15:30:53 -07:00
Maxime Beauchemin
e5e2988e2d More refactor and bugfixes 2015-09-18 15:29:49 -07:00
Maxime Beauchemin
116b1c01f5 Merge pull request #26 from mistercrunch/dash
Bugfix
2015-09-18 14:31:54 -07:00
Maxime Beauchemin
16550b9753 Bugfix 2015-09-18 14:30:54 -07:00
Maxime Beauchemin
c1f28a3e74 Merge pull request #25 from mistercrunch/dash
Adding basic dashboarding support!
2015-09-18 13:42:02 -07:00
Maxime Beauchemin
cd09b0ddef Bugfixes 2015-09-18 11:12:27 -07:00
Maxime Beauchemin
f6753afa75 Panoramix v1 dashboards is up 2015-09-17 18:06:47 -07:00
Maxime Beauchemin
521b000ab6 Checkpoint 2015-09-15 12:33:26 -07:00
Maxime Beauchemin
359a81eee3 Getting back into a working state 2015-09-15 09:17:59 -07:00
Maxime Beauchemin
0bc2e71ac6 Changing the way viz templates are defined using macros instead 2015-09-14 08:50:01 -07:00
Maxime Beauchemin
6daf92e3c1 About to start ajaxifying 2015-09-14 08:04:32 -07:00
Maxime Beauchemin
a5b896414d Dashboards 2015-09-13 19:07:54 -07:00
Maxime Beauchemin
95b080160f Pinning flask-login 2015-09-12 23:27:06 -07:00
Maxime Beauchemin
36351918c9 Dashboards 2015-09-12 23:25:43 -07:00
Maxime Beauchemin
e755854c29 Save and embed 2015-09-11 21:49:57 -07:00
Maxime Beauchemin
5f20a080f4 Setting up alembic 2015-09-11 15:32:42 -07:00
Maxime Beauchemin
ca3959783c New script 2015-09-09 22:21:38 -07:00
Maxime
9a63a312b6 Warn on row limit reached 2015-09-09 20:55:47 +00:00
Maxime
5825f4539d Allowing for form field description and tooltips 2015-09-09 20:10:42 +00:00
Maxime Beauchemin
53fe171466 Merge pull request #23 from mistercrunch/where
Custom WHERE clause for tables (not druid) + error handling refactor
2015-09-09 12:49:54 -07:00
Maxime
fab0670669 Custom WHERE clause for tables (not druid) + error handling refactor 2015-09-09 19:48:20 +00:00
Maxime
67c5f637d1 Linting 2015-09-09 17:52:11 +00:00
Maxime Beauchemin
9858304468 Merge pull request #22 from mistercrunch/forms
Form factory refactor
2015-09-09 10:00:17 -07:00
Maxime Beauchemin
7f9d96b024 Merge pull request #20 from wbchn/master
add tzinfo config, useful when start druid without utc timezone
2015-09-09 09:59:16 -07:00
Maxime Beauchemin
1283bc0788 Form factory refactor 2015-09-08 23:50:45 -07:00
Maxime
6dd81a3e95 Adding argparse for port and debug mode on bin/panoramix 2015-09-08 21:56:27 +00:00
Maxime
483935cc12 Marking distribution as is_timeseries=False 2015-09-08 21:01:40 +00:00
Maxime Beauchemin
bca1e15e44 Temp DB in /tmp 2015-09-07 08:39:42 -07:00
wangbin
11aaaf3e11 add tzinfo config, useful when start druid without utc timezone 2015-09-06 17:11:11 +08:00
191 changed files with 19012 additions and 18226 deletions

1
.coveralls.yml Normal file
View File

@@ -0,0 +1 @@
repo_token: EMkVRVEKYgUESKaNN9QyOhPnFnKNqyDcJ

18
.gitignore vendored
View File

@@ -1,9 +1,23 @@
*.pyc
babel
.DS_Store
.coverage
_build
_static
dashed/bin/dashedc
build
*.db
tmp
panoramix_config.py
dashed_config.py
local_config.py
env
dist
panoramix.egg-info/
dashed.egg-info/
app.db
*.bak
# Node.js, webpack artifacts
*.entry.js
*.js.map
node_modules
npm-debug.log

23
.landscape.yml Normal file
View File

@@ -0,0 +1,23 @@
doc-warnings: yes
test-warnings: no
strictness: medium
max-line-length: 90
uses:
- flask
autodetect: yes
pylint:
disable:
- cyclic-import
- invalid-name
- logging-format-interpolation
options:
docstring-min-length: 10
pep8:
full: true
ignore-paths:
- docs
- dashed/migrations/env.py
- dashed/ascii_art.py
ignore-patterns:
- ^example/doc_.*\.py$
- (^|/)docs(/|$)

21
.travis.yml Normal file
View File

@@ -0,0 +1,21 @@
language: python
python:
- "2.7"
- "3.4"
cache:
directories:
- $HOME/.wheelhouse/
install:
- pip wheel -w $HOME/.wheelhouse -f $HOME/.wheelhouse -r requirements.txt
- pip install --find-links=$HOME/.wheelhouse --no-index -rrequirements.txt
- python setup.py install
- cd dashed/assets
- "touch $HOME/.npm/foo.lock; rm -f $HOME/.npm/*.lock"
- npm install
- npm run prod
- cd $TRAVIS_BUILD_DIR
script: bash run_tests.sh
after_success:
- coveralls
- cd dashed/assets
- npm run lint

View File

@@ -9,7 +9,7 @@ You can contribute in many ways:
### Report Bugs
Report bugs through Gihub
Report bugs through Github
If you are reporting a bug, please include:
@@ -28,14 +28,10 @@ open to whoever wants to implement it.
Look through the GitHub issues for features. Anything tagged with
"feature" is open to whoever wants to implement it.
We've created the operators, hooks, macros and executors we needed, but we
made sure that this part of Airflow is extensible. New operators,
hooks and operators are very welcomed!
### Documentation
Airflow could always use better documentation,
whether as part of the official Airflow docs,
Dashed could always use better documentation,
whether as part of the official Dashed docs,
in docstrings, `docs/*.rst` or even on the web as blog posts or
articles.
@@ -51,40 +47,134 @@ If you are proposing a feature:
- Remember that this is a volunteer-driven project, and that
contributions are welcome :)
## Latests Documentation
## Latest Documentation
[API Documentation](http://pythonhosted.com/airflow)
[API Documentation](http://pythonhosted.com/dashed)
## Setting up a Python development environment
# fork the repo on github and then clone it
# alternatively you may want to clone the main repo but that won't work
# so well if you are planning on sending PRs
# git clone git@github.com:mistercrunch/dashed.git
# [optional] setup a virtual env and activate it
virtualenv env
source env/bin/activate
# install for development
python setup.py develop
# Create an admin user
fabmanager create-admin --app dashed
# Initialize the database
dashed db upgrade
# Create default roles and permissions
dashed init
# Load some data to play with
dashed load_examples
# start a dev web server
dashed runserver -d
## Setting up the node / npm javascript environment
`dashed/assets` contains all npm-managed, front end assets.
Flask-Appbuilder itself comes bundled with jQuery and bootstrap.
While these may be phased out over time, these packages are currently not
managed with npm.
### Using npm to generate bundled files
#### npm
First, npm must be available in your environment. If it is not you can run the following commands
(taken from [this source](https://gist.github.com/DanHerbert/9520689))
```
brew install node --without-npm
echo prefix=~/.npm-packages >> ~/.npmrc
curl -L https://www.npmjs.com/install.sh | sh
```
The final step is to add
`~/.node/bin` to your `PATH` so commands you install globally are usable.
Add something like this to your `.bashrc` file.
```
export PATH="$HOME/.node/bin:$PATH"
```
#### npm packages
To install third party libraries defined in `package.json`, run the
following within this directory which will install them in a
new `node_modules/` folder within `assets/`.
```
npm install
```
To parse and generate bundled files for dashed, run either of the
following commands. The `dev` flag will keep the npm script running and
re-run it upon any changes within the assets directory.
```
# Compiles the production / optimized js & css
npm run prod
# Start a web server that manages and updates your assets as you modify them
npm run dev
```
For every development session you will have to start a flask dev server
as well as an npm watcher
```
dashed runserver -d -p 8081
npm run dev
```
## Testing
Install development requirements:
pip install -r requirements.txt
Tests can then be run with:
./run_unit_tests.sh
Lint the project with:
# for python changes
flake8 changes tests
# for javascript
npm run lint
## API documentation
Generate the documentation with:
cd docs && ./build.sh
## CSS Themes
As part of the npm build process, CSS for Dashed is compiled from ```Less```, a dynamic stylesheet language.
It's possible to customize or add your own theme to Dashed, either by overriding CSS rules or preferably
by modifying the Less variables or files in ```assets/stylesheets/less/```.
The ```variables.less``` and ```bootswatch.less``` files that ship with Dashed are derived from
[Bootswatch](https://bootswatch.com) and thus extend Bootstrap. Modify variables in these files directly, or
swap them out entirely with the equivalent files from other Bootswatch (themes)[https://github.com/thomaspark/bootswatch.git]
## Pull Request Guidelines
Before you submit a pull request from your forked repo, check that it
Before you submit a pull request from your forked repo, check that it
meets these guidelines:
1. The pull request should include tests, either as doctests,
unit tests, or both.
2. If the pull request adds functionality, the docs should be updated
as part of the same PR. Doc string are often sufficient, make
as part of the same PR. Doc string are often sufficient, make
sure to follow the sphinx compatible standards.
3. The pull request should work for Python 2.6, 2.7, and ideally python 3.3.
`from __future__ import ` will be required in every `.py` file soon.

View File

@@ -1,2 +1,8 @@
recursive-include panoramix/templates *
recursive-include panoramix/static *
recursive-include dashed/templates *
recursive-include dashed/static *
recursive-exclude dashed/static/assets/node_modules *
recursive-include dashed/static/assets/node_modules/font-awesome *
recursive-exclude dashed/static/docs *
recursive-exclude tests *
recursive-include dashed/data *
recursive-include dashed/migrations *

199
README.md
View File

@@ -1,131 +1,128 @@
Panoramix
Dashed
=========
Panoramix is a data exploration platform designed to be visual, intuitive
[![PyPI version](https://badge.fury.io/py/dashed.svg)](https://badge.fury.io/py/dashed)
[![Coverage Status](https://coveralls.io/repos/airbnb/dashed/badge.svg?branch=master&service=github)](https://coveralls.io/github/airbnb/dashed?branch=master)
[![Code Health](https://landscape.io/github/airbnb/dashed/master/landscape.svg?style=flat)](https://landscape.io/github/airbnb/dashed/master)
[![Requirements Status](https://requires.io/github/airbnb/dashed/requirements.svg?branch=master)](https://requires.io/github/airbnb/dashed/requirements/?branch=master)
[![Join the chat at https://gitter.im/airbnb/dashed](https://badges.gitter.im/airbnb/dashed.svg)](https://gitter.im/airbnb/dashed?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Dashed is a data exploration platform designed to be visual, intuitive
and interactive.
Buzz Phrases
[this project used to be named **Panoramix**]
Video - Introduction to Dashed
---------------------------------
[![Dashed - ](http://img.youtube.com/vi/3Txm_nj_R7M/0.jpg)](http://www.youtube.com/watch?v=3Txm_nj_R7M)
Screenshots
------------
* Analytics at the speed of thought!
* Instantaneous learning curve
* Realtime analytics when querying [Druid.io](http://druid.io)
* Extentsible to infinity
![img](http://i.imgur.com/bi09J9X.png)
![img](http://i.imgur.com/aOaH0ty.png)
Dashed
---------
Dashed's main goal is to make it easy to slice, dice and visualize data.
It empowers its user to perform **analytics at the speed of thought**.
Dashed provides:
* A quick way to intuitively visualize datasets
* Create and share interactive dashboards
* A rich set of visualizations to analyze your data, as well as a flexible
way to extend the capabilities
* An extensible, high granularity security model allowing intricate rules
on who can access which features, and integration with major
authentication providers (database, OpenID, LDAP, OAuth & REMOTE_USER
through Flask AppBuiler)
* A simple semantic layer, allowing to control how data sources are
displayed in the UI,
by defining which fields should show up in which dropdown and which
aggregation and function (metrics) are made available to the user
* Deep integration with Druid allows for Dashed to stay blazing fast while
slicing and dicing large, realtime datasets
Database Support
----------------
Panoramix was originally designed on to of Druid.io, but quickly broadened
Dashed was originally designed on to of Druid.io, but quickly broadened
its scope to support other databases through the use of SqlAlchemy, a Python
ORM that is compatible with
[many external databases](http://docs.sqlalchemy.org/en/rel_1_0/core/engines.html).
ORM that is compatible with
[most common databases](http://docs.sqlalchemy.org/en/rel_1_0/core/engines.html).
What's Druid?
What is Druid?
-------------
From their website at http://druid.io
*Druid is an open-source analytics data store designed for
business intelligence (OLAP) queries on event data. Druid provides low
latency (real-time) data ingestion, flexible data exploration,
and fast data aggregation. Existing Druid deployments have scaled to
trillions of events and petabytes of data. Druid is best used to
*Druid is an open-source analytics data store designed for
business intelligence (OLAP) queries on event data. Druid provides low
latency (real-time) data ingestion, flexible data exploration,
and fast data aggregation. Existing Druid deployments have scaled to
trillions of events and petabytes of data. Druid is best used to
power analytic dashboards and applications.*
Panoramix
---------
Panoramix's main goal is to make it easy to slice, dice and visualize data
out of Druid. It empowers its user to perform **analytics
at the speed of thought**.
Panoramix started as a hackathon project at Airbnb in while running a POC
(proof of concept) on using Druid.
Panoramix provides:
* A way to query intuitively a Druid dataset, allowing for grouping, filtering
limiting and defining a time granularity
* Many charts and visualization to analyze your data, as well as a flexible
way to extend the visualization capabilities
* An extensible, high granularity security model allowing intricate rules
on who can access which features, and integration with major
authentication providers (through Flask AppBuiler)
* A simple semantic layer, allowing to control how Druid datasources are
displayed in the UI,
by defining which fields should show up in which dropdown and which
aggregation and function (metrics) are made available to the user
Installation
------------
Follow these few simple steps to install Panoramix
Dashed is currently only tested using Python 2.7.*. Python 3 support is
on the roadmap, Python 2.6 won't be supported.
Follow these few simple steps to install Dashed.
```
# Install panoramix
pip install panoramix
# Install dashed
pip install dashed
# Create an admin user
fabmanager create-admin --app panoramix
fabmanager create-admin --app dashed
# Clone the github repo
git clone https://github.com/mistercrunch/panoramix.git
# Initialize the database
dashed db upgrade
# Start the web server
panoramix
# Create default roles and permissions
dashed init
# Load some data to play with
dashed load_examples
# Start the development web server
dashed runserver -d
```
After installation, you should be able to point your browser to the right
hostname:port [http://localhost:8088](http://localhost:8088), login using
the credential you entered while creating the admin account, and navigate to
`Menu -> Admin -> Refresh Metadata`. This action should bring in all of
your datasources for Panoramix to be aware of, and they should show up in
your datasources for Dashed to be aware of, and they should show up in
`Menu -> Datasources`, from where you can start playing with your data!
Configuration
=======
[most common databases](http://docs.sqlalchemy.org/en/rel_1_0/core/engines.html).
Installation & Configuration
----------------------------
(See in the documentation)
[http://mistercrunch.github.io/panoramix-docs/installation.html]
What is Druid?
-------------
From their website at http://druid.io
To configure your application, you need to create a file (module)
`panoramix_config.py` and make sure it is in your PYTHONPATH. Here are some
of the parameters you can copy / paste in that configuration module:
*Druid is an open-source analytics data store designed for
business intelligence (OLAP) queries on event data. Druid provides low
latency (real-time) data ingestion, flexible data exploration,
and fast data aggregation. Existing Druid deployments have scaled to
trillions of events and petabytes of data. Druid is best used to
power analytic dashboards and applications.*
```
#---------------------------------------------------------
# Panoramix specifix config
#---------------------------------------------------------
ROW_LIMIT = 5000
WEBSERVER_THREADS = 8
PANORAMIX_WEBSERVER_PORT = 8088
#---------------------------------------------------------
#---------------------------------------------------------
# Flask App Builder configuration
#---------------------------------------------------------
# Your App secret key
SECRET_KEY = '\2\1thisismyscretkey\1\2\e\y\y\h'
# The SQLAlchemy connection string.
SQLALCHEMY_DATABASE_URI = 'sqlite:///tmp/panoramix.db'
# Flask-WTF flag for CSRF
CSRF_ENABLED = True
# Whether to run the web server in debug mode or not
DEBUG = True
```
This file also allows you to define configuration parameters used by
Flask App Builder, the web framework used by Panoramix. Please consult
the [Flask App Builder Documentation](http://flask-appbuilder.readthedocs.org/en/latest/config.html) for more information on how to configure Panoramix.
* From the UI, enter the information about your clusters in the
``Admin->Clusters`` menu by hitting the + sign.
* Once the Druid cluster connection information is entered, hit the
``Admin->Refresh Metadata`` menu item to populate
* Navigate to your datasources
More screenshots
----------------
@@ -133,3 +130,27 @@ More screenshots
![img](http://i.imgur.com/Rt6gNQ9.png)
![img](http://i.imgur.com/t7VOtqQ.png)
![img](http://i.imgur.com/PaiFQnH.png)
![img](http://i.imgur.com/CdcGHuC.png)
Related Links
-------------
* [Dashed Google Group] (https://groups.google.com/forum/#!forum/airbnb_dashed)
* [Gitter (live chat) Channel](https://gitter.im/airbnb/dashed)
Tip of the Hat
--------------
Dashed would not be possible without these great frameworks / libs
* Flask App Builder - Allowing us to focus on building the app quickly while
getting the foundation for free
* The Flask ecosystem - Simply amazing. So much Plug, easy play.
* NVD3 - One of the best charting library out there
* Much more, check out the requirements.txt file!
Contributing
------------
Interested in contributing? Casual hacking? Check out [Contributing.MD](https://github.com/airbnb/dashed/blob/master/CONTRIBUTING.md)

62
TODO.md
View File

@@ -1,7 +1,57 @@
# TODO
* in/notin filters autocomplete
* DRUID: Allow for post aggregations (ratios!)
* compare time ranges
* csv export out of table view
* Save / bookmark / url shortener
* SQL: Find a way to manage granularity
List of TODO items for Dashed
## Important
* **Getting proper JS testing:** unit tests on the Python side are pretty
solid, but now we need a test suite for the JS part of the site,
testing all the ajax-type calls
* **Viz Plugins:** Allow people to define and share visualization plugins.
ideally one would only need to drop in a set of files in a folder and
Dashed would discover and expose the plugins
## Features
* **Stars:** set dashboards, slices and datasets as favorites
* **Homepage:** a page that has links to your Slices and Dashes, favorited
content, feed of recent actions (people viewing your objects)
* **Dashboard URL filters:** `{dash_url}#fltin__fieldname__value1,value2`
* **Default slice:** choose a default slice for the dataset instead of
default endpoint
* **refresh freq**: specifying the refresh frequency of a dashboard and
specific slices within it, some randomization would be nice
* **Widget sets / chart grids:** a way to have all charts support making
a series of charts and putting them in a grid. The same way that you
can groupby for series, you could chart by. The form field set would be
common and use a single field to "grid by", a limit number of chart as
an N * N grid size.
* **Advanced dashboard configuration:** currently you can define which
slices in a dashboard are immune to filtering.
* **Annotations layers:** allow for people to maintain data annotations,
attached to a layer and time range. These layers can be added on top of
some visualizations as annotations. An example of a layer might be
"holidays" or "site outages", ...
* **Slack integration** - TBD
* **Sexy Viz Selector:** the visualization selector should be a nice large
modal with nice thumbnails for each one of the viz
* **Comments:** allow for people to comment on slices and dashes
## Easy-ish fix
* Kill switch for Druid in docs
* CREATE VIEW button from SQL editor
* Test button for when editing SQL expression
* Slider form element
* datasource in explore mode could be a dropdown
* [druid] Allow for post aggregations (ratios!)
* in/notin filters autocomplete (druid)
## New viz
* Maps that use geocodes
* Time animated scatter plots
* Horizon charts
* Calendar heatmap
* Chord diagram
* ...
## Community
* Create a proper user documentation (started using Sphinx and boostrap...)
* Usage vid

View File

@@ -29,7 +29,7 @@ script_location = migrations
# are written from script.py.mako
# output_encoding = utf-8
sqlalchemy.url = scheme://localhost/panoramix
sqlalchemy.url = scheme://localhost/dashed
# Logging configuration
[loggers]

View File

@@ -1,3 +0,0 @@
[python: **.py]
[jinja2: **/templates/**.html]
encoding = utf-8

View File

@@ -1 +0,0 @@

42
dashed/__init__.py Normal file
View File

@@ -0,0 +1,42 @@
"""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
from flask.ext.cache import Cache
APP_DIR = os.path.dirname(__file__)
CONFIG_MODULE = os.environ.get('DASHED_CONFIG', 'dashed.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)
cache = Cache(app, config=app.config.get('CACHE_CONFIG'))
migrate = Migrate(app, db, directory=APP_DIR + "/migrations")
class MyIndexView(IndexView):
@expose('/')
def index(self):
return redirect('/dashed/featured')
appbuilder = AppBuilder(
app, db.session,
base_template='dashed/base.html',
indexview=MyIndexView,
security_manager_class=app.config.get("CUSTOM_SECURITY_MANAGER"))
sm = appbuilder.sm
get_session = appbuilder.get_session
from dashed import config, views # noqa

68
dashed/ascii_art.py Normal file
View File

@@ -0,0 +1,68 @@
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="""
-------------------------------------------------------------------------------------------------------
=======================================================================================================
-------------------------------------------------------------------------------------------------------
___ ___ ___
( ) ( ) ( )
.--. | |_ .---. .--. | | ___ | |_ ___ .-. .---. .--. .--.
/ _ \ ( __) / .-, \ / \ | | ( ) ( __) ( ) \ / .-, \ / \ / \\
. .' `. ; | | (__) ; | | .-. ; | | ' / | | | ' .-. ; (__) ; | | .-. ; | .-. ;
| ' | | | | ___ .'` | | |(___) | |,' / | | ___ | / (___) .'` | | |(___) | | | |
_\_`.(___) | |( ) / .'| | | | | . '. | |( ) | | / .'| | | | | |/ |
( ). '. | | | | | / | | | | ___ | | `. \ | | | | | | | / | | | | ___ | ' _.'
| | `\ | | ' | | ; | ; | | '( ) | | \ \ | ' | | | | ; | ; | | '( ) | .'.-.
; '._,' ' ' `-' ; ' `-' | ' `-' | | | \ . ' `-' ; | | ' `-' | ' `-' | ' `-' /
'.___.' `.__. `.__.'_. `.__,' (___ ) (___) `.__. (___) `.__.'_. `.__,' `.__.'
-------------------------------------------------------------------------------------------------------
=======================================================================================================
-------------------------------------------------------------------------------------------------------
"""

3
dashed/assets/.babelrc Normal file
View File

@@ -0,0 +1,3 @@
{
"presets" : ["es2015", "react"]
}

View File

@@ -0,0 +1,3 @@
node_modules/*
vendor/*
javascripts/dist/*

234
dashed/assets/.eslintrc Normal file
View File

@@ -0,0 +1,234 @@
{
"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 }]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

View File

Before

Width:  |  Height:  |  Size: 552 KiB

After

Width:  |  Height:  |  Size: 552 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

View File

@@ -0,0 +1 @@
require('../stylesheets/less/index.less');

View File

@@ -0,0 +1,242 @@
var $ = window.$ = require('jquery');
var jQuery = window.jQuery = $;
var px = require('./modules/dashed.js');
var d3 = require('d3');
require('bootstrap');
var ace = require('brace');
require('brace/mode/css');
require('brace/theme/crimson_editor');
require('./dashed-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();
px.initFavStars();
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(true);
});
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_slice || [];
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) {
slice_id = parseInt(slice_id, 10);
for (var i=0; i < this.slices.length; i++) {
if (this.slices[i].data.slice_id === slice_id) {
return this.slices[i];
}
}
},
initDashboardView: function () {
dashboard = this;
var gridster = $(".gridster ul").gridster({
autogrow_cols: true,
widget_margins: [10, 10],
widget_base_dimensions: [95, 95],
draggable: {
handle: '.drag'
},
resize: {
enabled: true,
stop: function (e, ui, element) {
dashboard.getSlice($(element).attr('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');
// Displaying widget controls on hover
$('.chart-header').hover(
function () {
$(this).find('.chart-controls').fadeIn(300);
},
function () {
$(this).find('.chart-controls').fadeOut(300);
}
);
$("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: '/dashed/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());
});
$('#refresh_dash').click(function () {
dashboard.slices.forEach(function (slice) {
slice.render(true);
});
});
$("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'));
$('[data-toggle="tooltip"]').tooltip({ container: 'body' });
});

View File

@@ -0,0 +1,5 @@
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');

View File

@@ -0,0 +1,338 @@
// 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/dashed.js');
require('jquery-ui');
$.widget.bridge('uitooltip', $.ui.tooltip); // Shutting down jq-ui tooltips
require('bootstrap');
require('./dashed-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 druidify(force) {
if (force === undefined) {
force = false;
}
$('.query-and-save button').attr('disabled', 'disabled');
$('.btn-group.results span,a').attr('disabled', 'disabled');
$('div.alert').remove();
$('#is_cached').hide();
history.pushState({}, document.title, slice.querystring());
prepForm();
slice.render(force);
}
function initExploreView() {
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("||"));
}
px.initFavStars();
$('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 = '&nbsp;&nbsp;&nbsp;';
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(function () {
druidify(true);
});
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
druidify(false);
// make checkbox inputs display as toggles
$(':checkbox')
.addClass('pull-right')
.attr("data-onstyle", "default")
.bootstrapToggle({
size: 'mini'
});
$('div.toggle').addClass('pull-right');
slice.bindResizeToWindowResize();
});

View File

@@ -0,0 +1,19 @@
var $ = window.$ = require('jquery');
var jQuery = window.jQuery = $;
require('./modules/dashed.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();
});

View File

@@ -0,0 +1,18 @@
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>Dashed</h1>
<p>Extensible visualization tool for exploring data from any database.</p>
</Jumbotron>
);
}
}
render(<App />, document.getElementById('app'));

View File

@@ -0,0 +1,382 @@
var $ = require('jquery');
var jQuery = $;
var d3 = require('d3');
require('../../stylesheets/dashed.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);
};
}
function initFavStars() {
var baseUrl = '/dashed/favstar/';
// Init star behavihor for favorite
function show() {
if ($(this).hasClass('selected')) {
$(this).html('<i class="fa fa-star"></i>');
} else {
$(this).html('<i class="fa fa-star-o"></i>');
}
}
$('.favstar')
.attr('title', 'Click to favorite/unfavorite')
.each(show)
.each(function () {
var url = baseUrl + $(this).attr("class_name");
var star = this;
url += '/' + $(this).attr("obj_id") + '/';
$.getJSON(url + 'count/', function (data) {
if (data.count > 0) {
$(star)
.addClass('selected')
.each(show);
}
});
})
.click(function () {
$(this).toggleClass('selected');
var url = baseUrl + $(this).attr("class_name");
url += '/' + $(this).attr("obj_id") + '/';
if ($(this).hasClass('selected')) {
url += 'select/';
} else {
url += 'unselect/';
}
$.get(url);
$(this).each(show);
})
.tooltip();
}
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;
},
getWidgetHeader: function () {
return this.container.parents("li.widget").find(".chart-header");
},
jsonEndpoint: function () {
var parser = document.createElement('a');
parser.href = data.json_endpoint;
var endpoint = parser.pathname + this.querystring();
endpoint += "&json=true";
endpoint += "&force=" + this.force;
return endpoint;
},
done: function (data) {
clearInterval(timer);
token.find("img.loading").hide();
container.show();
var cachedSelector = null;
if (dashboard === undefined) {
cachedSelector = $('#is_cached');
if (data !== undefined && data.is_cached) {
cachedSelector
.attr('title', 'Served from data cached at ' + data.cached_dttm + '. Click to force-refresh')
.show()
.tooltip('fixTitle');
} else {
cachedSelector.hide();
}
} else {
var refresh = this.getWidgetHeader().find('.refresh');
if (data !== undefined && data.is_cached) {
refresh
.addClass('danger')
.attr(
'title',
'Served from data cached at ' + data.cached_dttm + '. Click to force-refresh')
.tooltip('fixTitle');
} else {
refresh
.removeClass('danger')
.attr(
'title',
'Click to force-refresh')
.tooltip('fixTitle');
}
}
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,a').removeAttr('disabled');
$('.query-and-save button').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');
$('.btn-group.results span,a').removeAttr('disabled');
$('.query-and-save button').removeAttr('disabled');
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 (force) {
if (force === undefined) {
force = false;
}
this.force = force;
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,
initFavStars: initFavStars
};
})();
module.exports = px;

View File

@@ -0,0 +1,55 @@
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
};

View File

@@ -0,0 +1,97 @@
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(
'/dashed/table/' + database_id + '/' + $("#dbtable").val() + '/');
}
$("#dbtable").on("change", showTableMetadata);
showTableMetadata();
$("#create_view").click(function () {
alert("Not implemented");
});
$(".sqlcontent").show();
function selectStarOnClick() {
$.ajax('/dashed/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: '/dashed/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();
});

View File

@@ -0,0 +1,13 @@
var $ = window.$ = require('jquery');
var jQuery = window.jQuery = $;
var px = require('./modules/dashed.js');
require('bootstrap');
$(document).ready(function () {
var slice;
var data = $('.slice').data('slice');
slice = px.Slice(data);
slice.render();
slice.bindResizeToWindowResize();
});

View File

@@ -0,0 +1,77 @@
{
"name": "dashed",
"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 ."
},
"repository": {
"type": "git",
"url": "git+https://github.com/mistercrunch/dashed.git"
},
"keywords": [
"big",
"data",
"exploratory",
"analysis",
"react",
"d3",
"airbnb",
"nerds",
"database",
"flask"
],
"author": "Airbnb",
"bugs": {
"url": "https://github.com/mistercrunch/dashed/issues"
},
"homepage": "https://github.com/mistercrunch/dashed#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"
}
}

View File

@@ -0,0 +1,278 @@
body {
margin: 0px !important;
}
.modal-dialog {
z-index: 1100;
}
.label {
font-size: 100%;
}
input.form-control {
background-color: white;
}
.chart-header a.danger {
color: red;
}
.col-left-fixed {
width:350px;
position: absolute;
float: left;
}
.col-offset {
margin-left: 365px;
}
.favstar {
margin-right: 10px;
opacity: 0.5;
cursor: pointer;
}
.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: 5px;
}
.widget-is-cached {
display: none;
}
.header span.label {
margin-left: 5px;
margin-right: 5px;
}
#timer {
width: 80px;
text-align: right;
}
.notbtn {
cursor: default;
box-shadow: none;
border: 1px solid #ccc;
}
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;
}
#is_cached {
display: none;
}
li.widget .chart-controls {
background-color: #f1f1f1;
position: absolute;
right: 0;
left: 0;
padding: 0px 5px;
opacity: 0.75;
display: none;
}
li.widget .slice_container {
overflow: auto;
}

View File

@@ -0,0 +1,616 @@
// 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;
}
}
}

View File

@@ -0,0 +1,5 @@
// Index .less, any imports here will be included in the final css build
@import "~bootstrap/less/bootstrap.less";
@import "./variables.less";
@import "./bootswatch.less";

View File

@@ -0,0 +1,881 @@
// 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;

View File

@@ -0,0 +1,71 @@
.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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,59 @@
// 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;
};

62
dashed/assets/vendor/pygments.css vendored Normal file
View File

@@ -0,0 +1,62 @@
.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 */

146
dashed/assets/vendor/select2.sortable.js vendored Normal file
View File

@@ -0,0 +1,146 @@
/**
* 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));

View File

@@ -0,0 +1,26 @@
.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;
}

View File

@@ -0,0 +1,162 @@
// JS
var d3 = window.d3 || require('d3');
// CSS
require('./big_number.css');
var px = require('../javascripts/modules/dashed.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;

View File

@@ -0,0 +1,19 @@
.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;
}

View File

@@ -0,0 +1,175 @@
// 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;

View File

@@ -0,0 +1,8 @@
.select2-highlighted > .filter_box {
background-color: transparent;
border: 1px dashed black;
}
.dashboard .filter_box .slice_container > div {
padding-top: 0;
}

View File

@@ -0,0 +1,82 @@
// JS
var $ = window.$ = require('jquery');
var jQuery = window.jQuery = $;
var d3 = window.d3 || require('d3');
// CSS
require('./filter_box.css');
require('../javascripts/dashed-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(payload);
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;

View File

@@ -0,0 +1,79 @@
.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%;
}

View File

@@ -0,0 +1,209 @@
// JS
var $ = window.$ || require('jquery');
var px = window.px || require('../javascripts/modules/dashed.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;

View File

@@ -0,0 +1,25 @@
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;

View File

@@ -0,0 +1,23 @@
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;

View File

@@ -0,0 +1,8 @@
g.dashed path {
stroke-dasharray: 5, 5;
}
.nvtooltip tr.highlight td {
font-weight: bold;
font-size: 15px !important;
}

View File

@@ -0,0 +1,208 @@
// JS
var $ = window.$ || require('jquery');
var d3 = window.d3 || require('d3');
var px = window.px || require('../javascripts/modules/dashed.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;

View File

@@ -0,0 +1,92 @@
// 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;

View File

@@ -0,0 +1,13 @@
.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;
}

View File

@@ -0,0 +1,31 @@
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
};
};

View File

@@ -0,0 +1,20 @@
.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;
}

View File

@@ -0,0 +1,140 @@
// CSS
require('./sankey.css');
// JS
var px = window.px || require('../javascripts/modules/dashed.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;

View File

@@ -0,0 +1,39 @@
.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;
}

View File

@@ -0,0 +1,359 @@
var d3 = window.d3 || require('d3');
var px = require('../javascripts/modules/dashed.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;

View File

@@ -0,0 +1,18 @@
.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;
}

View File

@@ -0,0 +1,125 @@
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)
.attr('width', '100%');
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;

View File

@@ -0,0 +1,91 @@
var px = window.px || require('../javascripts/modules/dashed.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(json);
});
}
return {
render: refresh,
resize: refresh
};
}
module.exports = wordCloudChart;

View File

@@ -0,0 +1,7 @@
.world_map svg {
background-color: #feffff;
}
.world_map {
position: relative;
}

View File

@@ -0,0 +1,110 @@
// 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;

View File

@@ -0,0 +1,51 @@
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;

0
dashed/bin/__init__.py Normal file
View File

91
dashed/bin/dashed Executable file
View File

@@ -0,0 +1,91 @@
#!/usr/bin/env python
from datetime import datetime
import logging
from subprocess import Popen
from flask.ext.script import Manager
from dashed import app
from flask.ext.migrate import MigrateCommand
import dashed
from dashed import db
from dashed 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("DASHED_WEBSERVER_PORT"),
help="Specify the port on which to run the web server")
@manager.option(
'-w', '--workers', default=config.get("DASHED_WORKERS", 16),
help="Number of gunicorn web server workers to fire up")
@manager.option(
'-t', '--timeout', default=config.get("DASHED_WEBSERVER_TIMEOUT"),
help="Specify the timeout (seconds) for the gunicorn web server")
def runserver(debug, port, timeout, workers):
"""Starts a Dashed 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} "
"dashed:app").format(**locals())
print("Starting server with command: " + cmd)
Popen(cmd, shell=True).wait()
@manager.command
def init():
"""Inits the Dashed application"""
utils.init(dashed)
@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 dashed 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()

120
dashed/config.py Normal file
View File

@@ -0,0 +1,120 @@
"""The main config file for Dashed
All configuration in this file can be overridden by providing a local_config
in your PYTHONPATH as there is a ``from local_config import *``
at the end of this file.
"""
import os
from flask_appbuilder.security.manager import AUTH_DB
from dateutil import tz
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
# ---------------------------------------------------------
# Dashed specifix config
# ---------------------------------------------------------
ROW_LIMIT = 50000
WEBSERVER_THREADS = 8
DASHED_WEBSERVER_PORT = 8088
DASHED_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/dashed.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 = "Dashed"
# 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)
CACHE_DEFAULT_TIMEOUT = None
CACHE_CONFIG = {'CACHE_TYPE': 'null'}
try:
from dashed_config import * # noqa
except Exception:
pass

623
dashed/data/__init__.py Normal file
View File

@@ -0,0 +1,623 @@
"""Loads datasets, dashboards and slices in a new dashed instance"""
import gzip
import json
import os
import textwrap
import pandas as pd
from sqlalchemy import String, DateTime
from dashed 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', 'country_name'])),
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": 2,
"size_x": 3,
"col": 1,
"slice_id": "1",
"row": 1
},
{
"size_y": 3,
"size_x": 3,
"col": 1,
"slice_id": "2",
"row": 3
},
{
"size_y": 8,
"size_x": 3,
"col": 10,
"slice_id": "3",
"row": 1
},
{
"size_y": 3,
"size_x": 6,
"col": 1,
"slice_id": "4",
"row": 6
},
{
"size_y": 5,
"size_x": 6,
"col": 4,
"slice_id": "5",
"row": 1
},
{
"size_y": 4,
"size_x": 6,
"col": 7,
"slice_id": "6",
"row": 9
},
{
"size_y": 3,
"size_x": 3,
"col": 7,
"slice_id": "7",
"row": 6
},
{
"size_y": 4,
"size_x": 6,
"col": 1,
"slice_id": "8",
"row": 9
}
]
"""
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 # noqa
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():
"""Loading birth name dataset from a zip file in the repo"""
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()

Binary file not shown.

Binary file not shown.

Binary file not shown.

355
dashed/data/countries.md Normal file
View File

@@ -0,0 +1,355 @@
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)

2493
dashed/data/countries.py Normal file

File diff suppressed because it is too large Load Diff

605
dashed/forms.py Normal file
View File

@@ -0,0 +1,605 @@
"""Contains the logic to create cohesive forms on the explore view"""
from wtforms import (
Form, SelectMultipleField, SelectField, TextField, TextAreaField,
BooleanField, IntegerField, HiddenField)
from wtforms import validators, widgets
from copy import copy
from dashed import app
from collections import OrderedDict
config = app.config
class BetterBooleanField(BooleanField):
"""Fixes the html checkbox to distinguish absent from unchecked
(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):
"""Dashedification 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 dashed.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 Dashed
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 Dashed 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):
"""The dynamic form object used for the explore view"""
fieldsets = copy(viz.fieldsets)
css_classes = field_css_classes
standalone = HiddenField()
async = HiddenField()
force = 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 Dashed 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
dashed/migrations/README Executable file
View File

@@ -0,0 +1 @@
Generic single-database configuration.

View File

View File

@@ -0,0 +1,45 @@
# 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

88
dashed/migrations/env.py Executable file
View File

@@ -0,0 +1,88 @@
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()

View File

@@ -0,0 +1,22 @@
"""${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"}

View File

@@ -0,0 +1,23 @@
"""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')

View File

@@ -0,0 +1,99 @@
"""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

View File

@@ -0,0 +1,26 @@
"""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')

View File

@@ -0,0 +1,21 @@
"""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')

View File

@@ -0,0 +1,26 @@
"""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')

View File

@@ -0,0 +1,28 @@
"""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')

View File

@@ -0,0 +1,23 @@
"""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')

View File

@@ -0,0 +1,30 @@
"""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')

View File

@@ -0,0 +1,23 @@
"""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')

Some files were not shown because too many files have changed in this diff Show More