├── .gitignore ├── .gitmodules ├── Gruntfile.coffee ├── LICENSE ├── README.md ├── cairo.py ├── common ├── favicon.ico ├── homepage │ └── Icon_Data.png ├── logo_header.png └── settings.png ├── deployTools └── install.py ├── docs ├── csv.md ├── oem.md └── sample_onprem_nginx.txt ├── images ├── common │ ├── favicon.ico │ ├── homepage │ │ └── Icon_Data.png │ ├── logo_header.png │ └── settings.png └── main │ ├── anim_loading_0.svg │ ├── anim_loading_1.svg │ ├── anim_loading_2.svg │ ├── bg_arrow_left.svg │ ├── broken_chart.svg │ ├── csv.svg │ ├── csv_gray.svg │ ├── dashboard-preview.png │ ├── drag.png │ ├── google_analytics.svg │ ├── google_analytics_gray.svg │ ├── gridtile.svg │ ├── icon_arrow_left.svg │ ├── icon_arrow_right.svg │ ├── icon_back_white.svg │ ├── icon_chart_types.svg │ ├── icon_chart_types_white.svg │ ├── icon_close.png │ ├── icon_close_black.svg │ ├── icon_close_red.svg │ ├── icon_delete.svg │ ├── icon_delete_white.svg │ ├── icon_edit.png │ ├── icon_export_white.svg │ ├── icon_minus.svg │ ├── icon_plus.png │ ├── icon_type_cat.svg │ ├── icon_type_date.svg │ ├── icon_type_green_cat.svg │ ├── icon_type_green_date.svg │ ├── icon_type_green_num.svg │ ├── icon_type_num.svg │ ├── icon_view.png │ ├── infobright.svg │ ├── infobright_gray.svg │ ├── logo.svg │ ├── mysql.svg │ ├── mysql_gray.svg │ ├── nux_arrow.svg │ ├── nux_arrow2.svg │ ├── nux_arrow_right.svg │ ├── postgresql.svg │ └── postgresql_gray.svg ├── lib ├── cookies.js ├── jquery-1.7.1.js ├── jquery-toast │ ├── javascript │ │ └── jquery.toastmessage.js │ └── resources │ │ ├── css │ │ └── jquery.toastmessage.css │ │ └── images │ │ ├── close.gif │ │ ├── error.png │ │ ├── notice.png │ │ ├── success.png │ │ └── warning.png ├── jquery.caret.js ├── jquery.event.drag-2.0.js ├── jquery.simple-color.js ├── jqueryui │ ├── jquery-ui-1.10.3.custom.min.js │ ├── jquery.ui.polychart.less │ └── jquery.ui.touch-punch.min.js ├── json2.js ├── knockout.js ├── moment.js ├── polychart2.js ├── polychart2.standalone.js ├── raphael.js ├── shjs │ ├── sh_javascript.js │ ├── sh_main.js │ └── sh_style.css ├── slickgrid │ ├── slick.core.js │ ├── slick.grid.js │ └── slick.grid.polychart.css ├── sourcesanspro │ ├── LICENSE.txt │ ├── OTF │ │ ├── SourceSansPro-Black.otf │ │ ├── SourceSansPro-BlackIt.otf │ │ ├── SourceSansPro-Bold.otf │ │ ├── SourceSansPro-BoldIt.otf │ │ ├── SourceSansPro-ExtraLight.otf │ │ ├── SourceSansPro-ExtraLightIt.otf │ │ ├── SourceSansPro-It.otf │ │ ├── SourceSansPro-Light.otf │ │ ├── SourceSansPro-LightIt.otf │ │ ├── SourceSansPro-Regular.otf │ │ ├── SourceSansPro-Semibold.otf │ │ └── SourceSansPro-SemiboldIt.otf │ ├── ReadMe.html │ ├── SourceSansProReadMe.html │ └── TTF │ │ ├── SourceSansPro-Black.ttf │ │ ├── SourceSansPro-BlackIt.ttf │ │ ├── SourceSansPro-Bold.ttf │ │ ├── SourceSansPro-BoldIt.ttf │ │ ├── SourceSansPro-ExtraLight.ttf │ │ ├── SourceSansPro-ExtraLightIt.ttf │ │ ├── SourceSansPro-It.ttf │ │ ├── SourceSansPro-Light.ttf │ │ ├── SourceSansPro-LightIt.ttf │ │ ├── SourceSansPro-Regular.ttf │ │ ├── SourceSansPro-Semibold.ttf │ │ └── SourceSansPro-SemiboldIt.ttf └── underscore.js ├── main ├── anim_loading_0.svg ├── anim_loading_1.svg ├── anim_loading_2.svg ├── bg_arrow_left.svg ├── broken_chart.svg ├── csv.svg ├── csv_gray.svg ├── dashboard-preview.png ├── drag.png ├── google_analytics.svg ├── google_analytics_gray.svg ├── gridtile.svg ├── icon_arrow_left.svg ├── icon_arrow_right.svg ├── icon_back_white.svg ├── icon_chart_types.svg ├── icon_chart_types_white.svg ├── icon_close.png ├── icon_close_black.svg ├── icon_close_red.svg ├── icon_delete.svg ├── icon_delete_white.svg ├── icon_edit.png ├── icon_export_white.svg ├── icon_minus.svg ├── icon_plus.png ├── icon_type_cat.svg ├── icon_type_date.svg ├── icon_type_green_cat.svg ├── icon_type_green_date.svg ├── icon_type_green_num.svg ├── icon_type_num.svg ├── icon_view.png ├── infobright.svg ├── infobright_gray.svg ├── logo.svg ├── mysql.svg ├── mysql_gray.svg ├── nux_arrow.svg ├── nux_arrow2.svg ├── nux_arrow_right.svg ├── postgresql.svg └── postgresql_gray.svg ├── makeTools ├── parseTemplates.py ├── runServerTask.coffee └── stitchTask.coffee ├── manage.py ├── package.json ├── packageData ├── README ├── dashstate.json ├── state.json └── test.csv ├── packageExamples ├── README ├── chartbuilder_example.html ├── chartviewer_example.html ├── custom_backend_example.coffee ├── custom_backend_example.html ├── dashbuilder_example.html └── tutorial.html ├── poly.coffee ├── poly ├── README.md ├── common │ ├── bootstrapMixins.less │ ├── events.coffee │ ├── fonts.less │ ├── goldilocks.css │ ├── oldLayout.less │ ├── oldMixins.less │ ├── serverApi.coffee │ └── toast.less ├── demoData │ ├── content.js │ ├── email.js │ ├── sales.js │ └── users.js ├── dsform.coffee ├── dsform.tmpl ├── home.coffee ├── home.less ├── main │ ├── README.md │ ├── anim.coffee │ ├── bindings.coffee │ ├── builder.coffee │ ├── chart │ │ ├── advanced.tmpl │ │ ├── advancedPanel.coffee │ │ ├── aes │ │ │ ├── base.coffee │ │ │ ├── base.tmpl │ │ │ ├── color.coffee │ │ │ ├── color.tmpl │ │ │ ├── size.coffee │ │ │ └── size.tmpl │ │ ├── chart.less │ │ ├── chartbuilder.coffee │ │ ├── chartbuilder.tmpl │ │ ├── filters.coffee │ │ ├── filters.tmpl │ │ ├── icon.less │ │ ├── joins.coffee │ │ ├── joins.less │ │ ├── joins.tmpl │ │ ├── layer.coffee │ │ ├── layer.tmpl │ │ └── selector.less │ ├── const.coffee │ ├── dash │ │ ├── aes.coffee │ │ ├── aes.tmpl │ │ ├── dash.less │ │ ├── dash.tmpl │ │ ├── dashboard.coffee │ │ ├── item │ │ │ ├── base.coffee │ │ │ ├── chart.coffee │ │ │ ├── chart.tmpl │ │ │ ├── comment.coffee │ │ │ ├── comment.tmpl │ │ │ ├── numeral.coffee │ │ │ ├── numeral.tmpl │ │ │ ├── pivottable.coffee │ │ │ ├── pivottable.tmpl │ │ │ ├── polyjs.coffee │ │ │ ├── text.coffee │ │ │ └── text.tmpl │ │ ├── quickadd.coffee │ │ ├── quickadd.tmpl │ │ ├── workspace.coffee │ │ └── workspace.tmpl │ ├── data │ │ ├── autocomplete.coffee │ │ ├── columnInfo.coffee │ │ ├── data.less │ │ ├── data.tmpl │ │ ├── dataSource.coffee │ │ ├── dataView.coffee │ │ ├── datatableView.coffee │ │ ├── datatableView.tmpl │ │ ├── metric │ │ │ ├── attached.coffee │ │ │ ├── attached.tmpl │ │ │ ├── base.coffee │ │ │ ├── base.tmpl │ │ │ ├── dropdown.coffee │ │ │ ├── dropdown.tmpl │ │ │ ├── facet.coffee │ │ │ ├── layer.coffee │ │ │ ├── quickadd.coffee │ │ │ └── quickaddtable.coffee │ │ ├── table.tmpl │ │ └── tableView.coffee │ ├── dnd.coffee │ ├── error │ │ ├── toast.coffee │ │ └── toast.less │ ├── events.coffee │ ├── header.coffee │ ├── header.tmpl │ ├── init.coffee │ ├── main │ │ ├── builder.coffee │ │ ├── chartbuilder.coffee │ │ ├── chartbuilder.tmpl │ │ ├── chartviewer.coffee │ │ ├── chartviewer.tmpl │ │ ├── dashbuilder.coffee │ │ ├── dashbuilder.tmpl │ │ ├── dashviewer.coffee │ │ ├── dashviewer.tmpl │ │ ├── tutorial.coffee │ │ └── viewer.coffee │ ├── mixins.less │ ├── numeral │ │ ├── numeralbuilder.coffee │ │ └── numeralbuilder.tmpl │ ├── overlay.coffee │ ├── overlay.tmpl │ ├── parser.coffee │ ├── polychart.less │ ├── reset.less │ ├── share.coffee │ ├── share.tmpl │ ├── table │ │ ├── aesGroup.coffee │ │ ├── aesGroup.tmpl │ │ ├── table.less │ │ ├── tablebuilder.coffee │ │ └── tablebuilder.tmpl │ ├── template.coffee │ └── ui.less ├── nux.coffee ├── nux.tmpl ├── signup.coffee └── verifyPendingDs.coffee ├── polychart ├── README.md ├── __init__.py ├── config │ ├── README.md │ ├── __init__.py │ ├── deploy.py │ ├── local.py │ ├── localOverrides.py │ └── shared.py ├── main │ ├── README.md │ ├── __init__.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ └── createuser.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto__add_field_userinfo_global_unique_id.py │ │ ├── 0003_auto__add_localdatasource__add_field_pendingdatasource_user__chg_field.py │ │ ├── 0004_auto__del_event.py │ │ ├── 0005_normalize_spec.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ ├── __init__.py │ │ ├── base.tmpl │ │ ├── callback.tmpl │ │ ├── dashEdit.tmpl │ │ ├── dashView.tmpl │ │ ├── demo.tmpl │ │ ├── forgotPassword.tmpl │ │ ├── home.tmpl │ │ ├── login.tmpl │ │ ├── logout.tmpl │ │ ├── oldBase.tmpl │ │ ├── resetPassword.tmpl │ │ ├── settings.tmpl │ │ ├── signup.tmpl │ │ └── verifyPendingDataSource.tmpl │ ├── urls.py │ ├── utils │ │ ├── __init__.py │ │ ├── colours.py │ │ ├── csvParser.py │ │ ├── email.py │ │ ├── emailConsole.py │ │ ├── postageapp.py │ │ ├── secureStorage.py │ │ ├── spec.py │ │ ├── svg.py │ │ ├── tools.py │ │ └── validate.py │ └── views │ │ ├── __init__.py │ │ ├── account.py │ │ ├── dashboard.py │ │ ├── dataSource.py │ │ ├── demo.py │ │ ├── export.py │ │ ├── home.py │ │ ├── ssh.py │ │ ├── tutorial.py │ │ ├── upload.py │ │ └── user.py ├── urls.py ├── utils.py └── wsgi.py ├── polychartQuery ├── README.md ├── __init__.py ├── abstract.py ├── connections.py ├── csv │ ├── __init__.py │ └── connection.py ├── expr.py ├── googleAnalytics │ ├── README.md │ ├── __init__.py │ ├── connection.py │ ├── expr.py │ ├── params.py │ └── query.py ├── httpService.py ├── oauth.md ├── oauth.py ├── query.py ├── sql │ ├── __init__.py │ ├── connection.py │ ├── expr.py │ └── query.py ├── utils.py └── validate.py ├── requirements ├── server ├── README ├── cairo.py ├── polychart │ ├── README.md │ ├── __init__.py │ ├── config │ │ ├── README.md │ │ ├── __init__.py │ │ ├── deploy.py │ │ ├── local.py │ │ └── shared.py │ ├── main │ │ ├── README.md │ │ ├── __init__.py │ │ ├── management │ │ │ ├── __init__.py │ │ │ └── commands │ │ │ │ ├── __init__.py │ │ │ │ └── createuser.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_auto__add_field_userinfo_global_unique_id.py │ │ │ ├── 0003_auto__add_localdatasource__add_field_pendingdatasource_user__chg_field.py │ │ │ ├── 0004_auto__del_event.py │ │ │ ├── 0005_normalize_spec.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── templates │ │ │ ├── __init__.py │ │ │ ├── base.tmpl │ │ │ ├── callback.tmpl │ │ │ ├── dashEdit.tmpl │ │ │ ├── dashView.tmpl │ │ │ ├── demo.tmpl │ │ │ ├── forgotPassword.tmpl │ │ │ ├── home.tmpl │ │ │ ├── login.tmpl │ │ │ ├── logout.tmpl │ │ │ ├── oldBase.tmpl │ │ │ ├── resetPassword.tmpl │ │ │ ├── settings.tmpl │ │ │ ├── signup.tmpl │ │ │ └── verifyPendingDataSource.tmpl │ │ ├── urls.py │ │ ├── utils │ │ │ ├── __init__.py │ │ │ ├── colours.py │ │ │ ├── csvParser.py │ │ │ ├── email.py │ │ │ ├── emailConsole.py │ │ │ ├── postageapp.py │ │ │ ├── secureStorage.py │ │ │ ├── spec.py │ │ │ ├── svg.py │ │ │ ├── tools.py │ │ │ └── validate.py │ │ └── views │ │ │ ├── __init__.py │ │ │ ├── account.py │ │ │ ├── dashboard.py │ │ │ ├── dataSource.py │ │ │ ├── demo.py │ │ │ ├── export.py │ │ │ ├── home.py │ │ │ ├── ssh.py │ │ │ ├── tutorial.py │ │ │ ├── upload.py │ │ │ └── user.py │ ├── urls.py │ ├── utils.py │ └── wsgi.py └── polychartQuery │ ├── README.md │ ├── __init__.py │ ├── abstract.py │ ├── connections.py │ ├── csv │ ├── __init__.py │ └── connection.py │ ├── expr.py │ ├── googleAnalytics │ ├── README.md │ ├── __init__.py │ ├── connection.py │ ├── expr.py │ ├── params.py │ └── query.py │ ├── httpService.py │ ├── oauth.md │ ├── oauth.py │ ├── query.py │ ├── sql │ ├── __init__.py │ ├── connection.py │ ├── expr.py │ └── query.py │ ├── utils.py │ └── validate.py ├── src ├── README.md ├── poly.coffee └── poly │ ├── README.md │ ├── common │ ├── bootstrapMixins.less │ ├── events.coffee │ ├── fonts.less │ ├── goldilocks.css │ ├── oldLayout.less │ ├── oldMixins.less │ ├── serverApi.coffee │ └── toast.less │ ├── demoData │ ├── content.js │ ├── email.js │ ├── sales.js │ └── users.js │ ├── dsform.coffee │ ├── dsform.tmpl │ ├── home.coffee │ ├── home.less │ ├── main │ ├── README.md │ ├── anim.coffee │ ├── bindings.coffee │ ├── builder.coffee │ ├── chart │ │ ├── advanced.tmpl │ │ ├── advancedPanel.coffee │ │ ├── aes │ │ │ ├── base.coffee │ │ │ ├── base.tmpl │ │ │ ├── color.coffee │ │ │ ├── color.tmpl │ │ │ ├── size.coffee │ │ │ └── size.tmpl │ │ ├── chart.less │ │ ├── chartbuilder.coffee │ │ ├── chartbuilder.tmpl │ │ ├── filters.coffee │ │ ├── filters.tmpl │ │ ├── icon.less │ │ ├── joins.coffee │ │ ├── joins.less │ │ ├── joins.tmpl │ │ ├── layer.coffee │ │ ├── layer.tmpl │ │ └── selector.less │ ├── const.coffee │ ├── dash │ │ ├── aes.coffee │ │ ├── aes.tmpl │ │ ├── dash.less │ │ ├── dash.tmpl │ │ ├── dashboard.coffee │ │ ├── item │ │ │ ├── base.coffee │ │ │ ├── chart.coffee │ │ │ ├── chart.tmpl │ │ │ ├── comment.coffee │ │ │ ├── comment.tmpl │ │ │ ├── numeral.coffee │ │ │ ├── numeral.tmpl │ │ │ ├── pivottable.coffee │ │ │ ├── pivottable.tmpl │ │ │ ├── polyjs.coffee │ │ │ ├── text.coffee │ │ │ └── text.tmpl │ │ ├── quickadd.coffee │ │ ├── quickadd.tmpl │ │ ├── workspace.coffee │ │ └── workspace.tmpl │ ├── data │ │ ├── autocomplete.coffee │ │ ├── columnInfo.coffee │ │ ├── data.less │ │ ├── data.tmpl │ │ ├── dataSource.coffee │ │ ├── dataView.coffee │ │ ├── datatableView.coffee │ │ ├── datatableView.tmpl │ │ ├── metric │ │ │ ├── attached.coffee │ │ │ ├── attached.tmpl │ │ │ ├── base.coffee │ │ │ ├── base.tmpl │ │ │ ├── dropdown.coffee │ │ │ ├── dropdown.tmpl │ │ │ ├── facet.coffee │ │ │ ├── layer.coffee │ │ │ ├── quickadd.coffee │ │ │ └── quickaddtable.coffee │ │ ├── table.tmpl │ │ └── tableView.coffee │ ├── dnd.coffee │ ├── error │ │ ├── toast.coffee │ │ └── toast.less │ ├── events.coffee │ ├── header.coffee │ ├── header.tmpl │ ├── init.coffee │ ├── main │ │ ├── builder.coffee │ │ ├── chartbuilder.coffee │ │ ├── chartbuilder.tmpl │ │ ├── chartviewer.coffee │ │ ├── chartviewer.tmpl │ │ ├── dashbuilder.coffee │ │ ├── dashbuilder.tmpl │ │ ├── dashviewer.coffee │ │ ├── dashviewer.tmpl │ │ ├── tutorial.coffee │ │ └── viewer.coffee │ ├── mixins.less │ ├── numeral │ │ ├── numeralbuilder.coffee │ │ └── numeralbuilder.tmpl │ ├── overlay.coffee │ ├── overlay.tmpl │ ├── parser.coffee │ ├── polychart.less │ ├── reset.less │ ├── share.coffee │ ├── share.tmpl │ ├── table │ │ ├── aesGroup.coffee │ │ ├── aesGroup.tmpl │ │ ├── table.less │ │ ├── tablebuilder.coffee │ │ └── tablebuilder.tmpl │ ├── template.coffee │ └── ui.less │ ├── nux.coffee │ ├── nux.tmpl │ ├── signup.coffee │ └── verifyPendingDs.coffee └── system_requirements /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.sublime-project 3 | *.sublime-workspace 4 | *.swo 5 | *.swp 6 | 7 | /*.db 8 | /compiledStatic 9 | /lib/jqueryui/jquery.ui.polychart.css 10 | /log/* 11 | /node_modules/* 12 | /onprem 13 | /polychart-onprem-*.zip 14 | /polychartPackaged 15 | /server/polychart/config/deployOverrides.py 16 | /server/polychart/config/localOverrides.py 17 | /server/polychart/config/overrides.py 18 | /tmp 19 | /uploadedData 20 | 21 | # Deployment 22 | /allStatic 23 | /gunicorn.conf 24 | /server/polychart/config/deployParams.py 25 | /virtualenv/* 26 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/polychart2"] 2 | path = lib/polychart2 3 | url = git@github.com:Polychart/polychart2.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Polychart Dashboard Builder 2 | =========================== 3 | 4 | Setting up a dev environment 5 | ---------------------------- 6 | 7 | Be sure to retrieve the repository's submodules: 8 | - `git submodule update --init --recursive` 9 | 10 | It is recommended that a recent version of Node.js be installed: 11 | - `sudo apt-get install python-software-properties` 12 | - `sudo apt-add-repository ppa:chris-lea/node.js` 13 | - `sudo apt-get update` 14 | - `sudo apt-get install nodejs` (this include `npm`) 15 | 16 | To see a list of dependencies, search for "package" in the 17 | [Chef recipe](https://github.com/Polychart/deployment/blob/master/cookbooks/polychart/recipes/default.rb). 18 | 19 | Install Python dependencies: 20 | - `sudo apt-get install python-setuptools python-virtualenv python2.7-dev` 21 | - `sudo pip install -U virtualenv` 22 | - `sudo pip install -r requirements`. 23 | 24 | Install Node.js dependencies: 25 | - `npm install` (in the repo directory) 26 | - `sudo npm install -g grunt-cli` 27 | 28 | Finally, run `grunt`. 29 | 30 | -------------------------------------------------------------------------------- /cairo.py: -------------------------------------------------------------------------------- 1 | from cairocffi import * 2 | -------------------------------------------------------------------------------- /common/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/common/favicon.ico -------------------------------------------------------------------------------- /common/homepage/Icon_Data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/common/homepage/Icon_Data.png -------------------------------------------------------------------------------- /common/logo_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/common/logo_header.png -------------------------------------------------------------------------------- /common/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/common/settings.png -------------------------------------------------------------------------------- /docs/sample_onprem_nginx.txt: -------------------------------------------------------------------------------- 1 | # Sample nginx config for use with onprem. 2 | # To be used in documentation that is coming soon! 3 | 4 | server { 5 | listen 80; 6 | server_name localhost; 7 | 8 | location / { 9 | proxy_pass http://localhost:7000; 10 | proxy_set_header Host $http_host; 11 | proxy_redirect http:// https://; 12 | } 13 | 14 | location /static { 15 | alias /polychart-onprem/static; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /images/common/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/images/common/favicon.ico -------------------------------------------------------------------------------- /images/common/homepage/Icon_Data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/images/common/homepage/Icon_Data.png -------------------------------------------------------------------------------- /images/common/logo_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/images/common/logo_header.png -------------------------------------------------------------------------------- /images/common/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/images/common/settings.png -------------------------------------------------------------------------------- /images/main/bg_arrow_left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /images/main/dashboard-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/images/main/dashboard-preview.png -------------------------------------------------------------------------------- /images/main/drag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/images/main/drag.png -------------------------------------------------------------------------------- /images/main/icon_arrow_left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /images/main/icon_arrow_right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /images/main/icon_back_white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /images/main/icon_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/images/main/icon_close.png -------------------------------------------------------------------------------- /images/main/icon_delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 11 | 13 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /images/main/icon_delete_white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 10 | 12 | 14 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /images/main/icon_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/images/main/icon_edit.png -------------------------------------------------------------------------------- /images/main/icon_export_white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 12 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /images/main/icon_plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/images/main/icon_plus.png -------------------------------------------------------------------------------- /images/main/icon_type_cat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /images/main/icon_type_date.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /images/main/icon_type_green_cat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /images/main/icon_type_green_date.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /images/main/icon_type_green_num.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 11 | 13 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /images/main/icon_type_num.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 11 | 13 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /images/main/icon_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/images/main/icon_view.png -------------------------------------------------------------------------------- /images/main/nux_arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /images/main/nux_arrow2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /images/main/nux_arrow_right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/cookies.js: -------------------------------------------------------------------------------- 1 | // Adapted from http://www.quirksmode.org/js/cookies.html 2 | var Cookies = { 3 | create: function (name,value,days) { 4 | if (days) { 5 | var date = new Date(); 6 | date.setTime(date.getTime()+(days*24*60*60*1000)); 7 | var expires = "; expires="+date.toGMTString(); 8 | } 9 | else var expires = ""; 10 | document.cookie = name+"="+value+expires+"; path=/"; 11 | }, 12 | read: function (name) { 13 | var nameEQ = name + "="; 14 | var ca = document.cookie.split(';'); 15 | for(var i=0;i < ca.length;i++) { 16 | var c = ca[i]; 17 | while (c.charAt(0)==' ') c = c.substring(1,c.length); 18 | if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); 19 | } 20 | return null; 21 | }, 22 | erase: function (name) { 23 | createCookie(name,"",-1); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /lib/jquery-toast/resources/images/close.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/jquery-toast/resources/images/close.gif -------------------------------------------------------------------------------- /lib/jquery-toast/resources/images/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/jquery-toast/resources/images/error.png -------------------------------------------------------------------------------- /lib/jquery-toast/resources/images/notice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/jquery-toast/resources/images/notice.png -------------------------------------------------------------------------------- /lib/jquery-toast/resources/images/success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/jquery-toast/resources/images/success.png -------------------------------------------------------------------------------- /lib/jquery-toast/resources/images/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/jquery-toast/resources/images/warning.png -------------------------------------------------------------------------------- /lib/jqueryui/jquery.ui.touch-punch.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI Touch Punch 0.2.2 3 | * 4 | * Copyright 2011, Dave Furfero 5 | * Dual licensed under the MIT or GPL Version 2 licenses. 6 | * 7 | * Depends: 8 | * jquery.ui.widget.js 9 | * jquery.ui.mouse.js 10 | */ 11 | (function(b){b.support.touch="ontouchend" in document;if(!b.support.touch){return;}var c=b.ui.mouse.prototype,e=c._mouseInit,a;function d(g,h){if(g.originalEvent.touches.length>1){return;}g.preventDefault();var i=g.originalEvent.changedTouches[0],f=document.createEvent("MouseEvents");f.initMouseEvent(h,true,true,window,1,i.screenX,i.screenY,i.clientX,i.clientY,false,false,false,false,0,null);g.target.dispatchEvent(f);}c._touchStart=function(g){var f=this;if(a||!f._mouseCapture(g.originalEvent.changedTouches[0])){return;}a=true;f._touchMoved=false;d(g,"mouseover");d(g,"mousemove");d(g,"mousedown");};c._touchMove=function(f){if(!a){return;}this._touchMoved=true;d(f,"mousemove");};c._touchEnd=function(f){if(!a){return;}d(f,"mouseup");d(f,"mouseout");if(!this._touchMoved){d(f,"click");}a=false;};c._mouseInit=function(){var f=this;f.element.bind("touchstart",b.proxy(f,"_touchStart")).bind("touchmove",b.proxy(f,"_touchMove")).bind("touchend",b.proxy(f,"_touchEnd"));e.call(f);};})(jQuery); -------------------------------------------------------------------------------- /lib/polychart2.js: -------------------------------------------------------------------------------- 1 | polychart2/polychart2.js -------------------------------------------------------------------------------- /lib/polychart2.standalone.js: -------------------------------------------------------------------------------- 1 | polychart2/polychart2.standalone.js -------------------------------------------------------------------------------- /lib/raphael.js: -------------------------------------------------------------------------------- 1 | polychart2/lib/raphael.js -------------------------------------------------------------------------------- /lib/sourcesanspro/OTF/SourceSansPro-Black.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/OTF/SourceSansPro-Black.otf -------------------------------------------------------------------------------- /lib/sourcesanspro/OTF/SourceSansPro-BlackIt.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/OTF/SourceSansPro-BlackIt.otf -------------------------------------------------------------------------------- /lib/sourcesanspro/OTF/SourceSansPro-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/OTF/SourceSansPro-Bold.otf -------------------------------------------------------------------------------- /lib/sourcesanspro/OTF/SourceSansPro-BoldIt.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/OTF/SourceSansPro-BoldIt.otf -------------------------------------------------------------------------------- /lib/sourcesanspro/OTF/SourceSansPro-ExtraLight.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/OTF/SourceSansPro-ExtraLight.otf -------------------------------------------------------------------------------- /lib/sourcesanspro/OTF/SourceSansPro-ExtraLightIt.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/OTF/SourceSansPro-ExtraLightIt.otf -------------------------------------------------------------------------------- /lib/sourcesanspro/OTF/SourceSansPro-It.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/OTF/SourceSansPro-It.otf -------------------------------------------------------------------------------- /lib/sourcesanspro/OTF/SourceSansPro-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/OTF/SourceSansPro-Light.otf -------------------------------------------------------------------------------- /lib/sourcesanspro/OTF/SourceSansPro-LightIt.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/OTF/SourceSansPro-LightIt.otf -------------------------------------------------------------------------------- /lib/sourcesanspro/OTF/SourceSansPro-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/OTF/SourceSansPro-Regular.otf -------------------------------------------------------------------------------- /lib/sourcesanspro/OTF/SourceSansPro-Semibold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/OTF/SourceSansPro-Semibold.otf -------------------------------------------------------------------------------- /lib/sourcesanspro/OTF/SourceSansPro-SemiboldIt.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/OTF/SourceSansPro-SemiboldIt.otf -------------------------------------------------------------------------------- /lib/sourcesanspro/TTF/SourceSansPro-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/TTF/SourceSansPro-Black.ttf -------------------------------------------------------------------------------- /lib/sourcesanspro/TTF/SourceSansPro-BlackIt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/TTF/SourceSansPro-BlackIt.ttf -------------------------------------------------------------------------------- /lib/sourcesanspro/TTF/SourceSansPro-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/TTF/SourceSansPro-Bold.ttf -------------------------------------------------------------------------------- /lib/sourcesanspro/TTF/SourceSansPro-BoldIt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/TTF/SourceSansPro-BoldIt.ttf -------------------------------------------------------------------------------- /lib/sourcesanspro/TTF/SourceSansPro-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/TTF/SourceSansPro-ExtraLight.ttf -------------------------------------------------------------------------------- /lib/sourcesanspro/TTF/SourceSansPro-ExtraLightIt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/TTF/SourceSansPro-ExtraLightIt.ttf -------------------------------------------------------------------------------- /lib/sourcesanspro/TTF/SourceSansPro-It.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/TTF/SourceSansPro-It.ttf -------------------------------------------------------------------------------- /lib/sourcesanspro/TTF/SourceSansPro-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/TTF/SourceSansPro-Light.ttf -------------------------------------------------------------------------------- /lib/sourcesanspro/TTF/SourceSansPro-LightIt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/TTF/SourceSansPro-LightIt.ttf -------------------------------------------------------------------------------- /lib/sourcesanspro/TTF/SourceSansPro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/TTF/SourceSansPro-Regular.ttf -------------------------------------------------------------------------------- /lib/sourcesanspro/TTF/SourceSansPro-Semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/TTF/SourceSansPro-Semibold.ttf -------------------------------------------------------------------------------- /lib/sourcesanspro/TTF/SourceSansPro-SemiboldIt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/lib/sourcesanspro/TTF/SourceSansPro-SemiboldIt.ttf -------------------------------------------------------------------------------- /main/bg_arrow_left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /main/dashboard-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/main/dashboard-preview.png -------------------------------------------------------------------------------- /main/drag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/main/drag.png -------------------------------------------------------------------------------- /main/icon_arrow_left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /main/icon_arrow_right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /main/icon_back_white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /main/icon_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/main/icon_close.png -------------------------------------------------------------------------------- /main/icon_delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 11 | 13 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /main/icon_delete_white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 10 | 12 | 14 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /main/icon_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/main/icon_edit.png -------------------------------------------------------------------------------- /main/icon_export_white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 12 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /main/icon_plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/main/icon_plus.png -------------------------------------------------------------------------------- /main/icon_type_cat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /main/icon_type_date.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /main/icon_type_green_cat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /main/icon_type_green_date.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /main/icon_type_green_num.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 11 | 13 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /main/icon_type_num.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 11 | 13 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /main/icon_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/main/icon_view.png -------------------------------------------------------------------------------- /main/nux_arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /main/nux_arrow2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /main/nux_arrow_right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /makeTools/runServerTask.coffee: -------------------------------------------------------------------------------- 1 | {spawn, exec} = require('child_process') 2 | 3 | module.exports.register = (grunt) -> 4 | grunt.registerTask 'runServer', "Run local Django development server.", () -> 5 | done = @async() 6 | exec "./manage.py syncdb --migrate -v 0", (err) -> 7 | if err? 8 | done(false) 9 | console.log err 10 | throw err 11 | server = spawn("./manage.py", ["runserver", "--nothreading"]) 12 | done(true) 13 | 14 | server.stdout.on 'data', (data) -> 15 | grunt.log.writeln data 16 | server.stderr.on 'data', (data) -> 17 | grunt.log.writeln "[Log]: #{data}" 18 | server.on 'close', (code) -> 19 | grunt.log.writeln "Server exited with code #{code}." 20 | -------------------------------------------------------------------------------- /makeTools/stitchTask.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | path = require 'path' 3 | stitch = require 'stitch' 4 | 5 | # Creates a directory and all missing parent directories 6 | createDirectory = (dirPath) -> 7 | if fs.statSync(path.dirname(dirPath)).isDirectory() 8 | return 9 | 10 | parentDirPath = path.dirname path 11 | if parentDirPath isnt dirPath 12 | createDirectory parentDirPath 13 | 14 | fs.mkdirSync dirPath 15 | 16 | module.exports.register = (grunt) -> 17 | grunt.registerMultiTask 'stitch', "Build and stitch files together", () -> 18 | done = @async() 19 | 20 | config = @files[0] 21 | pkg = stitch.createPackage(paths: ["#{grunt.config('tmpDir')}#{@target}_pkg"]) 22 | pkg.compile (err, src) -> 23 | if err 24 | done(false) 25 | console.log err 26 | throw err 27 | 28 | try 29 | createDirectory path.dirname config.dest 30 | fs.writeFile config.dest, src, (err) -> 31 | if err? then throw err 32 | done(true) 33 | catch err 34 | console.error err 35 | done(false) 36 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Convenient wrapper around django-admin.py. 5 | 6 | Example usage: ./manage.py syncdb 7 | """ 8 | 9 | import errno 10 | import os 11 | import sys 12 | from traceback import print_exc 13 | 14 | def _mkdir(path): 15 | try: 16 | os.makedirs(path) 17 | except OSError as err: 18 | # If already exists as directory 19 | if err.errno == errno.EEXIST and os.path.isdir(path): 20 | return 21 | 22 | raise 23 | 24 | if __name__ == "__main__": 25 | # Create directory required by logger 26 | _mkdir('log/app') 27 | 28 | # Set up environment properly 29 | sys.path = ['server'] + sys.path 30 | 31 | # Detect deployment vs local 32 | if os.path.isfile('server/polychart/config/deployParams.py'): 33 | os.environ["DJANGO_SETTINGS_MODULE"] = "polychart.config.deploy" 34 | else: 35 | os.environ["DJANGO_SETTINGS_MODULE"] = "polychart.config.local" 36 | 37 | # Attempt to import Django 38 | try: 39 | from django.core.management import execute_from_command_line 40 | except ImportError: 41 | print_exc() 42 | print 43 | print 'Try rerunning this command like this:' 44 | print ' virtualenv/bin/python2 manage.py' 45 | sys.exit(1) 46 | 47 | # Pass command-line args to Django 48 | execute_from_command_line(sys.argv) 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "polychart-dashboardbuilder", 3 | "version": "1.0.0", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/Polychart/dbb.git" 7 | }, 8 | "dependencies": { 9 | "faye-websocket": ">= 0.6.1", 10 | "jsdom": ">= 0.8.1", 11 | "underscore": ">= 1.5.1" 12 | }, 13 | "devDependencies": { 14 | "coffee-script": "~1.6.3", 15 | "grunt": "~0.4.1", 16 | "grunt-cli": "~0.1.9", 17 | "grunt-contrib-less": "~0.7.0", 18 | "grunt-contrib-copy": "~0.4.1", 19 | "grunt-contrib-watch": "~0.5.1", 20 | "grunt-contrib-concat": "~0.3.0", 21 | "grunt-contrib-clean": "~0.5.0", 22 | "stitch": "~0.3.3", 23 | "grunt-contrib-uglify": "~0.2.2", 24 | "grunt-contrib-cssmin": "~0.6.1", 25 | "grunt-text-replace": "~0.3.7" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packageData/README: -------------------------------------------------------------------------------- 1 | Example data for use by frontend only examples. 2 | -------------------------------------------------------------------------------- /packageData/state.json: -------------------------------------------------------------------------------- 1 | {"layers":[{"additionalInfo":{"joins":{"tables":["TEST"],"joins":[]}},"type":"bar","filter":{},"x":{"var":"[TEST.Category]","name":"Category","tableName":"TEST","bin":null,"stats":"None"},"meta":{"[TEST.Category]":{"type":"cat","tableName":"TEST"},"sum([TEST.Number\\[%\\]])":{"type":"num","tableName":"TEST"},"bin([TEST.Another Number long name very long!\\]\\[_+~],1)":{"type":"num","tableName":"TEST"}},"y":{"var":"sum([TEST.Number\\[%\\]])","name":"Number[%]","tableName":"TEST","bin":1,"stats":"Sum"},"color":{"var":"bin([TEST.Another Number long name very long!\\]\\[_+~],1)","name":"Another Number long name very long!][_+~","tableName":"TEST","bin":1,"stats":"None"}}],"coord":{"flip":false,"type":"cartesian"},"guides":{"x":{},"y":{}},"facet":{},"width":731,"height":535} 2 | -------------------------------------------------------------------------------- /packageData/test.csv: -------------------------------------------------------------------------------- 1 | Category,Number[%],Another Number long name very long!][_+~ 2 | A,2,3 3 | B,3,4 4 | C,1,5 5 | D,1,2 6 | -------------------------------------------------------------------------------- /packageExamples/README: -------------------------------------------------------------------------------- 1 | This folder contain examples for frontend-only uses of Polychart. 2 | 3 | -------------------------------------------------------------------------------- /packageExamples/custom_backend_example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Dashboard Builder Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /packageExamples/tutorial.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /poly/README.md: -------------------------------------------------------------------------------- 1 | This directory contains the frontend source files for the Polychart Dashboard 2 | Builder. The templates and Coffeescript files within this directory implement the 3 | `home` view for dashboards. 4 | 5 | `main` 6 | ------ 7 | This directory contains the main source code for the Dashboard builder. Look 8 | within for more information. 9 | 10 | `common` 11 | -------- 12 | Here, internal libraries and shared styles are contained. The `events.coffee` 13 | file implements the internal event bus for Polychart; the `serverApi.coffee` file 14 | defines a uniform internal method for communicating with a remote server; all 15 | other files are style files that are used in this application and other internal 16 | applications. 17 | 18 | `demoData` 19 | ---------- 20 | Data for the Dashboard Builder Demo is contained. 21 | 22 | `examples` 23 | ---------- 24 | This directory contains example source files interacting with the dashboard 25 | builder. 26 | -------------------------------------------------------------------------------- /poly/main/anim.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | # Define a basic animations library; useful for loading and transitions. 3 | ### 4 | ANIMATIONS = 5 | loading: 6 | interval: 200, 7 | frames: [ 8 | 'anim_loading_0.svg', 9 | 'anim_loading_1.svg', 10 | 'anim_loading_2.svg' 11 | ] 12 | 13 | class Animation 14 | constructor: (animName, container) -> 15 | anim = ANIMATIONS[animName] 16 | if !anim 17 | throw "Animation " + animName + " does not exist!" 18 | 19 | @div = div = $("
") 20 | div.addClass("anim") 21 | div.addClass(animName) 22 | $(container).append(div) 23 | 24 | _.defer () -> 25 | div.css 26 | width: div.height() 27 | marginLeft: -div.height()/2 28 | marginTop: -div.height()/2 29 | 30 | images = _.map anim.frames, (src) => 31 | img = new Image() 32 | img.src = '/static/main/images/' + src 33 | img 34 | 35 | curFrame = 0 36 | advFrame = () -> 37 | div.css 'background-image', 'url(' + images[curFrame].src + ')' 38 | curFrame = (curFrame + 1) % images.length 39 | @interval = setInterval advFrame, anim.interval 40 | 41 | remove: () => 42 | @div.remove() 43 | clearInterval @interval 44 | 45 | stopOnImage: (imgSrc) => 46 | @div.css 'background-image', 'url(' + imgSrc + ')' 47 | clearInterval @interval 48 | 49 | module.exports = Animation 50 | -------------------------------------------------------------------------------- /poly/main/chart/advanced.tmpl: -------------------------------------------------------------------------------- 1 | 8 | 9 | 27 | 28 | 37 | -------------------------------------------------------------------------------- /poly/main/chart/aes/base.tmpl: -------------------------------------------------------------------------------- 1 | 21 | 22 | -------------------------------------------------------------------------------- /poly/main/chart/aes/color.coffee: -------------------------------------------------------------------------------- 1 | Aesthetic = require('poly/main/chart/aes/base') 2 | 3 | class ColorAesthetic extends Aesthetic 4 | template: 'tmpl-aesthetic-color' 5 | constructor: (@aes, @name, @parent) -> 6 | super(@aes, @name, @parent) 7 | @selected = 'steelblue' 8 | @defaultValue = 'steelblue' 9 | @value = ko.observable(@defaultValue) 10 | @value.subscribe @render 11 | onMetricDiscard: (event, metricItem) => 12 | @metric(null) 13 | @render() 14 | @afterRender(@dom) 15 | afterRender: (dom) => 16 | @dom = dom 17 | simpleColor = $('.selector', dom) 18 | simpleColor.attr 'value', @value() 19 | simpleColor.simpleColor( 20 | cellWidth: 15 21 | cellHeight: 15 22 | boxWidth: 50 23 | boxHeight: 15 24 | border: 0 25 | columns: 9 26 | ) 27 | simpleColor.bind 'change', (evt) => 28 | @value evt.target.value 29 | $(".simpleColorContainer", dom).click () => false 30 | _setConstant: (spec) => 31 | if spec.const 32 | @value(spec.const) 33 | _getConstant: () => 34 | const: @value() 35 | 36 | module.exports = ColorAesthetic 37 | -------------------------------------------------------------------------------- /poly/main/chart/aes/color.tmpl: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /poly/main/chart/aes/size.coffee: -------------------------------------------------------------------------------- 1 | Aesthetic = require('poly/main/chart/aes/base') 2 | 3 | class SizeAesthetic extends Aesthetic 4 | template: 'tmpl-aesthetic-size' 5 | constructor: (@aes, @name, @parent) -> 6 | super(@aes, @name, @parent) 7 | @selected = 1 8 | @defaultValue = 2 9 | @value = ko.observable(@defaultValue) 10 | @value.subscribe @render 11 | onMetricDiscard: (event, metricItem) => 12 | @metric(null) 13 | @render() 14 | @afterRender(@dom) 15 | afterRender: (dom) => 16 | @dom = dom 17 | slider = $('.selector', dom) 18 | slider.slider( 19 | max: 10 20 | min: 1 21 | step: 1 22 | value: @value() 23 | ) 24 | slider.bind 'slidechange', (evt, ui) => 25 | @value(ui.value) 26 | _setConstant: (spec) => 27 | if spec.const 28 | @value(spec.const) 29 | _getConstant: () => 30 | const: @value() 31 | 32 | module.exports = SizeAesthetic 33 | -------------------------------------------------------------------------------- /poly/main/chart/aes/size.tmpl: -------------------------------------------------------------------------------- 1 | 25 | -------------------------------------------------------------------------------- /poly/main/chart/chartbuilder.tmpl: -------------------------------------------------------------------------------- 1 | 39 | 40 | 43 | -------------------------------------------------------------------------------- /poly/main/chart/layer.tmpl: -------------------------------------------------------------------------------- 1 | 36 | -------------------------------------------------------------------------------- /poly/main/dash/aes.tmpl: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /poly/main/dash/dash.tmpl: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /poly/main/dash/dashboard.coffee: -------------------------------------------------------------------------------- 1 | QuickAddView = require('poly/main/dash/quickadd') 2 | WorkspaceView = require('poly/main/dash/workspace') 3 | 4 | class DashboardView 5 | constructor: (title, tableMetaData) -> 6 | @workspaceView = new WorkspaceView(title, tableMetaData) 7 | @quickaddView = new QuickAddView(tableMetaData) 8 | 9 | serialize: () => 10 | @workspaceView.serialize() 11 | 12 | initialize: (initial) => @workspaceView.initialize(initial) 13 | 14 | module.exports = DashboardView 15 | -------------------------------------------------------------------------------- /poly/main/dash/item/chart.tmpl: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /poly/main/dash/item/comment.coffee: -------------------------------------------------------------------------------- 1 | TextItem = require('poly/main/dash/item/text') 2 | 3 | CONST = require('poly/main/const') 4 | 5 | class CommentItem extends TextItem 6 | constructor: (@author, value, position={}) -> 7 | @author or= PAGE_VARIABLE?.USERNAME ? '' 8 | position.width or= 5 9 | position.height or= 7 10 | 11 | @minWidth = 5 12 | @minHeight = 5 13 | 14 | @templateName = 'tmpl-comment-item' unless @templateName 15 | super(value, position, 'Write a comment here...') 16 | 17 | @shiftedZIndex = ko.computed => 18 | 1000000 + @zIndex() 19 | 20 | serialize: (s={}) => 21 | s.author = @author 22 | s.itemType = "CommentItem" 23 | super s 24 | 25 | deserialize: (s) => 26 | @author = s.author if s.author 27 | super(s) 28 | 29 | module.exports = CommentItem 30 | -------------------------------------------------------------------------------- /poly/main/dash/item/comment.tmpl: -------------------------------------------------------------------------------- 1 | 28 | -------------------------------------------------------------------------------- /poly/main/dash/item/numeral.tmpl: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /poly/main/dash/item/pivottable.tmpl: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /poly/main/dash/item/polyjs.coffee: -------------------------------------------------------------------------------- 1 | Animation = require('poly/main/anim') 2 | DashItem = require('poly/main/dash/item/base') 3 | Events = require('poly/main/events') 4 | 5 | CONST = require('poly/main/const') 6 | TOAST = require('poly/main/error/toast') 7 | 8 | PADDING = 10 # extra padding for chart view outside of chart 9 | 10 | class PolyJSItem extends DashItem 11 | constructor: (@spec=null, position) -> 12 | @redraw = _.debounce(@_redraw, 300, true) 13 | @templateName = 'tmpl-chart-item' unless @templateName 14 | super(position) 15 | 16 | init: (dom) => 17 | super(dom) 18 | @itemdom = $('.chart-inner, .inner', dom) 19 | @spec.dom = @itemdom[0] 20 | @_initSpec() 21 | Events.error.polyjs.data.on (event) => 22 | @loadingAnim.stopOnImage("/static/main/images/broken_chart.svg") 23 | @onResize() 24 | 25 | _initSpec: () => Error("Not implemented") 26 | 27 | onResize: => 28 | if @itemdom then @redraw() 29 | 30 | _redraw: () => 31 | if !@itemdom 32 | # init() has to be called first so that @spec has a DOM element 33 | throw "Can't make chart before init() is called!" 34 | 35 | @spec.width = @itemdom.width() - PADDING 36 | @spec.height = @itemdom.height() - PADDING 37 | prepare = 38 | if @isViewer() 39 | -> 40 | else 41 | () => @itemdom.empty() 42 | @_renderPolyJSItem(@spec, @loaded, prepare) 43 | 44 | setSpec: (@spec, isDeserializing=false) => 45 | unless isDeserializing 46 | Events.model.dashboarditem.update.trigger() 47 | 48 | deserialize: (s) => 49 | @setSpec(s.spec, true) 50 | super(s) 51 | 52 | module.exports = PolyJSItem 53 | -------------------------------------------------------------------------------- /poly/main/dash/item/text.coffee: -------------------------------------------------------------------------------- 1 | DashItem = require('poly/main/dash/item/base') 2 | Events = require('poly/main/events') 3 | 4 | CONST = require('poly/main/const') 5 | 6 | class TextItem extends DashItem 7 | constructor: (@textContent, position={}, @defaultText='Type here...') -> 8 | unless ko.isObservable(@textContent) 9 | @textContent = ko.observable @textContent 10 | 11 | @templateName = 'tmpl-text-item' unless @templateName 12 | super(position) 13 | 14 | init: (@dom) => 15 | super(dom) 16 | @loaded() 17 | 18 | onEditAreaBlur: -> 19 | Events.model.dashboarditem.update.trigger() 20 | 21 | return true 22 | 23 | serialize: (s={}) => 24 | s.itemType = s.itemType ? 'TextItem' 25 | s.textContent = @textContent() 26 | super s 27 | 28 | deserialize: (s) => 29 | @textContent(s.textContent) if s.textContent 30 | super(s) 31 | 32 | module.exports = TextItem 33 | -------------------------------------------------------------------------------- /poly/main/dash/item/text.tmpl: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /poly/main/dash/workspace.tmpl: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /poly/main/data/data.tmpl: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /poly/main/data/metric/base.coffee: -------------------------------------------------------------------------------- 1 | class MetricView 2 | constructor: (@columnInfo) -> 3 | {@name, @tableName, @gaType} = @columnInfo 4 | @type = @columnInfo.meta.type 5 | @gaType = @columnInfo.gaType 6 | @originalFormula = @columnInfo.formula 7 | @formula = 8 | if @originalFormula? 9 | @originalFormula 10 | else if @name == 'count(*)' 11 | @name 12 | else 13 | "[#{@tableName}.#{@name}]" 14 | @extraCSS = 15 | if @columnInfo.formula 16 | 'derived-var' 17 | else 18 | @gaType 19 | 20 | fullFormula: (tableMetaData, unbracket=false) => 21 | @columnInfo.getFormula(tableMetaData, unbracket) 22 | 23 | fullMeta: () -> @columnInfo.meta 24 | 25 | module.exports = MetricView 26 | -------------------------------------------------------------------------------- /poly/main/data/metric/base.tmpl: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | -------------------------------------------------------------------------------- /poly/main/data/metric/dropdown.coffee: -------------------------------------------------------------------------------- 1 | Events = require('poly/main/events') 2 | MetricView = require('poly/main/data/metric/base') 3 | 4 | CONST = require('poly/main/const') 5 | 6 | class DropdownMetricView extends MetricView 7 | constructor: (columnInfo, @label, @dropdownTemplate, @dropdownData, @dropdownAfterRender) -> 8 | @toggleDropdown = _.throttle @_toggleDropdown # ..or clicks gets registerd 2x 9 | @dropdownShowing = false 10 | super(columnInfo) 11 | 12 | _toggleDropdown: => 13 | if @name isnt 'count(*)' 14 | if @dropdownShowing 15 | Events.ui.dropdown.hide.trigger() 16 | else 17 | Events.ui.dropdown.show.trigger { 18 | templateName: @dropdownTemplate 19 | data: @dropdownData 20 | targetDom: @dom 21 | onRemove: @setInactive 22 | afterRender: @dropdownAfterRender 23 | info: {name: @label, value: @name} 24 | } 25 | 26 | setInactive: => 27 | @dropdownShowing = false 28 | if @dom 29 | @dom.removeClass 'dropdown-active' 30 | @dom.draggable 'enable' 31 | 32 | close: => 33 | Events.ui.dropdown.hide.trigger() 34 | 35 | attachDropdown: (dom) => 36 | @dom = $(dom) 37 | Events.ui.dropdown.shown.onElem @dom, => 38 | @dropdownShowing = true 39 | @dom.addClass 'dropdown-active' 40 | @dom.draggable 'disable' 41 | Events.ui.dropdown.hidden.onElem @dom, @setInactive 42 | 43 | module.exports = DropdownMetricView 44 | -------------------------------------------------------------------------------- /poly/main/data/metric/dropdown.tmpl: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /poly/main/data/metric/facet.coffee: -------------------------------------------------------------------------------- 1 | AttachedMetricView = require('poly/main/data/metric/attached') 2 | Events = require('poly/main/events') 3 | 4 | CONST = require('poly/main/const') 5 | 6 | class FacetMetricView extends AttachedMetricView 7 | constructor: (columnInfo) -> 8 | updateFn = () => Events.ui.chart.render.trigger() 9 | options = () -> CONST.facets 10 | attachedMetrics = () -> [] 11 | @removeText = "Remove \"" + columnInfo.name + "\" from facet" 12 | super columnInfo, @aes, updateFn, options, attachedMetrics 13 | 14 | module.exports = FacetMetricView 15 | -------------------------------------------------------------------------------- /poly/main/data/metric/layer.coffee: -------------------------------------------------------------------------------- 1 | AttachedMetricView = require('poly/main/data/metric/attached') 2 | Events = require('poly/main/events') 3 | 4 | CONST = require('poly/main/const') 5 | 6 | class LayerMetricView extends AttachedMetricView 7 | constructor: (columnInfo, @options, @layer, @aesName, defaults) -> 8 | updateFn = () => Events.ui.chart.render.trigger() 9 | attachedMetrics = @layer.attachedMetrics 10 | super columnInfo, @aesName, updateFn, @options, attachedMetrics, defaults 11 | 12 | module.exports = LayerMetricView 13 | -------------------------------------------------------------------------------- /poly/main/data/metric/quickadd.coffee: -------------------------------------------------------------------------------- 1 | AttachedMetricView = require('poly/main/data/metric/attached') 2 | 3 | class QuickAddMetricView extends AttachedMetricView 4 | constructor: (columnInfo, @options, @aes) -> 5 | updateFn = () -> 6 | attachedMetrics = () -> [] 7 | @removeText = "Remove \"" + columnInfo.name + "\" from " + @aes 8 | super columnInfo, @aes, updateFn, @options, attachedMetrics 9 | 10 | module.exports = QuickAddMetricView 11 | -------------------------------------------------------------------------------- /poly/main/data/metric/quickaddtable.coffee: -------------------------------------------------------------------------------- 1 | AttachedMetricView = require('poly/main/data/metric/attached') 2 | 3 | class QuickAddTableMetricView extends AttachedMetricView 4 | constructor: (columnInfo, @options, @aes, attachedMetrics) -> 5 | updateFn = () -> 6 | @removeText = "Remove \"" + columnInfo.name + "\" from " + @aes 7 | super columnInfo, @aes, updateFn, @options, attachedMetrics 8 | 9 | module.exports = QuickAddTableMetricView 10 | -------------------------------------------------------------------------------- /poly/main/data/table.tmpl: -------------------------------------------------------------------------------- 1 | 21 | 22 | -------------------------------------------------------------------------------- /poly/main/error/toast.coffee: -------------------------------------------------------------------------------- 1 | raise = (text) -> 2 | $().toastmessage('showErrorToast', text) 3 | 4 | module.exports = { 5 | raise 6 | } 7 | -------------------------------------------------------------------------------- /poly/main/header.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 35 | -------------------------------------------------------------------------------- /poly/main/init.coffee: -------------------------------------------------------------------------------- 1 | # Setup necessary prior to Polychart being used 2 | 3 | # load all the knockout templates 4 | require('poly/main/templates') 5 | 6 | # initiate custom KO bindings 7 | require('poly/main/bindings').init() 8 | -------------------------------------------------------------------------------- /poly/main/main/builder.coffee: -------------------------------------------------------------------------------- 1 | # Top level view entry point - abstract classes 2 | # Code that is shared for both chart and dashboard builder 3 | 4 | Events = require('poly/main/events') 5 | DataView = require('poly/main/data/dataView') 6 | HeaderView = require('poly/main/header') 7 | OverlayView = require('poly/main/overlay') 8 | ShareView = require('poly/main/share') 9 | 10 | class AbstractBuilderEntryPoint 11 | constructor: (@params) -> 12 | Events.registerDefaultListeners() 13 | 14 | {@dom, @dataCollection, @exportingEnabled} = params 15 | 16 | # TODO Support multiple data sources on client 17 | if @dataCollection 18 | unless _.isArray(@dataCollection) 19 | @dataCollection = [@dataCollection] 20 | @dataSource = poly.data @dataCollection[0] 21 | @dataView = new DataView(@dataSource) 22 | @overlayView = new OverlayView() 23 | else 24 | throw new Error('No data collection provided!') 25 | 26 | @hasHeader = params.header ? false 27 | @headerView = new HeaderView(params.isDemo) 28 | @shareView = new ShareView() 29 | 30 | if params.width is 'fill' 31 | $(@dom).width('100%') 32 | if _.isNumber(params.width) 33 | $(@dom).width(params.width) 34 | if params.height is 'fill' 35 | $(@dom).height('100%') 36 | if _.isNumber(params.height) 37 | $(@dom).height(params.height) 38 | if params.width is 'fill' and params.height is 'fill' 39 | $(@dom).addClass('fill') 40 | 41 | initialize: () => 42 | @dataView.initialize() 43 | 44 | module.exports = AbstractBuilderEntryPoint 45 | -------------------------------------------------------------------------------- /poly/main/main/chartbuilder.tmpl: -------------------------------------------------------------------------------- 1 | 28 | -------------------------------------------------------------------------------- /poly/main/main/chartviewer.coffee: -------------------------------------------------------------------------------- 1 | # Top level view for viewing a particular chart 2 | AbstractViewerEntryPoint = require('poly/main/main/viewer') 3 | 4 | class ChartViewerMainView extends AbstractViewerEntryPoint 5 | constructor: (@params) -> 6 | super(@params) 7 | @spec = params.initial ? {} 8 | @tableMetaData = @dataView.getTableMetaData() 9 | 10 | init: (dom) => 11 | initialCols = @spec.newcols ? [] 12 | @dataView.initialize initialCols, () => 13 | # instead of using spec.layer, use spec.layers 14 | if @spec.layer 15 | @spec.layers = [@spec.layer] 16 | delete @spec.layer 17 | # generate the polyJS object for each layer (given tableName) 18 | for layer in @spec.layers 19 | if not layer.data 20 | tableName = layer.tableName 21 | if not tableName? and layer.meta and m = _.toArray(layer.meta)[0] 22 | tableName = m.tableName 23 | layer.data = @tableMetaData.polyJsObjectFor {tableName} 24 | @spec.width ?= @params.width 25 | @spec.height ?= @params.height 26 | @spec.dom = dom[0] 27 | polyjs.chart(@spec) 28 | 29 | 30 | module.exports = ChartViewerMainView 31 | -------------------------------------------------------------------------------- /poly/main/main/chartviewer.tmpl: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /poly/main/main/dashviewer.coffee: -------------------------------------------------------------------------------- 1 | # Top level view for viewing a particular chart 2 | AbstractViewerEntryPoint = require('poly/main/main/viewer') 3 | WorkspaceView = require('poly/main/dash/workspace') 4 | 5 | class DashViewerMainView extends AbstractViewerEntryPoint 6 | constructor: (@params) -> 7 | super(@params) 8 | 9 | tableMetaData = @dataView.getTableMetaData() 10 | @workspaceView = new WorkspaceView(@params.name, tableMetaData, true) 11 | @initialize() 12 | 13 | initialize: () -> 14 | initial = @params.initial ? [] 15 | if _.isArray(@params.initial) 16 | initialItems = @params.initial 17 | initialCols = [] 18 | else if _.isObject(@params.initial) 19 | initialItems = @params.initial.items ? [] 20 | initialCols = @params.initial.newcols ? [] 21 | 22 | @dataView.initialize initialCols, () => @workspaceView.initialize(initialItems) 23 | 24 | module.exports = DashViewerMainView 25 | -------------------------------------------------------------------------------- /poly/main/main/dashviewer.tmpl: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /poly/main/main/viewer.coffee: -------------------------------------------------------------------------------- 1 | # Top level view entry point - abstract classes 2 | # Code that is shared for both chart and dashboard Viewer 3 | 4 | DataView = require('poly/main/data/dataView') 5 | Events = require('poly/main/events') 6 | 7 | class AbstractViewerEntryPoint 8 | constructor: (@params) -> 9 | Events.registerDefaultListeners() 10 | 11 | {@dom, @dataCollection} = params 12 | 13 | # TODO Support multiple data sources on client 14 | if @dataCollection 15 | unless _.isArray(@dataCollection) 16 | @dataCollection = [@dataCollection] 17 | # shared "views" 18 | @dataSource = poly.data @dataCollection[0] 19 | else 20 | throw new Error('No data collection provided!') 21 | 22 | @dataView = new DataView(@dataSource) 23 | 24 | initialize: () => 25 | @dataView.initialize() 26 | 27 | module.exports = AbstractViewerEntryPoint 28 | -------------------------------------------------------------------------------- /poly/main/numeral/numeralbuilder.tmpl: -------------------------------------------------------------------------------- 1 | 25 | 26 | 29 | -------------------------------------------------------------------------------- /poly/main/share.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | # Hook up events for the share panel. 3 | ### 4 | CONST = require('poly/main/const') 5 | Events = require('poly/main/events') 6 | serverApi = require('poly/common/serverApi') 7 | 8 | class ShareView 9 | constructor: () -> 10 | @defaultPanelRight = -250 11 | @panelRight = ko.observable(@defaultPanelRight) 12 | Events.nav.sharepanel.open.on @open 13 | Events.nav.sharepanel.close.on @close 14 | 15 | open: () => 16 | @panelRight(0) 17 | 18 | close: () => 19 | @panelRight(@defaultPanelRight) 20 | 21 | exportPDF: -> 22 | Events.export.pdf.click.trigger callback: @_export('pdf') 23 | 24 | exportPNG: -> 25 | Events.export.png.click.trigger callback: @_export('png') 26 | 27 | exportSVG: -> 28 | Events.export.svg.click.trigger callback: @_export('svg') 29 | 30 | _export: (type) -> (serial) -> 31 | serverApi.sendPost( 32 | "/dashboard/export/code" 33 | , {serial: serial, exportType: type} 34 | , (err, result) -> 35 | if err 36 | console.error err 37 | TOAST.raise 'Error exporting dashboard.' 38 | return 39 | 40 | window.location = "/api/dashboard/export/" + encodeURIComponent(result.code) 41 | ) 42 | 43 | module.exports = ShareView 44 | -------------------------------------------------------------------------------- /poly/main/share.tmpl: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /poly/main/table/aesGroup.tmpl: -------------------------------------------------------------------------------- 1 | 33 | 34 | -------------------------------------------------------------------------------- /poly/main/table/table.less: -------------------------------------------------------------------------------- 1 | .tablebuilder-table { 2 | height: 100%; 3 | overflow: scroll; 4 | padding: 2px; 5 | 6 | text-align: center; 7 | background-color: @white; 8 | } 9 | -------------------------------------------------------------------------------- /poly/main/table/tablebuilder.tmpl: -------------------------------------------------------------------------------- 1 | 32 | 33 | 36 | -------------------------------------------------------------------------------- /poly/signup.coffee: -------------------------------------------------------------------------------- 1 | Events = require('poly/main/events') 2 | 3 | init = () -> 4 | Events.signup.page.view.trigger() 5 | eulaClickTrap '#start', '#realsubmit', '#eula', Events.signup.eula.error 6 | Events.signup.form.submit.trackForm $('#realsubmit') 7 | $('input').on 'keypress keydown', _.once -> 8 | Events.signup.form.interact.trigger() 9 | 10 | eulaClickTrap = (submitBtn, hiddenBtn, eula, errorEvt) -> 11 | $(submitBtn).on 'click', (e) -> 12 | e.stopPropagation() 13 | if $(eula).prop 'checked' 14 | $(hiddenBtn).click() 15 | else 16 | if errorEvt then errorEvt.trigger() 17 | alert 'You must first read and accept the Terms and Conditions.' 18 | 19 | module.exports = { 20 | init 21 | } 22 | -------------------------------------------------------------------------------- /poly/verifyPendingDs.coffee: -------------------------------------------------------------------------------- 1 | serverApi = require('poly/common/serverApi') 2 | 3 | load = (dsParams) -> 4 | serverApi.sendPost '/data-source/create', dsParams, (err, response) -> 5 | if err 6 | console.error err 7 | else 8 | window.location.href = '/home' 9 | 10 | module.exports = {run} 11 | -------------------------------------------------------------------------------- /polychart/README.md: -------------------------------------------------------------------------------- 1 | Polychart Dashboard Builder Backend 2 | =================================== 3 | Herein lies the backend code for the Polychart Dashboard Builder. The server is 4 | written in Python and is built atop of the [Django] (https://www.djangoproject.com/) 5 | web framework. The primary directories here are `config/` and `main/`; the 6 | former contains Django-related configuration files that provide settings to the 7 | Django environment, with the possibility of differing setups in a local 8 | development environment and a production environment; the latter directory 9 | contains the actual logic and code for Dashboard Builder related processing. For 10 | a detailed description of the contents of each directory, see the documents 11 | contained in the individual directories. 12 | 13 | Within this directory lie three main files. 14 | 15 | First, `urls.py` determines the collection of URL patterns to be given to the 16 | application. This file is written in such a way that it may collect the URL 17 | patterns for a variety of applications, add to it some other predetermined 18 | patterns, and package them up for the Django application as a whole. 19 | 20 | The other two files, `utils.py` and `wsgi.py` are rather short and 21 | self-explanatory: the first defines a collection of Django-related utility 22 | functions that are oft used in View Handler functions and the latter defines the 23 | [WSGI] (http://en.wikipedia.org/wiki/Web_Server_Gateway_Interface) application 24 | settings for the Django environment. 25 | -------------------------------------------------------------------------------- /polychart/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/polychart/__init__.py -------------------------------------------------------------------------------- /polychart/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/polychart/config/__init__.py -------------------------------------------------------------------------------- /polychart/config/deploy.py: -------------------------------------------------------------------------------- 1 | """ 2 | Project settings for production-like deployments. 3 | 4 | Settings in this file are overridden by values in 5 | polychart.config.deployOverrides. 6 | """ 7 | 8 | from polychart.config.shared import * 9 | 10 | DEBUG = False 11 | TEMPLATE_DEBUG = False 12 | 13 | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https') 14 | 15 | try: 16 | from polychart.config.deployOverrides import * 17 | except ImportError: 18 | pass 19 | -------------------------------------------------------------------------------- /polychart/config/local.py: -------------------------------------------------------------------------------- 1 | """ 2 | Project settings used during local development. 3 | 4 | Settings in this file are overridden by values in 5 | polychart.config.localOverrides. 6 | """ 7 | 8 | from polychart.config.shared import * 9 | 10 | DEBUG = True 11 | TEMPLATE_DEBUG = True 12 | 13 | # Generate your own for production deployments! 14 | SECRET_KEY = 'TkLhoWRKORnKShe3PtdJo8jvTcm6RloPzj1sagGHbIbK3vd16U9OvZ96qvpA6TRW' 15 | 16 | HOST_URL = 'http://localhost:8000' 17 | 18 | DATABASES = { 19 | 'default': { 20 | 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. 21 | 'NAME': 'dev.db', # Or path to database file if using sqlite3. 22 | # The following settings are not used with sqlite3: 23 | 'USER': '', 24 | 'PASSWORD': '', 25 | 'HOST': '', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. 26 | 'PORT': '', # Set to empty string for default. 27 | } 28 | } 29 | 30 | CACHES = { 31 | 'default': { 32 | 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 33 | 'LOCATION': 'unique-snowflake', 34 | } 35 | } 36 | 37 | EMAIL_BACKEND = 'polychart.main.utils.emailConsole.ConsoleEmailBackend' 38 | 39 | try: 40 | from polychart.config.localOverrides import * 41 | except ImportError: 42 | pass 43 | -------------------------------------------------------------------------------- /polychart/config/localOverrides.py: -------------------------------------------------------------------------------- 1 | GOOGLE_ANALYTICS_CLIENT_ID = "740897306893.apps.googleusercontent.com" 2 | GOOGLE_ANALYTICS_CLIENT_SECRET = "si27VH63M7XNmhaM4ycLFTq-" 3 | 4 | ENABLED_DATA_SOURCE_TYPES = [ 5 | 'mysql', 6 | 'infobright', 7 | 'postgresql', 8 | 'csv', 9 | 10 | # Salesforce requires additional settings: 11 | # SALESFORCE_CLIENT_ID and SALESFORCE_CLIENT_SECRET 12 | # 'salesforce', 13 | 14 | # Google Analytics requires additional settings: 15 | # GOOGLE_ANALYTICS_CLIENT_ID and GOOGLE_ANALYTICS_CLIENT_SECRET 16 | 'googleAnalytics', 17 | ] 18 | -------------------------------------------------------------------------------- /polychart/main/README.md: -------------------------------------------------------------------------------- 1 | Polychart Dashboard Builder Main Application 2 | ============================================ 3 | This directory contains the Django application for the Polychart Dashboard 4 | Builder. The layout is typical of a Django application: 5 | 6 | * `models.py` define the models that are used in the database; 7 | * `urls.py` define the specific endpoints for which the Dashboard builder 8 | uses, and the corresponding handler function for these endpoints. 9 | * `management/` contains extensions to how `manage.py` works at the root level 10 | of the Django application; 11 | * `migrations/` contains generated and handwritten data base migrations for 12 | the Django application; 13 | * `templates/` directory contains the Django template files; 14 | * `utils/` directory contains an assortment of utility modules that are used 15 | throughout the code base; 16 | * `views/` directory contains handler functions that are used by the URL 17 | routing; 18 | 19 | One important thing to note here is that the backend code also relies on the 20 | Polychart Query package. 21 | -------------------------------------------------------------------------------- /polychart/main/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/polychart/main/__init__.py -------------------------------------------------------------------------------- /polychart/main/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/polychart/main/management/__init__.py -------------------------------------------------------------------------------- /polychart/main/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/polychart/main/management/commands/__init__.py -------------------------------------------------------------------------------- /polychart/main/management/commands/createuser.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand, CommandError 2 | from getpass import getpass 3 | 4 | from polychart.main.views.account import createUser 5 | 6 | class Command(BaseCommand): 7 | def handle(self, *args, **kwargs): 8 | userParams = { 9 | 'first_name': _prompt('First name: '), 10 | 'last_name': _prompt('Last name: '), 11 | 'username': _prompt('Username: '), 12 | 'password': _prompt('Password: ', maskInput=True), 13 | } 14 | 15 | createUser(userParams) 16 | 17 | def _prompt(msg, maskInput=False): 18 | while True: 19 | if maskInput: 20 | result = getpass(msg) 21 | else: 22 | result = raw_input(msg) 23 | 24 | result = result.strip() 25 | 26 | if result: 27 | return result 28 | -------------------------------------------------------------------------------- /polychart/main/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/polychart/main/migrations/__init__.py -------------------------------------------------------------------------------- /polychart/main/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/polychart/main/templates/__init__.py -------------------------------------------------------------------------------- /polychart/main/templates/base.tmpl: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% block title %}{% endblock %} 9 | 10 | 11 | 12 | 13 | {% block styles %} 14 | {% endblock %} 15 | 16 | 17 | 18 | 19 | {% block scripts %} 20 | {% endblock %} 21 | 22 | 23 | {% block body %}{% endblock %} 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /polychart/main/templates/callback.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | {% load staticfiles %} 3 | 4 | {% block title %}Loading...{% endblock %} 5 | {% block body %} 6 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /polychart/main/templates/dashEdit.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% load staticfiles %} 4 | 5 | {% block title %}Dashboard Builder{% endblock %} 6 | 7 | {% block body %} 8 |
9 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /polychart/main/templates/dashView.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% load staticfiles %} 4 | 5 | {% block title %}Dashboard Builder{% endblock %} 6 | 7 | {% block body %} 8 |
9 | 21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /polychart/main/templates/demo.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% load staticfiles %} 4 | 5 | {% block title %}Dashboard Builder Demo{% endblock %} 6 | 7 | {% block body %} 8 |
9 | 48 | {% endblock %} 49 | -------------------------------------------------------------------------------- /polychart/main/templates/forgotPassword.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "oldBase.tmpl" %} 2 | 3 | {% block body %} 4 | {% if status == 'success' %} 5 |
6 |
7 | Please check your email for a reset code. 8 |
9 |
10 | {% else %} 11 |
12 | {% if status == 'accountNotFound' %} 13 |
14 | We could not locate your account. 15 |
16 | {% endif %} 17 |
18 | 19 |
20 |

