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

Edit Domain: add custom format #6848

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
59 changes: 56 additions & 3 deletions Orange/widgets/data/oweditdomain.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

"""
from __future__ import annotations

import re
import warnings
from xml.sax.saxutils import escape
from itertools import zip_longest, repeat, chain, groupby
Expand Down Expand Up @@ -64,7 +66,29 @@
H = TypeVar("H", bound=Hashable) # pylint: disable=invalid-name

MAX_HINTS = 1000

CUSTOM_TOOLTIP = """%a Weekday abbreviated name
%A Weekday full name
%w Weekday as a number (0=Sunday, 6=Saturday)
%d Day of the month (01-31)
%b Month abbreviated name
%B Month full name
%m Month as a number (01-12)
%y Year without century (00-99)
%Y Year with century
%H Hour (00-23)
%I Hour (01-12)
%p AM or PM
%M Minute (00-59)
%S Second (00-59)
%f Microsecond (000000-999999)
%z UTC offset in the form +HHMM or -HHMM
%Z Time zone name
%j Day of the year (001-366)
%U Week number of the year (Sunday as the first day of the week)
%W Week number of the year (Monday as the first day of the week)
%c Locale's appropriate date and time representation
%x Locale's appropriate date representation
%X Locale's appropriate time representation"""

class _DataType:
def __eq__(self, other):
Expand Down Expand Up @@ -1523,6 +1547,7 @@ class ContinuousVariableEditor(VariableEditor):


class TimeVariableEditor(VariableEditor):
CUSTOM_FORMAT_LABEL = "Custom format"
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
form = self.layout().itemAt(0)
Expand All @@ -1532,8 +1557,14 @@ def __init__(self, parent=None, **kwargs):
Orange.data.TimeVariable.ADDITIONAL_FORMATS.items()
):
self.format_cb.addItem(item, StrpTime(item, *data))
self.format_cb.addItem(self.CUSTOM_FORMAT_LABEL)
self.format_cb.currentIndexChanged.connect(self.variable_changed)
self.custom_edit = QLineEdit()
self.custom_edit.setPlaceholderText("%Y-%m-%d %H:%M:%S")
self.custom_edit.setToolTip(CUSTOM_TOOLTIP)
self.custom_edit.textChanged.connect(self._on_custom_change)
form.insertRow(2, "Format:", self.format_cb)
form.insertRow(3, "Custom format:", self.custom_edit)

def set_data(self, var, transform=()):
super().set_data(var, transform)
Expand All @@ -1552,9 +1583,30 @@ def get_data(self):
var, tr = super().get_data()
if var is not None and (self.parent() is None or not isinstance(self.parent().var, Time)):
# do not add StrpTime when transforming from time to time
tr.insert(0, self.format_cb.currentData())
if self.format_cb.currentText() == self.CUSTOM_FORMAT_LABEL:
custom_text = self.custom_edit.text()
date_pat = r"%(-?)d|%(b|B)|%(-?)m|%(y|Y)|%(-?)j|%(-?)U|%(-?)W|%(a|A)|%w"
time_pat = r"%(-?)H|%(-?)I|%p|%(-?)M|%(-?)S|%f"
have_date = int(bool(re.search(date_pat, custom_text)))
have_time = int(bool(re.search(time_pat, custom_text)))
# this is done to ensure that the custom format is correct
if not have_date and not have_time:
trf = StrpTime(self.CUSTOM_FORMAT_LABEL, (None,),
have_date, have_time)
else:
trf = StrpTime(self.CUSTOM_FORMAT_LABEL, (custom_text,),
have_date, have_time)
else:
trf = self.format_cb.currentData()
tr.insert(0, trf)
return var, tr

def _on_custom_change(self):
if self.format_cb.currentText() != self.CUSTOM_FORMAT_LABEL:
self.format_cb.setCurrentIndex(self.format_cb.count() - 1)
else:
self.variable_changed.emit()


def variable_icon(var):
# type: (Union[Variable, Type[Variable], ReinterpretTransform]) -> QIcon
Expand Down Expand Up @@ -3080,4 +3132,5 @@ def column_str_repr_string(


if __name__ == "__main__": # pragma: no cover
WidgetPreview(OWEditDomain).run(Orange.data.Table("iris"))
WidgetPreview(OWEditDomain).run(Orange.data.Table(
"/Users/ajda/Desktop/test.csv"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget to remove this! I suggest you remove it now, and when you're making commits, add with add -p and skip this chunk.

33 changes: 33 additions & 0 deletions Orange/widgets/data/tests/test_oweditdomain.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# pylint: disable=all
import pickle
import unittest
from datetime import datetime, timezone
from functools import partial
from itertools import product, chain
from unittest import TestCase
Expand Down Expand Up @@ -340,6 +341,38 @@ def test_time_variable_preservation(self):
output = self.get_output(self.widget.Outputs.data)
self.assertEqual(str(table[0, 4]), str(output[0, 4]))

def test_custom_format(self):
time_variable = StringVariable("Date")
data = [
["2024-001"],
["2024-032"],
["2024-150"],
["2024-365"]
]
table = Table.from_list(Domain([], metas=[time_variable]), data)
self.send_signal(self.widget.Inputs.data, table)
index = self.widget.variables_view.model().index
self.widget.variables_view.setCurrentIndex(index(0))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your original variable is a string. You must change its type to time variable, like this:

        editor = self.widget.findChild(VariableEditor)
        tc = editor.layout().currentWidget().findChild(QComboBox,
                                                       name="type-combo")
        tc.setCurrentIndex(3)  # time variable
        tc.activated.emit(3)

When you saw an "unchanged" date, it was because your variable remained a string, and changes that you made in the edit dialog were ignored.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know the variable didn't change (it stayed StringVariable). I said I tried everything, we even had a look at the code with Lan, but couldn't figure out how to write a proper call.

editor = self.widget.findChild(VariableEditor)
tc = editor.layout().currentWidget().findChild(QComboBox,
name="type-combo")
tc.setCurrentIndex(3) # time variable
tc.activated.emit(3)

# le = editor.layout().currentWidget().findChild(QLineEdit)
# le.setText("%Y-%j")
# le.editingFinished.emit()

self.widget.commit()
output = self.get_output(self.widget.Outputs.data)
print(output)
self.assertEqual(table.metas[0, 0], "2024-001")
self.assertEqual(output.metas[0, 0],
datetime.strptime("2024-001",
"%Y-%j").replace(
tzinfo=timezone.utc).timestamp())

def test_restore(self):
iris = self.iris
viris = (
Expand Down
Loading