Skip to content

Commit

Permalink
intervals: use whole intervals
Browse files Browse the repository at this point in the history
With this the budget over/undershoot will be displayed relative to the
budget assigned for that whole interval, not relative to how far you are
through the interval
  • Loading branch information
carderne authored and yagebu committed Apr 1, 2023
1 parent 3be9db1 commit 0452ad2
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 15 deletions.
1 change: 1 addition & 0 deletions src/fava/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
from fava.util.date import interval_ends
from fava.util.typing import BeancountOptions


if TYPE_CHECKING: # pragma: no cover
from beancount.core.prices import PriceMap
from beancount.core.realization import RealAccount
Expand Down
38 changes: 35 additions & 3 deletions src/fava/util/date.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,39 @@ def format_date_filter(self, date: datetime.date) -> str:
return date.strftime("%Y-%m")


def get_prev_interval(
date: datetime.date, interval: Interval
) -> datetime.date:
"""Get the start date of the interval in which the date falls.
Args:
date: A date.
intereval: An interval.
Returns:
The start date of the `interval` before `date`.
"""
# pylint: disable=too-many-return-statements
try:
if interval is Interval.YEAR:
return datetime.date(date.year, 1, 1)
if interval is Interval.QUARTER:
for i in [10, 7, 4]:
if date.month > i:
return datetime.date(date.year, i, 1)
return datetime.date(date.year, 1, 1)
if interval is Interval.MONTH:
return datetime.date(date.year, date.month, 1)
if interval is Interval.WEEK:
return date - datetime.timedelta(date.weekday())
if interval is Interval.DAY:
return date
except (ValueError, OverflowError):
return datetime.date.max
raise NotImplementedError


def get_next_interval(
date: datetime.date, interval: Interval
) -> datetime.date:
Expand Down Expand Up @@ -151,11 +184,10 @@ def interval_ends(
Dates corresponding to the starts/ends of intervals between `first` and
`last`.
"""
yield get_prev_interval(first, interval)
while first < last:
yield first
first = get_next_interval(first, interval)

yield last
yield first


def substitute(string: str, fye: FiscalYearEnd | None = None) -> str:
Expand Down
4 changes: 2 additions & 2 deletions tests/__snapshots__/test_core_charts.py-test_interval_totals
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"budgets": {
"EUR": 31.42857142857142857142857145
},
"date": "2012-11-18"
"date": "2012-11-01"
},
{
"account_balances": {
Expand All @@ -26,7 +26,7 @@
"EUR": 80.00
},
"budgets": {
"EUR": 48.57142857142857142857142861
"EUR": 88.57142857142857142857142865
},
"date": "2012-12-01"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"budgets": {
"EUR": 31.42857142857142857142857145
},
"date": "2012-11-18"
"date": "2012-11-01"
},
{
"account_balances": {
Expand All @@ -26,7 +26,7 @@
"USD": 40.000
},
"budgets": {
"EUR": 48.57142857142857142857142861
"EUR": 88.57142857142857142857142865
},
"date": "2012-12-01"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"budgets": {
"EUR": -31.42857142857142857142857145
},
"date": "2012-11-18"
"date": "2012-11-01"
},
{
"account_balances": {
Expand All @@ -26,7 +26,7 @@
"EUR": -80.00
},
"budgets": {
"EUR": -48.57142857142857142857142861
"EUR": -88.57142857142857142857142865
},
"date": "2012-12-01"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"budgets": {
"EUR": -31.42857142857142857142857145
},
"date": "2012-11-18"
"date": "2012-11-01"
},
{
"account_balances": {
Expand All @@ -26,7 +26,7 @@
"USD": -40.000
},
"budgets": {
"EUR": -48.57142857142857142857142861
"EUR": -88.57142857142857142857142865
},
"date": "2012-12-01"
}
Expand Down
2 changes: 1 addition & 1 deletion tests/__snapshots__/test_core_charts.py-test_net_worth
Original file line number Diff line number Diff line change
Expand Up @@ -1426,6 +1426,6 @@
"USD": 102452.53144,
"VACHR": -82
},
"date": "2016-05-10"
"date": "2016-06-01"
}
]
36 changes: 33 additions & 3 deletions tests/test_util_date.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from fava.util.date import FiscalYearEnd
from fava.util.date import get_fiscal_period
from fava.util.date import get_next_interval
from fava.util.date import get_prev_interval
from fava.util.date import Interval
from fava.util.date import interval_ends
from fava.util.date import month_offset
Expand Down Expand Up @@ -80,14 +81,43 @@ def test_get_next_intervalfail2() -> None:
get_next_interval(date(2016, 4, 18), "decade") # type: ignore


@pytest.mark.parametrize(
"input_date_string,interval,expect",
[
("2016-01-01", Interval.DAY, "2016-01-01"),
("2016-01-01", Interval.WEEK, "2015-12-28"),
("2016-01-01", Interval.MONTH, "2016-01-01"),
("2016-01-01", Interval.QUARTER, "2016-01-01"),
("2016-01-01", Interval.YEAR, "2016-01-01"),
("2016-12-31", Interval.DAY, "2016-12-31"),
("2016-12-31", Interval.WEEK, "2016-12-26"),
("2016-12-31", Interval.MONTH, "2016-12-01"),
("2016-12-31", Interval.QUARTER, "2016-10-01"),
("2016-12-31", Interval.YEAR, "2016-01-01"),
("9999-12-31", Interval.QUARTER, "9999-10-01"),
("9999-12-31", Interval.YEAR, "9999-01-01"),
],
)
def test_get_prev_interval(
input_date_string: str, interval: Interval, expect: str
) -> None:
get = get_prev_interval(_to_date(input_date_string), interval)
assert get == _to_date(expect)


def test_get_prev_intervalfail2() -> None:
with pytest.raises(NotImplementedError):
get_prev_interval(date(2016, 4, 18), "decade") # type: ignore


def test_interval_tuples() -> None:
assert list(
interval_ends(date(2014, 3, 5), date(2014, 5, 5), Interval.MONTH)
) == [
date(2014, 3, 5),
date(2014, 3, 1),
date(2014, 4, 1),
date(2014, 5, 1),
date(2014, 5, 5),
date(2014, 6, 1),
]
assert list(
interval_ends(date(2014, 1, 1), date(2014, 5, 1), Interval.MONTH)
Expand All @@ -100,7 +130,7 @@ def test_interval_tuples() -> None:
]
assert list(
interval_ends(date(2014, 3, 5), date(2014, 5, 5), Interval.YEAR)
) == [date(2014, 3, 5), date(2014, 5, 5)]
) == [date(2014, 1, 1), date(2015, 1, 1)]
assert list(
interval_ends(date(2014, 1, 1), date(2015, 1, 1), Interval.YEAR)
) == [date(2014, 1, 1), date(2015, 1, 1)]
Expand Down

0 comments on commit 0452ad2

Please sign in to comment.