Forgot your password?

21 |

Just enter your email address or username below.

22 | 23 |
24 | {% csrf_token %} 25 |
26 | 27 |
28 | 29 |
30 | {{ password_form.username.errors }} 31 |
32 | 33 | 34 |
35 |
36 | {% endif %} 37 | {% endblock %} 38 | -------------------------------------------------------------------------------- /polychart/main/templates/login.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "oldBase.tmpl" %} 2 | {% block body %} 3 |
4 | {% if error == 'auth' %} 5 |
6 | Invalid username or password. 7 |
8 | {% elif error %} 9 |
10 | An unknown error occured. Please try again later! 11 |
12 | {% endif %} 13 |
14 | 15 |
16 |

Log In

17 | {% if settings.SIGNUP_ENABLED %} 18 |

19 | Don't have an account? Sign up for one here, it's free! 20 |

21 | {% endif %} 22 | {% if settings.PASSWORD_RESET_ENABLED %} 23 |

24 | If you already have an account but you've forgot your password, click here. 25 |

26 | {% endif %} 27 | 28 |
29 | 30 | {% csrf_token %} 31 | 32 |
33 | 34 |
35 | 36 |
37 |
38 | 39 |
40 | 41 |
42 | 43 |
44 |
45 | 46 |
47 | 48 |
49 |
50 |
51 | {% endblock %} 52 | -------------------------------------------------------------------------------- /polychart/main/templates/logout.tmpl: -------------------------------------------------------------------------------- 1 | Log Out 2 |
3 | {% csrf_token %} 4 |
5 | -------------------------------------------------------------------------------- /polychart/main/templates/resetPassword.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "oldBase.tmpl" %} 2 | 3 | {% block body %} 4 |
5 |

