kensho-kfinance 3.0.3__py3-none-any.whl → 3.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kensho-kfinance might be problematic. Click here for more details.

Files changed (46) hide show
  1. {kensho_kfinance-3.0.3.dist-info → kensho_kfinance-3.1.1.dist-info}/METADATA +1 -1
  2. {kensho_kfinance-3.0.3.dist-info → kensho_kfinance-3.1.1.dist-info}/RECORD +46 -45
  3. kfinance/CHANGELOG.md +6 -0
  4. kfinance/client/fetch.py +26 -17
  5. kfinance/client/kfinance.py +17 -18
  6. kfinance/client/meta_classes.py +2 -2
  7. kfinance/client/tests/test_fetch.py +36 -24
  8. kfinance/client/tests/test_objects.py +112 -120
  9. kfinance/conftest.py +49 -5
  10. kfinance/domains/business_relationships/business_relationship_tools.py +30 -19
  11. kfinance/domains/business_relationships/tests/test_business_relationship_tools.py +18 -15
  12. kfinance/domains/capitalizations/capitalization_models.py +1 -1
  13. kfinance/domains/capitalizations/capitalization_tools.py +41 -24
  14. kfinance/domains/capitalizations/tests/test_capitalization_tools.py +38 -13
  15. kfinance/domains/companies/company_identifiers.py +0 -175
  16. kfinance/domains/companies/company_models.py +98 -5
  17. kfinance/domains/companies/company_tools.py +33 -29
  18. kfinance/domains/companies/tests/test_company_tools.py +11 -4
  19. kfinance/domains/competitors/competitor_tools.py +21 -21
  20. kfinance/domains/competitors/tests/test_competitor_tools.py +21 -7
  21. kfinance/domains/cusip_and_isin/cusip_and_isin_tools.py +38 -26
  22. kfinance/domains/cusip_and_isin/tests/test_cusip_and_isin_tools.py +27 -26
  23. kfinance/domains/earnings/earning_tools.py +54 -47
  24. kfinance/domains/earnings/tests/test_earnings_tools.py +58 -63
  25. kfinance/domains/line_items/line_item_tools.py +29 -36
  26. kfinance/domains/line_items/tests/test_line_item_tools.py +23 -5
  27. kfinance/domains/mergers_and_acquisitions/merger_and_acquisition_models.py +15 -0
  28. kfinance/domains/mergers_and_acquisitions/merger_and_acquisition_tools.py +55 -38
  29. kfinance/domains/mergers_and_acquisitions/tests/test_merger_and_acquisition_tools.py +22 -9
  30. kfinance/domains/prices/price_models.py +9 -9
  31. kfinance/domains/prices/price_tools.py +49 -38
  32. kfinance/domains/prices/tests/test_price_tools.py +52 -36
  33. kfinance/domains/segments/segment_models.py +7 -0
  34. kfinance/domains/segments/segment_tools.py +37 -20
  35. kfinance/domains/segments/tests/test_segment_tools.py +13 -6
  36. kfinance/domains/statements/statement_models.py +7 -0
  37. kfinance/domains/statements/statement_tools.py +38 -40
  38. kfinance/domains/statements/tests/test_statement_tools.py +39 -10
  39. kfinance/integrations/tool_calling/prompts.py +21 -14
  40. kfinance/integrations/tool_calling/tests/test_tool_calling_models.py +2 -2
  41. kfinance/integrations/tool_calling/tool_calling_models.py +24 -5
  42. kfinance/version.py +16 -3
  43. {kensho_kfinance-3.0.3.dist-info → kensho_kfinance-3.1.1.dist-info}/WHEEL +0 -0
  44. {kensho_kfinance-3.0.3.dist-info → kensho_kfinance-3.1.1.dist-info}/licenses/AUTHORS.md +0 -0
  45. {kensho_kfinance-3.0.3.dist-info → kensho_kfinance-3.1.1.dist-info}/licenses/LICENSE +0 -0
  46. {kensho_kfinance-3.0.3.dist-info → kensho_kfinance-3.1.1.dist-info}/top_level.txt +0 -0
@@ -26,28 +26,34 @@ from kfinance.domains.business_relationships.business_relationship_models import
26
26
  RelationshipResponse,
27
27
  )
28
28
  from kfinance.domains.capitalizations.capitalization_models import Capitalizations
29
- from kfinance.domains.companies.company_models import CompanyIdAndName
29
+ from kfinance.domains.companies.company_models import CompanyIdAndName, IdentificationTriple
30
30
  from kfinance.domains.earnings.earning_models import EarningsCallResp
31
31
  from kfinance.domains.line_items.line_item_models import LineItemResponse
32
+ from kfinance.domains.mergers_and_acquisitions.merger_and_acquisition_models import MergersResp
33
+ from kfinance.domains.prices.price_models import HistoryMetadataResp
34
+ from kfinance.domains.segments.segment_models import SegmentsResp
35
+ from kfinance.domains.statements.statement_models import StatementsResp
32
36
 
33
37
 
34
- msft_company_id = "21835"
35
- msft_security_id = "2630412"
38
+ msft_company_id = 21835
39
+ msft_security_id = 2630412
36
40
  msft_isin = "US5949181045"
37
41
  msft_cusip = "594918104"
38
- msft_trading_item_id = "2630413"
42
+ msft_trading_item_id = 2630413
39
43
  msft_buys_mongo = "517414"
40
44
 
41
45
 
42
46
  MOCK_TRADING_ITEM_DB = {
43
47
  msft_trading_item_id: {
44
- "metadata": {
45
- "currency": "USD",
46
- "symbol": "MSFT",
47
- "exchange_name": "NasdaqGS",
48
- "instrument_type": "Equity",
49
- "first_trade_date": "1986-03-13",
50
- },
48
+ "metadata": HistoryMetadataResp.model_validate(
49
+ {
50
+ "currency": "USD",
51
+ "symbol": "MSFT",
52
+ "exchange_name": "NasdaqGS",
53
+ "instrument_type": "Equity",
54
+ "first_trade_date": "1986-03-13",
55
+ }
56
+ ),
51
57
  "price_chart": {
52
58
  "2020-01-01": {
53
59
  "2021-01-01": b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x0cIDATx\x9cc\xf8\xcf\xc0\x00\x00\x03"
@@ -96,16 +102,6 @@ MOCK_COMPANY_DB = {
96
102
  ]
97
103
  }
98
104
  ),
99
- "statements": {
100
- "income_statement": {
101
- "statements": {
102
- "2019": {
103
- "Revenues": "125843000000.000000",
104
- "Total Revenues": "125843000000.000000",
105
- }
106
- }
107
- }
108
- },
109
105
  "line_items": {
110
106
  "revenue": LineItemResponse.model_validate(
111
107
  {
@@ -119,57 +115,26 @@ MOCK_COMPANY_DB = {
119
115
  }
120
116
  )
121
117
  },
122
- "segments": {
123
- "2024": {
124
- "Intelligent Cloud": {"Operating Income": 49584000000.0, "Revenue": 105362000000.0},
125
- "More Personal Computing": {
126
- "Operating Income": 19309000000.0,
127
- "Revenue": 62032000000.0,
128
- },
129
- "Productivity and Business Processes": {
130
- "Operating Income": 40540000000.0,
131
- "Revenue": 77728000000.0,
132
- },
118
+ "segments": SegmentsResp.model_validate(
119
+ {
120
+ "segments": {
121
+ "2024": {
122
+ "Intelligent Cloud": {
123
+ "Operating Income": 49584000000.0,
124
+ "Revenue": 105362000000.0,
125
+ },
126
+ "More Personal Computing": {
127
+ "Operating Income": 19309000000.0,
128
+ "Revenue": 62032000000.0,
129
+ },
130
+ "Productivity and Business Processes": {
131
+ "Operating Income": 40540000000.0,
132
+ "Revenue": 77728000000.0,
133
+ },
134
+ }
135
+ }
133
136
  }
134
- },
135
- "mergers": {
136
- "target": [
137
- {
138
- "transaction_id": 10998717,
139
- "merger_title": "Closed M/A of Microsoft Corporation",
140
- "closed_date": "2021-01-01",
141
- },
142
- {
143
- "transaction_id": 28237969,
144
- "merger_title": "Closed M/A of Microsoft Corporation",
145
- "closed_date": "2022-01-01",
146
- },
147
- ],
148
- "buyer": [
149
- {
150
- "transaction_id": 517414,
151
- "merger_title": "Closed M/A of MongoMusic, Inc.",
152
- "closed_date": "2023-01-01",
153
- },
154
- {
155
- "transaction_id": 596722,
156
- "merger_title": "Closed M/A of Digital Anvil, Inc.",
157
- "closed_date": "2023-01-01",
158
- },
159
- ],
160
- "seller": [
161
- {
162
- "transaction_id": 455551,
163
- "merger_title": "Closed M/A of VacationSpot.com, Inc.",
164
- "closed_date": "2024-01-01",
165
- },
166
- {
167
- "transaction_id": 456045,
168
- "merger_title": "Closed M/A of TransPoint, LLC",
169
- "closed_date": "2025-01-01",
170
- },
171
- ],
172
- },
137
+ ),
173
138
  "advisors": {
174
139
  msft_buys_mongo: {
175
140
  "advisors": [
@@ -190,7 +155,6 @@ MOCK_COMPANY_DB = {
190
155
  ),
191
156
  },
192
157
  31696: {"info": {"name": "MongoMusic, Inc."}},
193
- 21835: {"info": {"name": "Microsoft Corporation"}},
194
158
  18805: {"info": {"name": "Angel Investors L.P."}},
195
159
  20087: {"info": {"name": "Draper Richards, L.P."}},
196
160
  22103: {"info": {"name": "BRV Partners, LLC"}},
@@ -225,33 +189,69 @@ MOCK_TRANSCRIPT_DB = {
225
189
  },
226
190
  }
227
191
 
192
+ INCOME_STATEMENT = StatementsResp.model_validate(
193
+ {
194
+ "statements": {
195
+ "2019": {
196
+ "Revenues": "125843000000.000000",
197
+ "Total Revenues": "125843000000.000000",
198
+ }
199
+ }
200
+ }
201
+ )
202
+
203
+ MERGERS_RESP = MergersResp.model_validate(
204
+ {
205
+ "target": [
206
+ {
207
+ "transaction_id": 10998717,
208
+ "merger_title": "Closed M/A of Microsoft Corporation",
209
+ "closed_date": "2021-01-01",
210
+ },
211
+ {
212
+ "transaction_id": 28237969,
213
+ "merger_title": "Closed M/A of Microsoft Corporation",
214
+ "closed_date": "2022-01-01",
215
+ },
216
+ ],
217
+ "buyer": [
218
+ {
219
+ "transaction_id": 517414,
220
+ "merger_title": "Closed M/A of MongoMusic, Inc.",
221
+ "closed_date": "2023-01-01",
222
+ },
223
+ {
224
+ "transaction_id": 596722,
225
+ "merger_title": "Closed M/A of Digital Anvil, Inc.",
226
+ "closed_date": "2023-01-01",
227
+ },
228
+ ],
229
+ "seller": [
230
+ {
231
+ "transaction_id": 455551,
232
+ "merger_title": "Closed M/A of VacationSpot.com, Inc.",
233
+ "closed_date": "2024-01-01",
234
+ },
235
+ {
236
+ "transaction_id": 456045,
237
+ "merger_title": "Closed M/A of TransPoint, LLC",
238
+ "closed_date": "2025-01-01",
239
+ },
240
+ ],
241
+ }
242
+ )
228
243
 
229
244
  MOCK_SECURITY_DB = {msft_security_id: {"isin": msft_isin, "cusip": msft_cusip}}
230
245
 
246
+ msft_id_triple = IdentificationTriple(
247
+ company_id=msft_company_id, security_id=msft_security_id, trading_item_id=msft_trading_item_id
248
+ )
231
249
 
232
- MOCK_TICKER_DB = {
233
- "MSFT": {
234
- "company_id": msft_company_id,
235
- "security_id": msft_security_id,
236
- "trading_item_id": msft_trading_item_id,
237
- }
238
- }
250
+ MOCK_TICKER_DB = {"MSFT": msft_id_triple.model_dump(mode="json")}
239
251
 
240
- MOCK_ISIN_DB = {
241
- msft_isin: {
242
- "company_id": msft_company_id,
243
- "security_id": msft_security_id,
244
- "trading_item_id": msft_trading_item_id,
245
- }
246
- }
252
+ MOCK_ISIN_DB = {msft_isin: msft_id_triple.model_dump(mode="json")}
247
253
 
248
- MOCK_CUSIP_DB = {
249
- msft_cusip: {
250
- "company_id": msft_company_id,
251
- "security_id": msft_security_id,
252
- "trading_item_id": msft_trading_item_id,
253
- }
254
- }
254
+ MOCK_CUSIP_DB = {msft_cusip: msft_id_triple.model_dump(mode="json")}
255
255
 
256
256
  MOCK_MERGERS_DB = {
257
257
  msft_buys_mongo: {
@@ -350,7 +350,7 @@ class MockKFinanceApiClient:
350
350
  end_quarter,
351
351
  ):
352
352
  """Get a statement"""
353
- return MOCK_COMPANY_DB[company_id]["statements"][statement_type]
353
+ return INCOME_STATEMENT
354
354
 
355
355
  def fetch_line_item(
356
356
  self, company_id, line_item, period_type, start_year, end_year, start_quarter, end_quarter
@@ -395,7 +395,7 @@ class MockKFinanceApiClient:
395
395
  end_quarter,
396
396
  ):
397
397
  """Get a segment"""
398
- return MOCK_COMPANY_DB[company_id]
398
+ return MOCK_COMPANY_DB[company_id]["segments"]
399
399
 
400
400
  def fetch_companies_from_business_relationship(
401
401
  self, company_id: int, relationship_type: BusinessRelationshipType
@@ -411,7 +411,7 @@ class MockKFinanceApiClient:
411
411
  return MOCK_TRANSCRIPT_DB[key_dev_id]
412
412
 
413
413
  def fetch_mergers_for_company(self, company_id):
414
- return copy.deepcopy(MOCK_COMPANY_DB[company_id]["mergers"])
414
+ return copy.deepcopy(MERGERS_RESP)
415
415
 
416
416
  def fetch_merger_info(self, transaction_id: int):
417
417
  return copy.deepcopy(MOCK_MERGERS_DB[str(transaction_id)])
@@ -424,14 +424,16 @@ class TestTradingItem(TestCase):
424
424
  def setUp(self):
425
425
  """setup tests"""
426
426
  self.kfinance_api_client = MockKFinanceApiClient()
427
- self.msft_trading_item_from_id = TradingItem(self.kfinance_api_client, msft_trading_item_id)
427
+ self.msft_trading_item_from_id = TradingItem(
428
+ self.kfinance_api_client, int(msft_trading_item_id)
429
+ )
428
430
  self.msft_trading_item_from_ticker = TradingItem.from_ticker(
429
431
  self.kfinance_api_client, "MSFT"
430
432
  )
431
433
 
432
434
  def test_trading_item_id(self) -> None:
433
435
  """test trading item id"""
434
- expected_trading_item_id = MOCK_TICKER_DB["MSFT"]["trading_item_id"]
436
+ expected_trading_item_id = int(msft_trading_item_id)
435
437
  trading_item_id = self.msft_trading_item_from_id.trading_item_id
436
438
  self.assertEqual(expected_trading_item_id, trading_item_id)
437
439
 
@@ -440,18 +442,11 @@ class TestTradingItem(TestCase):
440
442
 
441
443
  def test_history_metadata(self) -> None:
442
444
  """test history metadata"""
443
- expected_history_metadata = MOCK_TRADING_ITEM_DB[msft_trading_item_id]["metadata"].copy()
444
- expected_history_metadata["first_trade_date"] = datetime.strptime(
445
- expected_history_metadata["first_trade_date"], "%Y-%m-%d"
446
- ).date()
447
- expected_exchange_code = "NasdaqGS"
445
+ expected_history_metadata: HistoryMetadataResp = MOCK_TRADING_ITEM_DB[msft_trading_item_id][
446
+ "metadata"
447
+ ].copy()
448
448
  history_metadata = self.msft_trading_item_from_id.history_metadata
449
- self.assertEqual(expected_history_metadata, history_metadata)
450
- self.assertEqual(expected_exchange_code, self.msft_trading_item_from_id.exchange_code)
451
-
452
- history_metadata = self.msft_trading_item_from_ticker.history_metadata
453
- self.assertEqual(expected_history_metadata, history_metadata)
454
- self.assertEqual(expected_exchange_code, self.msft_trading_item_from_ticker.exchange_code)
449
+ assert history_metadata == expected_history_metadata
455
450
 
456
451
  def test_price_chart(self):
457
452
  """test price chart"""
@@ -525,12 +520,11 @@ class TestCompany(TestCase):
525
520
  def test_income_statement(self) -> None:
526
521
  """test income statement"""
527
522
  expected_income_statement = (
528
- pd.DataFrame(
529
- MOCK_COMPANY_DB[msft_company_id]["statements"]["income_statement"]["statements"]
530
- )
523
+ pd.DataFrame(INCOME_STATEMENT.model_dump(mode="json")["statements"])
531
524
  .apply(pd.to_numeric)
532
525
  .replace(np.nan, None)
533
526
  )
527
+
534
528
  income_statement = self.msft_company.company.income_statement()
535
529
  pd.testing.assert_frame_equal(expected_income_statement, income_statement)
536
530
 
@@ -551,7 +545,9 @@ class TestCompany(TestCase):
551
545
 
552
546
  def test_business_segments(self) -> None:
553
547
  """test business statement"""
554
- expected_segments = MOCK_COMPANY_DB[msft_company_id]["segments"]
548
+ expected_segments = MOCK_COMPANY_DB[msft_company_id]["segments"].model_dump(mode="json")[
549
+ "segments"
550
+ ]
555
551
 
556
552
  business_segment = self.msft_company.company.business_segments()
557
553
  self.assertEqual(expected_segments, business_segment)
@@ -583,7 +579,7 @@ class TestCompany(TestCase):
583
579
  self.assertEqual(suppliers_via_property, suppliers_via_method)
584
580
 
585
581
  def test_mergers(self) -> None:
586
- expected_mergers = MOCK_COMPANY_DB[msft_company_id]["mergers"]
582
+ expected_mergers = MERGERS_RESP.model_dump(mode="json")
587
583
  mergers = self.msft_company.company.mergers_and_acquisitions
588
584
  mergers_json = {
589
585
  "target": [
@@ -730,9 +726,6 @@ class TestTicker(TestCase):
730
726
  def test_history_metadata(self) -> None:
731
727
  """test history metadata"""
732
728
  expected_history_metadata = MOCK_TRADING_ITEM_DB[msft_trading_item_id]["metadata"].copy()
733
- expected_history_metadata["first_trade_date"] = datetime.strptime(
734
- expected_history_metadata["first_trade_date"], "%Y-%m-%d"
735
- ).date()
736
729
  history_metadata = self.msft_ticker_from_ticker.history_metadata
737
730
  expected_exchange_code = "NasdaqGS"
738
731
  self.assertEqual(expected_history_metadata, history_metadata)
@@ -848,12 +841,11 @@ class TestTicker(TestCase):
848
841
  def test_income_statement(self) -> None:
849
842
  """test income statement"""
850
843
  expected_income_statement = (
851
- pd.DataFrame(
852
- MOCK_COMPANY_DB[msft_company_id]["statements"]["income_statement"]["statements"]
853
- )
844
+ pd.DataFrame(INCOME_STATEMENT.model_dump(mode="json")["statements"])
854
845
  .apply(pd.to_numeric)
855
846
  .replace(np.nan, None)
856
847
  )
848
+
857
849
  income_statement = self.msft_ticker_from_ticker.income_statement()
858
850
  pd.testing.assert_frame_equal(expected_income_statement, income_statement)
859
851
 
kfinance/conftest.py CHANGED
@@ -21,13 +21,14 @@ def mock_client(requests_mock: Mocker) -> Client:
21
21
  client.kfinance_api_client._access_token_expiry = int(datetime(2100, 1, 1).timestamp()) # noqa: SLF001
22
22
 
23
23
  # Create a mock for the SPGI id triple.
24
+ spgi_id_triple = {
25
+ "trading_item_id": SPGI_TRADING_ITEM_ID,
26
+ "security_id": SPGI_SECURITY_ID,
27
+ "company_id": SPGI_COMPANY_ID,
28
+ }
24
29
  requests_mock.get(
25
30
  url="https://kfinance.kensho.com/api/v1/id/SPGI",
26
- json={
27
- "trading_item_id": SPGI_TRADING_ITEM_ID,
28
- "security_id": SPGI_SECURITY_ID,
29
- "company_id": SPGI_COMPANY_ID,
30
- },
31
+ json=spgi_id_triple,
31
32
  )
32
33
  requests_mock.get(
33
34
  url="https://kfinance.kensho.com/api/v1/id/MSFT",
@@ -45,4 +46,47 @@ def mock_client(requests_mock: Mocker) -> Client:
45
46
  json={"primary_trading_item": company_id},
46
47
  )
47
48
 
49
+ # Fetch SPGI
50
+ requests_mock.post(
51
+ url="https://kfinance.kensho.com/api/v1/ids",
52
+ additional_matcher=lambda req: req.json().get("identifiers") == ["SPGI"],
53
+ json={"data": {"SPGI": spgi_id_triple}},
54
+ )
55
+
56
+ # Fetch SPGI and a non-existent company (which will include an error)
57
+ requests_mock.post(
58
+ url="https://kfinance.kensho.com/api/v1/ids",
59
+ additional_matcher=lambda req: req.json().get("identifiers") == ["SPGI", "non-existent"],
60
+ json={
61
+ "data": {
62
+ "SPGI": spgi_id_triple,
63
+ "non-existent": {
64
+ "error": "No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
65
+ },
66
+ }
67
+ },
68
+ )
69
+ # Fetch SPGI and a private company (which will only have a company_id but no security or trading item id.)
70
+ requests_mock.post(
71
+ url="https://kfinance.kensho.com/api/v1/ids",
72
+ additional_matcher=lambda req: req.json().get("identifiers") == ["SPGI", "private_company"],
73
+ json={
74
+ "data": {
75
+ "SPGI": spgi_id_triple,
76
+ "private_company": {"company_id": 1, "security_id": None, "trading_item_id": None},
77
+ }
78
+ },
79
+ )
80
+
81
+ requests_mock.post(
82
+ url="https://kfinance.kensho.com/api/v1/ids",
83
+ additional_matcher=lambda req: req.json().get("identifiers") == ["C_1", "C_2"],
84
+ json={
85
+ "data": {
86
+ "C_1": {"company_id": 1, "security_id": 1, "trading_item_id": 1},
87
+ "C_2": {"company_id": 2, "security_id": 2, "trading_item_id": 2},
88
+ }
89
+ },
90
+ )
91
+
48
92
  return client
@@ -7,10 +7,7 @@ from kfinance.client.batch_request_handling import Task, process_tasks_in_thread
7
7
  from kfinance.client.permission_models import Permission
8
8
  from kfinance.domains.business_relationships.business_relationship_models import (
9
9
  BusinessRelationshipType,
10
- )
11
- from kfinance.domains.companies.company_identifiers import (
12
- fetch_company_ids_from_identifiers,
13
- parse_identifiers,
10
+ RelationshipResponse,
14
11
  )
15
12
  from kfinance.integrations.tool_calling.tool_calling_models import (
16
13
  KfinanceTool,
@@ -23,6 +20,12 @@ class GetBusinessRelationshipFromIdentifiersArgs(ToolArgsWithIdentifiers):
23
20
  business_relationship: BusinessRelationshipType
24
21
 
25
22
 
23
+ class GetBusinessRelationshipFromIdentifiersResp(BaseModel):
24
+ business_relationship: BusinessRelationshipType
25
+ results: dict[str, RelationshipResponse]
26
+ errors: list[str]
27
+
28
+
26
29
  class GetBusinessRelationshipFromIdentifiers(KfinanceTool):
27
30
  name: str = "get_business_relationship_from_identifiers"
28
31
  description: str = dedent("""
@@ -39,36 +42,44 @@ class GetBusinessRelationshipFromIdentifiers(KfinanceTool):
39
42
  """Sample response:
40
43
 
41
44
  {
42
- "SPGI": {
43
- "current": [{"company_id": "C_883103", "company_name": "CRISIL Limited"}],
44
- "previous": [
45
- {"company_id": "C_472898", "company_name": "Morgan Stanley"},
46
- {"company_id": "C_8182358", "company_name": "Eloqua, Inc."},
47
- ],
48
- }
49
- }
45
+ 'business_relationship': 'supplier',
46
+ 'results': {
47
+ 'SPGI': {
48
+ 'current': [
49
+ {'company_id': 'C_883103', 'company_name': 'CRISIL Limited'}
50
+ ],
51
+ 'previous': [
52
+ {'company_id': 'C_472898', 'company_name': 'Morgan Stanley'},
53
+ {'company_id': 'C_8182358', 'company_name': 'Eloqua, Inc.'}
54
+ ]
55
+ }
56
+ },
57
+ 'errors': ['No identification triple found for the provided identifier: NON-EXISTENT of type: ticker']}
50
58
  """
51
59
 
52
60
  api_client = self.kfinance_client.kfinance_api_client
53
- parsed_identifiers = parse_identifiers(identifiers=identifiers, api_client=api_client)
54
- identifiers_to_company_ids = fetch_company_ids_from_identifiers(
55
- identifiers=parsed_identifiers, api_client=api_client
56
- )
61
+ id_triple_resp = api_client.unified_fetch_id_triples(identifiers=identifiers)
57
62
 
58
63
  tasks = [
59
64
  Task(
60
65
  func=api_client.fetch_companies_from_business_relationship,
61
66
  kwargs=dict(
62
- company_id=company_id,
67
+ company_id=id_triple.company_id,
63
68
  relationship_type=business_relationship,
64
69
  ),
65
70
  result_key=identifier,
66
71
  )
67
- for identifier, company_id in identifiers_to_company_ids.items()
72
+ for identifier, id_triple in id_triple_resp.identifiers_to_id_triples.items()
68
73
  ]
69
74
 
70
75
  relationship_responses = process_tasks_in_thread_pool_executor(
71
76
  api_client=api_client, tasks=tasks
72
77
  )
73
78
 
74
- return {str(k): v.model_dump(mode="json") for k, v in relationship_responses.items()}
79
+ output_model = GetBusinessRelationshipFromIdentifiersResp(
80
+ business_relationship=business_relationship,
81
+ results=relationship_responses,
82
+ errors=list(id_triple_resp.errors.values()),
83
+ )
84
+
85
+ return output_model.model_dump(mode="json")
@@ -17,8 +17,8 @@ class TestGetBusinessRelationshipFromIdentifiers:
17
17
  ):
18
18
  """
19
19
  GIVEN the GetBusinessRelationshipFromIdentifiers tool
20
- WHEN we request SPGI suppliers
21
- THEN we get back the SPGI suppliers
20
+ WHEN we request suppliers for SPGI and a non-existent company
21
+ THEN we get back the SPGI suppliers and an error message
22
22
  """
23
23
  supplier_resp = {
24
24
  "current": [{"company_id": 883103, "company_name": "CRISIL Limited"}],
@@ -28,13 +28,19 @@ class TestGetBusinessRelationshipFromIdentifiers:
28
28
  ],
29
29
  }
30
30
  expected_result = {
31
- "SPGI": {
32
- "current": [{"company_id": "C_883103", "company_name": "CRISIL Limited"}],
33
- "previous": [
34
- {"company_id": "C_472898", "company_name": "Morgan Stanley"},
35
- {"company_id": "C_8182358", "company_name": "Eloqua, Inc."},
36
- ],
37
- }
31
+ "business_relationship": "supplier",
32
+ "results": {
33
+ "SPGI": {
34
+ "current": [{"company_id": "C_883103", "company_name": "CRISIL Limited"}],
35
+ "previous": [
36
+ {"company_id": "C_472898", "company_name": "Morgan Stanley"},
37
+ {"company_id": "C_8182358", "company_name": "Eloqua, Inc."},
38
+ ],
39
+ }
40
+ },
41
+ "errors": [
42
+ "No identification triple found for the provided identifier: NON-EXISTENT of type: ticker"
43
+ ],
38
44
  }
39
45
 
40
46
  requests_mock.get(
@@ -44,12 +50,9 @@ class TestGetBusinessRelationshipFromIdentifiers:
44
50
 
45
51
  tool = GetBusinessRelationshipFromIdentifiers(kfinance_client=mock_client)
46
52
  args = GetBusinessRelationshipFromIdentifiersArgs(
47
- identifiers=["SPGI"], business_relationship=BusinessRelationshipType.supplier
53
+ identifiers=["SPGI", "non-existent"],
54
+ business_relationship=BusinessRelationshipType.supplier,
48
55
  )
49
56
  resp = tool.run(args.model_dump(mode="json"))
50
-
51
- assert list(resp.keys()) == ["SPGI"]
52
- # Sort companies by ID to make the result deterministic
53
- resp["SPGI"]["current"].sort(key=lambda x: x["company_id"])
54
- resp["SPGI"]["previous"].sort(key=lambda x: x["company_id"])
57
+ resp["results"]["SPGI"]["previous"].sort(key=lambda x: x["company_id"])
55
58
  assert resp == expected_result
@@ -88,7 +88,7 @@ class Capitalizations(BaseModel):
88
88
  mode="json",
89
89
  include={ # type: ignore[arg-type]
90
90
  "capitalizations": {
91
- 0 if only_include_most_recent_value else "__all__": {
91
+ -1 if only_include_most_recent_value else "__all__": {
92
92
  "date",
93
93
  capitalization_metric.value,
94
94
  }