Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed datetime out of bounds issues on callbacks #3519

Merged
merged 3 commits into from
Feb 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 40 additions & 10 deletions holoviews/plotting/bokeh/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from pyviz_comms import JS_CALLBACK

from ...core import OrderedDict
from ...core.util import dimension_sanitizer, isscalar
from ...core.util import dimension_sanitizer, isscalar, dt64_to_dt
from ...streams import (Stream, PointerXY, RangeXY, Selection1D, RangeX,
RangeY, PointerX, PointerY, BoundsX, BoundsY,
Tap, SingleTap, DoubleTap, MouseEnter, MouseLeave,
Expand Down Expand Up @@ -525,15 +525,37 @@ class PointerXYCallback(Callback):

def _process_out_of_bounds(self, value, start, end):
"Clips out of bounds values"
if value < start:
if isinstance(value, np.datetime64):
v = dt64_to_dt(value)
if isinstance(start, (int, float)):
start = convert_timestamp(start)
if isinstance(end, (int, float)):
end = convert_timestamp(end)
s, e = start, end
if isinstance(s, np.datetime64):
s = dt64_to_dt(s)
if isinstance(e, np.datetime64):
e = dt64_to_dt(e)
else:
v, s, e = value, start, end

if v < s:
value = start
elif value > end:
elif v > e:
value = end

return value

def _process_msg(self, msg):
x_range = self.plot.handles.get('x_range')
y_range = self.plot.handles.get('y_range')
xaxis = self.plot.handles.get('xaxis')
yaxis = self.plot.handles.get('yaxis')

if 'x' in msg and isinstance(xaxis, DatetimeAxis):
msg['x'] = convert_timestamp(msg['x'])
if 'y' in msg and isinstance(yaxis, DatetimeAxis):
msg['y'] = convert_timestamp(msg['y'])

server_mode = self.plot.renderer.mode == 'server'
if isinstance(x_range, FactorRange) and isinstance(msg.get('x'), (int, float)):
Expand All @@ -560,12 +582,6 @@ def _process_msg(self, msg):
else:
msg['y'] = y

xaxis = self.plot.handles.get('xaxis')
yaxis = self.plot.handles.get('yaxis')
if 'x' in msg and isinstance(xaxis, DatetimeAxis):
msg['x'] = convert_timestamp(msg['x'])
if 'y' in msg and isinstance(yaxis, DatetimeAxis):
msg['y'] = convert_timestamp(msg['y'])
return msg


Expand Down Expand Up @@ -667,7 +683,21 @@ class TapCallback(PointerXYCallback):

def _process_out_of_bounds(self, value, start, end):
"Sets out of bounds values to None"
if value < start or value > end:
if isinstance(value, np.datetime64):
v = dt64_to_dt(value)
if isinstance(start, (int, float)):
start = convert_timestamp(start)
if isinstance(end, (int, float)):
end = convert_timestamp(end)
s, e = start, end
if isinstance(s, np.datetime64):
s = dt64_to_dt(s)
if isinstance(e, np.datetime64):
e = dt64_to_dt(e)
else:
v, s, e = value, start, end

if v < s or v > e:
value = None
return value

Expand Down
34 changes: 31 additions & 3 deletions holoviews/tests/plotting/bokeh/testcallbacks.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datetime as dt
from collections import deque, namedtuple

import numpy as np
Expand All @@ -8,15 +9,15 @@
from holoviews.element.comparison import ComparisonTestCase
from holoviews.streams import (PointDraw, PolyDraw, PolyEdit, BoxEdit,
PointerXY, PointerX, PlotReset, Selection1D,
RangeXY, PlotSize, CDSStream)
RangeXY, PlotSize, CDSStream, SingleTap)
import pyviz_comms as comms

try:
from bokeh.events import Tap
from bokeh.models import Range1d, Plot, ColumnDataSource, Selection, PolyEditTool
from holoviews.plotting.bokeh.callbacks import (
Callback, PointDrawCallback, PolyDrawCallback, PolyEditCallback,
BoxEditCallback, Selection1DCallback
BoxEditCallback, Selection1DCallback, PointerXCallback, TapCallback
)
from holoviews.plotting.bokeh.renderer import BokehRenderer
bokeh_server_renderer = BokehRenderer.instance(mode='server')
Expand Down Expand Up @@ -128,7 +129,34 @@ def record(resetting):
self.assertEqual(resets, [True])
self.assertIs(stream.source, curve)




class TestPointerCallbacks(CallbackTestCase):

def test_pointer_x_datetime_out_of_bounds(self):
points = Points([(dt.datetime(2017, 1, 1), 1), (dt.datetime(2017, 1, 3), 3)])
PointerX(source=points)
plot = bokeh_server_renderer.get_plot(points)
callback = plot.callbacks[0]
self.assertIsInstance(callback, PointerXCallback)
msg = callback._process_msg({'x': 1000})
self.assertEqual(msg['x'], np.datetime64(dt.datetime(2017, 1, 1)))
msg = callback._process_msg({'x': 10000000000000})
self.assertEqual(msg['x'], np.datetime64(dt.datetime(2017, 1, 3)))

def test_tap_datetime_out_of_bounds(self):
points = Points([(dt.datetime(2017, 1, 1), 1), (dt.datetime(2017, 1, 3), 3)])
SingleTap(source=points)
plot = bokeh_server_renderer.get_plot(points)
callback = plot.callbacks[0]
self.assertIsInstance(callback, TapCallback)
msg = callback._process_msg({'x': 1000, 'y': 2})
self.assertEqual(msg, {})
msg = callback._process_msg({'x': 10000000000000, 'y': 1})
self.assertEqual(msg, {})



class TestEditToolCallbacks(CallbackTestCase):

def test_point_draw_callback(self):
Expand Down