Reset password

6 |

Enter a new password for the user account “{{ username }}”.

7 |
8 | {% csrf_token %} 9 | 10 |
11 | 12 |
13 | 14 |
15 |
16 | 17 |
18 |
19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /polychart/main/templates/verifyPendingDataSource.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %}Connecting data source...{% end %} 4 | 5 | {% block body %} 6 | {% if result['status'] == 'error' %} 7 |
8 |

Oh, no!

9 |

10 | Unfortunately, we were not able to connect your data source to Polychart. 11 |

12 | {% if result['error']['type'] == 'connection' %} 13 |

{{ result['error']['message'] }}

14 |

15 | If you're not sure how to resolve this issue, 16 | feel free to contact us via the link in the bottom right corner. 17 |

18 | {% else %} 19 |

20 | We're not quite sure what went wrong. 21 | Please contact us via the link in the bottom right to resolve this issue. 22 |

23 | {% end %} 24 |
25 | {% endblock %} 26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /polychart/main/utils/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /polychart/main/utils/secureStorage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import Padding 4 | 5 | from Crypto.Cipher import AES 6 | from base64 import urlsafe_b64encode, urlsafe_b64decode 7 | from django.conf import settings 8 | from os import urandom 9 | from pbkdf2 import PBKDF2 10 | 11 | def getEncryptionKey(password, salt): 12 | assert password 13 | assert salt 14 | 15 | saltbytes = urlsafe_b64decode(salt.encode('utf-8')) 16 | hashThing = PBKDF2(password, saltbytes) 17 | return hashThing.read(32) 18 | 19 | DEFAULT_KEY = getEncryptionKey(settings.SECRET_KEY, 'ziIwStZZ') 20 | 21 | def encrypt(key, plaintext): 22 | assert key 23 | 24 | if plaintext == "": 25 | return "" 26 | 27 | aes = AES.new(key, AES.MODE_CBC, b"Polychart AES IV") 28 | padded = Padding.appendPadding(plaintext) 29 | cipherbytes = aes.encrypt(padded) 30 | return urlsafe_b64encode(cipherbytes) 31 | 32 | def decrypt(key, ciphertext): 33 | assert key 34 | 35 | if ciphertext == "": 36 | return "" 37 | 38 | aes = AES.new(key, AES.MODE_CBC, b"Polychart AES IV") 39 | cipherbytes = urlsafe_b64decode(ciphertext.encode('utf-8')) 40 | padded = aes.decrypt(cipherbytes) 41 | return Padding.removePadding(padded) 42 | 43 | def generateSalt(): 44 | return urlsafe_b64encode(urandom(8)) 45 | -------------------------------------------------------------------------------- /polychart/main/utils/spec.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions related to processing frontend spec objects. 3 | """ 4 | import re 5 | 6 | 7 | def getDsInfo(spec): 8 | """ 9 | Extracts the list of (tableName, dsKey) pairs from the clientside chart 10 | specification produced by the Dashboard Builder. 11 | 12 | Args: 13 | spec: A dictionary corresponding to the client side chart spec object. The 14 | spec object must have the field 'meta', whose value will be a dictionary 15 | with keys as column names and values as dictionaries that contain the 16 | desired information. For example: 17 | 18 | spec = { ... 19 | meta: { columnOne: { tableName: "Table_One", dsKey: "keyone" , ... } 20 | , columnTwo: { tableName: "Table_Two", dsKey: "keyone" , ...} 21 | ... } 22 | ... } 23 | 24 | Returns: 25 | A list of distinct pairs with tableName in the first entry and dsKey in the 26 | second. Both values will be strings. 27 | """ 28 | result = [] 29 | for _, val in spec['meta'].iteritems(): 30 | pair = (val.get('tableName'), val.get('dsKey')) 31 | if pair[0] is None and pair[1] is None: 32 | continue 33 | elif pair not in result: 34 | result.append(pair) 35 | return result 36 | -------------------------------------------------------------------------------- /polychart/main/utils/tools.py: -------------------------------------------------------------------------------- 1 | """ 2 | A collection of very miscellaneous tools. 3 | """ 4 | ### Filepath tools 5 | from base64 import urlsafe_b64encode 6 | import atexit 7 | import os 8 | 9 | # Ensure tmp directory exists 10 | if not os.path.exists('tmp'): 11 | os.mkdir('tmp') 12 | 13 | def getNewTempFilePath(): 14 | path = os.getcwd() + '/tmp/' + urlsafe_b64encode(os.urandom(18)) 15 | deleteOnExit(path) 16 | return path 17 | 18 | def deleteOnExit(path): 19 | def cleanup(): 20 | try: os.remove(path) 21 | except: pass 22 | atexit.register(cleanup) 23 | 24 | ### Misc functions 25 | def randomCode(): 26 | # 18 random bytes -- more than a UUID, and looks nicer in base 64 27 | return urlsafe_b64encode(os.urandom(18)) 28 | -------------------------------------------------------------------------------- /polychart/main/views/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/polychart/main/views/__init__.py -------------------------------------------------------------------------------- /polychart/main/views/demo.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django handlers to deal with the demo. 3 | """ 4 | import json 5 | 6 | from django.shortcuts import render 7 | from django.views.decorators.http import require_GET, require_http_methods 8 | 9 | from polychart.main.models import TutorialCompletion 10 | 11 | @require_GET 12 | def demoShow(request): 13 | """Django handler to show the demo dashboard.""" 14 | # pylint: disable = E1101 15 | # Pylint does not recognize Django model attributes. 16 | if request.user.is_authenticated(): 17 | showTutorial = not TutorialCompletion.objects.filter(user=request.user, type='nux').exists() 18 | else: 19 | showTutorial = True 20 | 21 | tutorialOverride = request.GET.get('showTutorial', None) 22 | if tutorialOverride == 'yes': 23 | showTutorial = True 24 | elif tutorialOverride == 'no': 25 | showTutorial = False 26 | 27 | return render( request 28 | , 'demo.tmpl' 29 | , dictionary = { 'showTutorial': showTutorial }) 30 | 31 | -------------------------------------------------------------------------------- /polychart/main/views/home.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django handlers to deal with the Dashboard Builder home. 3 | """ 4 | from django.contrib.auth.decorators import login_required 5 | from django.shortcuts import render 6 | from django.views.decorators.http import require_GET 7 | 8 | from polychart.main.models import DataSource, Dashboard, TutorialCompletion 9 | 10 | @require_GET 11 | @login_required 12 | def show(request): 13 | """Django handler to render home.""" 14 | # pylint: disable = E1101 15 | # Pylint does not notice Django model attributes 16 | newDataSourceKey = request.GET.get('newDataSourceKey', None) 17 | 18 | dataSources = DataSource.objects.filter(user=request.user) 19 | dashboards = Dashboard.objects.filter(user=request.user) 20 | tutorialCompleted = TutorialCompletion.objects.filter(user=request.user, type='nux').exists() 21 | 22 | return render( request 23 | , 'home.tmpl' 24 | , dictionary = { 'dataSources': dataSources 25 | , 'dashboards': dashboards 26 | , 'tutorialCompleted': tutorialCompleted 27 | , 'newDataSourceKey': newDataSourceKey }) 28 | -------------------------------------------------------------------------------- /polychart/main/views/tutorial.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django handlers related to the tutorial. 3 | """ 4 | import json 5 | 6 | from django.contrib.auth.decorators import login_required 7 | from django.views.decorators.http import require_POST 8 | 9 | from polychart.main.models import TutorialCompletion 10 | from polychart.utils import jsonResponse 11 | 12 | @require_POST 13 | @login_required 14 | def tutorialComplete(request): 15 | """Django handler for completion of the tutorial.""" 16 | body = json.loads(request.body) 17 | 18 | TutorialCompletion( 19 | user=request.user, 20 | type=body['type'] 21 | ).save() 22 | 23 | return jsonResponse({}) 24 | -------------------------------------------------------------------------------- /polychart/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | Defines how Django should route HTTP requests to views. 3 | Some applications have their own routing files. 4 | See `polychart.*.urls`. 5 | """ 6 | 7 | import polychart.main.urls as MAIN_URLS 8 | 9 | try: 10 | import polychart.site.urls as SITE_URLS 11 | except ImportError: 12 | SITE_URLS = None 13 | 14 | try: 15 | import polychart.jsSite.urls as JS_SITE_URLS 16 | except ImportError: 17 | JS_SITE_URLS = None 18 | 19 | try: 20 | import polychart.analytics.urls as ANALYTICS_URLS 21 | except ImportError: 22 | ANALYTICS_URLS = None 23 | 24 | from django.conf.urls import include, patterns, url 25 | from polychart.utils import permanentRedirect 26 | 27 | def _buildPatternList(): 28 | urls = [ 29 | # Main website 30 | url(r'^', include(SITE_URLS)) if SITE_URLS else None, 31 | 32 | # Main app 33 | url(r'^', include(MAIN_URLS)), 34 | 35 | # Polychart.js website 36 | url(r'^js/', include(JS_SITE_URLS)) if JS_SITE_URLS else None, 37 | 38 | # Analytics 39 | url('^', include(ANALYTICS_URLS)) if ANALYTICS_URLS else None, 40 | 41 | # Deprecated URLs 42 | url(r'^beta$', permanentRedirect('/signup')), 43 | url(r'^devkit.*$', permanentRedirect('/')), 44 | url(r'^embed/.*$', permanentRedirect('/')), 45 | ] 46 | 47 | # Filter out None 48 | urls = [x for x in urls if x] 49 | 50 | return patterns('polychart.main.views', *urls) 51 | 52 | urlpatterns = _buildPatternList() 53 | -------------------------------------------------------------------------------- /polychart/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for polychart project. 3 | 4 | This module contains the WSGI application used by Django's development server 5 | and any production WSGI deployments. It should expose a module-level variable 6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 7 | this application via the ``WSGI_APPLICATION`` setting. 8 | 9 | Usually you will have the standard Django WSGI application here, but it also 10 | might make sense to replace the whole Django WSGI application with a custom one 11 | that later delegates to the Django one. For example, you could introduce WSGI 12 | middleware here, or combine a Django application with an application of another 13 | framework. 14 | """ 15 | 16 | # This application object is used by any WSGI server configured to use this 17 | # file. This includes Django's development server, if the WSGI_APPLICATION 18 | # setting points here. 19 | from django.core.wsgi import get_wsgi_application 20 | application = get_wsgi_application() 21 | 22 | # Apply WSGI middleware here. 23 | # from helloworld.wsgi import HelloWorldApplication 24 | # application = HelloWorldApplication(application) 25 | -------------------------------------------------------------------------------- /polychartQuery/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Provide a public interface for constructing new SQL-esque data sources. 3 | """ 4 | from polychartQuery.sql import MySqlConn, InfobrightConn, PostgreSqlConn 5 | 6 | __all__ = [] 7 | 8 | def openConnection(type, *args, **kwargs): 9 | if type == 'mysql': 10 | return MySqlConn(*args, **kwargs) 11 | 12 | elif type == 'infobright': 13 | return InfobrightConn(*args, **kwargs) 14 | 15 | elif type == 'postgresql': 16 | return PostgreSqlConn(*args, **kwargs) 17 | -------------------------------------------------------------------------------- /polychartQuery/csv/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from connection import Conn as Connection 4 | -------------------------------------------------------------------------------- /polychartQuery/csv/connection.py: -------------------------------------------------------------------------------- 1 | """ 2 | Module defining a dummy CSV connection. 3 | """ 4 | from polychartQuery.abstract import DataSourceConnection 5 | 6 | class Conn(DataSourceConnection): 7 | """ 8 | Dummy class (for now) 9 | """ 10 | def __init__(self, *args, **kwargs): 11 | super(Conn, self).__init__(*args, **kwargs) 12 | pass 13 | -------------------------------------------------------------------------------- /polychartQuery/googleAnalytics/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from query import GAQuery as Query 4 | from connection import GAConn as Connection 5 | -------------------------------------------------------------------------------- /polychartQuery/sql/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from polychartQuery.sql.query import SqlQuery, PostgreSqlQuery 4 | from polychartQuery.sql.connection import MySqlConn, PostgreSqlConn, InfobrightConn 5 | -------------------------------------------------------------------------------- /requirements: -------------------------------------------------------------------------------- 1 | # Please include an exact version number with every package, 2 | # and use the correct case of the package name. 3 | # Unless necessary, do not use the inequality operators (e.g. ">"). 4 | # 5 | # This reduces the number of "works for me" problems that occur 6 | # between different installations of Polychart. 7 | 8 | # Django 9 | Django==1.5.5 10 | South==0.7.6 11 | django-jsonfield==0.8.11 12 | django-modeldict==1.3.6 13 | nexus==0.2.3 14 | 15 | # Database drivers 16 | MySQL-python==1.2.5 17 | psycopg2==2.5.1 18 | 19 | # Crypto 20 | Padding==0.4 21 | pbkdf2==1.3 22 | pycrypto==2.6 23 | 24 | # Build-related 25 | lxml==3.2.0 26 | virtualenv==1.10.1 27 | 28 | # Deployment-related 29 | gunicorn==17.5 30 | python-memcached==1.53 31 | 32 | # Export-related 33 | CairoSVG==0.5 # can't move to 1.0.X due to https://github.com/Kozea/CairoSVG/issues/37 34 | WeasyPrint==0.19.2 35 | cairocffi==0.5.1 36 | svgwrite==1.1.2 37 | websocket-client==0.11.0 38 | 39 | # Misc 40 | analytics-python==0.4.1 41 | pika==0.9.5 42 | python-dateutil==2.1 43 | raven==1.6.1 44 | requests==1.2.3 45 | unittest-xml-reporting==1.3.2 46 | -------------------------------------------------------------------------------- /server/README: -------------------------------------------------------------------------------- 1 | Backend files for Polychart. 2 | 3 | The folder `polychartQuery` contains the logic for turning frontend 4 | drag-and-drop actions into SQL queries, google analytics calls, etc. 5 | 6 | -------------------------------------------------------------------------------- /server/cairo.py: -------------------------------------------------------------------------------- 1 | from cairocffi import * 2 | -------------------------------------------------------------------------------- /server/polychart/README.md: -------------------------------------------------------------------------------- 1 | Polychart Dashboard Builder Backend 2 | =================================== 3 | Herein lies the backend code for the Polychart Dashboard Builder. The server is 4 | written in Python and is built atop of the [Django] (https://www.djangoproject.com/) 5 | web framework. The primary directories here are `config/` and `main/`; the 6 | former contains Django-related configuration files that provide settings to the 7 | Django environment, with the possibility of differing setups in a local 8 | development environment and a production environment; the latter directory 9 | contains the actual logic and code for Dashboard Builder related processing. For 10 | a detailed description of the contents of each directory, see the documents 11 | contained in the individual directories. 12 | 13 | Within this directory lie three main files. 14 | 15 | First, `urls.py` determines the collection of URL patterns to be given to the 16 | application. This file is written in such a way that it may collect the URL 17 | patterns for a variety of applications, add to it some other predetermined 18 | patterns, and package them up for the Django application as a whole. 19 | 20 | The other two files, `utils.py` and `wsgi.py` are rather short and 21 | self-explanatory: the first defines a collection of Django-related utility 22 | functions that are oft used in View Handler functions and the latter defines the 23 | [WSGI] (http://en.wikipedia.org/wiki/Web_Server_Gateway_Interface) application 24 | settings for the Django environment. 25 | -------------------------------------------------------------------------------- /server/polychart/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/server/polychart/__init__.py -------------------------------------------------------------------------------- /server/polychart/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/server/polychart/config/__init__.py -------------------------------------------------------------------------------- /server/polychart/config/deploy.py: -------------------------------------------------------------------------------- 1 | """ 2 | Project settings for production-like deployments. 3 | 4 | Settings in this file are overridden by values in 5 | polychart.config.deployOverrides. 6 | """ 7 | 8 | from polychart.config.shared import * 9 | 10 | DEBUG = False 11 | TEMPLATE_DEBUG = False 12 | 13 | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https') 14 | 15 | try: 16 | from polychart.config.deployOverrides import * 17 | except ImportError: 18 | pass 19 | -------------------------------------------------------------------------------- /server/polychart/config/local.py: -------------------------------------------------------------------------------- 1 | """ 2 | Project settings used during local development. 3 | 4 | Settings in this file are overridden by values in 5 | polychart.config.localOverrides. 6 | """ 7 | 8 | from polychart.config.shared import * 9 | 10 | DEBUG = True 11 | TEMPLATE_DEBUG = True 12 | 13 | # Generate your own for production deployments! 14 | SECRET_KEY = 'TkLhoWRKORnKShe3PtdJo8jvTcm6RloPzj1sagGHbIbK3vd16U9OvZ96qvpA6TRW' 15 | 16 | HOST_URL = 'http://localhost:8000' 17 | 18 | DATABASES = { 19 | 'default': { 20 | 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. 21 | 'NAME': 'dev.db', # Or path to database file if using sqlite3. 22 | # The following settings are not used with sqlite3: 23 | 'USER': '', 24 | 'PASSWORD': '', 25 | 'HOST': '', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. 26 | 'PORT': '', # Set to empty string for default. 27 | } 28 | } 29 | 30 | CACHES = { 31 | 'default': { 32 | 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 33 | 'LOCATION': 'unique-snowflake', 34 | } 35 | } 36 | 37 | EMAIL_BACKEND = 'polychart.main.utils.emailConsole.ConsoleEmailBackend' 38 | 39 | try: 40 | from polychart.config.localOverrides import * 41 | except ImportError: 42 | pass 43 | -------------------------------------------------------------------------------- /server/polychart/main/README.md: -------------------------------------------------------------------------------- 1 | Polychart Dashboard Builder Main Application 2 | ============================================ 3 | This directory contains the Django application for the Polychart Dashboard 4 | Builder. The layout is typical of a Django application: 5 | 6 | * `models.py` define the models that are used in the database; 7 | * `urls.py` define the specific endpoints for which the Dashboard builder 8 | uses, and the corresponding handler function for these endpoints. 9 | * `management/` contains extensions to how `manage.py` works at the root level 10 | of the Django application; 11 | * `migrations/` contains generated and handwritten data base migrations for 12 | the Django application; 13 | * `templates/` directory contains the Django template files; 14 | * `utils/` directory contains an assortment of utility modules that are used 15 | throughout the code base; 16 | * `views/` directory contains handler functions that are used by the URL 17 | routing; 18 | 19 | One important thing to note here is that the backend code also relies on the 20 | Polychart Query package. 21 | -------------------------------------------------------------------------------- /server/polychart/main/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/server/polychart/main/__init__.py -------------------------------------------------------------------------------- /server/polychart/main/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/server/polychart/main/management/__init__.py -------------------------------------------------------------------------------- /server/polychart/main/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/server/polychart/main/management/commands/__init__.py -------------------------------------------------------------------------------- /server/polychart/main/management/commands/createuser.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand, CommandError 2 | from getpass import getpass 3 | 4 | from polychart.main.views.account import createUser 5 | 6 | class Command(BaseCommand): 7 | def handle(self, *args, **kwargs): 8 | userParams = { 9 | 'first_name': _prompt('First name: '), 10 | 'last_name': _prompt('Last name: '), 11 | 'username': _prompt('Username: '), 12 | 'password': _prompt('Password: ', maskInput=True), 13 | } 14 | 15 | createUser(userParams) 16 | 17 | def _prompt(msg, maskInput=False): 18 | while True: 19 | if maskInput: 20 | result = getpass(msg) 21 | else: 22 | result = raw_input(msg) 23 | 24 | result = result.strip() 25 | 26 | if result: 27 | return result 28 | -------------------------------------------------------------------------------- /server/polychart/main/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/server/polychart/main/migrations/__init__.py -------------------------------------------------------------------------------- /server/polychart/main/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/server/polychart/main/templates/__init__.py -------------------------------------------------------------------------------- /server/polychart/main/templates/base.tmpl: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% block title %}{% endblock %} 9 | 10 | 11 | 12 | 13 | {% block styles %} 14 | {% endblock %} 15 | 16 | 17 | 18 | 19 | {% block scripts %} 20 | {% endblock %} 21 | 22 | 23 | {% block body %}{% endblock %} 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /server/polychart/main/templates/callback.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | {% load staticfiles %} 3 | 4 | {% block title %}Loading...{% endblock %} 5 | {% block body %} 6 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /server/polychart/main/templates/dashEdit.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% load staticfiles %} 4 | 5 | {% block title %}Dashboard Builder{% endblock %} 6 | 7 | {% block body %} 8 |
9 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /server/polychart/main/templates/dashView.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% load staticfiles %} 4 | 5 | {% block title %}Dashboard Builder{% endblock %} 6 | 7 | {% block body %} 8 |
9 | 21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /server/polychart/main/templates/demo.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% load staticfiles %} 4 | 5 | {% block title %}Dashboard Builder Demo{% endblock %} 6 | 7 | {% block body %} 8 |
9 | 48 | {% endblock %} 49 | -------------------------------------------------------------------------------- /server/polychart/main/templates/forgotPassword.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "oldBase.tmpl" %} 2 | 3 | {% block body %} 4 | {% if status == 'success' %} 5 |
6 |
7 | Please check your email for a reset code. 8 |
9 |
10 | {% else %} 11 |
12 | {% if status == 'accountNotFound' %} 13 |
14 | We could not locate your account. 15 |
16 | {% endif %} 17 |
18 | 19 |
20 |

Forgot your password?

21 |

Just enter your email address or username below.

22 | 23 |
24 | {% csrf_token %} 25 |
26 | 27 |
28 | 29 |
30 | {{ password_form.username.errors }} 31 |
32 | 33 | 34 |
35 |
36 | {% endif %} 37 | {% endblock %} 38 | -------------------------------------------------------------------------------- /server/polychart/main/templates/login.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "oldBase.tmpl" %} 2 | {% block body %} 3 |
4 | {% if error == 'auth' %} 5 |
6 | Invalid username or password. 7 |
8 | {% elif error %} 9 |
10 | An unknown error occured. Please try again later! 11 |
12 | {% endif %} 13 |
14 | 15 |
16 |

Log In

17 | {% if settings.SIGNUP_ENABLED %} 18 |

19 | Don't have an account? Sign up for one here, it's free! 20 |

21 | {% endif %} 22 | {% if settings.PASSWORD_RESET_ENABLED %} 23 |

24 | If you already have an account but you've forgot your password, click here. 25 |

26 | {% endif %} 27 | 28 |
29 | 30 | {% csrf_token %} 31 | 32 |
33 | 34 |
35 | 36 |
37 |
38 | 39 |
40 | 41 |
42 | 43 |
44 |
45 | 46 |
47 | 48 |
49 |
50 |
51 | {% endblock %} 52 | -------------------------------------------------------------------------------- /server/polychart/main/templates/logout.tmpl: -------------------------------------------------------------------------------- 1 | Log Out 2 |
3 | {% csrf_token %} 4 |
5 | -------------------------------------------------------------------------------- /server/polychart/main/templates/resetPassword.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "oldBase.tmpl" %} 2 | 3 | {% block body %} 4 |
5 |

