Compare commits

..

181 Commits
0.8.0 ... 0.8.6

Author SHA1 Message Date
Maxime Beauchemin
c5dead4791 v0.8.6 2016-04-07 14:26:28 -07:00
Maxime Beauchemin
d933a21216 Prettyfying an image in the docs 2016-04-07 14:26:28 -07:00
michellethomas
59169bfc96 Merge pull request #212 from airbnb/big_number_total
Adding a big number total viz type that is not a timeseries metric
2016-04-07 14:22:12 -07:00
Maxime Beauchemin
bcca840f01 Adding from __future__ imports (#288)
* Adding from __future__ imports

* fixes

* Fixing doctests

* Removing unused ColorFactory (in js now)

* linting
2016-04-07 08:39:08 -07:00
mherr
90a3b9f2c4 Update INTHEWILD.md
added #GfKDataLab as a Caravel-User
2016-04-06 23:56:19 -07:00
Maxime Beauchemin
a37e431150 Adding _images to .gitignore 2016-04-06 23:32:35 -07:00
Maxime Beauchemin
bf38c714a5 Adding missing images 2016-04-06 21:10:41 -07:00
Julian Nadeau
6b0b03e009 Fix localhost link in installation docs 2016-04-06 20:13:04 -07:00
Maxime Beauchemin
8556b098f9 Enable Time Grain Option for Redshift 2016-04-06 20:12:24 -07:00
Julian Nadeau
d122b37f5d Add python-pip to the install docs 2016-04-06 18:11:24 -07:00
Maxime Beauchemin
1756c27930 Adding upgrade instructions to docs 2016-04-06 18:02:40 -07:00
Aaron Critchley
7867267608 SqlAlchemy -> SQLAlchemy in README.md
* SqlAlchemy -> SQLAlchemy in README.md

Source: http://www.sqlalchemy.org/

* SqlAlchemy -> SQLAlchemy in tutorial.rst
2016-04-06 17:54:41 -07:00
Maxime Beauchemin
92d588694b Improving the Installation docs 2016-04-06 08:46:32 -07:00
Maxime Beauchemin
d10eaeccc9 Adding a Gallery to the docs 2016-04-06 08:46:21 -07:00
greens231
c2bb49fec5 Fix 4e6a06bad7a8_init.py migration script to work with Postgres
* Update 4e6a06bad7a8_init.py

* removed comments
2016-04-06 08:24:52 -07:00
Maxime Beauchemin
062f2b81cf Datasource dropdown in Explore view 2016-04-06 08:23:27 -07:00
andrewhn
65e72d0d07 Csv download improvements
* name + extension for generated csv and json files

* write csv index where data is meaningful
2016-04-06 08:22:49 -07:00
Maxime Beauchemin
345727635e Adding y_axis_format to DistributionBarViz 2016-04-06 08:20:24 -07:00
Maxime Beauchemin
c2baa53b06 bugfix datatables move to new package 2016-04-05 21:40:24 -07:00
Maxime Beauchemin
31758827ae + button on Slice list view redirects to Table view with alert 2016-04-05 13:33:02 -07:00
Maxime Beauchemin
81de51bf6f Minor tweaks 2016-04-04 21:39:33 -07:00
andrewhn
0d1f27dbc1 add postgres grains 2016-04-04 20:56:10 -07:00
Maxime Beauchemin
c7282882d5 Fixing the pagination display on welcome 2016-04-04 20:47:12 -07:00
Maxime Beauchemin
f9d04e8a72 Fixed refresh_datasource redirect 2016-04-04 20:43:06 -07:00
Maxime Beauchemin
bf2e804331 Removed trailing coma in Database.extra default 2016-04-04 17:42:31 -07:00
Maxime Beauchemin
c349b0a1c1 Fixed link url in docs 2016-04-04 17:14:08 -07:00
Maxime Beauchemin
4d640b5a3d [fix] panel overflowing on welcome page 2016-04-04 16:56:10 -07:00
Maxime Beauchemin
380c3f0c75 Using boostrap panels for form fieldsets in explore view 2016-04-04 16:14:55 -07:00
andrewhn
e3e8202c98 clear element before redrawing sankey 2016-04-04 16:13:54 -07:00
Maxime Beauchemin
889844407f Adding extra options to deeper configure sqlalchemy 2016-04-04 16:13:08 -07:00
Maxime Beauchemin
f1830c36cf A better welcome page 2016-04-04 16:12:28 -07:00
Chris Williams
92f73b67ca Move window.alert() calls to bootstrap modals. Also log errors to console.
* Move window.alert() calls to bootstrap modals. Also log errors that occur to console.

* move misc modal to basic template so it's available on all pages.
2016-04-04 16:11:23 -07:00
skje
9c1af66ba4 Fix ignored SQL where clauses 2016-04-04 16:03:21 -07:00
Maxime Beauchemin
2b31ab498b [hotfix] fixing json endpoint 2016-04-04 15:20:10 -07:00
Maxime Beauchemin
034fd077e1 Doc formating fix 2016-04-04 10:36:51 -07:00
Maxime Beauchemin
ca4443247e Prettyfying the Caravel on README 2016-04-03 20:08:36 -07:00
Maxime Beauchemin
6f96252e45 A logo on the navbar 2016-04-03 20:03:27 -07:00
Maxime Beauchemin
0b93fd373d [hotfix] hashing unicode in py3 2016-04-03 14:04:53 -07:00
Jiayu Liu
c3789d53b4 Removing duplicate get_table in fetch_metadata 2016-04-03 07:40:58 -07:00
Maxime Beauchemin
aec3c0b358 Fixing bug when datasource has been deleted 2016-04-03 07:37:18 -07:00
Karel Vervaeke
ef45c20558 Hash cache keys to avoid too keys being too long. Resolves #240 2016-04-03 07:33:43 -07:00
Maxime Beauchemin
10ab678fc6 Finishing up the tutorial 2016-04-02 23:34:47 -07:00
Maxime Beauchemin
87fb40ae26 Bumping version of npm up 2016-04-02 23:24:36 -07:00
Maxime Beauchemin
d245fb91b5 travis tweaks 2016-04-02 23:20:05 -07:00
Maxime Beauchemin
c60032acce Pillow with a capital P 2016-04-02 23:17:27 -07:00
Maxime Beauchemin
d2f51900f1 Adding a tutorial 2016-04-02 23:11:52 -07:00
Maxime Beauchemin
93405dc23a Clarify SQLALCHEMY_DATABASE_URI in the docs 2016-04-01 16:41:02 -07:00
Maxime Beauchemin
dafdb51f35 Adding Caravel Docker link to README 2016-04-01 16:16:21 -07:00
Maxime Beauchemin
2d0fdf7e59 Adding a CHANGELOG.md 2016-04-01 13:55:04 -07:00
Maxime Beauchemin
718de6cd50 v0.8.5 2016-04-01 13:30:12 -07:00
Maxime Beauchemin
38062f160a Adding an INTHEWILD file 2016-04-01 13:23:51 -07:00
Maxime Beauchemin
9a5a27a101 Merge pull request #234 from airbnb/pinpandas
Pin pandas, remove numpy
2016-04-01 11:52:41 -07:00
Maxime Beauchemin
481d821721 Tweaking docs 2016-04-01 08:37:19 -07:00
Maxime Beauchemin
7b111b790e Adding an OS dependencies section to the install docs 2016-04-01 08:33:28 -07:00
Maxime Beauchemin
ab92e7a94d Pin pandas, remove numpy 2016-04-01 08:04:44 -07:00
Maxime Beauchemin
22d2b7fe0f Merge pull request #229 from jmcomets/patch-1
Remove "requirements.txt" mention from README
2016-04-01 00:23:51 -07:00
Jean-Marie Comets
23319eed10 Remove "requirements.txt" mention from README
I noticed that there isn't the file `requirements.txt` was removed when moving to PyPi. Plus there's no such thing as an open-source project without an "update README" PR. ;)
2016-04-01 09:05:12 +02:00
Maxime Beauchemin
12cc064059 Merge pull request #225 from airbnb/chris/remove-random-power-units
remove power units from sankey diagram
2016-03-31 23:23:42 -07:00
Maxime Beauchemin
ffa29b3909 Merge pull request #219 from airbnb/chris/sunburst-conditional-percent
Add 'Percent of previous' to sunburst vis. Appease npm warnings for data tables and d3.layout.cloud
2016-03-31 23:23:27 -07:00
Maxime Beauchemin
ee4e3c1b98 Merge pull request #224 from cyrusstoller/typo_fix
Fixing minor typos in the readme
2016-03-31 23:21:12 -07:00
Maxime Beauchemin
ebc16bb3fa Merge pull request #214 from airbnb/kim/fix_druid_datasource_bug
Fix an installation bug.
2016-03-31 23:19:44 -07:00
Chris Williams
0e2c0ce858 remove power units from sankey diagram 2016-03-31 16:59:39 -07:00
Cyrus Stoller
807f4dd0e5 Fixing minor typos in the readme 2016-03-31 15:38:34 -04:00
Chris Williams
b87d8a0fbf change 'of previous' to 'of parent' 2016-03-31 12:26:49 -07:00
Chris Williams
0b3e2e00cc Add 'Percent of previous' to sunburst vis. Appease npm warnings for data tables and d3-sankey. 2016-03-31 12:26:49 -07:00
Maxime Beauchemin
167fb64d0c Merge pull request #218 from airbnb/redirects
Redirecting URL from previous names to caravel
2016-03-31 12:24:16 -07:00
Maxime Beauchemin
eeba80c487 Merge branch 'master' into redirects 2016-03-31 12:07:35 -07:00
Maxime Beauchemin
a3f92f687d Merge pull request #223 from thebucknerlife/master
Fixed typo in README
2016-03-31 12:05:32 -07:00
Maxime Beauchemin
a4b61e97db Merge pull request #222 from brchristian/patch-1
remove duplicate Druid.io section in README.md
2016-03-31 12:04:59 -07:00
Greg Buckner
9595dcff4d Fixed typo in README 2016-03-31 12:02:26 -07:00
brchristian
58ab4e2415 remove duplicate Druid.io section in README.md 2016-03-31 11:47:33 -07:00
Maxime Beauchemin
2b71b72065 Redirecting URL from previous names to caravel 2016-03-31 09:31:07 -07:00
Maxime Beauchemin
bd9051a168 Fixing README link 2016-03-31 08:50:47 -07:00
Maxime Beauchemin
6f63b3033f Dedupping info in README by pointing to docs 2016-03-31 08:48:34 -07:00
Kim Pham
f659caa06b Fix an installation bug. 2016-03-30 19:08:38 -07:00
Maxime Beauchemin
5b7fe2b643 Merge pull request #213 from airbnb/kim/fix_druid_datasource_bug
Fix a bug when loading DruidDatasource.
2016-03-30 17:02:16 -07:00
Kim Pham
e5553ab45e Fix lint warning. 2016-03-30 16:38:02 -07:00
Kim Pham
4a77b70046 A better fix. 2016-03-30 16:28:08 -07:00
Kim Pham
f67c6b5f46 Fix a bug when loading DruidDatasource. 2016-03-30 16:11:14 -07:00
Maxime Beauchemin
fd407424ad A few replacements related to the rename
mostly just s/mistercrunch/airbnb/g
2016-03-30 14:29:07 -07:00
Maxime Beauchemin
e1b871982e Adding docs link badge 2016-03-29 21:28:43 -07:00
Maxime Beauchemin
b164244cd8 Renaming forgotten doc file 2016-03-29 21:24:01 -07:00
Maxime Beauchemin
00226cce7f Changing image size, second try 2016-03-29 21:18:17 -07:00
Maxime Beauchemin
3a3d5cd7fb Changing image size 2016-03-29 21:16:53 -07:00
Maxime Beauchemin
c6b77206ea Adding build status badge and image to README 2016-03-29 21:14:44 -07:00
Maxime Beauchemin
d885dd34f9 Getting coveralls to work 2016-03-29 12:24:00 -07:00
Maxime Beauchemin
54fe2348fc Dummy commit to trigger travis 2016-03-29 12:00:37 -07:00
Maxime Beauchemin
6e1413d2dd Adding dev-reqs.txt to build 2016-03-29 11:59:25 -07:00
Maxime Beauchemin
0296b839ec Changing repo token for coveralls 2016-03-29 11:28:06 -07:00
Maxime Beauchemin
1c44f34490 Merge pull request #204 from airbnb/fixes
Fixing the order and coverage report for the unit tests
2016-03-29 11:24:37 -07:00
Maxime Beauchemin
10f3991e1f Merge pull request #209 from airbnb/screenshots
Fresh screenshots
2016-03-29 11:12:10 -07:00
Maxime Beauchemin
cda1bd59f9 Fresh screenshots 2016-03-29 11:11:13 -07:00
Maxime Beauchemin
8e27099866 Fixing the tests for py3 2016-03-29 10:02:15 -07:00
Maxime Beauchemin
60bce9ed59 Fixing the order and coverage report for the unit tests 2016-03-28 23:50:11 -07:00
Maxime Beauchemin
1b4e750b2a Merge pull request #206 from airbnb/caravel
Caravel
2016-03-28 23:45:38 -07:00
Maxime Beauchemin
619d35878f [dashed->caravel] Replace in files 2016-03-28 22:01:21 -07:00
Maxime Beauchemin
d48796f00e Caravel - renaming files 2016-03-28 21:53:24 -07:00
Chris Williams
5561a4932a Merge pull request #205 from airbnb/chris/sunburst-fix
fix sunburst error. add `less` to package.json
2016-03-28 17:07:51 -07:00
Chris Williams
1aee5b7801 fix sunburst error. add less to package.json because less-loader complains if it's not there. 2016-03-28 15:20:08 -07:00
Maxime Beauchemin
74c72b3ce4 Merge pull request #203 from airbnb/fixes
Fixing mysql install
2016-03-27 23:03:19 -07:00
Maxime Beauchemin
09021aacad Adding LONG to list of numeric types, updating TODO 2016-03-27 22:51:45 -07:00
Maxime Beauchemin
26c725171b Fixing mysql install 2016-03-27 22:37:04 -07:00
Maxime Beauchemin
4a0ea5fff7 Merge pull request #202 from airbnb/better_tests
Using setup.py nosetests to run tests
2016-03-27 19:55:45 -07:00
Maxime Beauchemin
301dce2dd1 Using setup.py nosetests to run tests 2016-03-27 19:15:21 -07:00
Maxime Beauchemin
6dce6df6b2 JS Lint breaks the build 2016-03-27 15:36:32 -07:00
Maxime Beauchemin
2102e04fec Adding tests for ping and health enpoint 2016-03-27 14:23:49 -07:00
Maxime Beauchemin
2397645ed4 Fix intermitent bug with npm locks 2016-03-27 14:23:49 -07:00
Maxime Beauchemin
0a8a1eda94 Merge pull request #199 from airbnb/touchups
Fix a few minor bugs
2016-03-27 08:09:08 -07:00
Maxime Beauchemin
4d2492d8fd Merge pull request #200 from airbnb/sankey
Add a sankey example
2016-03-27 08:08:36 -07:00
Maxime Beauchemin
48210d5275 Add a sankey example 2016-03-27 00:27:48 -07:00
Maxime Beauchemin
5804991b19 Fix a few minor bugs 2016-03-27 00:18:26 -07:00
Maxime Beauchemin
5be7b03ba4 [hotfix] Fix tooltip overflow to be visible outside widget 2016-03-26 21:10:41 -07:00
Maxime Beauchemin
a898c27403 Minor CSS for navbar brand to not decorate the link 2016-03-26 18:10:40 -07:00
Maxime Beauchemin
49747150ae [hotfix] merging 2 migration scripts 2016-03-26 15:15:21 -07:00
Maxime Beauchemin
fdce2aac0d Merge pull request #192 from airbnb/kim/misc_hack
Fix Druid metadata refresh.
2016-03-25 22:16:54 -07:00
Maxime Beauchemin
1c19f7fc77 Merge pull request #198 from airbnb/welcome
A welcome page
2016-03-25 22:15:28 -07:00
Maxime Beauchemin
12e20ca440 Merge pull request #197 from NiharikaRay/master
Adding a DRUID_IS_ACTIVE flag and changing nav bar
2016-03-25 22:14:46 -07:00
Maxime Beauchemin
0ccc19ff9b Merge pull request #196 from airbnb/fix_audit
Fixing issues around fk nullable=False on audit fields
2016-03-25 22:13:45 -07:00
Maxime Beauchemin
e7bed92519 [hotfix] can't back out of history after entering explore 2016-03-25 22:02:50 -07:00
Maxime Beauchemin
8c0870e6ea Linting to perfection 2016-03-25 21:55:28 -07:00
Maxime Beauchemin
c9203554e7 a welcome page 2016-03-25 18:08:33 -07:00
Kim Pham
2378fdf9ce Disable polymorphism in DruidMetric as well 2016-03-25 16:45:35 -07:00
Kim Pham
23b6bca89e Fix style warning 2016-03-25 13:49:45 -07:00
Kim Pham
7a7eb33348 Disable Datasource sql polymorphism & Fix refresh Druid metadata. 2016-03-25 13:44:35 -07:00
Niharika Ray
fee6b3fafa Adding a DRUID_IS_ACTIVE flag and changing nav bar 2016-03-25 11:26:59 -07:00
Maxime Beauchemin
0f637bdd2e Fixing issues around fk nullable=False on audit fields 2016-03-25 08:13:15 -07:00
Maxime Beauchemin
10a1eddaa7 Improving the docs build script 2016-03-24 08:08:05 -07:00
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
204 changed files with 4034 additions and 2004 deletions

View File

@@ -1 +1 @@
repo_token: EMkVRVEKYgUESKaNN9QyOhPnFnKNqyDcJ
repo_token: eESbYiv4An6KEvjpmguDs4L7YkubXbqn1

10
.gitignore vendored
View File

@@ -1,17 +1,21 @@
*.pyc
babel
.DS_Store
.coverage
_build
_static
panoramix/bin/panoramixc
_images
caravel/bin/caravelc
env_py3
.eggs
build
*.db
tmp
panoramix_config.py
caravel_config.py
local_config.py
env
dist
panoramix.egg-info/
caravel.egg-info/
app.db
*.bak

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
- caravel/migrations/env.py
- caravel/ascii_art.py
ignore-patterns:
- ^example/doc_.*\.py$
- (^|/)docs(/|$)

View File

@@ -5,16 +5,18 @@ python:
cache:
directories:
- $HOME/.wheelhouse/
before_install:
- npm install -g npm@'>=2.7.1'
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 panoramix/assets
- pip wheel -w $HOME/.wheelhouse -f $HOME/.wheelhouse .
- pip install --find-links=$HOME/.wheelhouse --no-index .
- pip install --find-links=$HOME/.wheelhouse --no-index -r dev-reqs.txt
- cd caravel/assets
- npm --version
- npm install
- npm run lint
- npm run prod
- cd $TRAVIS_BUILD_DIR
script: bash run_tests.sh
after_success:
- coveralls
- cd panoramix/assets
- npm run lint

185
CHANGELOG.md Normal file
View File

