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

Use tomorrow and today while formatting date #108

Merged
merged 26 commits into from
Feb 21, 2017

Conversation

rimshaakhan
Copy link
Contributor

@rimshaakhan rimshaakhan commented Feb 19, 2017

fixes #82

Copy link
Member

@untitaker untitaker left a comment

Choose a reason for hiding this comment

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

In principle this looks good to me, still have a few nitpicks.

Also you have a few styleguide violations. After running pip install tox, you can get all style nitpicks using tox -eflake8.

EDIT: The output is also available here: https://travis-ci.org/pimutils/todoman/jobs/203232655#L249

todoman/ui.py Outdated
if date:
rv = date.strftime(self.date_format)
date_tomorrow = datetime.today().date() + timedelta(days = 1)
Copy link
Member

Choose a reason for hiding this comment

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

Instead of calling datetime.today() here, please use self.now. This is because we might want to replace self.now with a fixed value in the testsuite.

todoman/ui.py Outdated
date_tomorrow = datetime.today().date() + timedelta(days = 1)
# Get a dictionary which stores the dates for which we don't have to merely
# return a date string and map it to the special string we need to return
dates_for_which_not_to_return_simple_date = {
Copy link
Member

Choose a reason for hiding this comment

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

This is quite a long variable name, I suggest something like special_dates.

todoman/ui.py Outdated
datetime.today().date() : "Today",
date_tomorrow : "Tomorrow" ,
}
if date in dates_for_which_not_to_return_simple_date.keys():
Copy link
Member

Choose a reason for hiding this comment

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

.keys() is not necessary, just if date in dates_for_which_not_to_return_simple_date.

@rimshaakhan
Copy link
Contributor Author

Thanks @untitaker for your kind help!

Looking forward to correct my mistakes

@rimshaakhan
Copy link
Contributor Author

Hello @untitaker, do I need to write a test case for testing this feature?

todoman/ui.py Outdated
self.now.date(): "Today",
date_tomorrow: "Tomorrow",
}
if date in special_dates():
Copy link
Member

Choose a reason for hiding this comment

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

You need to write special_dates instead of special_dates().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops! Sorry

Copy link
Member

Choose a reason for hiding this comment

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

No need to apologize!

@untitaker
Copy link
Member

untitaker commented Feb 19, 2017

do I need to write a test case for testing this feature?

That would be nice. You have two options:

  1. Write a test that simulates running todo, it would belong into https://github.com/pimutils/todoman/blob/master/tests/test_basic.py

    For that option I imagine it would be enough to run todo new --due tomorrow hello and check if todo list outputs Tomorrow at some point. Also a similar testcase for Today.

  2. Unit tests for the formatter. We currently have none, so this would require creating a new file tests/test_formatter.py. In the test you could create an instance of TodoFormatter (with a datetime format string of your choice), and test format_date with some input

@WhyNotHugo
Copy link
Member

Hi. Thanks for picking this up with be being a bit away, @untitaker.

Another detail is that, if you run todo, the outputs are aligned, so today and yesterday are probably need to have an exact width. This is why no-date returns empty_date, which is a string with spaces to fill up the column.

You can probably do something like "today".rjust(WIDTH). I'd do this is the constructor (__init__) so we only compute this once (this function is called once per todo).

@WhyNotHugo
Copy link
Member

It resolves Issue #82

Hint: if the message says something like "closes #82" or "fixes #82", merging the PR will automatically close the issue. ;)

@rimshaakhan
Copy link
Contributor Author

rimshaakhan commented Feb 20, 2017

The line which is making flake8 violations is here:

return special_dates[date] if date in special_dates else date.strftime(self.date_format)

Isn't it the optimal way of doing it or should I switch back to the if-else tree?

@untitaker
Copy link
Member

@rimshaakhan The line is too long. I personally prefer the if-else variant.

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.

This looks quite on the right track, but there's some tiny optimizations I'd like to see.

We're a bit picky with code being very well polished, so don't feel unmotivated by all the back and forth. 😄

