Skip to content

Commit

Permalink
OWSelectRows: Connect time in calendar; Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
aturanjanin committed Jun 25, 2020
1 parent 208632b commit 63340e3
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 39 deletions.
79 changes: 47 additions & 32 deletions Orange/widgets/data/owselectrows.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,14 @@ def _plural(s):
class CalendarWidgetWithTime(QCalendarWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
timeedit = QDateTimeEdit(displayFormat="hh:mm:ss")
self.timeedit = QDateTimeEdit(displayFormat="hh:mm:ss")
self.timeedit.setTime(self.parent().min_datetime.time())

self._time_layout = sublay = QHBoxLayout()
sublay.setContentsMargins(6, 6, 6, 6)
sublay.addStretch(1)
sublay.addWidget(QLabel("Time: "))
sublay.addWidget(timeedit)
sublay.addWidget(self.timeedit)
sublay.addStretch(1)
self.layout().addLayout(sublay)

Expand Down Expand Up @@ -529,30 +530,42 @@ def add_numeric(contents):
box.controls.append(add_textual(lc[1]))
elif vtype == 4: # time:
datetime_format = (var.have_date, var.have_time)
widget = DateTimeWidget(self, var_idx, datetime_format)
widget.set_datetime(lc[0])
box.controls = [widget]
box.layout().addWidget(widget)
self._box = []
column = self.data[:, var_idx]
w = DateTimeWidget(self, column, datetime_format)
w.set_datetime(lc[0])
box.controls = [w]
box.layout().addWidget(w)
w.dateTimeChanged.connect(self._datetime_changed)
self._box = box
if oper > 5:
gui.widgetLabel(box, " and ")
widget_ = DateTimeWidget(self, var_idx, datetime_format)
widget_.set_datetime(lc[1])
box.layout().addWidget(widget_)
box.controls.append(widget_)
w_ = DateTimeWidget(self, column, datetime_format)
w_.set_datetime(lc[1])
box.layout().addWidget(w_)
box.controls.append(w_)
self._invalidate_datetime()
w_.dateTimeChanged.connect(self._datetime_changed)
self._box = box
self._invalidate_dates()
else:
box.controls = []
if not adding_all:
self.conditions_changed()

def _invalidate_dates(self):
if getattr(self, "_box", None):
widget, widget_ = self._box.controls[0], self._box.controls[1]
if widget.dateTime() > widget_.dateTime():
widget_.setDateTime(widget.dateTime())
widget_.dateTimeChanged.connect(self.conditions_changed)
def _invalidate_datetime(self):
w = self._box.controls[0]
if len(self._box.controls) > 1:
w_ = self._box.controls[1]
if w.dateTime() > w_.dateTime():
w_.setDateTime(w.dateTime())
if w.format == (1, 1):
w.calendarWidget.timeedit.setTime(w.time())
w_.calendarWidget.timeedit.setTime(w_.time())
elif w.format == (1, 1):
w.calendarWidget.timeedit.setTime(w.time())

def _datetime_changed(self):
self.conditions_changed()
self._invalidate_datetime()

@Inputs.data
def set_data(self, data):
Expand Down Expand Up @@ -869,54 +882,56 @@ def resizeEvent(self, QResizeEvent):


class DateTimeWidget(QDateTimeEdit):
def __init__(self, parent, col_idx, datetime_format):
def __init__(self, parent, column, datetime_format):
QDateTimeEdit.__init__(self, parent)

self.parent = parent
self.format = datetime_format
self.have_date, self.have_time = datetime_format[0], datetime_format[1]
self.column = parent.data[:, col_idx]
self.set_format()
self.set_format(column)
self.setSizePolicy(
QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
self.dateTimeChanged.connect(parent._invalidate_dates)

def set_format(self):
def set_format(self, column):
str_format = Qt.ISODate
if self.have_date and self.have_time:
self.setDisplayFormat("yyyy-MM-dd hh:mm:ss")
self.setCalendarPopup(True)
self._calendarWidget = CalendarWidgetWithTime(self)
self.setCalendarWidget(self._calendarWidget)
c_format = "%Y-%m-%d %H:%M:%S"
min_datetime, max_datetime = self.find_range(self.column, c_format)
min_datetime, max_datetime = self.find_range(column, c_format)
self.min_datetime = QDateTime.fromString(min_datetime, str_format)
self.max_datetime = QDateTime.fromString(max_datetime, str_format)
self.setCalendarPopup(True)
self.calendarWidget = CalendarWidgetWithTime(self)
self.calendarWidget.timeedit.timeChanged.connect(
self.set_datetime)
self.setCalendarWidget(self.calendarWidget)
self.setDateTimeRange(self.min_datetime, self.max_datetime)

elif self.have_date and not self.have_time:
self.setDisplayFormat("yyyy-MM-dd")
self.setCalendarPopup(True)
min_datetime, max_datetime = self.find_range(self.column, "%Y-%m-%d")
min_datetime, max_datetime = self.find_range(column, "%Y-%m-%d")
self.min_datetime = QDate.fromString(min_datetime, str_format)
self.max_datetime = QDate.fromString(max_datetime, str_format)
self.setDateRange(self.min_datetime, self.max_datetime)

elif not self.have_date and self.have_time:
self.setDisplayFormat("hh:mm:ss")
min_datetime, max_datetime = self.find_range(self.column, "%H:%M:%S")
min_datetime, max_datetime = self.find_range(column, "%H:%M:%S")
self.min_datetime = QTime.fromString(min_datetime, str_format)
self.max_datetime = QTime.fromString(max_datetime, str_format)
self.setTimeRange(self.min_datetime, self.max_datetime)

def set_datetime(self, datetime):
if self.have_date and self.have_time:
self.setDateTime(datetime if datetime else self.min_datetime)
if isinstance(datetime, QTime):
self.setDateTime(
QDateTime(self.date(), self.calendarWidget.timeedit.time()))
else:
self.setDateTime(datetime if datetime else self.min_datetime)
elif self.have_date and not self.have_time:
self.setDate(datetime if datetime else self.min_datetime)
elif not self.have_date and self.have_time:
self.setTime(datetime if datetime else self.min_datetime)
self.dateTimeChanged.connect(self.parent.conditions_changed)

def find_range(self, column, convert_format):
def convert_timestamp(timestamp):
Expand Down
55 changes: 48 additions & 7 deletions Orange/widgets/data/tests/test_owselectrows.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@
# pylint: disable=missing-docstring,unsubscriptable-object
import time
from unittest.mock import Mock, patch
import numpy as np

from AnyQt.QtCore import QLocale, Qt
from AnyQt.QtCore import QLocale, Qt, QDate
from AnyQt.QtTest import QTest
from AnyQt.QtWidgets import QLineEdit, QComboBox

import numpy as np

from Orange.data import (
Table, Variable, ContinuousVariable, StringVariable, DiscreteVariable,
Domain)
from Orange.preprocess import discretize
from Orange.widgets.data import owselectrows
from Orange.widgets.data.owselectrows import (
OWSelectRows, FilterDiscreteType, SelectRowsContextHandler)
OWSelectRows, FilterDiscreteType, SelectRowsContextHandler, DateTimeWidget)
from Orange.widgets.tests.base import WidgetTest, datasets

from Orange.data.filter import FilterContinuous, FilterString
Expand Down Expand Up @@ -430,6 +429,42 @@ def test_keep_operator(self):
self.assertEqual(
self.widget.cond_list.cellWidget(0, 1).currentText(), "is")

def test_calendar_dates(self):
data = Table(test_filename("datasets/cyber-security-breaches.tab"))
self.send_signal(self.widget.Inputs.data, data)
simulate.combobox_activate_item(
self.widget.cond_list.cellWidget(0, 0), "Date_Posted_or_Updated",
delay=0)
value_combo = self.widget.cond_list.cellWidget(0, 2).children()[1]
self.assertIsInstance(value_combo, DateTimeWidget)

# first displayed date is min date
self.assertEqual(value_combo.date(), QDate(2014, 1, 23))
self.assertEqual(len(self.get_output("Matching Data")), 691)
self.widget.remove_all_button.click()
self.enterFilter("Date_Posted_or_Updated", "is below",
QDate(2014, 4, 17))
self.assertEqual(len(self.get_output("Matching Data")), 840)
self.enterFilter("Date_Posted_or_Updated", "is greater than",
QDate(2014, 6, 30))
self.assertIsNone(self.get_output("Matching Data"))
self.widget.remove_all_button.click()
# date is in range min-max date
self.enterFilter("Date_Posted_or_Updated", "equals", QDate(2013, 1, 1))
self.assertEqual(self.widget.conditions[0][2][0], QDate(2014, 1, 23))
self.enterFilter("Date_Posted_or_Updated", "equals", QDate(2015, 1, 1))
self.assertEqual(self.widget.conditions[1][2][0], QDate(2014, 6, 30))
self.widget.remove_all_button.click()
# no date crossings
self.enterFilter("Date_Posted_or_Updated", "is between",
QDate(2014, 4, 17), QDate(2014, 1, 23))
self.assertEqual(self.widget.conditions[0][2],
(QDate(2014, 4, 17), QDate(2014, 4, 17)))
self.widget.remove_all_button.click()
self.enterFilter("Date_Posted_or_Updated", "is between",
QDate(2014, 4, 17), QDate(2014, 4, 30))
self.assertEqual(len(self.get_output("Matching Data")), 58)

@patch.object(owselectrows.QMessageBox, "question",
return_value=owselectrows.QMessageBox.Ok)
def test_add_all(self, msgbox):
Expand Down Expand Up @@ -551,9 +586,13 @@ def __get_value_widgets(self, row):
if isinstance(value_inputs, QComboBox):
value_inputs = [value_inputs]
else:
value_inputs = [
w for w in value_inputs.children()
if isinstance(w, QLineEdit)]
value_input = []
for widget in value_inputs.children():
if isinstance(widget, QLineEdit):
value_input.append(widget)
elif isinstance(widget, DateTimeWidget):
value_input.append(widget)
return value_input
return value_inputs

@staticmethod
Expand All @@ -564,5 +603,7 @@ def __set_value(widget, value):
QTest.keyClick(widget, Qt.Key_Enter)
elif isinstance(widget, QComboBox):
simulate.combobox_activate_item(widget, value)
elif isinstance(widget, DateTimeWidget):
widget.setDate(value)
else:
raise ValueError("Unsupported widget {}".format(widget))

0 comments on commit 63340e3

Please sign in to comment.