Skip to content

Commit

Permalink
1.7.9
Browse files Browse the repository at this point in the history
- added support for google colab using flask_ngrok
- bugfixes for  #73, #72, #71
  • Loading branch information
Andrew Schonfeld authored and aschonfeld committed Feb 24, 2020
1 parent a8da8ae commit 55ffabe
Show file tree
Hide file tree
Showing 19 changed files with 425 additions and 290 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Expand Up @@ -5,7 +5,7 @@ defaults: &defaults
CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
CODECOV_TOKEN: b0d35139-0a75-427a-907b-2c78a762f8f0
VERSION: 1.7.8
VERSION: 1.7.9
PANDOC_RELEASES_URL: https://github.com/jgm/pandoc/releases
steps:
- checkout
Expand Down
4 changes: 4 additions & 0 deletions CHANGES.md
@@ -1,5 +1,9 @@
## Changelog

### 1.7.9 (2020-2-24)
* support for google colab
* bugfixes: [#71](https://github.com/man-group/dtale/issues/71), [#72](https://github.com/man-group/dtale/issues/72), [#73](https://github.com/man-group/dtale/issues/73)

### 1.7.8 (2020-2-22)
* [#77](https://github.com/man-group/dtale/issues/77), removal of multiprocessed timeouts

Expand Down
2 changes: 1 addition & 1 deletion docker/2_7/Dockerfile
Expand Up @@ -44,4 +44,4 @@ WORKDIR /app

RUN set -eux \
; . /root/.bashrc \
; easy_install dtale-1.7.8-py2.7.egg
; easy_install dtale-1.7.9-py2.7.egg
2 changes: 1 addition & 1 deletion docker/3_6/Dockerfile
Expand Up @@ -44,4 +44,4 @@ WORKDIR /app

RUN set -eux \
; . /root/.bashrc \
; easy_install dtale-1.7.8-py3.7.egg
; easy_install dtale-1.7.9-py3.7.egg
4 changes: 2 additions & 2 deletions docs/source/conf.py
Expand Up @@ -64,9 +64,9 @@
# built documents.
#
# The short X.Y version.
version = u'1.7.8'
version = u'1.7.9'
# The full version, including alpha/beta/rc tags.
release = u'1.7.8'
release = u'1.7.9'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
37 changes: 15 additions & 22 deletions dtale/app.py
Expand Up @@ -104,7 +104,7 @@ def run(self, *args, **kwargs):
:param args: Optional arguments to be passed to :meth:`flask:flask.run`
:param kwargs: Optional keyword arguments to be passed to :meth:`flask:flask.run`
"""
self.port = str(kwargs.get('port'))
self.port = str(kwargs.get('port') or '')
if kwargs.get('debug', False):
self.reaper_on = False
self.build_reaper()
Expand Down Expand Up @@ -445,27 +445,24 @@ def show(data=None, host=None, port=None, name=None, debug=False, subprocess=Tru
..link displayed in logging can be copied and pasted into any browser
"""
global ACTIVE_HOST, ACTIVE_PORT
global ACTIVE_HOST, ACTIVE_PORT, USE_NGROK

try:
logfile, log_level, verbose = map(kwargs.get, ['logfile', 'log_level', 'verbose'])
setup_logging(logfile, log_level or 'info', verbose)

initialize_process_props(host, port, force)
if USE_NGROK:
try:
from flask_ngrok import _run_ngrok
except ImportError:
raise ImportError((
'In order to use this functionality please install flask-ngrok!\n'
'You can try running "pip install dtale[ngrok]" if you are using pip.'
))
if not PY3:
raise Exception('In order to use ngrok you must be using Python 3 or higher!')

from flask_ngrok import _run_ngrok

ACTIVE_HOST = _run_ngrok()
ACTIVE_PORT = None
url = ACTIVE_HOST
else:
url = build_url(ACTIVE_PORT, ACTIVE_HOST)
initialize_process_props(host, port, force)

url = build_url(ACTIVE_PORT, ACTIVE_HOST)
instance = startup(url, data=data, data_loader=data_loader, name=name, context_vars=context_vars,
ignore_duplicate=ignore_duplicate)
is_active = not running_with_flask_debug() and is_up(url)
Expand All @@ -474,9 +471,14 @@ def _start():
if open_browser:
instance.open_browser()
else:
if USE_NGROK:
thread = Timer(1, _run_ngrok)
thread.setDaemon(True)
thread.start()

def _start():
app = build_app(url, reaper_on=reaper_on, host=ACTIVE_HOST)
if debug:
if debug and not USE_NGROK:
app.jinja_env.auto_reload = True
app.config['TEMPLATES_AUTO_RELOAD'] = True
else:
Expand All @@ -491,15 +493,6 @@ def _start():
cli.show_server_banner = lambda *x: None

if USE_NGROK:
try:
from flask_ngrok import run_with_ngrok
except ImportError:
raise ImportError((
'In order to use this functionality please install flask-ngrok!\n'
'You can try running "pip install dtale[ngrok]" if you are using pip.'
))

run_with_ngrok(app)
app.run(threaded=True)
else:
app.run(host='0.0.0.0', port=ACTIVE_PORT, debug=debug, threaded=True)
Expand Down
14 changes: 2 additions & 12 deletions dtale/static/css/main.css
Expand Up @@ -8638,7 +8638,7 @@ caption {
.column-toggle__dropdown::before {
position: absolute;
bottom: 100%;
left: 0.6em;
/*left: 0.6em;*/
content: ' ';
z-index: 2;
width: 0;
Expand All @@ -8649,15 +8649,10 @@ caption {
border-left: 0.5em solid transparent;
}

.column-toggle__dropdown.right::before {
left: auto;
right: 1.6em;
}

.column-toggle__dropdown::after {
position: absolute;
bottom: calc(100% - .1em);
left: 0.6em;
/*left: 0.6em;*/
content: ' ';
z-index: 2;
width: 0;
Expand All @@ -8668,11 +8663,6 @@ caption {
border-left: 0.5em solid transparent;
}

.column-toggle__dropdown.right::after {
left: auto;
right: 1.6em;
}

.column-toggle__dropdown header {
border-bottom: solid 1px #a7b3b7;
padding: .3em .5em;
Expand Down
5 changes: 3 additions & 2 deletions dtale/utils.py
Expand Up @@ -75,9 +75,10 @@ def build_url(port, host):
:type host: str, optional
:return: str
"""
final_port = ':{}'.format(port) if port is not None else ''
if (host or '').startswith('http'):
return '{}{}'.format(host, ':{}'.format(port) if port is not None else '')
return 'http://{}:{}'.format(host, port)
return '{}{}'.format(host, final_port)
return 'http://{}{}'.format(host, final_port)


def build_shutdown_url(base):
Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "dtale",
"version": "1.7.8",
"version": "1.7.9",
"description": "Visualizer for Pandas Data Structures",
"main": "main.js",
"directories": {
Expand Down
6 changes: 3 additions & 3 deletions setup.py
Expand Up @@ -50,7 +50,7 @@ def run_tests(self):

setup(
name="dtale",
version="1.7.8",
version="1.7.9",
author="MAN Alpha Technology",
author_email="ManAlphaTech@man.com",
description="Web Client for Visualizing Pandas Objects",
Expand All @@ -65,6 +65,7 @@ def run_tests(self):
"dash_daq",
"Flask-Compress",
"Flask",
"flask-ngrok; python_version > '3.0'",
"future",
"itsdangerous",
"pandas",
Expand All @@ -77,8 +78,7 @@ def run_tests(self):
'r': [
"rpy2<=2.8.6; python_version < '3.0'",
"rpy2; python_version > '3.0'",
],
'ngrok': ["flask-ngrok; python_version > '3.0'"]
]
},
tests_require=[
"ipython",
Expand Down
Expand Up @@ -103,7 +103,7 @@ describe("DataViewer tests", () => {
tsChart.cfg.options.onClick({});
setTimeout(() => {
result.update();
t.ok(result.find(RemovableError).length == 1, "should render scatter error");
t.equal(result.find(RemovableError).length, 1, "should render scatter error");
done();
}, 400);
}, 400);
Expand Down
35 changes: 35 additions & 0 deletions static/__tests__/iframe/ColumnMenu-position-test.jsx
@@ -0,0 +1,35 @@
import { mount } from "enzyme";
import React from "react";

import { positionMenu } from "../../dtale/iframe/ColumnMenu";
import * as t from "../jest-assertions";
import { buildInnerHTML } from "../test-utils";

const originalInnerWidth = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "innerWidth");

describe("ColumnMenu position tests", () => {
beforeAll(() => {
Object.defineProperty(window, "innerWidth", {
configurable: true,
value: 100,
});
});

afterAll(() => {
Object.defineProperty(window, "innerWidth", originalInnerWidth);
});

test("ColumnMenu: calculations for menus on edge of browser window...", done => {
buildInnerHTML({ settings: "" });
mount(<span>Hello</span>, { attachTo: document.getElementById("content") });
const menuDiv = { width: () => 20 };
menuDiv.css = props => (menuDiv.currCss = props);
positionMenu({ offset: () => ({ left: 90 }) }, menuDiv);
t.equal(menuDiv.currCss.left, 60);
positionMenu({ offset: () => ({ left: 10 }) }, menuDiv);
t.equal(menuDiv.currCss.left, 10);
positionMenu({ offset: () => ({ left: 15 }) }, menuDiv);
t.equal(menuDiv.currCss.left, 15);
done();
});
});
121 changes: 63 additions & 58 deletions static/dtale/Filter.jsx
Expand Up @@ -13,6 +13,7 @@ class Filter extends React.Component {
super(props);
this.state = { query: "", error: null };
this.save = this.save.bind(this);
this.renderBody = this.renderBody.bind(this);
}

componentDidUpdate(prevProps) {
Expand All @@ -37,10 +38,70 @@ class Filter extends React.Component {
});
}

render() {
renderBody() {
if (!this.props.visible) {
return null;
}
return [
<RemovableError key={0} {...this.state} onRemove={() => this.setState({ error: null, traceback: null })} />,
<div key={1} className="row">
<div className="col-md-7">
<textarea
style={{ width: "100%", height: "100%" }}
value={this.state.query || ""}
onChange={event => this.setState({ query: event.target.value })}
/>
</div>
<div className="col-md-5">
<p className="font-weight-bold">Example queries</p>
<ul>
<li>
{"drop NaN values: "}
<span className="font-weight-bold">{"Col == Col"}</span>
</li>
<li>
{"show only NaN values: "}
<span className="font-weight-bold">{"Col != Col"}</span>
</li>
<li>
{"date filtering: "}
<span className="font-weight-bold">{`Col == '${moment().format("YYYYMMDD")}'`}</span>
</li>
<li>
{"in-clause on string column: "}
<span className="font-weight-bold">{"Col in ('foo','bar')"}</span>
</li>
<li>
{"and-clause on numeric column: "}
<span className="font-weight-bold">{"Col1 > 1 and Col2 <= 1"}</span>
</li>
<li>
{"or-clause on numeric columns: "}
<span className="font-weight-bold">{"Col1 > 1 or Col2 < 1"}</span>
</li>
<li>
{"negative-clause: "}
<span className="font-weight-bold">{"~(Col > 1)"}</span>
</li>
<li>
{"parenthesis usage: "}
<span className="font-weight-bold">{"(Col1 > 1 or Col2 < 1) and (Col3 == 3)"}</span>
</li>
<li>
{"regex usage (search for substrings 'foo' or 'bar'):"}
<br />
<span className="font-weight-bold">{"Col.str.contains('(foo|bar)', case=False)"}</span>
</li>
</ul>
</div>
</div>,
<div key={2} className="row">
<ContextVariables dataId={this.props.dataId} />
</div>,
];
}

render() {
const hide = () => this.props.propagateState({ filterOpen: false });
return (
<Modal isOpen={this.props.visible} onRequestHide={hide} size="modal-lg" backdrop={false}>
Expand All @@ -51,63 +112,7 @@ class Filter extends React.Component {
</ModalTitle>
<ModalClose onClick={hide} />
</ModalHeader>
<ModalBody>
<RemovableError {...this.state} onRemove={() => this.setState({ error: null, traceback: null })} />
<div className="row">
<div className="col-md-7">
<textarea
style={{ width: "100%", height: "100%" }}
value={this.state.query || ""}
onChange={event => this.setState({ query: event.target.value })}
/>
</div>
<div className="col-md-5">
<p className="font-weight-bold">Example queries</p>
<ul>
<li>
{"drop NaN values: "}
<span className="font-weight-bold">{"Col == Col"}</span>
</li>
<li>
{"show only NaN values: "}
<span className="font-weight-bold">{"Col != Col"}</span>
</li>
<li>
{"date filtering: "}
<span className="font-weight-bold">{`Col == '${moment().format("YYYYMMDD")}'`}</span>
</li>
<li>
{"in-clause on string column: "}
<span className="font-weight-bold">{"Col in ('foo','bar')"}</span>
</li>
<li>
{"and-clause on numeric column: "}
<span className="font-weight-bold">{"Col1 > 1 and Col2 <= 1"}</span>
</li>
<li>
{"or-clause on numeric columns: "}
<span className="font-weight-bold">{"Col1 > 1 or Col2 < 1"}</span>
</li>
<li>
{"negative-clause: "}
<span className="font-weight-bold">{"~(Col > 1)"}</span>
</li>
<li>
{"parenthesis usage: "}
<span className="font-weight-bold">{"(Col1 > 1 or Col2 < 1) and (Col3 == 3)"}</span>
</li>
<li>
{"regex usage (search for substrings 'foo' or 'bar'):"}
<br />
<span className="font-weight-bold">{"Col.str.contains('(foo|bar)', case=False)"}</span>
</li>
</ul>
</div>
</div>
<div className="row">
<ContextVariables dataId={this.props.dataId} />
</div>
</ModalBody>
<ModalBody>{this.renderBody()}</ModalBody>
<ModalFooter>
<button
className="btn btn-secondary"
Expand Down

0 comments on commit 55ffabe

Please sign in to comment.