Again, feel free to ask any questions you may have.

todoman/ui.py Outdated
# Get a dictionary which stores the dates for which
# we don't have to merely return a date string and
# map it to the special string we need to return
WIDTH = len(date.strftime(self.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.

Actually, len(empty_date) was fine. They should both have the same with, and we avoid formatting this date when it's [possibly] unnecessary.

I'd also prefer move this over to __init__ , so we only calculate this width once, instead every time we format a date (pretty much like we only calculate empty_date once.

Let me know if you're not very familiar with contructors/__init__ or if this is otherwise unclear.

Also, date_width is actually a better variable name, but that one's my fault because I used WIDTH on my example, which really was a bad idea on my part, sorry.

Copy link
Contributor Author

@rimshaakhan rimshaakhan Feb 20, 2017

Choose a reason for hiding this comment

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

I guess you would want something like this:

def __init__(self, date_format):
    self.date_format = date_format
    self._localtimezone = tzlocal()
    self.now = datetime.now().replace(tzinfo=self._localtimezone)
    # An empty date which should be used in case no date is present
    self.empty_date = " " * len(self.format_date(self.now))
    self.date_width = len(self.empty_date)
    self.date_tomorrow = self.now.date() + timedelta(days=1)
    # Get a dictionary which stores the dates for which
    # we don't have to merely return a date string and
    # map it to the special string we need to return
    self.special_dates = {
        self.now.date(): "Today".rjust(self.date_width, " "),
        self.date_tomorrow: "Tomorrow".rjust(self.date_width, " "),
    }

    self._parsedatetime_calendar = parsedatetime.Calendar()

But then wouldn't self.empty_date call self.format_date, where self.date_width and self.special_dates still won't exist and there would be an exception.
Should we find a different way for calculating self.empty_date?

Copy link
Member

Choose a reason for hiding this comment

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

Ugh, you're completely right, I missed that! I think we need to rewrite how we calculate empty_date and avoid that trap:

empty_date = self.now.format(self.date_format)

I don't think we need the comment explaining what that dictionary is; it seems quite clear. Maybe just a short one-liner, at most.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also, since self.empty_date calls self.format_date(self.now)), it would not be an empty_date, rather something like " Today" (ideally - when we can get the code to run) and I think it would hinder the utility of having an empty_date and therefore would render it useless! :(

Copy link
Member

Choose a reason for hiding this comment

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

Oops (again!). Not by best day. I meant this:

self.date_width = len(self.now.format(date_format))
self.empty_date = " " * self.date_width

todoman/ui.py Outdated
# map it to the special string we need to return
WIDTH = len(date.strftime(self.date_format))
special_dates = {
self.now.date(): "Today".rjust(WIDTH, " "),
Copy link
Member

Choose a reason for hiding this comment

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

Maybe the same as above; define the strings in the constructor.

@rimshaakhan
Copy link
Contributor Author

No, I am not feeling demotivated, but on the contrary it gets even more exciting and adventurous.
It's feeling fun and on a serious note: I need to learn all these stuff, when else could have been a better time? - I am really grateful for having such technically insightful and creative critique on my work!
Thank You

Copy link
Member

@untitaker untitaker left a comment

Choose a reason for hiding this comment

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

This looks good, I think the only thing missing are:

  • tests (see above, I suspect option 2 may be easier)
  • changelog entry (probably)
  • Adding yourself to AUTHORS

@untitaker untitaker dismissed WhyNotHugo’s stale review February 20, 2017 17:36

the requests in this review have been addressed

@WhyNotHugo
Copy link
Member

FWIW, these final items are listed in the patch review checklist.

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.

LGTM so far, just authorship and tests 🎉

todoman/ui.py Outdated
@@ -204,8 +204,16 @@ def __init__(self, date_format):
self.date_format = date_format
self._localtimezone = tzlocal()
self.now = datetime.now().replace(tzinfo=self._localtimezone)
self.empty_date = " " * len(self.format_date(self.now))

self.date_tomorrow = self.now.date() + timedelta(days=1)
Copy link
Member

Choose a reason for hiding this comment

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

Just call this self.tomorrow, for consistency.

@rimshaakhan
Copy link
Contributor Author

What should be added to changelog?

Perhpas, just a line saying: * Proper date formatting enabled would be enough!

@untitaker
Copy link
Member

I would rather describe the concrete changes to todoman's behavior. The PR title is pretty close to that, though the word formatting may be too technical for the changelog, which is supposed to be a summary targeted at users.

Copy link
Member

@untitaker untitaker left a comment

Choose a reason for hiding this comment

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

In #109 @hobarrera noticed that this PR has a bug

todoman/ui.py Outdated
@@ -251,8 +259,18 @@ def detailed(self, todo):
return rv

def format_date(self, date):
"""
:param datetime.date date: a datetime object
Copy link
Member

Choose a reason for hiding this comment

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

In fact format_date recieves a datetime object (i.e. datetime.datetime instead of datetime.date). It is probably useful to add an assert isinstance(date, datetime) in format_date.

todoman/ui.py Outdated
if date:
rv = date.strftime(self.date_format)
if date in self.special_dates:
Copy link
Member

Choose a reason for hiding this comment

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

Since date is a datetime (I know, misleading variable name), you need to use date.date() here, otherwise special_dates is never used.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, sorry, this is something we've dragged for a while. #109 will fix this in future, but for now, just update this to date.date().

Tests the format_date function in todoman.ui.TodoFormatter
"""
formatter = ui.TodoFormatter(DATE_FORMAT)
today = datetime.now().date()
Copy link
Member

@untitaker untitaker Feb 20, 2017

Choose a reason for hiding this comment

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

Please make today a datetime instead of date

@untitaker
Copy link
Member

@rimshaakhan if you run tox -epy locally, you will see that your current testcase fails. Also there are a few styling problems with your latest commits (see tox -eflake8)

@rimshaakhan
Copy link
Contributor Author

rimshaakhan commented Feb 21, 2017

The color checking test:

@pytest.mark.parametrize('hours', [72, -72])
def test_color_due_dates(tmpdir, runner, create, hours):
    due = datetime.datetime.now() + datetime.timedelta(hours=hours)
    create(
        'test.ics',
        'SUMMARY:aaa\n'
        'STATUS:IN-PROGRESS\n'
        'DUE;VALUE=DATE-TIME;TZID=ART:{}\n'
        .format(due.strftime('%Y%m%dT%H%M%S'))
    )

    result = runner.invoke(cli, ['--color', 'always'])
    assert not result.exception
    due_str = due.strftime('%Y-%m-%d')
    if hours == 1:
        assert result.output == \
            '  1 [ ]   {} aaa @default\x1b[0m\n'.format(due_str)
    else:
        assert result.output == \
            '  1 [ ]   \x1b[31m{}\x1b[0m aaa @default\x1b[0m\n'.format(due_str)

is failing with the error message:

            '  1 [ ]   {} aaa @default\x1b[0m\n'.format(due_str)
    else:
>       assert result.output == \
            '  1 [ ]   \x1b[31m{}\x1b[0m aaa @default\x1b[0m\n'.format(due_str)
E       assert '  1 [ ]   20...ault\x1b[0m\n' == '  1 [ ]   \x1...ault\x1b[0m\n'
E         -   1 [ ]   2017-02-25 aaa @default
E         +   1 [ ]   2017-02-25 aaa @default
E         ?           +++++          ++++

tests/test_basic.py:353: AssertionError

Can someone please share any insight @hobarrera @untitaker ?
Thanks!

@untitaker
Copy link
Member

Yes, in that test there is a line called if hours == 1, it also needs to be changed to if hours == 72

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 think it's that tiny change and we're done. 🎉

CHANGELOG.rst Outdated
@@ -23,6 +23,7 @@ v2.1.0
* Fix crash when running ``--porcelain show``.
* Show ``id`` for todos everywhere (eg: including new, etc).
* Add the ``ctrl-s`` shortcut for saving in the interactive editor.
* Show "Today" or "Tomorrow" when due date is today or tomorrow
Copy link
Member

Choose a reason for hiding this comment

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

v2.1.0 was released a couple of days ago, so you need to move this up. ^_^

Note that I try and keep things in reverse-chronological order too, so it should be at the very top.

todoman/ui.py Outdated
@@ -251,8 +259,19 @@ def detailed(self, todo):
return rv

def format_date(self, date):
"""
:param datetime.datetime date: a datetime object
Copy link
Member

Choose a reason for hiding this comment

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

:param: need to be at the bottom of the docstring, not at the top.

Copy link
Member

@untitaker untitaker left a comment

Choose a reason for hiding this comment

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

Which editor do you use @rimshaakhan? There are probably plugins which immediately tell you about styleguide violations using flake8.

formatter = ui.TodoFormatter(DATE_FORMAT)
today = datetime.now()
tomorrow = today + timedelta(days = 1)
any_day = today + timedelta(days = randrange(2, 8))
Copy link
Member

Choose a reason for hiding this comment

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

So close! A styleguide violation here, no spaces around the =. Same for the previous line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @untitaker, I am using Sublime Text 3
It was so close! :)

Copy link
Member

Choose a reason for hiding this comment

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

Back when I used sublime I used a plugin called Anaconda that did some python style checks and stuff (don't recall what else). Might be worth looking into.

@untitaker
Copy link
Member

I think this review comment is still pending, but then that'd be really it :)

@rimshaakhan
Copy link
Contributor Author

Also, should I consider filing a issue because I am unable to filter out results when I run flake8 , it reports a issues in the env files.

Please have a look at the log file

@WhyNotHugo
Copy link
Member

WhyNotHugo commented Feb 21, 2017

Also, should I consider filing a issue because I am unable to filter out results when I run flake8 , it reports a issues in the env files.

Run tox -e flake8 to only check todoman's files.

Scrub that, that'll also fail.

@rimshaakhan
Copy link
Contributor Author

Run tox -e flake8 to only check todoman files.

Yeah! that was the command I ran, but didn't quite well worked for me!
Perhaps, it is because I have named the virtualenv as todoman_virtualenv (I may sound like a complete idiot )

@WhyNotHugo
Copy link
Member

No, it's not really anything you've done. Personally, I never came across that because I use mkvirtualenv (which creates all the virtualenvs in a separate location). I'm not sure how other projects deal with that though.

@WhyNotHugo WhyNotHugo merged commit 7d79f67 into pimutils:master Feb 21, 2017
@WhyNotHugo
Copy link
Member

Thanks for you contribution! 🎉
Hope this has been useful and a worthwhile learning experience!

@untitaker
Copy link
Member

In general I would recommend creating the virtualenv outside of the git repository, otherwise you may accidentally commit it into git, or misconfigured tools may crawl it like pytest or flake8.

@rimshaakhan
Copy link
Contributor Author

@hobarrera You just beat me up while I was writing this Thank You :)
@untitaker @hobarrera Thank You sir for being so wonderfully patient and for giving me critical reviews.

I am looking forward to apply for RGSoC and am very excited to learn: how to make fascinating code which looks so amazingly beautiful!

Its a wonderful feeling of seeing myself in the author list
Thank You

@untitaker
Copy link
Member

@rimshaakhan "beat me up" doesn't mean what you think it does! :D It has been a pleasure working with you so far!

@rimshaakhan
Copy link
Contributor Author

In general I would recommend creating the virtualenv outside of the git repository, otherwise you may accidentally commit it into git, or misconfigured tools may crawl it like pytest or flake8.

@untitaker Should I re-configure for the same!
I did thought of it earlier, but couldn't think of a possible solution! (There's mkvirtualenv now :) as @hobarrera pointed out)

@untitaker untitaker mentioned this pull request Feb 22, 2017
@untitaker untitaker mentioned this pull request Mar 2, 2017
3 tasks
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.

Use "tomorrow" and "today" when formatting date
3 participants