quite a bit of development around get rates input handling

This commit is contained in:
Pablo Martin 2024-06-07 16:10:35 +02:00
parent 46988352ca
commit 4f81ac2e62
8 changed files with 199 additions and 20 deletions

13
poetry.lock generated
View file

@ -135,6 +135,17 @@ files = [
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
] ]
[[package]]
name = "currencies"
version = "2020.12.12"
description = "Display money format and its filthy currencies, for all money lovers out there."
optional = false
python-versions = "<4"
files = [
{file = "currencies-2020.12.12-py3-none-any.whl", hash = "sha256:33b017bd11b0a70707ffa917e80cd8c4e07a1a6b412239ee19c10f145ed5f031"},
{file = "currencies-2020.12.12.tar.gz", hash = "sha256:400cf313b8f6f33a59dcc9c9723dbe458eb8ff18a74ad2b79eba8a295a44b556"},
]
[[package]] [[package]]
name = "idna" name = "idna"
version = "3.7" version = "3.7"
@ -226,4 +237,4 @@ requests = ">=2.19.1"
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.10" python-versions = "^3.10"
content-hash = "fc10bcffee4d61f71d09d8ee3ecd62c907cf50c34e802bf747bc0e17770e7646" content-hash = "2fea7228b5d6197e7c654a55dfc2415d44e9b6f98abd859a7067910a4fc89571"

View file

@ -11,6 +11,7 @@ click = "^8.1.7"
python-dotenv = "^1.0.1" python-dotenv = "^1.0.1"
pyfiglet = "^1.0.2" pyfiglet = "^1.0.2"
xecd-rates-client = "^1.0.0" xecd-rates-client = "^1.0.0"
currencies = "^2020.12.12"
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core"]

View file

@ -35,15 +35,3 @@ def test_get_rates_breaks_without_output():
runner = CliRunner() runner = CliRunner()
result = runner.invoke(get_rates) result = runner.invoke(get_rates)
assert result.exit_code == 2 assert result.exit_code == 2
def test_get_rates_replaces_future_dates_properly():
assert False
def test_get_rates_rejects_start_date_after_end_date():
assert False
def test_get_rates_rejects_invalid_currency_codes():
assert False

View file

@ -0,0 +1,94 @@
import datetime
import pytest
from currencies import Currency
from currencies.exceptions import CurrencyDoesNotExist
from xexe.inputs_handling import handle_get_rates_inputs
from xexe.utils import DateRange
def test_handle_input_rates_works_with_full_correct_inputs():
handled_inputs = handle_get_rates_inputs(
start_date=datetime.datetime.now(),
end_date=datetime.datetime.now() + datetime.timedelta(days=7),
currencies="USD,EUR,GBP",
dry_run=False,
output="test_output.csv",
)
expected_result = {
"date_range": DateRange(
start_date=datetime.datetime.now().date(),
end_date=(datetime.datetime.now() + datetime.timedelta(days=7)).date(),
),
"currencies": {Currency("USD"), Currency("EUR"), Currency("GBP")},
"dry_run": False,
"output": pathlib.Path("test_output.csv"),
}
assert handled_inputs == expected_result
def test_handle_input_rates_raises_with_bad_currency_code():
with pytest.raises(CurrencyDoesNotExist):
handle_get_rates_inputs(
start_date=datetime.datetime.now(),
end_date=datetime.datetime.now() + datetime.timedelta(days=7),
currencies="not_a_currency,USD,not_this_either",
dry_run=False,
output="test_output.csv",
)
def test_handle_input_rates_raises_with_start_date_after_end_date():
with pytest.raises(ValueError):
handle_get_rates_inputs(
start_date=datetime.datetime.now(),
end_date=datetime.datetime.now() - datetime.timedelta(days=7),
currencies="GBP,USD",
dry_run=False,
output="test_output.csv",
)
def test_handle_input_rates_raises_with_output_different_than_csv():
with pytest.raises(ValueError):
handle_get_rates_inputs(
start_date=datetime.datetime.now(),
end_date=datetime.datetime.now() + datetime.timedelta(days=7),
currencies="GBP,USD",
dry_run=False,
output="test_output.xlsx",
)
def test_handle_input_rates_brings_future_end_date_to_today():
handled_inputs = handle_get_rates_inputs(
start_date=datetime.datetime.now() - datetime.timedelta(days=7),
end_date=datetime.datetime.now() + datetime.timedelta(days=7),
currencies="USD,EUR,GBP",
dry_run=False,
output="test_output.csv",
)
assert handled_inputs["date_range"].end_date == datetime.datetime.now().date()
def test_handle_input_rates_start_and_end_date_equal_works_fine():
handled_inputs = handle_get_rates_inputs(
start_date=datetime.datetime.now(),
end_date=datetime.datetime.now(),
currencies="USD,EUR,GBP",
dry_run=False,
output="test_output.csv",
)
expected_result = {
"date_range": DateRange(
start_date=datetime.datetime.now().date(),
end_date=(datetime.datetime.now() + datetime.timedelta(days=7)).date(),
),
"currencies": {Currency("USD"), Currency("EUR"), Currency("GBP")},
"dry_run": False,
"output": "test_output.csv",
}
assert handled_inputs == expected_result

