From ca360413c32c669f4e0ca1d2f411042c790f3921 Mon Sep 17 00:00:00 2001 From: Maximilian Fajnberg Date: Fri, 3 Dec 2021 14:17:49 +0100 Subject: [PATCH] test for new function to get any statement from many companies; renamed functions --- src/mwfin/constants.py | 1 + src/mwfin/functions.py | 29 ++++++++++++-------- tests/test_functions.py | 61 +++++++++++++++++++++++++++-------------- 3 files changed, 59 insertions(+), 32 deletions(-) diff --git a/src/mwfin/constants.py b/src/mwfin/constants.py index 5042263..278ed1b 100644 --- a/src/mwfin/constants.py +++ b/src/mwfin/constants.py @@ -3,6 +3,7 @@ MAIN_LOGGER_NAME = 'mwfin' HTML_PARSER = 'html.parser' DOMAIN = 'www.marketwatch.com' BASE_URL = f'https://{DOMAIN}/investing/stock' +DEFAULT_CONCURRENT_BATCH_SIZE = 1 BS, IS, CF = 'Balance Sheet', 'Income Statement', 'Cash Flow Statement' FIN_STMT_URL_SUFFIX = { diff --git a/src/mwfin/functions.py b/src/mwfin/functions.py index 2f9cf18..523aa7f 100644 --- a/src/mwfin/functions.py +++ b/src/mwfin/functions.py @@ -81,8 +81,8 @@ def extract_all_data(soup: BeautifulSoup) -> ResultDict: @in_async_session -async def _get_financial_statement(statement: str, ticker_symbol: str, quarterly: bool = False, - session: ClientSession = None) -> ResultDict: +async def _get_single_company_fin_stmt(statement: str, ticker_symbol: str, quarterly: bool = False, + session: ClientSession = None) -> ResultDict: """ Returns data from the specified financial statement of the specified company. """ @@ -93,6 +93,13 @@ async def _get_financial_statement(statement: str, ticker_symbol: str, quarterly return extract_all_data(soup) +@in_async_session +async def _get_multi_companies_fin_stmt(statement: str, *ticker_symbols: str, quarterly: bool = False, + concurrent_batch_size: int = constants.DEFAULT_CONCURRENT_BATCH_SIZE, + session: ClientSession = None) -> Union[ResultDict, Dict[str, ResultDict]]: + pass + + @in_async_session async def get_balance_sheet(*ticker_symbols: str, quarterly: bool = False, session: ClientSession = None) -> Union[ResultDict, Dict[str, ResultDict]]: @@ -100,9 +107,9 @@ async def get_balance_sheet(*ticker_symbols: str, quarterly: bool = False, Returns data from the balance sheet of the specified company. """ if len(ticker_symbols) == 1: - return await _get_financial_statement(constants.BS, ticker_symbols[0], quarterly, session) + return await _get_single_company_fin_stmt(constants.BS, ticker_symbols[0], quarterly, session) return { - sym: await _get_financial_statement(constants.BS, sym, quarterly, session) + sym: await _get_single_company_fin_stmt(constants.BS, sym, quarterly, session) for sym in ticker_symbols } @@ -114,9 +121,9 @@ async def get_income_statement(*ticker_symbols: str, quarterly: bool = False, Returns data from the income statement of the specified company. """ if len(ticker_symbols) == 1: - return await _get_financial_statement(constants.IS, ticker_symbols[0], quarterly, session) + return await _get_single_company_fin_stmt(constants.IS, ticker_symbols[0], quarterly, session) return { - sym: await _get_financial_statement(constants.IS, sym, quarterly, session) + sym: await _get_single_company_fin_stmt(constants.IS, sym, quarterly, session) for sym in ticker_symbols } @@ -128,9 +135,9 @@ async def get_cash_flow_statement(*ticker_symbols: str, quarterly: bool = False, Returns data from the cash flow statement of the specified company. """ if len(ticker_symbols) == 1: - return await _get_financial_statement(constants.CF, ticker_symbols[0], quarterly, session) + return await _get_single_company_fin_stmt(constants.CF, ticker_symbols[0], quarterly, session) return { - sym: await _get_financial_statement(constants.CF, sym, quarterly, session) + sym: await _get_single_company_fin_stmt(constants.CF, sym, quarterly, session) for sym in ticker_symbols } @@ -139,9 +146,9 @@ async def get_cash_flow_statement(*ticker_symbols: str, quarterly: bool = False, async def _get_single_company_financials(ticker_symbol: str, quarterly: bool = False, session: ClientSession = None) -> Dict[str, ResultDict]: return { - constants.BS: await _get_financial_statement(constants.BS, ticker_symbol, quarterly, session), - constants.IS: await _get_financial_statement(constants.IS, ticker_symbol, quarterly, session), - constants.CF: await _get_financial_statement(constants.CF, ticker_symbol, quarterly, session) + constants.BS: await _get_single_company_fin_stmt(constants.BS, ticker_symbol, quarterly, session), + constants.IS: await _get_single_company_fin_stmt(constants.IS, ticker_symbol, quarterly, session), + constants.CF: await _get_single_company_fin_stmt(constants.CF, ticker_symbol, quarterly, session) } diff --git a/tests/test_functions.py b/tests/test_functions.py index 0ae8e1a..5bac56f 100644 --- a/tests/test_functions.py +++ b/tests/test_functions.py @@ -101,7 +101,7 @@ class FunctionsTestCase(IsolatedAsyncioTestCase): @patch.object(functions, 'extract_all_data') @patch.object(functions, 'soup_from_url') - async def test__get_financial_statement(self, mock_soup_from_url, mock_extract_all_data): + async def test__get_single_company_fin_stmt(self, mock_soup_from_url, mock_extract_all_data): mock_session = MagicMock() test_ticker, statement = 'bar', BS test_url = f'{BASE_URL}/{test_ticker}/financials{FIN_STMT_URL_SUFFIX[statement]}' @@ -109,7 +109,7 @@ class FunctionsTestCase(IsolatedAsyncioTestCase): mock_extract_all_data.return_value = expected_output = {'foo': 'bar'} quarterly = False - output = await functions._get_financial_statement(statement, test_ticker, quarterly, mock_session) + output = await functions._get_single_company_fin_stmt(statement, test_ticker, quarterly, mock_session) self.assertDictEqual(expected_output, output) mock_soup_from_url.assert_called_once_with(test_url, mock_session) mock_extract_all_data.assert_called_once_with(mock_soup) @@ -117,14 +117,33 @@ class FunctionsTestCase(IsolatedAsyncioTestCase): mock_extract_all_data.reset_mock() quarterly = True - output = await functions._get_financial_statement(statement, test_ticker, quarterly, mock_session) + output = await functions._get_single_company_fin_stmt(statement, test_ticker, quarterly, mock_session) self.assertDictEqual(expected_output, output) mock_soup_from_url.assert_called_once_with(test_url + '/quarter', mock_session) mock_extract_all_data.assert_called_once_with(mock_soup) - async def _helper_test_get_any_statement(self, statement: str, mock__get_financial_statement): + @patch.object(functions, '_get_single_company_fin_stmt') + async def test__get_multi_companies_fin_stmt(self, mock__get_single_company_fin_stmt): + statement, symbol1, symbol2, quarterly, mock_session = 'xyz', 'foo', 'bar', False, MagicMock() + mock__get_single_company_fin_stmt.return_value = expected_output = 'baz' + output = await functions._get_multi_companies_fin_stmt(statement, symbol1, + quarterly=quarterly, session=mock_session) + self.assertEqual(expected_output, output) + mock__get_single_company_fin_stmt.assert_called_once_with(statement, symbol1, quarterly, mock_session) + mock__get_single_company_fin_stmt.reset_mock() + + expected_output = {symbol1: expected_output, symbol2: expected_output} + output = await functions._get_multi_companies_fin_stmt(symbol1, symbol2, + quarterly=quarterly, session=mock_session) + self.assertDictEqual(expected_output, output) + mock__get_single_company_fin_stmt.assert_has_calls([ + call(statement, symbol1, quarterly, mock_session), + call(statement, symbol2, quarterly, mock_session) + ]) + + async def _helper_test_get_any_statement(self, statement: str, mock__get_single_company_fin_stmt): symbol, quarterly, mock_session = 'foo', False, MagicMock() - mock__get_financial_statement.return_value = expected_output = 'bar' + mock__get_single_company_fin_stmt.return_value = expected_output = 'bar' if statement == BS: function = functions.get_balance_sheet elif statement == IS: @@ -135,38 +154,38 @@ class FunctionsTestCase(IsolatedAsyncioTestCase): raise ValueError output = await function(symbol, quarterly=quarterly, session=mock_session) self.assertEqual(expected_output, output) - mock__get_financial_statement.assert_called_once_with(statement, symbol, quarterly, mock_session) - mock__get_financial_statement.reset_mock() + mock__get_single_company_fin_stmt.assert_called_once_with(statement, symbol, quarterly, mock_session) + mock__get_single_company_fin_stmt.reset_mock() symbol1, symbol2 = 'x', 'y' expected_output = {symbol1: expected_output, symbol2: expected_output} output = await function(symbol1, symbol2, quarterly=quarterly, session=mock_session) self.assertDictEqual(expected_output, output) - mock__get_financial_statement.assert_has_calls([ + mock__get_single_company_fin_stmt.assert_has_calls([ call(statement, symbol1, quarterly, mock_session), call(statement, symbol2, quarterly, mock_session), ]) - @patch.object(functions, '_get_financial_statement') - async def test_get_balance_sheet(self, mock__get_financial_statement): - await self._helper_test_get_any_statement(BS, mock__get_financial_statement) + @patch.object(functions, '_get_single_company_fin_stmt') + async def test_get_balance_sheet(self, mock__get_single_company_fin_stmt): + await self._helper_test_get_any_statement(BS, mock__get_single_company_fin_stmt) - @patch.object(functions, '_get_financial_statement') - async def test_get_income_statement(self, mock__get_financial_statement): - await self._helper_test_get_any_statement(IS, mock__get_financial_statement) + @patch.object(functions, '_get_single_company_fin_stmt') + async def test_get_income_statement(self, mock__get_single_company_fin_stmt): + await self._helper_test_get_any_statement(IS, mock__get_single_company_fin_stmt) - @patch.object(functions, '_get_financial_statement') - async def test_get_cash_flow_statement(self, mock__get_financial_statement): - await self._helper_test_get_any_statement(CF, mock__get_financial_statement) + @patch.object(functions, '_get_single_company_fin_stmt') + async def test_get_cash_flow_statement(self, mock__get_single_company_fin_stmt): + await self._helper_test_get_any_statement(CF, mock__get_single_company_fin_stmt) - @patch.object(functions, '_get_financial_statement') - async def test__get_single_company_financials(self, mock__get_financial_statement): + @patch.object(functions, '_get_single_company_fin_stmt') + async def test__get_single_company_financials(self, mock__get_single_company_fin_stmt): symbol, quarterly, mock_session = 'foo', False, MagicMock() - mock__get_financial_statement.return_value = bar = 'bar' + mock__get_single_company_fin_stmt.return_value = bar = 'bar' expected_output = {BS: bar, IS: bar, CF: bar} output = await functions._get_single_company_financials(symbol, quarterly, mock_session) self.assertDictEqual(expected_output, output) - mock__get_financial_statement.assert_has_calls([ + mock__get_single_company_fin_stmt.assert_has_calls([ call(BS, symbol, quarterly, mock_session), call(IS, symbol, quarterly, mock_session), call(CF, symbol, quarterly, mock_session)