@@ -0,0 +1,185 @@
## Change Log
### 0.8.5 (2016/04/01 20:30 +00:00)
- [#234](https://github.com/airbnb/caravel/pull/234) Pin pandas, remove numpy (@mistercrunch)
- [#229](https://github.com/airbnb/caravel/pull/229) Remove "requirements.txt" mention from README (@jmcomets)
- [#225](https://github.com/airbnb/caravel/pull/225) remove power units from sankey diagram (@williaster)
- [#219](https://github.com/airbnb/caravel/pull/219) Add 'Percent of previous' to sunburst vis. Appease npm warnings for data tables and d3.layout.cloud (@williaster)
- [#224](https://github.com/airbnb/caravel/pull/224) Fixing minor typos in the readme (@cyrusstoller)
- [#214](https://github.com/airbnb/caravel/pull/214) Fix an installation bug. (@kim-pham-airbnb)
- [#218](https://github.com/airbnb/caravel/pull/218) Redirecting URL from previous names to caravel (@mistercrunch)
- [#223](https://github.com/airbnb/caravel/pull/223) Fixed typo in README (@thebucknerlife)
- [#222](https://github.com/airbnb/caravel/pull/222) remove duplicate Druid.io section in README.md (@brchristian)
- [#213](https://github.com/airbnb/caravel/pull/213) Fix a bug when loading DruidDatasource. (@kim-pham-airbnb)
- [#204](https://github.com/airbnb/caravel/pull/204) Fixing the order and coverage report for the unit tests (@mistercrunch)
- [#209](https://github.com/airbnb/caravel/pull/209) Fresh screenshots (@mistercrunch)
- [#206](https://github.com/airbnb/caravel/pull/206) Caravel (@mistercrunch)
- [#205](https://github.com/airbnb/caravel/pull/205) fix sunburst error. add `less` to package.json (@williaster)
- [#203](https://github.com/airbnb/caravel/pull/203) Fixing mysql install (@mistercrunch)
- [#202](https://github.com/airbnb/caravel/pull/202) Using setup.py nosetests to run tests (@mistercrunch)
- [#199](https://github.com/airbnb/caravel/pull/199) Fix a few minor bugs (@mistercrunch)
- [#200](https://github.com/airbnb/caravel/pull/200) Add a sankey example (@mistercrunch)
- [#192](https://github.com/airbnb/caravel/pull/192) Fix Druid metadata refresh. (@kim-pham-airbnb)
- [#198](https://github.com/airbnb/caravel/pull/198) A welcome page (@mistercrunch)
- [#197](https://github.com/airbnb/caravel/pull/197) Adding a DRUID_IS_ACTIVE flag and changing nav bar (@NiharikaRay)
- [#196](https://github.com/airbnb/caravel/pull/196) Fixing issues around fk nullable=False on audit fields (@mistercrunch)
### 0.8.4 (2016/03/24 05:26 +00:00)
- [#193](https://github.com/airbnb/caravel/pull/193) Adding favorites for Slices and Dashboards (@mistercrunch)
### 0.8.2 (2016/03/23 20:43 +00:00)
- [#188](https://github.com/airbnb/caravel/pull/188) Introducing a caching layer! (@mistercrunch)
### 0.8.1 (2016/03/21 23:41 +00:00)
- [#191](https://github.com/airbnb/caravel/pull/191) Add week ending and week start to grain (@airbnb)
- [#190](https://github.com/airbnb/caravel/pull/190) Cranking up version numbers (@mistercrunch)
- [#184](https://github.com/airbnb/caravel/pull/184) sunburst improvements (@williaster)
- [#186](https://github.com/airbnb/caravel/pull/186) Adding docstrings ! (@mistercrunch)
- [#181](https://github.com/airbnb/caravel/pull/181) Dynamic time granularity on any datetime column (@mistercrunch)
- [#182](https://github.com/airbnb/caravel/pull/182) more css fixes (@williaster)
- [#178](https://github.com/airbnb/caravel/pull/178) Allowing all extra fields in AuditMixin to be nullable (@mistercrunch)
- [#175](https://github.com/airbnb/caravel/pull/175) refactor dashboard chart html, make several css improvements. (@williaster)
### 0.8.0 (2016/03/11 03:33 +00:00)
- [#172](https://github.com/airbnb/caravel/pull/172) Fixing the python and js packaging (@mistercrunch)
- [#171](https://github.com/airbnb/caravel/pull/171) Fixing multiple refresh bug in filter_box (@mistercrunch)
- [#169](https://github.com/airbnb/caravel/pull/169) Fixing the look of select2 components (@mistercrunch)
- [#168](https://github.com/airbnb/caravel/pull/168) Getting travis to build the npm related stuff (@mistercrunch)
- [#166](https://github.com/airbnb/caravel/pull/166) make css theme customization easier by using less for bootstrap themes (@williaster)
- [#163](https://github.com/airbnb/caravel/pull/163) Shipping with CSS templates out of the box (@mistercrunch)
- [#164](https://github.com/airbnb/caravel/pull/164) Improving the docs (@mistercrunch)
- [#165](https://github.com/airbnb/caravel/pull/165) Fixing window resize for explore and standalone (@mistercrunch)
- [#161](https://github.com/airbnb/caravel/pull/161) Add linting to package.json, do all of the linting. (@williaster)
- [#160](https://github.com/airbnb/caravel/pull/160) Fixing the dashed line when using time compare (@mistercrunch)
- [#159](https://github.com/airbnb/caravel/pull/159) Fixing the standalone mode (@mistercrunch)
- [#158](https://github.com/airbnb/caravel/pull/158) Refactor (@mistercrunch)
- [#154](https://github.com/airbnb/caravel/pull/154) Digging into leap year bug and improvming tests (@mistercrunch)
- [#157](https://github.com/airbnb/caravel/pull/157) add button to auto-copy short URLs in /explore page (@williaster)
- [#149](https://github.com/airbnb/caravel/pull/149) Allowing to make certain widgets immune to filter (@mistercrunch)
- [#151](https://github.com/airbnb/caravel/pull/151) Linting (@mistercrunch)
- [#153](https://github.com/airbnb/caravel/pull/153) Improve README (@tay)
- [#139](https://github.com/airbnb/caravel/pull/139) NPMification & Reactification (@williaster, @mistercrunch)
- [#147](https://github.com/airbnb/caravel/pull/147) Tackling Featured Datasets (@mistercrunch)
- [#148](https://github.com/airbnb/caravel/pull/148) Fix typo (@tay)
- [#145](https://github.com/airbnb/caravel/pull/145) Moving files around ... yay! (@mistercrunch)
- [#142](https://github.com/airbnb/caravel/pull/142) A few cosmetic fixes (nvd3 tooltips, buttons, tables) (@mistercrunch)
- [#141](https://github.com/airbnb/caravel/pull/141) A simple base template for npm (@mistercrunch)
- [#140](https://github.com/airbnb/caravel/pull/140) use the latest segment to extract metadata (@dayzzz)
- [#136](https://github.com/airbnb/caravel/pull/136) Improved the bar char to allow for dimensional breakdowns (@mistercrunch)
- [#134](https://github.com/airbnb/caravel/pull/134) Fixing the roles auto maintenance (@mistercrunch)
- [#132](https://github.com/airbnb/caravel/pull/132) [nvd3] fixing the legend toggle bug (@mistercrunch)
- [#131](https://github.com/airbnb/caravel/pull/131) More tests using doctests! (@mistercrunch)
- [#130](https://github.com/airbnb/caravel/pull/130) Logging more (@mistercrunch)
- [#129](https://github.com/airbnb/caravel/pull/129) Renaming Classes related to Druid (@mistercrunch)
- [#127](https://github.com/airbnb/caravel/pull/127) SQL editor, eventually will be tied to a flow to create views (@mistercrunch)
- [#128](https://github.com/airbnb/caravel/pull/128) Allowing definition of css templates (@mistercrunch)
- [#126](https://github.com/airbnb/caravel/pull/126) New viz: Heatmap! (@mistercrunch)
- [#125](https://github.com/airbnb/caravel/pull/125) Consistent colors rendered client side (@mistercrunch)
- [#124](https://github.com/airbnb/caravel/pull/124) A more cohesive color strategy (@mistercrunch)
### 0.7.0 (2016/01/23 15:16 +00:00)
- [#123](https://github.com/airbnb/caravel/pull/123) Adding a color factory (@mistercrunch)
- [#122](https://github.com/airbnb/caravel/pull/122) Adding Parallel coordinates viz (@mistercrunch)
- [#121](https://github.com/airbnb/caravel/pull/121) Iframe (@mistercrunch)
- [#120](https://github.com/airbnb/caravel/pull/120) Slice information can be displayed in dashboard (@mistercrunch)
- [#117](https://github.com/airbnb/caravel/pull/117) Doing some refactoring (@mistercrunch)
- [#115](https://github.com/airbnb/caravel/pull/115) Providing options for Y axis number formating (@mistercrunch)
- [#112](https://github.com/airbnb/caravel/pull/112) Adding an URL shortner (@mistercrunch)
- [#113](https://github.com/airbnb/caravel/pull/113) Prettier checkboxes (@mistercrunch)
- [#111](https://github.com/airbnb/caravel/pull/111) Loading another example amazing dash (@mistercrunch)
- [#109](https://github.com/airbnb/caravel/pull/109) Getting browser history to work on the explore view (@mistercrunch)
- [#108](https://github.com/airbnb/caravel/pull/108) pulling to the front on hover (@BradBaker)
- [#104](https://github.com/airbnb/caravel/pull/104) simplifying tooltip code (@BradBaker)
- [#105](https://github.com/airbnb/caravel/pull/105) adding stagger for all charts that have a date axis (@BradBaker)
- [#102](https://github.com/airbnb/caravel/pull/102) Fix for 2-axis charts where it shrinks them a little bit (@bradmbaker, @BradBaker)
- [#101](https://github.com/airbnb/caravel/pull/101) Add a Gitter chat badge to README.md (@gitter-badger)
- [#100](https://github.com/airbnb/caravel/pull/100) Update tooltips with new classes (@bradmbaker)
- [#99](https://github.com/airbnb/caravel/pull/99) Time resampling as in Pandas (@mistercrunch)
- [#98](https://github.com/airbnb/caravel/pull/98) Change Scaling to Operate on SVG instead of Div (@bradmbaker)
- [#96](https://github.com/airbnb/caravel/pull/96) Adding a filter box widget (@mistercrunch)
- [#95](https://github.com/airbnb/caravel/pull/95) Working on docs (@mistercrunch)
- [#94](https://github.com/airbnb/caravel/pull/94) Massive js refactor + Dashboard filters (@mistercrunch)
- [#93](https://github.com/airbnb/caravel/pull/93) Controller (@mistercrunch)
- [#92](https://github.com/airbnb/caravel/pull/92) Allowing not to group by on table view (@mistercrunch)
- [#91](https://github.com/airbnb/caravel/pull/91) Exports (@mistercrunch)
- [#90](https://github.com/airbnb/caravel/pull/90) A basic squeleton for the docs (@mistercrunch)
- [#89](https://github.com/airbnb/caravel/pull/89) Featured datasets (@michellethomas)
- [#87](https://github.com/airbnb/caravel/pull/87) fixing a few bugs with tool tip overflow (@BradBaker)
- [#88](https://github.com/airbnb/caravel/pull/88) World Map viz with bubbles (@mistercrunch)
- [#86](https://github.com/airbnb/caravel/pull/86) adjusting date formats (@BradBaker)
- [#85](https://github.com/airbnb/caravel/pull/85) Adding sankey diagrams (@mistercrunch)
- [#84](https://github.com/airbnb/caravel/pull/84) Adding directed force layout viz (@mistercrunch)
- [#83](https://github.com/airbnb/caravel/pull/83) Big JS refactor (@mistercrunch)
- [#82](https://github.com/airbnb/caravel/pull/82) letting tooltips in the dashboard overflow (@BradBaker)
- [#81](https://github.com/airbnb/caravel/pull/81) Slightly better layout for explore page (@mistercrunch)
- [#80](https://github.com/airbnb/caravel/pull/80) Checkboxes everywhere (@mistercrunch)
- [#79](https://github.com/airbnb/caravel/pull/79) Cleanup around multiple select fields (@mistercrunch)
### 0.6.0 (2015/12/11 01:17 +00:00)
- [#77](https://github.com/airbnb/caravel/pull/77) Better tooltips and more ways to integrate them easily (@mistercrunch)
- [#76](https://github.com/airbnb/caravel/pull/76) Introducing form overrides for label and tooltips (@mistercrunch)
- [#75](https://github.com/airbnb/caravel/pull/75) New viz: sunbursts (@mistercrunch)
- [#74](https://github.com/airbnb/caravel/pull/74) Introducing fieldsets (@mistercrunch)
- [#73](https://github.com/airbnb/caravel/pull/73) Airflowlike theme (@mistercrunch)
- [#72](https://github.com/airbnb/caravel/pull/72) Logging slice and dash views (@mistercrunch)
- [#70](https://github.com/airbnb/caravel/pull/70) Adding url slug support for dashboard model (@mistercrunch)
- [#71](https://github.com/airbnb/caravel/pull/71) Add option to show minmax on x axis (@mistercrunch)
- [#69](https://github.com/airbnb/caravel/pull/69) Allowing for [Save AS] and [Overwrite] (@mistercrunch)
- [#68](https://github.com/airbnb/caravel/pull/68) Adding cumsum to rolling functions (@mistercrunch)
- [#67](https://github.com/airbnb/caravel/pull/67) Fix debug mode calls get_json twice (@mistercrunch)
- [#66](https://github.com/airbnb/caravel/pull/66) Adding a PivotTableViz (@mistercrunch)
- [#65](https://github.com/airbnb/caravel/pull/65) Adding custom HAVING clause (@mistercrunch)
- [#64](https://github.com/airbnb/caravel/pull/64) Preserving the ordering in selectmultiple (@mistercrunch)
- [#63](https://github.com/airbnb/caravel/pull/63) Encrypting the passwords out of connection strings (@mistercrunch)
- [#61](https://github.com/airbnb/caravel/pull/61) BetterBooleanField to fix html omitting non-checked <input> (@patrickleotardif)
- [#60](https://github.com/airbnb/caravel/pull/60) Fix Markup Widget bug (@NiharikaRay)
- [#59](https://github.com/airbnb/caravel/pull/59) Adding y-axis format option (@patrickleotardif)
- [#58](https://github.com/airbnb/caravel/pull/58) Setting min_periods to 1 for rolling windows (@mistercrunch)
- [#56](https://github.com/airbnb/caravel/pull/56) adding sort order of the slices on changed_on field (@mistercrunch)
### 0.5.2 (2015/10/24 01:06 +00:00)
- [#53](https://github.com/airbnb/caravel/pull/53) Py3 (@mistercrunch)
- [#51](https://github.com/airbnb/caravel/pull/51) Adding timezone offset as a datasource param (@mistercrunch)
- [#52](https://github.com/airbnb/caravel/pull/52) Speed up travis builds with wheels (@mistercrunch)
- [#48](https://github.com/airbnb/caravel/pull/48) Allowing to specify the gunicorn timeout in CLI and config (@mistercrunch)
### 0.5.0 (2015/10/13 01:09 +00:00)
- [#46](https://github.com/airbnb/caravel/pull/46) Allowing to change the "Time Column" on SqlA (@mistercrunch)
- [#45](https://github.com/airbnb/caravel/pull/45) Bootstrapping widgets from javascript initializer. (@akuhn)
- [#43](https://github.com/airbnb/caravel/pull/43) Supporting arbitrary expressions (@mistercrunch)
- [#42](https://github.com/airbnb/caravel/pull/42) Adding ability to style a dashboard with CSS (@mistercrunch)
- [#41](https://github.com/airbnb/caravel/pull/41) Cleaning up the static folder (@mistercrunch)
- [#35](https://github.com/airbnb/caravel/pull/35) A first draft on default security roles (@mistercrunch)
- [#40](https://github.com/airbnb/caravel/pull/40) Introducing time comparison (@mistercrunch)
- [#39](https://github.com/airbnb/caravel/pull/39) Adding interpolation choice for line charts (@mistercrunch)
- [#38](https://github.com/airbnb/caravel/pull/38) Extract css rules and scripts into separate files. (@akuhn)
- [#37](https://github.com/airbnb/caravel/pull/37) Viz type (@mistercrunch)
- [#36](https://github.com/airbnb/caravel/pull/36) Extract widget javascript to separate files. (@akuhn)
- [#34](https://github.com/airbnb/caravel/pull/34) Ripping out Highcharts. (@mistercrunch)
### 0.4.0 (2015/09/27 04:39 +00:00)
- [#33](https://github.com/airbnb/caravel/pull/33) Adding nvd3 support (@mistercrunch)
- [#32](https://github.com/airbnb/caravel/pull/32) Adding a foundation for unit tests (@mistercrunch)
- [#31](https://github.com/airbnb/caravel/pull/31) Adding a button to test connections (@mistercrunch)
- [#30](https://github.com/airbnb/caravel/pull/30) Word cloud widget! (@mistercrunch)
- [#29](https://github.com/airbnb/caravel/pull/29) Adding support for markup (html/markdown) widgets (@mistercrunch)
- [#28](https://github.com/airbnb/caravel/pull/28) Fix default Sqlite path. (@noddi)
- [#27](https://github.com/airbnb/caravel/pull/27) More refactor and bugfixes (@mistercrunch)
- [#26](https://github.com/airbnb/caravel/pull/26) Bugfix (@mistercrunch)
- [#25](https://github.com/airbnb/caravel/pull/25) Adding basic dashboarding support! (@mistercrunch)
- [#23](https://github.com/airbnb/caravel/pull/23) Custom WHERE clause for tables (not druid) + error handling refactor (@mistercrunch)
- [#22](https://github.com/airbnb/caravel/pull/22) Form factory refactor (@mistercrunch)
- [#20](https://github.com/airbnb/caravel/pull/20) add tzinfo config, useful when start druid without utc timezone (@wbchn)
### 0.2.1 (2015/09/05 22:08 +00:00)
- [#19](https://github.com/airbnb/caravel/pull/19) Preparing pypi package (@mistercrunch)
### 0.2.0 (2015/09/05 20:43 +00:00)
- [#16](https://github.com/airbnb/caravel/pull/16) Adding Bubble charts (@mistercrunch)
- [#13](https://github.com/airbnb/caravel/pull/13) Now supporting SQL Multiple database (@mistercrunch)
- [#12](https://github.com/airbnb/caravel/pull/12) Cosmetricks (@mistercrunch)
- [#11](https://github.com/airbnb/caravel/pull/11) Fixing the ways metrics are autogenerated (@mistercrunch)
- [#10](https://github.com/airbnb/caravel/pull/10) Now enabling multi-cluster, connection info managed in UI (@mistercrunch)
- [#9](https://github.com/airbnb/caravel/pull/9) Multi delete action on datasources (@mistercrunch)
- [#8](https://github.com/airbnb/caravel/pull/8) Preventing bad json from creating problems (@mistercrunch)
- [#3](https://github.com/airbnb/caravel/pull/3) Implementing my own highcharts wrapper (@mistercrunch)

View File

@@ -30,8 +30,8 @@ Look through the GitHub issues for features. Anything tagged with
### Documentation
Panoramix could always use better documentation,
whether as part of the official Panoramix docs,
Caravel could always use better documentation,
whether as part of the official Caravel docs,
in docstrings, `docs/*.rst` or even on the web as blog posts or
articles.
@@ -49,14 +49,14 @@ If you are proposing a feature:
## Latest Documentation
[API Documentation](http://pythonhosted.com/panoramix)
[API Documentation](http://pythonhosted.com/caravel)
## 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/panoramix.git
# git clone git@github.com:airbnb/caravel.git
# [optional] setup a virtual env and activate it
virtualenv env
@@ -66,24 +66,24 @@ If you are proposing a feature:
python setup.py develop
# Create an admin user
fabmanager create-admin --app panoramix
fabmanager create-admin --app caravel
# Initialize the database
panoramix db upgrade
caravel db upgrade
# Create default roles and permissions
panoramix init
caravel init
# Load some data to play with
panoramix load_examples
caravel load_examples
# start a dev web server
panoramix runserver -d
caravel runserver -d
## Setting up the node / npm javascript environment
`panoramix/assets` contains all npm-managed, front end assets.
`caravel/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.
@@ -116,7 +116,7 @@ new `node_modules/` folder within `assets/`.
npm install
```
To parse and generate bundled files for panoramix, run either of the
To parse and generate bundled files for caravel, 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.
@@ -132,7 +132,7 @@ For every development session you will have to start a flask dev server
as well as an npm watcher
```
panoramix runserver -d -p 8081
caravel runserver -d -p 8081
npm run dev
```
@@ -144,8 +144,12 @@ Tests can then be run with:
Lint the project with:
# for python changes
flake8 changes tests
# for javascript
npm run lint
## API documentation
Generate the documentation with:
@@ -153,12 +157,12 @@ Generate the documentation with:
cd docs && ./build.sh
## CSS Themes
As part of the npm build process, CSS for Panoramix is compiled from ```Less```, a dynamic stylesheet language.
As part of the npm build process, CSS for Caravel is compiled from ```Less```, a dynamic stylesheet language.
It's possible to customize or add your own theme to Panoramix, either by overriding CSS rules or preferably
It's possible to customize or add your own theme to Caravel, 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 Panoramix are derived from
The ```variables.less``` and ```bootswatch.less``` files that ship with Caravel 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]

11
INTHEWILD.md Normal file
View File

@@ -0,0 +1,11 @@
Please use [pull requests](https://github.com/airbnb/caravel/pull/new/master)
to add your organization and/or project to this document!
Organizations
----------
- [Airbnb](https://github.com/airbnb)
- [GfK Data Lab] (http://datalab.gfk.com)
Projects
----------
- None we know of yet

View File

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

179
README.md
View File

@@ -1,183 +1,110 @@
Panoramix
Caravel
=========
<img src="http://i.imgur.com/H0Kyvyi.jpg" style="border-radius: 20px; box-shadow:5px 5px 5px gray;" alt="Caravel" width="500"/>
[![Join the chat at https://gitter.im/mistercrunch/panoramix](https://badges.gitter.im/mistercrunch/panoramix.svg)](https://gitter.im/mistercrunch/panoramix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
![img](https://travis-ci.org/mistercrunch/panoramix.svg?branch=master)
[![Coverage Status](https://coveralls.io/repos/mistercrunch/panoramix/badge.svg?branch=master&service=github)](https://coveralls.io/github/mistercrunch/panoramix?branch=master)
[![Code Health](https://landscape.io/github/mistercrunch/panoramix/immune_to_filter/landscape.svg?style=flat)](https://landscape.io/github/mistercrunch/panoramix/master)
[![Build Status](https://travis-ci.org/airbnb/caravel.svg?branch=master)](https://travis-ci.org/airbnb/caravel)
[![PyPI version](https://badge.fury.io/py/caravel.svg)](https://badge.fury.io/py/caravel)
[![Coverage Status](https://coveralls.io/repos/airbnb/caravel/badge.svg?branch=master&service=github)](https://coveralls.io/github/airbnb/caravel?branch=master)
[![Code Health](https://landscape.io/github/airbnb/caravel/master/landscape.svg?style=flat)](https://landscape.io/github/airbnb/caravel/master)
[![Requirements Status](https://requires.io/github/airbnb/caravel/requirements.svg?branch=master)](https://requires.io/github/airbnb/caravel/requirements/?branch=master)
[![Join the chat at https://gitter.im/airbnb/caravel](https://badges.gitter.im/airbnb/caravel.svg)](https://gitter.im/airbnb/caravel?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Documentation](https://img.shields.io/badge/docs-airbnb.io-blue.svg)](http://airbnb.io/caravel/)
Panoramix is a data exploration platform designed to be visual, intuitive
Caravel is a data exploration platform designed to be visual, intuitive
and interactive.
[this project used to be named **Panoramix**]
Video - Introduction to Panoramix
Video - Introduction to Caravel
---------------------------------
[![Panoramix - ](http://img.youtube.com/vi/3Txm_nj_R7M/0.jpg)](http://www.youtube.com/watch?v=3Txm_nj_R7M)
[![Caravel - ](http://img.youtube.com/vi/3Txm_nj_R7M/0.jpg)](http://www.youtube.com/watch?v=3Txm_nj_R7M)
Screenshots
------------
![img](http://i.imgur.com/bi09J9X.png)
![img](http://i.imgur.com/aOaH0ty.png)
![img](http://i.imgur.com/JRbTnTx.png)
![img](http://i.imgur.com/4wRtxwb.png)
Panoramix
Caravel
---------
Panoramix'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**.
Caravel's main goal is to make it easy to slice, dice and visualize data.
It empowers users to perform **analytics at the speed of thought**.
Panoramix provides:
* A quick way to intuitively visualize datasets
* Create and share interactive dashboards
Caravel provides:
* A quick way to intuitively visualize datasets by allowing users to 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 Panoramix to stay blazing fast while
* 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 Caravel to stay blazing fast while
slicing and dicing large, realtime datasets
Buzz Phrases
------------
* Analytics at the speed of thought!
* Instantaneous learning curve
* Realtime analytics when querying [Druid.io](http://druid.io)
* Extentsible to infinity
Database Support
----------------
Panoramix was originally designed on to of Druid.io, but quickly broadened
its scope to support other databases through the use of SqlAlchemy, a Python
Caravel was originally designed on top of Druid.io, but quickly broadened
its scope to support other databases through the use of SQLAlchemy, a Python
ORM that is compatible with
[most common databases](http://docs.sqlalchemy.org/en/rel_1_0/core/engines.html).
[most common databases](http://docs.sqlalchemy.org/en/rel_1_0/core/engines.html).
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.*
Installation
------------
Installation & Configuration
----------------------------
Panoramix is currently only tested using Python 2.7.*. Python 3 support is
on the roadmap, Python 2.6 won't be supported.
[See in the documentation](http://airbnb.io/caravel/installation.html)
Follow these few simple steps to install Panoramix.
```
# Install panoramix
pip install panoramix
# Create an admin user
fabmanager create-admin --app panoramix
# Initialize the database
panoramix db upgrade
# Create default roles and permissions
panoramix init
# Load some data to play with
panoramix load_examples
# Start the development web server
panoramix 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
`Menu -> Datasources`, from where you can start playing with your data!
Configuration
-------------
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:
```
#---------------------------------------------------------
# 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
----------------
![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)
![img](http://i.imgur.com/MAFZTtU.png)
![img](http://i.imgur.com/xcy1QjN.png)
![img](http://i.imgur.com/RWqA8ly.png)
![img](http://i.imgur.com/D2kZL7q.png)
![img](http://i.imgur.com/0UPTK61.png)
![img](http://i.imgur.com/ahHoCuS.png)
Related Links
Resources
-------------
* [Panoramix Google Group] (https://groups.google.com/forum/#!forum/airbnb_panoramix)
* [Gitter (live chat) Channel](https://gitter.im/mistercrunch/panoramix)
* [Caravel Google Group](https://groups.google.com/forum/#!forum/airbnb_caravel)
* [Gitter (live chat) Channel](https://gitter.im/airbnb/caravel)
* [Docker image](https://hub.docker.com/r/kochalex/caravel/) (community contributed)
Tip of the Hat
--------------
Panoramix would not be possible without these great frameworks / libs
Caravel 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!
* NVD3 - One of the best charting libraries out there
* Much more, check out the `install_requires` section in the [setup.py](https://github.com/airbnb/caravel/blob/master/setup.py) file!
Contributing
------------
Interested in contributing? Casual hacking? Check out [Contributing.MD](https://github.com/mistercrunch/panoramix/blob/master/CONTRIBUTING.md)
Interested in contributing? Casual hacking? Check out [Contributing.MD](https://github.com/airbnb/caravel/blob/master/CONTRIBUTING.md)

12
TODO.md
View File

@@ -1,5 +1,5 @@
# TODO
List of TODO items for Panoramix
List of TODO items for Caravel
## Important
* **Getting proper JS testing:** unit tests on the Python side are pretty
@@ -7,13 +7,9 @@ List of TODO items for Panoramix
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
Panoramix would discover and expose the plugins
Caravel 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)
* **Comments:** allow for people to comment on slices and dashes
* **Dashboard URL filters:** `{dash_url}#fltin__fieldname__value1,value2`
* **Default slice:** choose a default slice for the dataset instead of
default endpoint
@@ -33,9 +29,13 @@ List of TODO items for Panoramix
* **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
* Build matrix to include mysql using tox
* Figure out why coverage isn't working
* Kill switch for Druid in docs
* CREATE VIEW button from SQL editor
* Test button for when editing SQL expression
* Slider form element

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/caravel
# Logging configuration
[loggers]

View File

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

View File

@@ -1 +0,0 @@

View File

@@ -1,13 +1,20 @@
"""Package's main module!"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
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('PANORAMIX_CONFIG', 'panoramix.config')
CONFIG_MODULE = os.environ.get('CARAVEL_CONFIG', 'caravel.config')
# Logging configuration
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(name)s:%(message)s')
@@ -16,21 +23,24 @@ 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('/panoramix/featured')
return redirect('/caravel/welcome')
appbuilder = AppBuilder(
app, db.session,
base_template='panoramix/base.html',
base_template='caravel/base.html',
indexview=MyIndexView,
security_manager_class=app.config.get("CUSTOM_SECURITY_MANAGER"))
sm = appbuilder.sm
get_session = appbuilder.get_session
from panoramix import config, views
from caravel import config, views # noqa

View File

@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
error = (
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+

View File

@@ -57,13 +57,13 @@
}],
"max-depth": [2, 5],
"max-len": [0, 80, 4],
"max-nested-callbacks": [1, 2],
"max-nested-callbacks": [1, 3],
"max-params": [1, 4],
"new-parens": [2],
"newline-after-var": [0],
"no-bitwise": [0],
"no-cond-assign": [2],
"no-console": [2],
"no-console": [1, { allow: ["warn", "error"] }],
"no-const-assign": [2],
"no-constant-condition": [2],
"no-control-regex": [2],

View File

Before

Width:  |  Height:  |  Size: 459 KiB

After

Width:  |  Height:  |  Size: 459 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

Before

Width:  |  Height:  |  Size: 552 KiB

After

Width:  |  Height:  |  Size: 552 KiB

View File

Before

Width:  |  Height:  |  Size: 236 KiB

After

Width:  |  Height:  |  Size: 236 KiB

View File

Before

Width:  |  Height:  |  Size: 702 KiB

After

Width:  |  Height:  |  Size: 702 KiB

View File

Before

Width:  |  Height:  |  Size: 328 KiB

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 93 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 121 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

View File

@@ -1,5 +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('../node_modules/jquery-ui/themes/base/jquery-ui.css');
require('select2');
require('../vendor/select2.sortable.js');

View File

@@ -1,14 +1,15 @@
var $ = window.$ = require('jquery');
var jQuery = window.jQuery = $;
var px = require('./modules/panoramix.js');
var px = require('./modules/caravel.js');
var d3 = require('d3');
var showModal = require('./modules/utils.js').showModal;
require('bootstrap');
var ace = require('brace');
require('brace/mode/css');
require('brace/theme/crimson_editor');
require('./panoramix-select2.js');
require('./caravel-select2.js');
require('../node_modules/gridster/dist/jquery.gridster.min.css');
require('../node_modules/gridster/dist/jquery.gridster.min.js');
@@ -17,15 +18,21 @@ var Dashboard = function (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 () {
if (data.error) {
var html = '<div class="alert alert-danger">' + data.error + '</div>';
$("#slice_" + data.slice_id).find('.token').html(html);
} else {
var slice = px.Slice(data, dash);
$("#slice_" + data.slice_id).find('a.refresh').click(function () {
slice.render(true);
});
sliceObjects.push(slice);
slice.render();
});
sliceObjects.push(slice);
slice.render();
}
});
this.slices = sliceObjects;
},
@@ -51,7 +58,7 @@ var Dashboard = function (dashboardData) {
return JSON.stringify(this.filters, null, 4);
},
refreshExcept: function (slice_id) {
var immune = this.metadata.filter_immune_slices;
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();
@@ -77,25 +84,26 @@ var Dashboard = function (dashboardData) {
this.refreshExcept(slice_id);
},
getSlice: function (slice_id) {
this.slices.forEach(function (slice, i) {
if (slice.slice_id === slice_id) {
return slice;
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: [100, 100],
widget_base_dimensions: [95, 95],
draggable: {
handle: '.drag'
},
resize: {
enabled: true,
stop: function (e, ui, element) {
var slice_data = $(element).data('slice');
dashboard.getSlice(slice_data.slice_id).resize();
dashboard.getSlice($(element).attr('slice_id')).resize();
}
},
serialize_params: function (_w, wgd) {
@@ -108,6 +116,16 @@ var Dashboard = function (dashboardData) {
};
}
}).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 = {};
@@ -125,15 +143,22 @@ var Dashboard = function (dashboardData) {
};
$.ajax({
type: "POST",
url: '/panoramix/save_dash/' + dashboard.id + '/',
url: '/caravel/save_dash/' + dashboard.id + '/',
data: {
data: JSON.stringify(data)
},
success: function () {
alert("Saved!");
showModal({
title: "Success",
body: "This dashboard was saved successfully."
});
},
error: function () {
alert("Error :(");
error: function (error) {
showModal({
title: "Error",
body: "Sorry, there was an error saving this dashboard:<br />" + error
});
console.warn("Save dashboard error", error);
}
});
});
@@ -144,7 +169,8 @@ var Dashboard = function (dashboardData) {
editor.setTheme("ace/theme/crimson_editor");
editor.setOptions({
minLines: 16,
maxLines: Infinity
maxLines: Infinity,
useWorker: false
});
editor.getSession().setMode("ace/mode/css");
@@ -154,34 +180,72 @@ var Dashboard = function (dashboardData) {
$("#css_template").on("change", function () {
var css = $(this).find('option:selected').data('css');
editor.setValue(css);
$('#dash_css').val(css);
$("#user_style").html(css);
injectCss("dashboard-template", css);
});
$('#filters').click(function () {
alert(dashboard.readFilters());
showModal({
title: "<span class='fa fa-info-circle'></span> Current Global Filters",
body: "The following global filters are currently applied:<br/>" + dashboard.readFilters()
});
});
$("a.closeslice").click(function () {
$('#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);
});
$(".slice_info").click(function () {
var widget = $(this).parents('.widget');
var slice_description = widget.find('.slice_description');
slice_description.slideToggle(500, function () {
widget.find('.refresh').click();
});
});
$("table.slice_header").mouseover(function () {
$(this).find("td.icons nobr").show();
});
$("table.slice_header").mouseout(function () {
$(this).find("td.icons nobr").hide();
$("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);
$("#user_style").html(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();
@@ -190,4 +254,5 @@ var Dashboard = function (dashboardData) {
$(document).ready(function () {
Dashboard($('.dashboard').data('dashboard'));
$('[data-toggle="tooltip"]').tooltip({ container: 'body' });
});

View File

@@ -5,13 +5,14 @@
// js
var $ = window.$ = require('jquery');
var jQuery = window.jQuery = $;
var px = require('./modules/panoramix.js');
var px = require('./modules/caravel.js');
var showModal = require('./modules/utils.js').showModal;
require('jquery-ui');
$.widget.bridge('uitooltip', $.ui.tooltip); // Shutting down jq-ui tooltips
require('bootstrap');
require('./panoramix-select2.js');
require('./caravel-select2.js');
require('../node_modules/bootstrap-toggle/js/bootstrap-toggle.min.js');
@@ -53,19 +54,23 @@ function prepForm() {
});
}
function renderSlice() {
function query(force, pushState) {
if (force === undefined) {
force = false;
}
if (pushState !== false) {
history.pushState({}, document.title, slice.querystring());
}
$('.query-and-save button').attr('disabled', 'disabled');
$('.btn-group.results span,a').attr('disabled', 'disabled');
$('div.alert').remove();
$('#is_cached').hide();
prepForm();
slice.render();
slice.render(force);
}
function initExploreView() {
function druidify() {
$('div.alert').remove();
history.pushState({}, document.title, slice.querystring());
renderSlice();
}
function get_collapsed_fieldsets() {
var collapsed_fieldsets = $("#collapsed_fieldsets").val();
@@ -85,9 +90,9 @@ function initExploreView() {
if (parent.hasClass("collapsed")) {
if (animation) {
parent.find(".fieldset_content").slideDown();
parent.find(".panel-body").slideDown();
} else {
parent.find(".fieldset_content").show();
parent.find(".panel-body").show();
}
parent.removeClass("collapsed");
parent.find("span.collapser").text("[-]");
@@ -99,9 +104,9 @@ function initExploreView() {
}
} else { // not collapsed
if (animation) {
parent.find(".fieldset_content").slideUp();
parent.find(".panel-body").slideUp();
} else {
parent.find(".fieldset_content").hide();
parent.find(".panel-body").hide();
}
parent.addClass("collapsed");
@@ -115,8 +120,11 @@ function initExploreView() {
$("#collapsed_fieldsets").val(collapsed_fieldsets.join("||"));
}
$('legend').click(function () {
px.initFavStars();
$('form .panel-heading').click(function () {
toggle_fieldset($(this), true);
$(this).css('cursor', 'pointer');
});
function copyURLToClipboard(url) {
@@ -175,15 +183,25 @@ function initExploreView() {
$shortner.popover('destroy');
}
},
error: function () {
alert("Error :(");
error: function (error) {
showModal({
title: "Error",
body: "Sorry, an error occurred during this operation:<br/>" + error
});
console.warn("Short URL error", error);
}
});
});
$("#viz_type").change(function () {
$("#query").submit();
});
$("#datasource_id").change(function () {
var url = $(this).find('option:selected').attr('url');
window.location = url;
});
var collapsed_fieldsets = get_collapsed_fieldsets();
for (var i = 0; i < collapsed_fieldsets.length; i++) {
toggle_fieldset($('legend:contains("' + collapsed_fieldsets[i] + '")'), false);
@@ -199,9 +217,7 @@ function initExploreView() {
bindOrder: 'sortableStop'
});
$("form").show();
$('[data-toggle="tooltip"]').tooltip({
container: 'body'
});
$('[data-toggle="tooltip"]').tooltip({ container: 'body' });
$(".ui-helper-hidden-accessible").remove(); // jQuery-ui 1.11+ creates a div for every tooltip
function set_filters() {
@@ -256,7 +272,9 @@ function initExploreView() {
}
});
$(".druidify").click(druidify);
$(".query").click(function () {
query(true);
});
function create_choices(term, data) {
var filtered = $(data).filter(function () {
@@ -319,7 +337,7 @@ $(document).ready(function () {
$('.slice').data('slice', slice);
// call vis render method, which issues ajax
renderSlice();
query(false, false);
// make checkbox inputs display as toggles
$(':checkbox')

View File

@@ -8,7 +8,7 @@ class App extends React.Component {
render () {
return (
<Jumbotron>
<h1>Panoramix</h1>
<h1>Caravel</h1>
<p>Extensible visualization tool for exploring data from any database.</p>
</Jumbotron>
);

View File

@@ -2,14 +2,13 @@ var $ = require('jquery');
var jQuery = $;
var d3 = require('d3');
require('../../stylesheets/panoramix.css');
// vis sources
var sourceMap = {
area: 'nvd3_vis.js',
bar: 'nvd3_vis.js',
bubble: 'nvd3_vis.js',
big_number: 'big_number.js',
big_number_total: 'big_number.js',
compare: 'nvd3_vis.js',
dist_bar: 'nvd3_vis.js',
directed_force: 'directed_force.js',
@@ -46,7 +45,7 @@ var color = function () {
// Color factory
var seen = {};
return function (s) {
// next line is for dashed series that should have the same color
// next line is for caravel series that should have the same color
s = s.replace('---', '');
if (seen[s] === undefined) {
seen[s] = Object.keys(seen).length;
@@ -132,6 +131,46 @@ var px = (function () {
};
}
function initFavStars() {
var baseUrl = '/caravel/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);
@@ -169,16 +208,51 @@ var px = (function () {
}
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() + "&json=true";
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);
}
@@ -194,7 +268,8 @@ var px = (function () {
$('#csv').click(function () {
window.location = data.csv_endpoint;
});
$('.btn-group.results span').removeAttr('disabled');
$('.btn-group.results span,a').removeAttr('disabled');
$('.query-and-save button').removeAttr('disabled');
always(data);
},
error: function (msg) {
@@ -204,6 +279,8 @@ var px = (function () {
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 () {
@@ -216,8 +293,8 @@ var px = (function () {
if (slice_description.is(":visible")) {
others += widget.find('.slice_description').height() + 25;
}
others += widget.find('.slice_header').height();
return widget.height() - others;
others += widget.find('.chart-header').height();
return widget.height() - others - 10;
},
bindResizeToWindowResize: function () {
var resizeTimer;
@@ -228,8 +305,11 @@ var px = (function () {
}, 500);
});
},
render: function () {
$('.btn-group.results span').attr('disabled', 'disabled');
render: function (force) {
if (force === undefined) {
force = false;
}
this.force = force;
token.find("img.loading").show();
container.hide();
container.html('');
@@ -293,7 +373,8 @@ var px = (function () {
formatDate: formatDate,
timeFormatFactory: timeFormatFactory,
color: color(),
getParam: getParam
getParam: getParam,
initFavStars: initFavStars
};
})();

View File

@@ -0,0 +1,80 @@
var $ = require('jquery');
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);
}
});
}
/**
* Sets the body and title content of a modal, and shows it. Assumes HTML for modal exists and that
* it handles closing (i.e., works with bootstrap)
*
* @param {object} options object of the form
* {
* title: {string},
* body: {string},
* modalSelector: {string, default: '.misc-modal' },
* titleSelector: {string, default: '.misc-modal .modal-title' },
* bodySelector: {string, default: '.misc-modal .modal-body' },
* }
*/
function showModal(options) {
options.modalSelector = options.modalSelector || ".misc-modal";
options.titleSelector = options.titleSelector || ".misc-modal .modal-title";
options.bodySelector = options.bodySelector || ".misc-modal .modal-body";
$(options.titleSelector).html(options.title || "");
$(options.bodySelector).html(options.body || "");
$(options.modalSelector).modal("show");
}
module.exports = {
wrapSvgText: wrapSvgText,
showModal: showModal
};

View File

@@ -1,7 +1,10 @@
var $ = window.$ = require('jquery');
var jQuery = window.jQuery = $;
var showModal = require('./modules/utils.js').showModal;
require('select2');
require('datatables');
require('datatables.net-bs');
require('../node_modules/datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css');
require('bootstrap');
var ace = require('brace');
@@ -36,17 +39,20 @@ $(document).ready(function () {
function showTableMetadata() {
$(".metadata").load(
'/panoramix/table/' + database_id + '/' + $("#dbtable").val() + '/');
'/caravel/table/' + database_id + '/' + $("#dbtable").val() + '/');
}
$("#dbtable").on("change", showTableMetadata);
showTableMetadata();
$("#create_view").click(function () {
alert("Not implemented");
showModal({
title: "Error",
body: "Sorry, this feature is not yet implemented"
});
});
$(".sqlcontent").show();
function selectStarOnClick() {
$.ajax('/panoramix/select_star/' + database_id + '/' + $("#dbtable").val() + '/')
$.ajax('/caravel/select_star/' + database_id + '/' + $("#dbtable").val() + '/')
.done(function (msg) {
editor.setValue(msg);
});
@@ -67,7 +73,7 @@ $(document).ready(function () {
history.pushState({}, document.title, '?sql=' + encodeURIComponent(editor.getValue()));
$.ajax({
type: "POST",
url: '/panoramix/runsql/',
url: '/caravel/runsql/',
data: {
data: JSON.stringify({
database_id: $('#database_id').val(),

View File

@@ -1,6 +1,6 @@
var $ = window.$ = require('jquery');
var jQuery = window.jQuery = $;
var px = require('./modules/panoramix.js');
var px = require('./modules/caravel.js');
require('bootstrap');

View File

@@ -0,0 +1,67 @@
var $ = window.$ = require('jquery');
var jQuery = window.jQuery = $;
require('../stylesheets/welcome.css');
require('bootstrap');
require('datatables.net-bs');
require('../node_modules/datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css');
require('../node_modules/cal-heatmap/cal-heatmap.css');
var CalHeatMap = require('cal-heatmap');
function modelViewTable(selector, modelEndpoint) {
// Builds a dataTable from a flask appbuilder api endpoint
$.getJSON(modelEndpoint + '/api/read', function (data) {
var tableData = jQuery.map(data.result, function (el, i) {
var row = $.map(data.list_columns, function (col, i) {
return el[col];
});
return [row];
});
var cols = jQuery.map(data.list_columns, function (col, i) {
return { sTitle: data.label_columns[col] };
});
var panel = $(selector).parents('.panel');
panel.find("img.loading").remove();
$(selector).DataTable({
aaData: tableData,
aoColumns: cols,
bPaginate: true,
pageLength: 10,
bLengthChange: false,
aaSorting: [],
searching: true,
bInfo: false
});
// Hack to move the searchbox in the right spot
var search = panel.find(".dataTables_filter input");
search.addClass('form-control').detach();
search.appendTo(panel.find(".search"));
panel.find('.dataTables_filter').remove();
// Hack to display the page navigator properly
panel.find('.col-sm-5').remove();
var nav = panel.find('.col-sm-7');
nav.removeClass('col-sm-7');
nav.addClass('col-sm-12');
$(selector).slideDown();
$('[data-toggle="tooltip"]').tooltip({ container: 'body' });
});
}
$(document).ready(function () {
var cal = new CalHeatMap();
cal.init({
start: new Date().setFullYear(new Date().getFullYear() - 1),
range: 13,
data: '/caravel/activity_per_day',
domain: "month",
subDomain: "day",
itemName: "action",
tooltip: true
});
modelViewTable('#dash_table', '/dashboardmodelviewasync');
modelViewTable('#slice_table', '/sliceasync');
});

View File

@@ -1,5 +1,5 @@
{
"name": "panoramix",
"name": "caravel",
"version": "0.1.0",
"description": "Any database to any visualization",
"directories": {
@@ -11,11 +11,11 @@
"dev": "webpack -d --watch --colors",
"prod": "webpack -p --colors",
"lint": "npm run --silent lint:js",
"lint:js": "eslint --ignore-path=.eslintignore --ext .js .; exit 0;"
"lint:js": "eslint --ignore-path=.eslintignore --ext .js ."
},
"repository": {
"type": "git",
"url": "git+https://github.com/mistercrunch/panoramix.git"
"url": "git+https://github.com/airbnb/caravel.git"
},
"keywords": [
"big",
@@ -31,9 +31,9 @@
],
"author": "Airbnb",
"bugs": {
"url": "https://github.com/mistercrunch/panoramix/issues"
"url": "https://github.com/airbnb/caravel/issues"
},
"homepage": "https://github.com/mistercrunch/panoramix#readme",
"homepage": "https://github.com/airbnb/caravel#readme",
"dependencies": {
"babel-loader": "^6.2.1",
"babel-polyfill": "^6.3.14",
@@ -43,21 +43,22 @@
"bootstrap-datepicker": "^1.6.0",
"bootstrap-toggle": "^2.2.1",
"brace": "^0.7.0",
"cal-heatmap": "3.5.4",
"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",
"datatables.net-bs": "^1.10.11",
"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": "^2.6.1",
"less-loader": "^2.2.2",
"nvd3": "1.8.2",
"react": "^0.14.7",

View File

@@ -2,10 +2,25 @@ body {
margin: 0px !important;
}
.modal-dialog {
z-index: 1100;
}
.label {
font-size: 100%;
}
.no-wrap {
white-space: nowrap;
}
input.form-control {
background-color: white;
}
.chart-header a.danger {
color: red;
}
.col-left-fixed {
width:350px;
position: absolute;
@@ -15,15 +30,10 @@ input.form-control {
margin-left: 365px;
}
.modal-content.css {
transition: opacity 0.5s ease;
.favstar {
margin-right: 10px;
opacity: 0.5;
border-color: black;
}
.modal-content.css:hover {
transition: opacity 0.5s ease;
opacity: 1;
cursor: pointer;
}
.slice_description{
@@ -39,7 +49,7 @@ input.form-control {
cursor: pointer;
}
.padded{
.padded {
padding: 10px;
}
@@ -48,9 +58,6 @@ input.form-control {
overflow: auto;
}
.slice_container {
//height: 100%;
}
.container-fluid {
text-align: left;
}
@@ -65,10 +72,24 @@ form div {
}
.navbar-brand a {
color: white;
text-decoration: none;
}
.navbar-brand a:hover {
color: white;
text-decoration: none;
}
.header span{
margin-left: 3px;
.header span {
margin-left: 5px;
}
.widget-is-cached {
display: none;
}
.header span.label {
margin-left: 5px;
margin-right: 5px;
}
#timer {
@@ -78,7 +99,10 @@ form div {
.notbtn {
cursor: default;
box-shadow: none;
border: 1px solid #ccc;
}
hr {
margin-top: 15px;
margin-bottom: 15px;
@@ -92,25 +116,6 @@ span.title-block {
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;
@@ -118,10 +123,6 @@ legend.legend-style {
.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;
@@ -131,42 +132,6 @@ legend {
.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;
}
@@ -179,7 +144,6 @@ legend {
img.loading {
width: 40px;
}
.dashboard a i {
cursor: pointer;
}
@@ -196,11 +160,9 @@ img.loading {
.gridster li.widget{
list-style-type: none;
border-radius: 0;
padding: 10px;
margin: 5px;
border: 1px solid #ccc;
box-shadow: 2px 1px 5px -2px #aaa;
overflow: hidden;
background-color: #fff;
}
.dashboard .gridster .dragging,
@@ -224,22 +186,41 @@ img.loading {
width: 100%;
height: 100%;
}
.dashboard table.slice_header {
width: 100%;
height: 20px;
}
.dashboard div.nvtooltip {
z-index: 888; /* this lets tool tips go on top of other slices */
}
.dashboard td.icons {
width: 50px;
}
.dashboard td.icons nobr {
display: none;
}
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,21 @@
.table i {
padding-top: 6px;
}
img.loading {
width: 25px;
}
.welcome table {
display: none;
width: 100%;
}
input {
margin-left: 5px;
margin-top: 8px;
}
.panel-body {
overflow: auto;
}

View File

@@ -1,4 +1,5 @@
.big_number g.axis text {
.big_number g.axis text,
.big_number_total g.axis text {
font-size: 10px;
font-weight: normal;
color: gray;
@@ -8,18 +9,21 @@
font-weight: none;
}
.big_number text.big {
.big_number text.big,
.big_number_total text.big{
stroke: black;
text-anchor: middle;
fill: black;
}
.big_number g.tick line {
.big_number g.tick line,
.big_number_total g.tick line{
stroke-width: 1px;
stroke: grey;
}
.big_number .domain {
.big_number .domain,
.big_number_total .domain{
fill: none;
stroke: black;
stroke-width: 1;

View File

@@ -4,7 +4,7 @@ var d3 = window.d3 || require('d3');
// CSS
require('./big_number.css');
var px = require('../javascripts/modules/panoramix.js');
var px = require('../javascripts/modules/caravel.js');
function bigNumberVis(slice) {
var div = d3.select(slice.selector);
@@ -24,13 +24,19 @@ function bigNumberVis(slice) {
var fp = d3.format('+.1%');
var width = slice.width();
var height = slice.height();
div.selectAll("*").remove();
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];
var v = null;
if (data.length > 1) {
v = data[data.length - 1][1];
} else {
v = data[data.length - 1][0];
}
if (json.compare_lag > 0) {
var pos = data.length - (json.compare_lag + 1);
if (pos >= 0) {
@@ -96,6 +102,19 @@ function bigNumberVis(slice) {
.style('font-size', d3.min([height, width]) / 3.5)
.attr('fill', 'white');
//Printing big number subheader text
if (json.subheader !== null) {
g.append('text')
.attr('x', width / 2)
.attr('y', y + d3.min([height, width]) / 4.5)
.text(json.subheader)
.attr('id', 'subheader_text')
.style('font-size', d3.min([height, width]) / 16)
.style('text-anchor', 'middle')
.attr('fill', c)
.attr('stroke', c);
}
var c = scale_color(v_compare);
//Printing compare %

View File

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

View File

@@ -5,7 +5,7 @@ var d3 = window.d3 || require('d3');
// CSS
require('./filter_box.css');
require('../javascripts/panoramix-select2.js');
require('../javascripts/caravel-select2.js');
function filterBox(slice) {
var filtersObj = {};
@@ -56,7 +56,7 @@ function filterBox(slice) {
})
.on('change', fltChanged);
}
slice.done();
slice.done(payload);
function select2Formatter(result, container /*, query, escapeMarkup*/) {
var perc = Math.round((result.metric / maxes[result.filter]) * 100);

View File

@@ -1,6 +1,6 @@
// JS
var $ = window.$ || require('jquery');
var px = window.px || require('../javascripts/modules/panoramix.js');
var px = window.px || require('../javascripts/modules/caravel.js');
var d3 = require('d3');
d3.tip = require('d3-tip'); //using window.d3 doesn't capture events properly bc of multiple instances

View File

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

View File

@@ -1,7 +1,7 @@
// JS
var $ = window.$ || require('jquery');
var d3 = window.d3 || require('d3');
var px = window.px || require('../javascripts/modules/panoramix.js');
var px = window.px || require('../javascripts/modules/caravel.js');
var nv = require('nvd3');
// CSS
@@ -27,7 +27,7 @@ function nvd3Vis(slice) {
chart.lines2.xScale(d3.time.scale.utc());
chart.x2Axis
.showMaxMin(fd.x_axis_showminmax)
.staggerLabels(true);
.staggerLabels(false);
} else {
chart = nv.models.lineChart();
}
@@ -37,7 +37,7 @@ function nvd3Vis(slice) {
chart.interpolate(fd.line_interpolation);
chart.xAxis
.showMaxMin(fd.x_axis_showminmax)
.staggerLabels(true);
.staggerLabels(false);
break;
case 'bar':
@@ -131,9 +131,6 @@ function nvd3Vis(slice) {
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');
@@ -148,6 +145,22 @@ function nvd3Vis(slice) {
if (fd.x_log_scale) {
chart.xScale(d3.scale.log());
}
var xAxisFormatter = null;
if (viz_type === 'bubble') {
xAxisFormatter = d3.format('.3s');
} else if (fd.x_axis_format === 'smart_date') {
xAxisFormatter = px.formatDate;
chart.xAxis.tickFormat(xAxisFormatter);
} else if (fd.x_axis_format !== undefined) {
xAxisFormatter = px.timeFormatFactory(fd.x_axis_format);
chart.xAxis.tickFormat(xAxisFormatter);
}
if (chart.hasOwnProperty("x2Axis")) {
chart.x2Axis.tickFormat(xAxisFormatter);
height += 30;
}
if (viz_type === 'bubble') {
chart.xAxis.tickFormat(d3.format('.3s'));
} else if (fd.x_axis_format === 'smart_date') {

View File

@@ -1,7 +1,7 @@
var $ = window.$ = require('jquery');
var jQuery = window.jQuery = $;
require('datatables');
require('datatables.net-bs');
require('./pivot_table.css');
require('../node_modules/datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css');

View File

@@ -1,7 +1,7 @@
// CSS
require('./sankey.css');
// JS
var px = window.px || require('../javascripts/modules/panoramix.js');
var px = window.px || require('../javascripts/modules/caravel.js');
var d3 = window.d3 || require('d3');
d3.sankey = require('d3-sankey').sankey;
@@ -18,11 +18,9 @@ function sankeyVis(slice) {
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 formatNumber = d3.format(",.0f");
div.selectAll("*").remove();
var svg = div.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
@@ -70,7 +68,7 @@ function sankeyVis(slice) {
link.append("title")
.text(function (d) {
return d.source.name + " → " + d.target.name + "\n" + format(d.value);
return d.source.name + " → " + d.target.name + "\n" + formatNumber(d.value);
});
var node = svg.append("g").selectAll(".node")
@@ -103,7 +101,7 @@ function sankeyVis(slice) {
})
.append("title")
.text(function (d) {
return d.name + "\n" + format(d.value);
return d.name + "\n" + formatNumber(d.value);
});
node.append("text")

View File

@@ -0,0 +1,49 @@
.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-abs-percent {
font-size: 3.5em;
font-weight: 400;
}
.sunburst .path-cond-percent {
font-size: 2em;
}
.sunburst .path-metrics {
font-size: 1.5em;
}
.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-abs-percent {
font-size: 2.5em;
}
.dashboard .sunburst .path-cond-percent {
font-size: 1.75em;
}
.dashboard .sunburst .path-metrics {
font-size: 1em;
}
.dashboard .sunburst .path-ratio {
font-size: 1em;
}

View File

@@ -0,0 +1,375 @@
var d3 = window.d3 || require('d3');
var px = require('../javascripts/modules/caravel.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 formatNum = d3.format(".3s");
var formatPerc = 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 sequenceArray = getAncestors(d);
var parentOfD = sequenceArray[sequenceArray.length - 2] || null;
var absolutePercentage = (d.m1 / totalSize).toPrecision(3);
var conditionalPercentage = parentOfD ? (d.m1 / parentOfD.m1).toPrecision(3) : null;
var absolutePercString = formatPerc(absolutePercentage);
var conditionalPercString = parentOfD ? formatPerc(conditionalPercentage) : "";
var yOffsets = ["-25", "7", "35", "60"]; // 3 levels of text if inner-most level, 4 otherwise
var offsetIndex = 0;
// If metrics match, assume we are coloring by category
var metricsMatch = Math.abs(d.m1 - d.m2) < 0.00001;
gMiddleText.selectAll("*").remove();
gMiddleText.append("text")
.attr("class", "path-abs-percent")
.attr("y", yOffsets[offsetIndex++])
.text(absolutePercString + " of total");
if (conditionalPercString) {
gMiddleText.append("text")
.attr("class", "path-cond-percent")
.attr("y", yOffsets[offsetIndex++])
.text(conditionalPercString + " of parent");
}
gMiddleText.append("text")
.attr("class", "path-metrics")
.attr("y", yOffsets[offsetIndex++])
.text("m1: " + formatNum(d.m1) + (metricsMatch ? "" : ", m2: " + formatNum(d.m2)));
gMiddleText.append("text")
.attr("class", "path-ratio")
.attr("y", yOffsets[offsetIndex++])
.text((metricsMatch ? "" : ("m2/m1: " + formatPerc(d.m2 / d.m1))) );
// Reset and fade all the segments.
arcs.selectAll("path")
.style("stroke-width", null)
.style("stroke", null)
.style("opacity", 0.7);
// 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, absolutePercString);
}
// 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;

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