View file

@ -74,7 +74,7 @@ def get_rates(
dry_run: bool, dry_run: bool,
output: pathlib.Path, output: pathlib.Path,
): ):
handle_get_rates_inputs( inputs = handle_get_rates_inputs(
start_date=start_date, start_date=start_date,
end_date=end_date, end_date=end_date,
currencies=currencies, currencies=currencies,

View file

@ -1,6 +1,10 @@
import pathlib import pathlib
from dataclasses import dataclass from dataclasses import dataclass
from currencies import Currency
DEFAULT_CURRENCIES = {Currency("EUR"), Currency("GBP"), Currency("USD")}
@dataclass @dataclass
class PATHS: class PATHS:

View file

@ -1,12 +1,51 @@
import datetime
import logging import logging
import pathlib
from typing import Union
from currencies import Currency
from xexe.constants import DEFAULT_CURRENCIES
from xexe.utils import DateRange
logger = logging.getLogger() logger = logging.getLogger()
def handle_get_rates_inputs(start_date, end_date, currencies, dry_run, output): def handle_get_rates_inputs(
start_date: Union[datetime.datetime, datetime.date],
end_date: Union[datetime.datetime, datetime.date],
currencies: Union[None, str],
dry_run: bool,
output: Union[str, pathlib.Path],
):
logger.info("Handling inputs.") logger.info("Handling inputs.")
logger.debug(f"Received start_date: {start_date}")
logger.debug(f"Received end_date: {end_date}") date_range = DateRange(start_date=start_date.date(), end_date=end_date.date())
logger.debug(f"Received currencies: {currencies}")
logger.debug(f"dry_run state: {dry_run}") if date_range.end_date > datetime.datetime.today().date():
logger.debug(f"Received output: {output}") date_range.end_date = datetime.datetime.today().date()
if currencies:
# CLI input comes as string of comma-separated currency codes
currencies = [currency_code.strip() for currency_code in currencies.split(",")]
tmp = [Currency(currency_code) for currency_code in currencies]
currencies = tmp
if currencies is None:
currencies = DEFAULT_CURRENCIES
# The Path constructor is idempotent, so this works equally fine if output
# is a string or an actual Path object.
output = pathlib.Path(output)
if output.suffix != ".csv":
raise ValueError("Output must be a .csv file.")
prepared_inputs = {
"date_range": date_range,
"currencies": currencies,
"dry_run": dry_run,
"output": output,
}
logger.debug(prepared_inputs)
return prepared_inputs

42
xexe/utils.py Normal file
View file

@ -0,0 +1,42 @@
import datetime
class DateRange:
def __init__(self, start_date: datetime.date, end_date: datetime.date):
if type(start_date) != datetime.date or type(end_date) != datetime.date:
raise TypeError("start_date and end_date must be date objects.")
if start_date > end_date:
raise ValueError("start_date can't be after end_date.")
self._start_date = start_date
self._end_date = end_date
@property
def start_date(self):
return self._start_date
@start_date.setter
def start_date(self, value: datetime.date):
if type(value) != datetime.date:
raise TypeError("start_date must be a date object.")
if value > self._end_date:
raise ValueError("start_date can't be after end_date.")
self._start_date = value
@property
def end_date(self):
return self._end_date
@end_date.setter
def end_date(self, value: datetime.date):
if type(value) != datetime.date:
raise TypeError("end_date must be a date object.")
if value < self._start_date:
raise ValueError("end_date can't be before start_date.")
self._end_date = value