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

Support due times #117

Merged
merged 1 commit into from
Feb 23, 2017
Merged

Support due times #117

merged 1 commit into from
Feb 23, 2017

Conversation

untitaker
Copy link
Member

Fix #109

dt_separator is necessary because of parsing due dates

Copy link
Member

@WhyNotHugo WhyNotHugo left a comment

Choose a reason for hiding this comment

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

I really like this approach because we suppress the need to customize compact_detail, which is ugly. 👍

@@ -14,6 +14,13 @@ color = option('always', 'auto', 'never', default='auto')
# this option is not specified the system locale's is used.
date_format = string(default='%x')

# The date format used both for displaying times, and parsing input times.
# Default is `%H:%M`.
Copy link
Member

Choose a reason for hiding this comment

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

There's no need to clarify this default due to how the doc is rendered (the one from below will be picked up and shown already).

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

todoman/ui.py Outdated
assert isinstance(date, datetime)
if date.date() in self.special_dates:
rv = self.special_dates[date.date()]
assert type(date) is datetime.date
Copy link
Member

Choose a reason for hiding this comment

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

We don't really need this assertion any more due to how we call this function (we've also made it sort of non-public now).

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

todoman/ui.py Outdated
return None
def _format_time(self, time):
if time:
assert isinstance(time, datetime.time)
Copy link
Member

Choose a reason for hiding this comment

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

Ditto.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

time_format = string(default='%H:%M')

# The string used to separate date and time when displaying and parsing.
dt_separator = string(default=' ')
Copy link
Member

Choose a reason for hiding this comment

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

As a default, I'd prefer to have:

if time_format:
    return ' '
else:
    return ''

This way, we avoid an extra space when there's no time_format (I think it's burdensome for users to have to disable the separator when they don't use times.

Copy link
Member Author

Choose a reason for hiding this comment

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

If one of time_format or date_format is an empty string, dt_separator is not used. Hence the filter(bool, ...) construction.

assert formatter.format_date(any_day) == any_day.strftime(DATE_FORMAT)
assert formatter.format_datetime("") == " "
assert formatter._format_date(today.date()) == " Today"
assert formatter._format_date(tomorrow.date()) == "Tomorrow"
Copy link
Member

Choose a reason for hiding this comment

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

Can we also have a test for ' Tomorrow 12:00'?

date_part = None
time_part = None
else:
assert isinstance(dt, datetime.datetime)
Copy link
Member

Choose a reason for hiding this comment

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

Since we're doing this asserting, can't we do dt.strtime(self.datetime_format)? Can a datetime not have a time component?

To deal with special_dates, we can just substitute 'Tomorrow' with 'Tomorror' + SEPARATOR + TIME_FORMAT (at construction time).

Copy link
Member Author

Choose a reason for hiding this comment

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

Using dt.strftime obliterates the entire special-casing done in #108

Copy link
Member

@WhyNotHugo WhyNotHugo Feb 22, 2017

Choose a reason for hiding this comment

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

Not the way I was thinking it:

self.special_dates = {
    self.now.date(): "today".rjust(self.date_width) + self.dt_separator + self.time_format,

Then, in format_datetime:

if dt.date() in self.special_dates
    rv = dt.strftime(self.special_dates[date.date()])

Copy link
Member Author

Choose a reason for hiding this comment

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

This would limit our capability to add special strings for times in the future ("in one hour" comes to mind)

Copy link
Member

Choose a reason for hiding this comment

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

"In one hour" is a datetime format, not a time format. Eg: you'd never see 'Tomorrow in one hour". So this would actually make that simpler.

Copy link
Member Author

Choose a reason for hiding this comment

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

I just noticed that for "in one hour" we'd need an if-else chain, not just a mapping. In either case it's updated now.

@untitaker
Copy link
Member Author

@hobarrera I think I made all requested changes.

Copy link
Member

@WhyNotHugo WhyNotHugo left a comment

Choose a reason for hiding this comment

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

There some bits I'm a bit unsure of, but I think it's mostly me getting confused.

todoman/ui.py Outdated
# Map special dates to the special string we need to return
self.special_datetimes = {}
for key, value in mapping:
if isinstance(value, tuple):
Copy link
Member

Choose a reason for hiding this comment

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

Is this if ever false?

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed. Left-over when I was trying to add "in one hour" (but as said that would be only implementable as if-else).

todoman/ui.py Outdated
if dt in self.special_datetimes:
format_string = self.special_datetimes[dt]
elif dt.date() in self.special_datetimes:
format_string = self.special_datetimes[dt.date()]
Copy link
Member

Choose a reason for hiding this comment

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

Won't this exclude the time?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, as you asked me to do, the format strings now include the time as well. E.g. there's a value like "Today %H:%M" in that dictionary (assuming a particular time_format).

Copy link
Member

Choose a reason for hiding this comment

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

Oh, I completely misread this, yes, it makes sense.

I don't think the first bit of the if is neccessary though, since we never format mere dates.

todoman/ui.py Outdated
return self.empty_date
format_string = self.datetime_format
if not dt:
return format_string
Copy link
Member

Choose a reason for hiding this comment

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

Should this be empty string?

Copy link
Member Author

Choose a reason for hiding this comment

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

That'd miss the " "-padding such that the columns are aligned properly. It is self.special_datetimes[None].

todoman/ui.py Outdated
)
rv = datetime.fromtimestamp(mktime(rv))

rv = self._parse_datetime_naive(dt)
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can merge these functions now that they'd so much shorter?

Copy link
Member Author

Choose a reason for hiding this comment

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