Reset password

6 |

Enter a new password for the user account “{{ username }}”.

7 |
8 | {% csrf_token %} 9 | 10 |
11 | 12 |
13 | 14 |
15 |
16 | 17 |
18 |
19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /server/polychart/main/templates/verifyPendingDataSource.tmpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tmpl" %} 2 | 3 | {% block title %}Connecting data source...{% end %} 4 | 5 | {% block body %} 6 | {% if result['status'] == 'error' %} 7 |
8 |

Oh, no!

9 |

10 | Unfortunately, we were not able to connect your data source to Polychart. 11 |

12 | {% if result['error']['type'] == 'connection' %} 13 |

{{ result['error']['message'] }}

14 |

15 | If you're not sure how to resolve this issue, 16 | feel free to contact us via the link in the bottom right corner. 17 |

18 | {% else %} 19 |

20 | We're not quite sure what went wrong. 21 | Please contact us via the link in the bottom right to resolve this issue. 22 |

23 | {% end %} 24 |
25 | {% endblock %} 26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /server/polychart/main/utils/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /server/polychart/main/utils/secureStorage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import Padding 4 | 5 | from Crypto.Cipher import AES 6 | from base64 import urlsafe_b64encode, urlsafe_b64decode 7 | from django.conf import settings 8 | from os import urandom 9 | from pbkdf2 import PBKDF2 10 | 11 | def getEncryptionKey(password, salt): 12 | assert password 13 | assert salt 14 | 15 | saltbytes = urlsafe_b64decode(salt.encode('utf-8')) 16 | hashThing = PBKDF2(password, saltbytes) 17 | return hashThing.read(32) 18 | 19 | DEFAULT_KEY = getEncryptionKey(settings.SECRET_KEY, 'ziIwStZZ') 20 | 21 | def encrypt(key, plaintext): 22 | assert key 23 | 24 | if plaintext == "": 25 | return "" 26 | 27 | aes = AES.new(key, AES.MODE_CBC, b"Polychart AES IV") 28 | padded = Padding.appendPadding(plaintext) 29 | cipherbytes = aes.encrypt(padded) 30 | return urlsafe_b64encode(cipherbytes) 31 | 32 | def decrypt(key, ciphertext): 33 | assert key 34 | 35 | if ciphertext == "": 36 | return "" 37 | 38 | aes = AES.new(key, AES.MODE_CBC, b"Polychart AES IV") 39 | cipherbytes = urlsafe_b64decode(ciphertext.encode('utf-8')) 40 | padded = aes.decrypt(cipherbytes) 41 | return Padding.removePadding(padded) 42 | 43 | def generateSalt(): 44 | return urlsafe_b64encode(urandom(8)) 45 | -------------------------------------------------------------------------------- /server/polychart/main/utils/spec.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions related to processing frontend spec objects. 3 | """ 4 | import re 5 | 6 | 7 | def getDsInfo(spec): 8 | """ 9 | Extracts the list of (tableName, dsKey) pairs from the clientside chart 10 | specification produced by the Dashboard Builder. 11 | 12 | Args: 13 | spec: A dictionary corresponding to the client side chart spec object. The 14 | spec object must have the field 'meta', whose value will be a dictionary 15 | with keys as column names and values as dictionaries that contain the 16 | desired information. For example: 17 | 18 | spec = { ... 19 | meta: { columnOne: { tableName: "Table_One", dsKey: "keyone" , ... } 20 | , columnTwo: { tableName: "Table_Two", dsKey: "keyone" , ...} 21 | ... } 22 | ... } 23 | 24 | Returns: 25 | A list of distinct pairs with tableName in the first entry and dsKey in the 26 | second. Both values will be strings. 27 | """ 28 | result = [] 29 | for _, val in spec['meta'].iteritems(): 30 | pair = (val.get('tableName'), val.get('dsKey')) 31 | if pair[0] is None and pair[1] is None: 32 | continue 33 | elif pair not in result: 34 | result.append(pair) 35 | return result 36 | -------------------------------------------------------------------------------- /server/polychart/main/utils/tools.py: -------------------------------------------------------------------------------- 1 | """ 2 | A collection of very miscellaneous tools. 3 | """ 4 | ### Filepath tools 5 | from base64 import urlsafe_b64encode 6 | import atexit 7 | import os 8 | 9 | # Ensure tmp directory exists 10 | if not os.path.exists('tmp'): 11 | os.mkdir('tmp') 12 | 13 | def getNewTempFilePath(): 14 | path = os.getcwd() + '/tmp/' + urlsafe_b64encode(os.urandom(18)) 15 | deleteOnExit(path) 16 | return path 17 | 18 | def deleteOnExit(path): 19 | def cleanup(): 20 | try: os.remove(path) 21 | except: pass 22 | atexit.register(cleanup) 23 | 24 | ### Misc functions 25 | def randomCode(): 26 | # 18 random bytes -- more than a UUID, and looks nicer in base 64 27 | return urlsafe_b64encode(os.urandom(18)) 28 | -------------------------------------------------------------------------------- /server/polychart/main/views/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polychart/builder/a352ccd62a145c7379e954253c722e9704178f20/server/polychart/main/views/__init__.py -------------------------------------------------------------------------------- /server/polychart/main/views/demo.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django handlers to deal with the demo. 3 | """ 4 | import json 5 | 6 | from django.shortcuts import render 7 | from django.views.decorators.http import require_GET, require_http_methods 8 | 9 | from polychart.main.models import TutorialCompletion 10 | 11 | @require_GET 12 | def demoShow(request): 13 | """Django handler to show the demo dashboard.""" 14 | # pylint: disable = E1101 15 | # Pylint does not recognize Django model attributes. 16 | if request.user.is_authenticated(): 17 | showTutorial = not TutorialCompletion.objects.filter(user=request.user, type='nux').exists() 18 | else: 19 | showTutorial = True 20 | 21 | tutorialOverride = request.GET.get('showTutorial', None) 22 | if tutorialOverride == 'yes': 23 | showTutorial = True 24 | elif tutorialOverride == 'no': 25 | showTutorial = False 26 | 27 | return render( request 28 | , 'demo.tmpl' 29 | , dictionary = { 'showTutorial': showTutorial }) 30 | 31 | -------------------------------------------------------------------------------- /server/polychart/main/views/home.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django handlers to deal with the Dashboard Builder home. 3 | """ 4 | from django.contrib.auth.decorators import login_required 5 | from django.shortcuts import render 6 | from django.views.decorators.http import require_GET 7 | 8 | from polychart.main.models import DataSource, Dashboard, TutorialCompletion 9 | 10 | @require_GET 11 | @login_required 12 | def show(request): 13 | """Django handler to render home.""" 14 | # pylint: disable = E1101 15 | # Pylint does not notice Django model attributes 16 | newDataSourceKey = request.GET.get('newDataSourceKey', None) 17 | 18 | dataSources = DataSource.objects.filter(user=request.user) 19 | dashboards = Dashboard.objects.filter(user=request.user) 20 | tutorialCompleted = TutorialCompletion.objects.filter(user=request.user, type='nux').exists() 21 | 22 | return render( request 23 | , 'home.tmpl' 24 | , dictionary = { 'dataSources': dataSources 25 | , 'dashboards': dashboards 26 | , 'tutorialCompleted': tutorialCompleted 27 | , 'newDataSourceKey': newDataSourceKey }) 28 | -------------------------------------------------------------------------------- /server/polychart/main/views/tutorial.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django handlers related to the tutorial. 3 | """ 4 | import json 5 | 6 | from django.contrib.auth.decorators import login_required 7 | from django.views.decorators.http import require_POST 8 | 9 | from polychart.main.models import TutorialCompletion 10 | from polychart.utils import jsonResponse 11 | 12 | @require_POST 13 | @login_required 14 | def tutorialComplete(request): 15 | """Django handler for completion of the tutorial.""" 16 | body = json.loads(request.body) 17 | 18 | TutorialCompletion( 19 | user=request.user, 20 | type=body['type'] 21 | ).save() 22 | 23 | return jsonResponse({}) 24 | -------------------------------------------------------------------------------- /server/polychart/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | Defines how Django should route HTTP requests to views. 3 | Some applications have their own routing files. 4 | See `polychart.*.urls`. 5 | """ 6 | 7 | import polychart.main.urls as MAIN_URLS 8 | 9 | try: 10 | import polychart.site.urls as SITE_URLS 11 | except ImportError: 12 | SITE_URLS = None 13 | 14 | try: 15 | import polychart.jsSite.urls as JS_SITE_URLS 16 | except ImportError: 17 | JS_SITE_URLS = None 18 | 19 | try: 20 | import polychart.analytics.urls as ANALYTICS_URLS 21 | except ImportError: 22 | ANALYTICS_URLS = None 23 | 24 | from django.conf.urls import include, patterns, url 25 | from polychart.utils import permanentRedirect 26 | 27 | def _buildPatternList(): 28 | urls = [ 29 | # Main website 30 | url(r'^', include(SITE_URLS)) if SITE_URLS else None, 31 | 32 | # Main app 33 | url(r'^', include(MAIN_URLS)), 34 | 35 | # Polychart.js website 36 | url(r'^js/', include(JS_SITE_URLS)) if JS_SITE_URLS else None, 37 | 38 | # Analytics 39 | url('^', include(ANALYTICS_URLS)) if ANALYTICS_URLS else None, 40 | 41 | # Deprecated URLs 42 | url(r'^beta$', permanentRedirect('/signup')), 43 | url(r'^devkit.*$', permanentRedirect('/')), 44 | url(r'^embed/.*$', permanentRedirect('/')), 45 | ] 46 | 47 | # Filter out None 48 | urls = [x for x in urls if x] 49 | 50 | return patterns('polychart.main.views', *urls) 51 | 52 | urlpatterns = _buildPatternList() 53 | -------------------------------------------------------------------------------- /server/polychart/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for polychart project. 3 | 4 | This module contains the WSGI application used by Django's development server 5 | and any production WSGI deployments. It should expose a module-level variable 6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 7 | this application via the ``WSGI_APPLICATION`` setting. 8 | 9 | Usually you will have the standard Django WSGI application here, but it also 10 | might make sense to replace the whole Django WSGI application with a custom one 11 | that later delegates to the Django one. For example, you could introduce WSGI 12 | middleware here, or combine a Django application with an application of another 13 | framework. 14 | """ 15 | 16 | # This application object is used by any WSGI server configured to use this 17 | # file. This includes Django's development server, if the WSGI_APPLICATION 18 | # setting points here. 19 | from django.core.wsgi import get_wsgi_application 20 | application = get_wsgi_application() 21 | 22 | # Apply WSGI middleware here. 23 | # from helloworld.wsgi import HelloWorldApplication 24 | # application = HelloWorldApplication(application) 25 | -------------------------------------------------------------------------------- /server/polychartQuery/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Provide a public interface for constructing new SQL-esque data sources. 3 | """ 4 | from polychartQuery.sql import MySqlConn, InfobrightConn, PostgreSqlConn 5 | 6 | __all__ = [] 7 | 8 | def openConnection(type, *args, **kwargs): 9 | if type == 'mysql': 10 | return MySqlConn(*args, **kwargs) 11 | 12 | elif type == 'infobright': 13 | return InfobrightConn(*args, **kwargs) 14 | 15 | elif type == 'postgresql': 16 | return PostgreSqlConn(*args, **kwargs) 17 | -------------------------------------------------------------------------------- /server/polychartQuery/csv/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from connection import Conn as Connection 4 | -------------------------------------------------------------------------------- /server/polychartQuery/csv/connection.py: -------------------------------------------------------------------------------- 1 | """ 2 | Module defining a dummy CSV connection. 3 | """ 4 | from polychartQuery.abstract import DataSourceConnection 5 | 6 | class Conn(DataSourceConnection): 7 | """ 8 | Dummy class (for now) 9 | """ 10 | def __init__(self, *args, **kwargs): 11 | super(Conn, self).__init__(*args, **kwargs) 12 | pass 13 | -------------------------------------------------------------------------------- /server/polychartQuery/googleAnalytics/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from query import GAQuery as Query 4 | from connection import GAConn as Connection 5 | -------------------------------------------------------------------------------- /server/polychartQuery/sql/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from polychartQuery.sql.query import SqlQuery, PostgreSqlQuery 4 | from polychartQuery.sql.connection import MySqlConn, PostgreSqlConn, InfobrightConn 5 | -------------------------------------------------------------------------------- /src/poly/README.md: -------------------------------------------------------------------------------- 1 | This directory contains the frontend source files for the Polychart Dashboard 2 | Builder. The templates and Coffeescript files within this directory implement the 3 | `home` view for dashboards. 4 | 5 | `main` 6 | ------ 7 | This directory contains the main source code for the Dashboard builder. Look 8 | within for more information. 9 | 10 | `common` 11 | -------- 12 | Here, internal libraries and shared styles are contained. The `events.coffee` 13 | file implements the internal event bus for Polychart; the `serverApi.coffee` file 14 | defines a uniform internal method for communicating with a remote server; all 15 | other files are style files that are used in this application and other internal 16 | applications. 17 | 18 | `demoData` 19 | ---------- 20 | Data for the Dashboard Builder Demo is contained. 21 | 22 | `examples` 23 | ---------- 24 | This directory contains example source files interacting with the dashboard 25 | builder. 26 | -------------------------------------------------------------------------------- /src/poly/main/anim.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | # Define a basic animations library; useful for loading and transitions. 3 | ### 4 | ANIMATIONS = 5 | loading: 6 | interval: 200, 7 | frames: [ 8 | 'anim_loading_0.svg', 9 | 'anim_loading_1.svg', 10 | 'anim_loading_2.svg' 11 | ] 12 | 13 | class Animation 14 | constructor: (animName, container) -> 15 | anim = ANIMATIONS[animName] 16 | if !anim 17 | throw "Animation " + animName + " does not exist!" 18 | 19 | @div = div = $("
") 20 | div.addClass("anim") 21 | div.addClass(animName) 22 | $(container).append(div) 23 | 24 | _.defer () -> 25 | div.css 26 | width: div.height() 27 | marginLeft: -div.height()/2 28 | marginTop: -div.height()/2 29 | 30 | images = _.map anim.frames, (src) => 31 | img = new Image() 32 | img.src = '/static/main/images/' + src 33 | img 34 | 35 | curFrame = 0 36 | advFrame = () -> 37 | div.css 'background-image', 'url(' + images[curFrame].src + ')' 38 | curFrame = (curFrame + 1) % images.length 39 | @interval = setInterval advFrame, anim.interval 40 | 41 | remove: () => 42 | @div.remove() 43 | clearInterval @interval 44 | 45 | stopOnImage: (imgSrc) => 46 | @div.css 'background-image', 'url(' + imgSrc + ')' 47 | clearInterval @interval 48 | 49 | module.exports = Animation 50 | -------------------------------------------------------------------------------- /src/poly/main/chart/advanced.tmpl: -------------------------------------------------------------------------------- 1 | 8 | 9 | 27 | 28 | 37 | -------------------------------------------------------------------------------- /src/poly/main/chart/aes/base.tmpl: -------------------------------------------------------------------------------- 1 | 21 | 22 | -------------------------------------------------------------------------------- /src/poly/main/chart/aes/color.coffee: -------------------------------------------------------------------------------- 1 | Aesthetic = require('poly/main/chart/aes/base') 2 | 3 | class ColorAesthetic extends Aesthetic 4 | template: 'tmpl-aesthetic-color' 5 | constructor: (@aes, @name, @parent) -> 6 | super(@aes, @name, @parent) 7 | @selected = 'steelblue' 8 | @defaultValue = 'steelblue' 9 | @value = ko.observable(@defaultValue) 10 | @value.subscribe @render 11 | onMetricDiscard: (event, metricItem) => 12 | @metric(null) 13 | @render() 14 | @afterRender(@dom) 15 | afterRender: (dom) => 16 | @dom = dom 17 | simpleColor = $('.selector', dom) 18 | simpleColor.attr 'value', @value() 19 | simpleColor.simpleColor( 20 | cellWidth: 15 21 | cellHeight: 15 22 | boxWidth: 50 23 | boxHeight: 15 24 | border: 0 25 | columns: 9 26 | ) 27 | simpleColor.bind 'change', (evt) => 28 | @value evt.target.value 29 | $(".simpleColorContainer", dom).click () => false 30 | _setConstant: (spec) => 31 | if spec.const 32 | @value(spec.const) 33 | _getConstant: () => 34 | const: @value() 35 | 36 | module.exports = ColorAesthetic 37 | -------------------------------------------------------------------------------- /src/poly/main/chart/aes/color.tmpl: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /src/poly/main/chart/aes/size.coffee: -------------------------------------------------------------------------------- 1 | Aesthetic = require('poly/main/chart/aes/base') 2 | 3 | class SizeAesthetic extends Aesthetic 4 | template: 'tmpl-aesthetic-size' 5 | constructor: (@aes, @name, @parent) -> 6 | super(@aes, @name, @parent) 7 | @selected = 1 8 | @defaultValue = 2 9 | @value = ko.observable(@defaultValue) 10 | @value.subscribe @render 11 | onMetricDiscard: (event, metricItem) => 12 | @metric(null) 13 | @render() 14 | @afterRender(@dom) 15 | afterRender: (dom) => 16 | @dom = dom 17 | slider = $('.selector', dom) 18 | slider.slider( 19 | max: 10 20 | min: 1 21 | step: 1 22 | value: @value() 23 | ) 24 | slider.bind 'slidechange', (evt, ui) => 25 | @value(ui.value) 26 | _setConstant: (spec) => 27 | if spec.const 28 | @value(spec.const) 29 | _getConstant: () => 30 | const: @value() 31 | 32 | module.exports = SizeAesthetic 33 | -------------------------------------------------------------------------------- /src/poly/main/chart/aes/size.tmpl: -------------------------------------------------------------------------------- 1 | 25 | -------------------------------------------------------------------------------- /src/poly/main/chart/chartbuilder.tmpl: -------------------------------------------------------------------------------- 1 | 39 | 40 | 43 | -------------------------------------------------------------------------------- /src/poly/main/chart/layer.tmpl: -------------------------------------------------------------------------------- 1 | 36 | -------------------------------------------------------------------------------- /src/poly/main/dash/aes.tmpl: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /src/poly/main/dash/dash.tmpl: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /src/poly/main/dash/dashboard.coffee: -------------------------------------------------------------------------------- 1 | QuickAddView = require('poly/main/dash/quickadd') 2 | WorkspaceView = require('poly/main/dash/workspace') 3 | 4 | class DashboardView 5 | constructor: (title, tableMetaData) -> 6 | @workspaceView = new WorkspaceView(title, tableMetaData) 7 | @quickaddView = new QuickAddView(tableMetaData) 8 | 9 | serialize: () => 10 | @workspaceView.serialize() 11 | 12 | initialize: (initial) => @workspaceView.initialize(initial) 13 | 14 | module.exports = DashboardView 15 | -------------------------------------------------------------------------------- /src/poly/main/dash/item/chart.tmpl: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /src/poly/main/dash/item/comment.coffee: -------------------------------------------------------------------------------- 1 | TextItem = require('poly/main/dash/item/text') 2 | 3 | CONST = require('poly/main/const') 4 | 5 | class CommentItem extends TextItem 6 | constructor: (@author, value, position={}) -> 7 | @author or= PAGE_VARIABLE?.USERNAME ? '' 8 | position.width or= 5 9 | position.height or= 7 10 | 11 | @minWidth = 5 12 | @minHeight = 5 13 | 14 | @templateName = 'tmpl-comment-item' unless @templateName 15 | super(value, position, 'Write a comment here...') 16 | 17 | @shiftedZIndex = ko.computed => 18 | 1000000 + @zIndex() 19 | 20 | serialize: (s={}) => 21 | s.author = @author 22 | s.itemType = "CommentItem" 23 | super s 24 | 25 | deserialize: (s) => 26 | @author = s.author if s.author 27 | super(s) 28 | 29 | module.exports = CommentItem 30 | -------------------------------------------------------------------------------- /src/poly/main/dash/item/comment.tmpl: -------------------------------------------------------------------------------- 1 | 28 | -------------------------------------------------------------------------------- /src/poly/main/dash/item/numeral.tmpl: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /src/poly/main/dash/item/pivottable.tmpl: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /src/poly/main/dash/item/polyjs.coffee: -------------------------------------------------------------------------------- 1 | Animation = require('poly/main/anim') 2 | DashItem = require('poly/main/dash/item/base') 3 | Events = require('poly/main/events') 4 | 5 | CONST = require('poly/main/const') 6 | TOAST = require('poly/main/error/toast') 7 | 8 | PADDING = 10 # extra padding for chart view outside of chart 9 | 10 | class PolyJSItem extends DashItem 11 | constructor: (@spec=null, position) -> 12 | @redraw = _.debounce(@_redraw, 300, true) 13 | @templateName = 'tmpl-chart-item' unless @templateName 14 | super(position) 15 | 16 | init: (dom) => 17 | super(dom) 18 | @itemdom = $('.chart-inner, .inner', dom) 19 | @spec.dom = @itemdom[0] 20 | @_initSpec() 21 | Events.error.polyjs.data.on (event) => 22 | @loadingAnim.stopOnImage("/static/main/images/broken_chart.svg") 23 | @onResize() 24 | 25 | _initSpec: () => Error("Not implemented") 26 | 27 | onResize: => 28 | if @itemdom then @redraw() 29 | 30 | _redraw: () => 31 | if !@itemdom 32 | # init() has to be called first so that @spec has a DOM element 33 | throw "Can't make chart before init() is called!" 34 | 35 | @spec.width = @itemdom.width() - PADDING 36 | @spec.height = @itemdom.height() - PADDING 37 | prepare = 38 | if @isViewer() 39 | -> 40 | else 41 | () => @itemdom.empty() 42 | @_renderPolyJSItem(@spec, @loaded, prepare) 43 | 44 | setSpec: (@spec, isDeserializing=false) => 45 | unless isDeserializing 46 | Events.model.dashboarditem.update.trigger() 47 | 48 | deserialize: (s) => 49 | @setSpec(s.spec, true) 50 | super(s) 51 | 52 | module.exports = PolyJSItem 53 | -------------------------------------------------------------------------------- /src/poly/main/dash/item/text.coffee: -------------------------------------------------------------------------------- 1 | DashItem = require('poly/main/dash/item/base') 2 | Events = require('poly/main/events') 3 | 4 | CONST = require('poly/main/const') 5 | 6 | class TextItem extends DashItem 7 | constructor: (@textContent, position={}, @defaultText='Type here...') -> 8 | unless ko.isObservable(@textContent) 9 | @textContent = ko.observable @textContent 10 | 11 | @templateName = 'tmpl-text-item' unless @templateName 12 | super(position) 13 | 14 | init: (@dom) => 15 | super(dom) 16 | @loaded() 17 | 18 | onEditAreaBlur: -> 19 | Events.model.dashboarditem.update.trigger() 20 | 21 | return true 22 | 23 | serialize: (s={}) => 24 | s.itemType = s.itemType ? 'TextItem' 25 | s.textContent = @textContent() 26 | super s 27 | 28 | deserialize: (s) => 29 | @textContent(s.textContent) if s.textContent 30 | super(s) 31 | 32 | module.exports = TextItem 33 | -------------------------------------------------------------------------------- /src/poly/main/dash/item/text.tmpl: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /src/poly/main/dash/workspace.tmpl: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /src/poly/main/data/data.tmpl: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /src/poly/main/data/metric/base.coffee: -------------------------------------------------------------------------------- 1 | class MetricView 2 | constructor: (@columnInfo) -> 3 | {@name, @tableName, @gaType} = @columnInfo 4 | @type = @columnInfo.meta.type 5 | @gaType = @columnInfo.gaType 6 | @originalFormula = @columnInfo.formula 7 | @formula = 8 | if @originalFormula? 9 | @originalFormula 10 | else if @name == 'count(*)' 11 | @name 12 | else 13 | "[#{@tableName}.#{@name}]" 14 | @extraCSS = 15 | if @columnInfo.formula 16 | 'derived-var' 17 | else 18 | @gaType 19 | 20 | fullFormula: (tableMetaData, unbracket=false) => 21 | @columnInfo.getFormula(tableMetaData, unbracket) 22 | 23 | fullMeta: () -> @columnInfo.meta 24 | 25 | module.exports = MetricView 26 | -------------------------------------------------------------------------------- /src/poly/main/data/metric/base.tmpl: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | -------------------------------------------------------------------------------- /src/poly/main/data/metric/dropdown.coffee: -------------------------------------------------------------------------------- 1 | Events = require('poly/main/events') 2 | MetricView = require('poly/main/data/metric/base') 3 | 4 | CONST = require('poly/main/const') 5 | 6 | class DropdownMetricView extends MetricView 7 | constructor: (columnInfo, @label, @dropdownTemplate, @dropdownData, @dropdownAfterRender) -> 8 | @toggleDropdown = _.throttle @_toggleDropdown # ..or clicks gets registerd 2x 9 | @dropdownShowing = false 10 | super(columnInfo) 11 | 12 | _toggleDropdown: => 13 | if @name isnt 'count(*)' 14 | if @dropdownShowing 15 | Events.ui.dropdown.hide.trigger() 16 | else 17 | Events.ui.dropdown.show.trigger { 18 | templateName: @dropdownTemplate 19 | data: @dropdownData 20 | targetDom: @dom 21 | onRemove: @setInactive 22 | afterRender: @dropdownAfterRender 23 | info: {name: @label, value: @name} 24 | } 25 | 26 | setInactive: => 27 | @dropdownShowing = false 28 | if @dom 29 | @dom.removeClass 'dropdown-active' 30 | @dom.draggable 'enable' 31 | 32 | close: => 33 | Events.ui.dropdown.hide.trigger() 34 | 35 | attachDropdown: (dom) => 36 | @dom = $(dom) 37 | Events.ui.dropdown.shown.onElem @dom, => 38 | @dropdownShowing = true 39 | @dom.addClass 'dropdown-active' 40 | @dom.draggable 'disable' 41 | Events.ui.dropdown.hidden.onElem @dom, @setInactive 42 | 43 | module.exports = DropdownMetricView 44 | -------------------------------------------------------------------------------- /src/poly/main/data/metric/dropdown.tmpl: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /src/poly/main/data/metric/facet.coffee: -------------------------------------------------------------------------------- 1 | AttachedMetricView = require('poly/main/data/metric/attached') 2 | Events = require('poly/main/events') 3 | 4 | CONST = require('poly/main/const') 5 | 6 | class FacetMetricView extends AttachedMetricView 7 | constructor: (columnInfo) -> 8 | updateFn = () => Events.ui.chart.render.trigger() 9 | options = () -> CONST.facets 10 | attachedMetrics = () -> [] 11 | @removeText = "Remove \"" + columnInfo.name + "\" from facet" 12 | super columnInfo, @aes, updateFn, options, attachedMetrics 13 | 14 | module.exports = FacetMetricView 15 | -------------------------------------------------------------------------------- /src/poly/main/data/metric/layer.coffee: -------------------------------------------------------------------------------- 1 | AttachedMetricView = require('poly/main/data/metric/attached') 2 | Events = require('poly/main/events') 3 | 4 | CONST = require('poly/main/const') 5 | 6 | class LayerMetricView extends AttachedMetricView 7 | constructor: (columnInfo, @options, @layer, @aesName, defaults) -> 8 | updateFn = () => Events.ui.chart.render.trigger() 9 | attachedMetrics = @layer.attachedMetrics 10 | super columnInfo, @aesName, updateFn, @options, attachedMetrics, defaults 11 | 12 | module.exports = LayerMetricView 13 | -------------------------------------------------------------------------------- /src/poly/main/data/metric/quickadd.coffee: -------------------------------------------------------------------------------- 1 | AttachedMetricView = require('poly/main/data/metric/attached') 2 | 3 | class QuickAddMetricView extends AttachedMetricView 4 | constructor: (columnInfo, @options, @aes) -> 5 | updateFn = () -> 6 | attachedMetrics = () -> [] 7 | @removeText = "Remove \"" + columnInfo.name + "\" from " + @aes 8 | super columnInfo, @aes, updateFn, @options, attachedMetrics 9 | 10 | module.exports = QuickAddMetricView 11 | -------------------------------------------------------------------------------- /src/poly/main/data/metric/quickaddtable.coffee: -------------------------------------------------------------------------------- 1 | AttachedMetricView = require('poly/main/data/metric/attached') 2 | 3 | class QuickAddTableMetricView extends AttachedMetricView 4 | constructor: (columnInfo, @options, @aes, attachedMetrics) -> 5 | updateFn = () -> 6 | @removeText = "Remove \"" + columnInfo.name + "\" from " + @aes 7 | super columnInfo, @aes, updateFn, @options, attachedMetrics 8 | 9 | module.exports = QuickAddTableMetricView 10 | -------------------------------------------------------------------------------- /src/poly/main/data/table.tmpl: -------------------------------------------------------------------------------- 1 | 21 | 22 | -------------------------------------------------------------------------------- /src/poly/main/error/toast.coffee: -------------------------------------------------------------------------------- 1 | raise = (text) -> 2 | $().toastmessage('showErrorToast', text) 3 | 4 | module.exports = { 5 | raise 6 | } 7 | -------------------------------------------------------------------------------- /src/poly/main/header.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 35 | -------------------------------------------------------------------------------- /src/poly/main/init.coffee: -------------------------------------------------------------------------------- 1 | # Setup necessary prior to Polychart being used 2 | 3 | # load all the knockout templates 4 | require('poly/main/templates') 5 | 6 | # initiate custom KO bindings 7 | require('poly/main/bindings').init() 8 | -------------------------------------------------------------------------------- /src/poly/main/main/builder.coffee: -------------------------------------------------------------------------------- 1 | # Top level view entry point - abstract classes 2 | # Code that is shared for both chart and dashboard builder 3 | 4 | Events = require('poly/main/events') 5 | DataView = require('poly/main/data/dataView') 6 | HeaderView = require('poly/main/header') 7 | OverlayView = require('poly/main/overlay') 8 | ShareView = require('poly/main/share') 9 | 10 | class AbstractBuilderEntryPoint 11 | constructor: (@params) -> 12 | Events.registerDefaultListeners() 13 | 14 | {@dom, @dataCollection, @exportingEnabled} = params 15 | 16 | # TODO Support multiple data sources on client 17 | if @dataCollection 18 | unless _.isArray(@dataCollection) 19 | @dataCollection = [@dataCollection] 20 | @dataSource = poly.data @dataCollection[0] 21 | @dataView = new DataView(@dataSource) 22 | @overlayView = new OverlayView() 23 | else 24 | throw new Error('No data collection provided!') 25 | 26 | @hasHeader = params.header ? false 27 | @headerView = new HeaderView(params.isDemo) 28 | @shareView = new ShareView() 29 | 30 | if params.width is 'fill' 31 | $(@dom).width('100%') 32 | if _.isNumber(params.width) 33 | $(@dom).width(params.width) 34 | if params.height is 'fill' 35 | $(@dom).height('100%') 36 | if _.isNumber(params.height) 37 | $(@dom).height(params.height) 38 | if params.width is 'fill' and params.height is 'fill' 39 | $(@dom).addClass('fill') 40 | 41 | initialize: () => 42 | @dataView.initialize() 43 | 44 | module.exports = AbstractBuilderEntryPoint 45 | -------------------------------------------------------------------------------- /src/poly/main/main/chartbuilder.tmpl: -------------------------------------------------------------------------------- 1 | 28 | -------------------------------------------------------------------------------- /src/poly/main/main/chartviewer.coffee: -------------------------------------------------------------------------------- 1 | # Top level view for viewing a particular chart 2 | AbstractViewerEntryPoint = require('poly/main/main/viewer') 3 | 4 | class ChartViewerMainView extends AbstractViewerEntryPoint 5 | constructor: (@params) -> 6 | super(@params) 7 | @spec = params.initial ? {} 8 | @tableMetaData = @dataView.getTableMetaData() 9 | 10 | init: (dom) => 11 | initialCols = @spec.newcols ? [] 12 | @dataView.initialize initialCols, () => 13 | # instead of using spec.layer, use spec.layers 14 | if @spec.layer 15 | @spec.layers = [@spec.layer] 16 | delete @spec.layer 17 | # generate the polyJS object for each layer (given tableName) 18 | for layer in @spec.layers 19 | if not layer.data 20 | tableName = layer.tableName 21 | if not tableName? and layer.meta and m = _.toArray(layer.meta)[0] 22 | tableName = m.tableName 23 | layer.data = @tableMetaData.polyJsObjectFor {tableName} 24 | @spec.width ?= @params.width 25 | @spec.height ?= @params.height 26 | @spec.dom = dom[0] 27 | polyjs.chart(@spec) 28 | 29 | 30 | module.exports = ChartViewerMainView 31 | -------------------------------------------------------------------------------- /src/poly/main/main/chartviewer.tmpl: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /src/poly/main/main/dashviewer.coffee: -------------------------------------------------------------------------------- 1 | # Top level view for viewing a particular chart 2 | AbstractViewerEntryPoint = require('poly/main/main/viewer') 3 | WorkspaceView = require('poly/main/dash/workspace') 4 | 5 | class DashViewerMainView extends AbstractViewerEntryPoint 6 | constructor: (@params) -> 7 | super(@params) 8 | 9 | tableMetaData = @dataView.getTableMetaData() 10 | @workspaceView = new WorkspaceView(@params.name, tableMetaData, true) 11 | @initialize() 12 | 13 | initialize: () -> 14 | initial = @params.initial ? [] 15 | if _.isArray(@params.initial) 16 | initialItems = @params.initial 17 | initialCols = [] 18 | else if _.isObject(@params.initial) 19 | initialItems = @params.initial.items ? [] 20 | initialCols = @params.initial.newcols ? [] 21 | 22 | @dataView.initialize initialCols, () => @workspaceView.initialize(initialItems) 23 | 24 | module.exports = DashViewerMainView 25 | -------------------------------------------------------------------------------- /src/poly/main/main/dashviewer.tmpl: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/poly/main/main/viewer.coffee: -------------------------------------------------------------------------------- 1 | # Top level view entry point - abstract classes 2 | # Code that is shared for both chart and dashboard Viewer 3 | 4 | DataView = require('poly/main/data/dataView') 5 | Events = require('poly/main/events') 6 | 7 | class AbstractViewerEntryPoint 8 | constructor: (@params) -> 9 | Events.registerDefaultListeners() 10 | 11 | {@dom, @dataCollection} = params 12 | 13 | # TODO Support multiple data sources on client 14 | if @dataCollection 15 | unless _.isArray(@dataCollection) 16 | @dataCollection = [@dataCollection] 17 | # shared "views" 18 | @dataSource = poly.data @dataCollection[0] 19 | else 20 | throw new Error('No data collection provided!') 21 | 22 | @dataView = new DataView(@dataSource) 23 | 24 | initialize: () => 25 | @dataView.initialize() 26 | 27 | module.exports = AbstractViewerEntryPoint 28 | -------------------------------------------------------------------------------- /src/poly/main/numeral/numeralbuilder.tmpl: -------------------------------------------------------------------------------- 1 | 25 | 26 | 29 | -------------------------------------------------------------------------------- /src/poly/main/share.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | # Hook up events for the share panel. 3 | ### 4 | CONST = require('poly/main/const') 5 | Events = require('poly/main/events') 6 | serverApi = require('poly/common/serverApi') 7 | 8 | class ShareView 9 | constructor: () -> 10 | @defaultPanelRight = -250 11 | @panelRight = ko.observable(@defaultPanelRight) 12 | Events.nav.sharepanel.open.on @open 13 | Events.nav.sharepanel.close.on @close 14 | 15 | open: () => 16 | @panelRight(0) 17 | 18 | close: () => 19 | @panelRight(@defaultPanelRight) 20 | 21 | exportPDF: -> 22 | Events.export.pdf.click.trigger callback: @_export('pdf') 23 | 24 | exportPNG: -> 25 | Events.export.png.click.trigger callback: @_export('png') 26 | 27 | exportSVG: -> 28 | Events.export.svg.click.trigger callback: @_export('svg') 29 | 30 | _export: (type) -> (serial) -> 31 | serverApi.sendPost( 32 | "/dashboard/export/code" 33 | , {serial: serial, exportType: type} 34 | , (err, result) -> 35 | if err 36 | console.error err 37 | TOAST.raise 'Error exporting dashboard.' 38 | return 39 | 40 | window.location = "/api/dashboard/export/" + encodeURIComponent(result.code) 41 | ) 42 | 43 | module.exports = ShareView 44 | -------------------------------------------------------------------------------- /src/poly/main/share.tmpl: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /src/poly/main/table/aesGroup.tmpl: -------------------------------------------------------------------------------- 1 | 33 | 34 | -------------------------------------------------------------------------------- /src/poly/main/table/table.less: -------------------------------------------------------------------------------- 1 | .tablebuilder-table { 2 | height: 100%; 3 | overflow: scroll; 4 | padding: 2px; 5 | 6 | text-align: center; 7 | background-color: @white; 8 | } 9 | -------------------------------------------------------------------------------- /src/poly/main/table/tablebuilder.tmpl: -------------------------------------------------------------------------------- 1 | 32 | 33 | 36 | -------------------------------------------------------------------------------- /src/poly/signup.coffee: -------------------------------------------------------------------------------- 1 | Events = require('poly/main/events') 2 | 3 | init = () -> 4 | Events.signup.page.view.trigger() 5 | eulaClickTrap '#start', '#realsubmit', '#eula', Events.signup.eula.error 6 | Events.signup.form.submit.trackForm $('#realsubmit') 7 | $('input').on 'keypress keydown', _.once -> 8 | Events.signup.form.interact.trigger() 9 | 10 | eulaClickTrap = (submitBtn, hiddenBtn, eula, errorEvt) -> 11 | $(submitBtn).on 'click', (e) -> 12 | e.stopPropagation() 13 | if $(eula).prop 'checked' 14 | $(hiddenBtn).click() 15 | else 16 | if errorEvt then errorEvt.trigger() 17 | alert 'You must first read and accept the Terms and Conditions.' 18 | 19 | module.exports = { 20 | init 21 | } 22 | -------------------------------------------------------------------------------- /src/poly/verifyPendingDs.coffee: -------------------------------------------------------------------------------- 1 | serverApi = require('poly/common/serverApi') 2 | 3 | load = (dsParams) -> 4 | serverApi.sendPost '/data-source/create', dsParams, (err, response) -> 5 | if err 6 | console.error err 7 | else 8 | window.location.href = '/home' 9 | 10 | module.exports = {run} 11 | -------------------------------------------------------------------------------- /system_requirements: -------------------------------------------------------------------------------- 1 | # Package names on Ubuntu. 2 | # Don't forget to also update the Chef recipe. 3 | 4 | libffi-dev 5 | libmysqlclient-dev 6 | libpq-dev 7 | libxml2-dev 8 | libxslt-dev 9 | mysql-client-core-5.5 10 | mysql-server 11 | python-setuptools 12 | python-virtualenv 13 | python2.7-dev 14 | socat 15 | --------------------------------------------------------------------------------