diff --git a/MANIFEST.in b/MANIFEST.in index 8bf87f21..6660f22c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ include README.md include LICENSE -recursive-include qgridjs *.* +recursive-include qgrid/qgridjs *.* +recursive-include qgrid/templates *.* diff --git a/qgrid.py b/qgrid.py deleted file mode 100644 index f12af8c2..00000000 --- a/qgrid.py +++ /dev/null @@ -1,156 +0,0 @@ -import pandas as pd -import numpy as np -from textwrap import dedent -import uuid -import json -import pkgutil -import IPython.html.nbextensions as nb_ext - -from IPython.display import display_html, display_javascript - -SLICK_GRID_CSS = dedent( - """ - -
-
-
- """ -) - -SLICK_GRID_JS = dedent( - """ - var path_dictionary = {{ - jquery_drag: "{cdn_base_url}/lib/jquery.event.drag-2.2", - slick_core: "{cdn_base_url}/lib/slick.core.2.2", - slick_data_view: "{cdn_base_url}/lib/slick.dataview.2.2", - slick_grid: "{cdn_base_url}/lib/slick.grid.2.2", - data_grid: "{cdn_base_url}/qgrid", - date_filter: "{cdn_base_url}/qgrid.datefilter", - slider_filter: "{cdn_base_url}/qgrid.sliderfilter", - filter_base: "{cdn_base_url}/qgrid.filterbase", - handlebars: "https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.min" - }}; - - var existing_config = require.s.contexts._.config; - if (!existing_config.paths['underscore']){{ - path_dictionary['underscore'] = "https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min"; - }} - - if (!existing_config.paths['moment']){{ - path_dictionary['moment'] = "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.3/moment.min"; - }} - - if (!existing_config.paths['jqueryui']){{ - path_dictionary['jqueryui'] = "https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/jquery-ui.min"; - }} - - require.config({{ - paths: path_dictionary - }}); - - if (typeof jQuery === 'function') {{ - define('jquery', function() {{ return jQuery; }}); - }} - - require([ - 'jquery', - 'jquery_drag', - 'slick_core', - 'slick_data_view' - ], - function($){{ - $('#{div_id}').closest('.rendered_html').removeClass('rendered_html'); - require(['slick_grid'], function(){{ - require(["data_grid"], function(dgrid){{ - var grid = new dgrid.QGrid('#{div_id}', {data_frame_json}, {column_types_json}); - grid.initialize_slick_grid(); - }}); - }}); - }}); - """ -) - -class SlickGrid(object): - remote_mode = True - def __init__(self, data_frame): - self.data_frame = data_frame - self.div_id = str(uuid.uuid4()) - - self.df_copy = data_frame.copy() - - if type(self.df_copy.index) == pd.core.index.MultiIndex: - self.df_copy.reset_index(inplace=True) - else: - self.df_copy.insert(0, self.df_copy.index.name, self.df_copy.index) - - tc = dict(np.typecodes) - for key in np.typecodes.keys(): - if "All" in key: - del tc[key] - - self.column_types = [] - for col_name, dtype in self.df_copy.dtypes.iteritems(): - found_type = False - column_type = {'field': col_name} - for type_name, type_codes in tc.items(): - if dtype.kind in type_codes: - found_type = True - column_type['type'] = type_name - break - self.column_types.append(column_type) - - self.precision = pd.get_option('display.precision') - 1 - - def _ipython_display_(self): - try: - column_types_json = json.dumps(self.column_types) - data_frame_json = self.df_copy.to_json(orient='records', date_format='iso', double_precision=self.precision) - - if SlickGrid.remote_mode: - cdn_base_url = "https://cdn.rawgit.com/quantopian/qgrid/a1abd45434b0dddc835fbb1279670dd689dbfc30/qgridjs" - else: - cdn_base_url = "/nbextensions/qgridjs" - - raw_html = SLICK_GRID_CSS.format(div_id=self.div_id, cdn_base_url=cdn_base_url) - raw_js = SLICK_GRID_JS.format(cdn_base_url=cdn_base_url, - div_id=self.div_id, - data_frame_json=data_frame_json, - column_types_json=column_types_json) - - display_html(raw_html, raw=True) - display_javascript(raw_js, raw=True) - except Exception, err: - display_html('ERROR: {}'.format(str(err)), raw=True) - -def set_remote_mode(remote_mode=True): - SlickGrid.remote_mode = remote_mode - -def show_grid(data_frame): - return SlickGrid(data_frame) - -def load_ipython_extension(ipython): - """ - Entrypoint for ipython. Add objects to the user's namespace by adding them - to the dictionary passed to ipython.push. - """ - js_pkg = pkgutil.get_loader("qgridjs") - if js_pkg != None: - qgridjs_path = js_pkg.filename - nb_ext.install_nbextension(qgridjs_path, overwrite=True, symlink=False, verbose=0) - SlickGrid.remote_mode = False - else: - SlickGrid.remote_mode = True - # ipython.push( - # { - # 'qgrid': qgrid - # } - # ) diff --git a/qgrid/__init__.py b/qgrid/__init__.py new file mode 100644 index 00000000..25d1e82b --- /dev/null +++ b/qgrid/__init__.py @@ -0,0 +1,25 @@ +from .grid import SlickGrid + + +def show_grid(data_frame, remote_js=False): + return SlickGrid(data_frame, remote_js) + + +def nbinstall(overwrite=False): + """ + """ + # Lazy imports so we don't pollute the namespace. + import os + from IPython.html.nbextensions import install_nbextension + + qgridjs_path = os.path.join( + os.path.dirname(__file__), + 'qgridjs', + ) + + install_nbextension( + qgridjs_path, + overwrite=overwrite, + symlink=False, + verbose=0, + ) diff --git a/qgrid/grid.py b/qgrid/grid.py new file mode 100644 index 00000000..541c9676 --- /dev/null +++ b/qgrid/grid.py @@ -0,0 +1,83 @@ +import pandas as pd +import numpy as np +import os +import uuid +import json + +from IPython.display import display_html, display_javascript + + +def template_contents(filename): + template_filepath = os.path.join( + os.path.dirname(__file__), + 'templates', + filename, + ) + with open(template_filepath) as f: + return f.read() + + +SLICK_GRID_CSS = template_contents('slickgrid.css.template') +SLICK_GRID_JS = template_contents('slickgrid.js.template') + + +class SlickGrid(object): + + def __init__(self, data_frame, remote_js=False): + self.data_frame = data_frame + self.remote_js = remote_js + self.div_id = str(uuid.uuid4()) + + self.df_copy = data_frame.copy() + + if type(self.df_copy.index) == pd.core.index.MultiIndex: + self.df_copy.reset_index(inplace=True) + else: + self.df_copy.insert(0, self.df_copy.index.name, self.df_copy.index) + + tc = dict(np.typecodes) + for key in np.typecodes.keys(): + if "All" in key: + del tc[key] + + self.column_types = [] + for col_name, dtype in self.df_copy.dtypes.iteritems(): + column_type = {'field': col_name} + for type_name, type_codes in tc.items(): + if dtype.kind in type_codes: + column_type['type'] = type_name + break + self.column_types.append(column_type) + + self.precision = pd.get_option('display.precision') - 1 + + def _ipython_display_(self): + try: + column_types_json = json.dumps(self.column_types) + data_frame_json = self.df_copy.to_json( + orient='records', + date_format='iso', + double_precision=self.precision, + ) + + if self.remote_js: + cdn_base_url = \ + "https://rawgit.com/quantopian/qgrid/master/qgrid/qgridjs" + else: + cdn_base_url = "/nbextensions/qgridjs" + + raw_html = SLICK_GRID_CSS.format( + div_id=self.div_id, + cdn_base_url=cdn_base_url, + ) + raw_js = SLICK_GRID_JS.format( + cdn_base_url=cdn_base_url, + div_id=self.div_id, + data_frame_json=data_frame_json, + column_types_json=column_types_json, + ) + + display_html(raw_html, raw=True) + display_javascript(raw_js, raw=True) + except Exception as err: + display_html('ERROR: {}'.format(str(err)), raw=True) diff --git a/qgridjs/__init__.py b/qgrid/qgridjs/__init__.py similarity index 100% rename from qgridjs/__init__.py rename to qgrid/qgridjs/__init__.py diff --git a/qgridjs/lib/collapse.gif b/qgrid/qgridjs/lib/collapse.gif similarity index 100% rename from qgridjs/lib/collapse.gif rename to qgrid/qgridjs/lib/collapse.gif diff --git a/qgridjs/lib/expand.gif b/qgrid/qgridjs/lib/expand.gif similarity index 100% rename from qgridjs/lib/expand.gif rename to qgrid/qgridjs/lib/expand.gif diff --git a/qgridjs/lib/jquery.event.drag-2.2.js b/qgrid/qgridjs/lib/jquery.event.drag-2.2.js similarity index 100% rename from qgridjs/lib/jquery.event.drag-2.2.js rename to qgrid/qgridjs/lib/jquery.event.drag-2.2.js diff --git a/qgridjs/lib/slick-default-theme.css b/qgrid/qgridjs/lib/slick-default-theme.css similarity index 100% rename from qgridjs/lib/slick-default-theme.css rename to qgrid/qgridjs/lib/slick-default-theme.css diff --git a/qgridjs/lib/slick.autotooltips.js b/qgrid/qgridjs/lib/slick.autotooltips.js similarity index 100% rename from qgridjs/lib/slick.autotooltips.js rename to qgrid/qgridjs/lib/slick.autotooltips.js diff --git a/qgridjs/lib/slick.checkboxselectcolumn.js b/qgrid/qgridjs/lib/slick.checkboxselectcolumn.js similarity index 100% rename from qgridjs/lib/slick.checkboxselectcolumn.js rename to qgrid/qgridjs/lib/slick.checkboxselectcolumn.js diff --git a/qgridjs/lib/slick.core.2.2.js b/qgrid/qgridjs/lib/slick.core.2.2.js similarity index 100% rename from qgridjs/lib/slick.core.2.2.js rename to qgrid/qgridjs/lib/slick.core.2.2.js diff --git a/qgridjs/lib/slick.dataview.2.2.js b/qgrid/qgridjs/lib/slick.dataview.2.2.js similarity index 100% rename from qgridjs/lib/slick.dataview.2.2.js rename to qgrid/qgridjs/lib/slick.dataview.2.2.js diff --git a/qgridjs/lib/slick.grid.2.2.js b/qgrid/qgridjs/lib/slick.grid.2.2.js similarity index 100% rename from qgridjs/lib/slick.grid.2.2.js rename to qgrid/qgridjs/lib/slick.grid.2.2.js diff --git a/qgridjs/lib/slick.grid.css b/qgrid/qgridjs/lib/slick.grid.css similarity index 100% rename from qgridjs/lib/slick.grid.css rename to qgrid/qgridjs/lib/slick.grid.css diff --git a/qgridjs/lib/slick.rowselectionmodel.js b/qgrid/qgridjs/lib/slick.rowselectionmodel.js similarity index 100% rename from qgridjs/lib/slick.rowselectionmodel.js rename to qgrid/qgridjs/lib/slick.rowselectionmodel.js diff --git a/qgridjs/lib/sort_asc.png b/qgrid/qgridjs/lib/sort_asc.png similarity index 100% rename from qgridjs/lib/sort_asc.png rename to qgrid/qgridjs/lib/sort_asc.png diff --git a/qgridjs/lib/sort_desc.png b/qgrid/qgridjs/lib/sort_desc.png similarity index 100% rename from qgridjs/lib/sort_desc.png rename to qgrid/qgridjs/lib/sort_desc.png diff --git a/qgridjs/qgrid.css b/qgrid/qgridjs/qgrid.css similarity index 100% rename from qgridjs/qgrid.css rename to qgrid/qgridjs/qgrid.css diff --git a/qgridjs/qgrid.datefilter.js b/qgrid/qgridjs/qgrid.datefilter.js similarity index 100% rename from qgridjs/qgrid.datefilter.js rename to qgrid/qgridjs/qgrid.datefilter.js diff --git a/qgridjs/qgrid.filterbase.js b/qgrid/qgridjs/qgrid.filterbase.js similarity index 100% rename from qgridjs/qgrid.filterbase.js rename to qgrid/qgridjs/qgrid.filterbase.js diff --git a/qgridjs/qgrid.js b/qgrid/qgridjs/qgrid.js similarity index 100% rename from qgridjs/qgrid.js rename to qgrid/qgridjs/qgrid.js diff --git a/qgridjs/qgrid.securityfilter.js b/qgrid/qgridjs/qgrid.securityfilter.js similarity index 100% rename from qgridjs/qgrid.securityfilter.js rename to qgrid/qgridjs/qgrid.securityfilter.js diff --git a/qgridjs/qgrid.sliderfilter.js b/qgrid/qgridjs/qgrid.sliderfilter.js similarity index 100% rename from qgridjs/qgrid.sliderfilter.js rename to qgrid/qgridjs/qgrid.sliderfilter.js diff --git a/qgridjs/qgrid.textfilter.js b/qgrid/qgridjs/qgrid.textfilter.js similarity index 100% rename from qgridjs/qgrid.textfilter.js rename to qgrid/qgridjs/qgrid.textfilter.js diff --git a/qgrid/templates/slickgrid.css.template b/qgrid/templates/slickgrid.css.template new file mode 100644 index 00000000..dffce00c --- /dev/null +++ b/qgrid/templates/slickgrid.css.template @@ -0,0 +1,13 @@ + +
+
+
\ No newline at end of file diff --git a/qgrid/templates/slickgrid.js.template b/qgrid/templates/slickgrid.js.template new file mode 100644 index 00000000..ac89c86a --- /dev/null +++ b/qgrid/templates/slickgrid.js.template @@ -0,0 +1,48 @@ +var path_dictionary = {{ + jquery_drag: "{cdn_base_url}/lib/jquery.event.drag-2.2", + slick_core: "{cdn_base_url}/lib/slick.core.2.2", + slick_data_view: "{cdn_base_url}/lib/slick.dataview.2.2", + slick_grid: "{cdn_base_url}/lib/slick.grid.2.2", + data_grid: "{cdn_base_url}/qgrid", + date_filter: "{cdn_base_url}/qgrid.datefilter", + slider_filter: "{cdn_base_url}/qgrid.sliderfilter", + filter_base: "{cdn_base_url}/qgrid.filterbase", + handlebars: "https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.min" +}}; + +var existing_config = require.s.contexts._.config; +if (!existing_config.paths['underscore']){{ + path_dictionary['underscore'] = "https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min"; +}} + +if (!existing_config.paths['moment']){{ + path_dictionary['moment'] = "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.3/moment.min"; +}} + +if (!existing_config.paths['jqueryui']){{ + path_dictionary['jqueryui'] = "https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/jquery-ui.min"; +}} + +require.config({{ + paths: path_dictionary +}}); + +if (typeof jQuery === 'function') {{ + define('jquery', function() {{ return jQuery; }}); +}} + +require([ + 'jquery', + 'jquery_drag', + 'slick_core', + 'slick_data_view' +], +function($){{ + $('#{div_id}').closest('.rendered_html').removeClass('rendered_html'); + require(['slick_grid'], function(){{ + require(["data_grid"], function(dgrid){{ + var grid = new dgrid.QGrid('#{div_id}', {data_frame_json}, {column_types_json}); + grid.initialize_slick_grid(); + }}); + }}); +}}); \ No newline at end of file diff --git a/setup.py b/setup.py index c8ba47a4..599b0137 100644 --- a/setup.py +++ b/setup.py @@ -13,17 +13,15 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import sys -from setuptools import setup, find_packages +from setuptools import setup setup( name='qgrid', version='0.1.0', - description='An extension for viewing pandas DataFrames in IPython notebook.', + description='A Pandas DataFrame viewer for IPython Notebook.', author='Quantopian Inc.', author_email='tshawver@quantopian.com', - packages=['qgridjs'], - py_modules = ['qgrid'], + packages=['qgrid'], license='Apache 2.0', include_package_data=True, zip_safe=False, @@ -38,11 +36,11 @@ 'Intended Audience :: Science/Research', 'Topic :: Office/Business :: Financial', 'Topic :: Scientific/Engineering :: Information Analysis', - 'Topic :: System :: Distributed Computing', ], install_requires=[ 'numpy', - 'pandas' + 'pandas', + 'ipython[notebook]', ], url="https://github.com/quantopian/qgrid" )