_parse_datetime_naive has multiple return paths, those would all have to add the tzinfo. That's the only reason for the split. (BTW the parsing functions didn't change in my recent commits, they've been like that from the beginning)

Copy link
Member

Choose a reason for hiding this comment

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

👍

@untitaker
Copy link
Member Author

re ab7a995: Yeah, that's probably cleaner.

@WhyNotHugo
Copy link
Member

re ab7a995: Yeah, that's probably cleaner.

Great, I just though it was faster to push it than explain it. 😄

@untitaker
Copy link
Member Author

Ok i think this is ready to merge

assert formatter.format_date(tomorrow) == "Tomorrow"
assert formatter.format_date(any_day) == any_day.strftime(DATE_FORMAT)
assert formatter.format_datetime("") == " "
assert "Today" in formatter.format_datetime(today.date())
Copy link
Member

Choose a reason for hiding this comment

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

I prefer the version with the string with spaces (eg: " Today"), because it also checks that we don't break column width.

@@ -39,13 +39,13 @@ def __init__(self, todo, lists, formatter):

if todo.due:
# TODO: use proper date_format
Copy link
Member

Choose a reason for hiding this comment

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

I think this TODO is old an obsolete?

@untitaker
Copy link
Member Author

Since you don't appear to be on IRC:

I would rather revert back to the old variant. Eventually I want to add support for bare dates for DUE (without time component) and this preprocessing makes it very hard.

@WhyNotHugo
Copy link
Member

Yeah, I jump in and out of IRC, TBH.

So, does any of the rest of the stack (rfc/icalendar/Todo/cache) support bare dates?

@untitaker
Copy link
Member Author

No, it would all have to be refactored for that. My point is that this code will require more changes with your proposed implementation.

@WhyNotHugo
Copy link
Member

You're right, this won't work formatting separately, sadly. :(

I wonder if we might just as well using something like arrow.humanize (code) to do the work for us.

@WhyNotHugo
Copy link
Member

I've also had my eye on maya for some time now, and it might also be capable of doing some parsing (though we really should review it before changing anything).

@untitaker
Copy link
Member Author

Possibly in the future, but that's a lot of code there. I think we should be more conservative about "humanizing" datetimes, preferrably without loss of precision

@WhyNotHugo
Copy link
Member

Back to this PR though:

  • Yes, my proposal is bad.
  • We should also support things like --due 10:30. If the event has a date, only change the time. If it doesn't make it today at 10:30.

@untitaker
Copy link
Member Author

I force-pushed the old variant.

@untitaker
Copy link
Member Author

If the event has a date, only change the time.

This will be complicated. Right now only datetimes are passed around.

@WhyNotHugo
Copy link
Member

Sure, let's keep this simple and discuss more improvements in a separate issue.

@untitaker
Copy link
Member Author

I pushed a commit that allows the user to enter --due 12:00, and it will be parsed as "today 12am" (regardless of previous due value)

@WhyNotHugo
Copy link
Member

If the event has a date, only change the time.

I meant, "if it has a datetime" (as opposed to having None).

@untitaker
Copy link
Member Author

So to be clear, if I set todo.due to a time, you want that only the time component gets updated, not the entire value replaced? This sounds counterintuitive.

@WhyNotHugo
Copy link
Member

WhyNotHugo commented Feb 23, 2017

My idea was that, basically, when a user sets a time (note: this is really pseuocode, not python), with something like todo edit 2 --due 10:30:

if todo.due:
   todo.due.set_time(time):
else:
   todo.due = datetime(today, time)

Does this seem too unintuitive (I'm biased because it's my idea)?

@untitaker
Copy link
Member Author

In the CLI module todo properties are set in a much more generic way.

@WhyNotHugo
Copy link
Member

Okay then, I won't distract any more, and let's aim to keep this simple. ^_^

Copy link
Member

@WhyNotHugo WhyNotHugo left a comment

Choose a reason for hiding this comment

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

Please add a changelog entry, otherwise, LGTM 🚀

@untitaker
Copy link
Member Author

I added a bit of validation to the config parsing code, please review again.

@@ -83,4 +83,18 @@ def load_config():
'Bad {} setting, {}'.format(key, error.args[0])
)

date_format = config['main']['date_format']
Copy link
Member

Choose a reason for hiding this comment

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

I'd rather see new validators that include this in this block (eg: like validate_colour and validate_cache_path.
Mostly for separation of concerns, and to avoid this function growing too much.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done!

@WhyNotHugo
Copy link
Member

I pushed the changes I was suggesting for the validation.

@untitaker
Copy link
Member Author

Oops, sorry I didn't notice that there are already ConfigObj validators, or that ConfigObj even had that capability.

@WhyNotHugo
Copy link
Member

LGTM, but since I pushed the last change, I'll also wait for you thumbs-up.

@untitaker
Copy link
Member Author

untitaker commented Feb 23, 2017

I would but I cannot approve my own PR

@WhyNotHugo
Copy link
Member

WhyNotHugo commented Feb 23, 2017

Feel free to merge 🚀
(once CI passes).

@untitaker
Copy link
Member Author

Travis triggers a push-type build, but that is nonsensical since the PR branch is outside of the main repo. Since this is a required status check I can't merge the PR

@WhyNotHugo
Copy link
Member

I squash-pushed this, travis should stop being dumb (I think it got confused when I pushed to pimutils/time-support, but that's still a bug on their side.

@untitaker untitaker merged commit 1bcdd38 into pimutils:master Feb 23, 2017
@untitaker untitaker deleted the time-support branch February 23, 2017 17:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants