earningscall 1.1.1__tar.gz → 1.2.0__tar.gz

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.
Files changed (74) hide show
  1. earningscall-1.2.0/.python-version +1 -0
  2. {earningscall-1.1.1 → earningscall-1.2.0}/CHANGELOG.md +4 -0
  3. {earningscall-1.1.1 → earningscall-1.2.0}/PKG-INFO +15 -2
  4. {earningscall-1.1.1 → earningscall-1.2.0}/README.md +14 -1
  5. {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/__init__.py +9 -2
  6. {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/api.py +14 -3
  7. earningscall-1.2.0/earningscall/calendar.py +30 -0
  8. earningscall-1.2.0/earningscall/exports.py +86 -0
  9. {earningscall-1.1.1 → earningscall-1.2.0}/pyproject.toml +1 -1
  10. earningscall-1.2.0/scripts/get_calendar.py +14 -0
  11. earningscall-1.2.0/tests/data/get-calendar-500-error.yaml +14 -0
  12. earningscall-1.2.0/tests/data/get-calendar-not-found-response.yaml +14 -0
  13. earningscall-1.2.0/tests/data/get-calendar-successful-response.yaml +62 -0
  14. {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_download_audio_files.py +0 -8
  15. earningscall-1.2.0/tests/test_get_calendar.py +106 -0
  16. earningscall-1.1.1/.python-version +0 -1
  17. earningscall-1.1.1/earningscall/exports.py +0 -45
  18. {earningscall-1.1.1 → earningscall-1.2.0}/.github/workflows/release.yml +0 -0
  19. {earningscall-1.1.1 → earningscall-1.2.0}/.github/workflows/test.yml +0 -0
  20. {earningscall-1.1.1 → earningscall-1.2.0}/.gitignore +0 -0
  21. {earningscall-1.1.1 → earningscall-1.2.0}/DEVELOPMENT.md +0 -0
  22. {earningscall-1.1.1 → earningscall-1.2.0}/LICENSE +0 -0
  23. {earningscall-1.1.1 → earningscall-1.2.0}/TODO.md +0 -0
  24. {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/company.py +0 -0
  25. {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/errors.py +0 -0
  26. {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/event.py +0 -0
  27. {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/sectors.py +0 -0
  28. {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/symbols.py +0 -0
  29. {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/transcript.py +0 -0
  30. {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/utils.py +0 -0
  31. {earningscall-1.1.1 → earningscall-1.2.0}/hatch.toml +0 -0
  32. {earningscall-1.1.1 → earningscall-1.2.0}/scripts/download_audio_files.py +0 -0
  33. {earningscall-1.1.1 → earningscall-1.2.0}/scripts/download_single_audio_file.py +0 -0
  34. {earningscall-1.1.1 → earningscall-1.2.0}/scripts/download_sp500_audio_files.py +0 -0
  35. {earningscall-1.1.1 → earningscall-1.2.0}/scripts/get_all_company_transcripts.py +0 -0
  36. {earningscall-1.1.1 → earningscall-1.2.0}/scripts/get_all_sp500_transcript_texts.py +0 -0
  37. {earningscall-1.1.1 → earningscall-1.2.0}/scripts/get_single_transcript.py +0 -0
  38. {earningscall-1.1.1 → earningscall-1.2.0}/scripts/list_companies.py +0 -0
  39. {earningscall-1.1.1 → earningscall-1.2.0}/setup.cfg +0 -0
  40. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2022-advanced-data-level-2.yaml +0 -0
  41. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2022-advanced-data-level-3.yaml +0 -0
  42. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2022-advanced-data-level-4.yaml +0 -0
  43. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2022-speaker-name-map-v2.yaml +0 -0
  44. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2030-not-authorized-l2.yaml +0 -0
  45. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2030-not-authorized.yaml +0 -0
  46. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2030-not-found.yaml +0 -0
  47. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2030-server-error.yaml +0 -0
  48. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/demo-symbols-v2-alpha.yaml +0 -0
  49. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/demo-symbols-v2.yaml +0 -0
  50. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/meta-q3-2024-not-authorized.yaml +0 -0
  51. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/meta-q3-2024-not-found.yaml +0 -0
  52. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/meta-q3-2024-other-error.yaml +0 -0
  53. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/msft-company-events.yaml +0 -0
  54. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/msft-q1-2022-audio-file-short-clip.yaml +0 -0
  55. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/msft-transcript-response.yaml +0 -0
  56. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/sp500-company-list-failed.yaml +0 -0
  57. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/sp500-company-list.yaml +0 -0
  58. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/symbols-v2-missing-edge-cases.yaml +0 -0
  59. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/symbols-v2.yaml +0 -0
  60. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/symbols.txt +0 -0
  61. {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/symbols.yaml +0 -0
  62. {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_api.py +0 -0
  63. {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_company.py +0 -0
  64. {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_earnings_event.py +0 -0
  65. {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_errors.py +0 -0
  66. {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_exports.py +0 -0
  67. {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_get_company_events.py +0 -0
  68. {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_get_sp500_companies_api.py +0 -0
  69. {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_get_transcript.py +0 -0
  70. {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_helper.py +0 -0
  71. {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_responses_mocking.py +0 -0
  72. {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_sectors.py +0 -0
  73. {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_symbols.py +0 -0
  74. {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_utils.py +0 -0
@@ -0,0 +1 @@
1
+ 3.12.9
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## Release `1.2.0` - 2025-02-12
4
+
5
+ * Add `get_calendar` function to get the calendar for a given date.
6
+
3
7
  ## Release `1.1.1` - 2025-01-26
4
8
 
5
9
  * Modify default retry strategy to use 1s base delay and 10 max attempts (necessary for starter plan).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: earningscall
3
- Version: 1.1.1
3
+ Version: 1.2.0
4
4
  Summary: The EarningsCall Python library provides convenient access to the EarningsCall API. It includes a pre-defined set of classes for API resources that initialize themselves dynamically from API responses.
5
5
  Project-URL: Homepage, https://earningscall.biz
6
6
  Project-URL: Documentation, https://github.com/EarningsCall/earningscall-python
@@ -260,6 +260,20 @@ print("Downloading audio file for Apple Inc. Q3 2021...")
260
260
  audio_file = company.download_audio_file(year=2021, quarter=3, file_name="Apple Q3 2021.mp3")
261
261
  ```
262
262
 
263
+ ## Get Earnings Event Calendar
264
+
265
+ ```python
266
+ from datetime import date
267
+
268
+ from earningscall import get_calendar
269
+
270
+ calendar = get_calendar(date(2025, 1, 10))
271
+
272
+ for event in calendar:
273
+ print(f"{event.company_name} - Q{event.quarter} {event.year} on: {event.conference_date.astimezone().isoformat()} Transcript Ready: {event.transcript_ready}")
274
+ ```
275
+
276
+
263
277
  ## List All Companies
264
278
 
265
279
  ```python
@@ -297,7 +311,6 @@ for company in get_sp500_companies():
297
311
  print(f"{company.company_info} -- {company.company_info.sector} -- {company.company_info.industry}")
298
312
  ```
299
313
 
300
-
301
314
  ## Advanced
302
315
 
303
316
  ### Disable Caching
@@ -208,6 +208,20 @@ print("Downloading audio file for Apple Inc. Q3 2021...")
208
208
  audio_file = company.download_audio_file(year=2021, quarter=3, file_name="Apple Q3 2021.mp3")
209
209
  ```
210
210
 
211
+ ## Get Earnings Event Calendar
212
+
213
+ ```python
214
+ from datetime import date
215
+
216
+ from earningscall import get_calendar
217
+
218
+ calendar = get_calendar(date(2025, 1, 10))
219
+
220
+ for event in calendar:
221
+ print(f"{event.company_name} - Q{event.quarter} {event.year} on: {event.conference_date.astimezone().isoformat()} Transcript Ready: {event.transcript_ready}")
222
+ ```
223
+
224
+
211
225
  ## List All Companies
212
226
 
213
227
  ```python
@@ -245,7 +259,6 @@ for company in get_sp500_companies():
245
259
  print(f"{company.company_info} -- {company.company_info.sector} -- {company.company_info.industry}")
246
260
  ```
247
261
 
248
-
249
262
  ## Advanced
250
263
 
251
264
  ### Disable Caching
@@ -1,10 +1,17 @@
1
1
  from typing import Dict, Optional, Union
2
2
 
3
- from earningscall.exports import get_company, get_all_companies, get_sp500_companies
3
+ from earningscall.exports import get_company, get_all_companies, get_sp500_companies, get_calendar
4
4
  from earningscall.symbols import Symbols, load_symbols
5
5
 
6
6
  api_key: Optional[str] = None
7
7
  enable_requests_cache: bool = True
8
8
  retry_strategy: Optional[Dict[str, Union[str, int, float]]] = None
9
9
 
10
- __all__ = ["get_company", "get_all_companies", "get_sp500_companies", "Symbols", "load_symbols"]
10
+ __all__ = [
11
+ "get_company",
12
+ "get_all_companies",
13
+ "get_sp500_companies",
14
+ "Symbols",
15
+ "load_symbols",
16
+ "get_calendar",
17
+ ]
@@ -1,9 +1,9 @@
1
1
  import importlib
2
- import platform
3
- import urllib.parse
4
2
  import logging
5
3
  import os
4
+ import platform
6
5
  import time
6
+ import urllib.parse
7
7
  from importlib.metadata import PackageNotFoundError
8
8
  from typing import Dict, Optional, Union
9
9
 
@@ -169,7 +169,18 @@ def do_get(
169
169
  return response # Return the last response if all retries failed
170
170
 
171
171
 
172
- def get_events(exchange: str, symbol: str):
172
+ def get_calendar_api_operation(year: int, month: int, day: int) -> dict:
173
+ params = {
174
+ "year": str(year),
175
+ "month": str(month),
176
+ "day": str(day),
177
+ }
178
+ response = do_get("calendar", params=params)
179
+ response.raise_for_status()
180
+ return response.json()
181
+
182
+
183
+ def get_events(exchange: str, symbol: str) -> Optional[dict]:
173
184
  params = {
174
185
  "exchange": exchange,
175
186
  "symbol": symbol,
@@ -0,0 +1,30 @@
1
+ from dataclasses import dataclass, field
2
+ from datetime import datetime
3
+ from typing import Optional
4
+
5
+ from dataclasses_json import config
6
+ from dataclasses_json import dataclass_json
7
+ from marshmallow import fields
8
+
9
+
10
+ @dataclass_json
11
+ @dataclass
12
+ class CalendarEvent:
13
+ """
14
+ CalendarEvent
15
+ """
16
+
17
+ company_name: str
18
+ exchange: str
19
+ symbol: str
20
+ year: int
21
+ quarter: int
22
+ transcript_ready: bool
23
+ conference_date: Optional[datetime] = field(
24
+ default=None,
25
+ metadata=config(
26
+ encoder=lambda date: date.isoformat() if date else None,
27
+ decoder=lambda date: datetime.fromisoformat(date) if date else None,
28
+ mm_field=fields.DateTime(format="iso"),
29
+ ),
30
+ )
@@ -0,0 +1,86 @@
1
+ import datetime
2
+ from datetime import date, timedelta
3
+ from typing import List, Optional, Iterator
4
+
5
+ import requests
6
+
7
+ from earningscall import api
8
+ from earningscall.api import get_sp500_companies_txt_file, is_demo_account
9
+ from earningscall.calendar import CalendarEvent
10
+ from earningscall.company import Company
11
+ from earningscall.errors import InsufficientApiAccessError
12
+ from earningscall.symbols import get_symbols
13
+
14
+
15
+ def get_company(symbol: str, exchange: Optional[str] = None) -> Optional[Company]:
16
+ """
17
+ Get a company by symbol and optionally an exchange.
18
+
19
+ :param str symbol: The symbol to get the company for.
20
+ :param Optional[str] exchange: The exchange to get the company for.
21
+
22
+ :return: The company for the given symbol and exchange.
23
+ """
24
+ company_info = get_symbols().lookup_company(symbol=symbol, exchange=exchange)
25
+ if company_info:
26
+ return Company(company_info=company_info)
27
+ return None
28
+
29
+
30
+ def get_all_companies() -> Iterator[Company]:
31
+ """
32
+ Get all companies.
33
+
34
+ :return: An iterator of all companies that is available to the current API plan.
35
+ """
36
+ for company_info in get_symbols().get_all():
37
+ yield Company(company_info=company_info)
38
+
39
+
40
+ def get_sp500_companies() -> Iterator[Company]:
41
+ """
42
+ Get all S&P 500 companies.
43
+
44
+ :return: An iterator of all S&P 500 companies that is available to the current API plan.
45
+ """
46
+ resp = get_sp500_companies_txt_file()
47
+ if not resp:
48
+ return
49
+ for ticker_symbol in resp.split("\n"):
50
+ company_info = get_symbols().lookup_company(ticker_symbol)
51
+ if company_info:
52
+ yield Company(company_info=company_info)
53
+
54
+
55
+ def get_calendar(input_date: date) -> List[CalendarEvent]:
56
+ """
57
+ Get the earnings event calendar for a given input date.
58
+
59
+ :param date input_date: The date to get the calendar for.
60
+
61
+ :return: A list of CalendarEvent objects.
62
+ """
63
+ if not input_date:
64
+ raise ValueError("Date is required")
65
+ if isinstance(input_date, datetime.datetime):
66
+ input_date = input_date.date()
67
+ if not isinstance(input_date, date):
68
+ raise ValueError("Date must be a date object")
69
+ if input_date < date(2018, 1, 1):
70
+ raise ValueError("input_date must be greater than or equal to 2018-01-01")
71
+ # Check if input_date is greater than 30 days from today
72
+ if input_date > datetime.datetime.now().date() + timedelta(days=30):
73
+ raise ValueError("input_date must be less than 30 days from today")
74
+ if is_demo_account() and input_date != date(2025, 1, 10):
75
+ raise InsufficientApiAccessError(
76
+ f"\"{input_date}\" requires an API Key for access. To get your API Key,"
77
+ f" see: https://earningscall.biz/api-pricing"
78
+ )
79
+ try:
80
+ json_data = api.get_calendar_api_operation(input_date.year, input_date.month, input_date.day)
81
+ return [CalendarEvent.from_dict(event) for event in json_data] # type: ignore
82
+ except requests.exceptions.HTTPError as error:
83
+ if error.response.status_code == 404:
84
+ # Calendar Date not found, simply return an empty list
85
+ return []
86
+ raise error
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "earningscall"
3
- version = "1.1.1"
3
+ version = "1.2.0"
4
4
  description = "The EarningsCall Python library provides convenient access to the EarningsCall API. It includes a pre-defined set of classes for API resources that initialize themselves dynamically from API responses."
5
5
  readme = "README.md"
6
6
  authors = [{ name = "EarningsCall", email = "dev@earningscall.biz" }]
@@ -0,0 +1,14 @@
1
+ from datetime import date
2
+ import earningscall # noqa: F401
3
+
4
+ from earningscall import get_calendar
5
+
6
+ # TODO: Set your API key here:
7
+ # earningscall.api_key = "YOUR API KEY HERE"
8
+
9
+ events = get_calendar(date(2025, 1, 10))
10
+
11
+ for event in events:
12
+ print(
13
+ f"{event.company_name} - Q{event.quarter} {event.year} on: {event.conference_date.astimezone().isoformat()} Transcript Ready: {event.transcript_ready}"
14
+ )
@@ -0,0 +1,14 @@
1
+ responses:
2
+ - response:
3
+ auto_calculate_content_length: false
4
+ body: ''
5
+ content_type: text/plain
6
+ headers:
7
+ Via: 1.1 1cae0bb0106fc058447f3b32dee7b228.cloudfront.net (CloudFront)
8
+ X-Amz-Cf-Id: GJG7c5roSXmd3WXEWTNzyrl8VN9fWmIm3OR8oYxEZg8bWpoiZE6XvA==
9
+ X-Amz-Cf-Pop: IAH50-C4
10
+ X-Cache: FunctionGeneratedResponse from cloudfront
11
+ X-Plan-Name: demo
12
+ method: GET
13
+ status: 500
14
+ url: https://v2.api.earningscall.biz/calendar?apikey=XXXXXXXXXXX&year=2020&month=1&day=1
@@ -0,0 +1,14 @@
1
+ responses:
2
+ - response:
3
+ auto_calculate_content_length: false
4
+ body: ''
5
+ content_type: text/plain
6
+ headers:
7
+ Via: 1.1 1cae0bb0106fc058447f3b32dee7b228.cloudfront.net (CloudFront)
8
+ X-Amz-Cf-Id: GJG7c5roSXmd3WXEWTNzyrl8VN9fWmIm3OR8oYxEZg8bWpoiZE6XvA==
9
+ X-Amz-Cf-Pop: IAH50-C4
10
+ X-Cache: FunctionGeneratedResponse from cloudfront
11
+ X-Plan-Name: starter
12
+ method: GET
13
+ status: 404
14
+ url: https://v2.api.earningscall.biz/calendar?apikey=XXXXXXXXXXX&year=2018&month=1&day=1
@@ -0,0 +1,62 @@
1
+ responses:
2
+ - response:
3
+ auto_calculate_content_length: false
4
+ body: '[{"exchange": "NASDAQ", "symbol": "MPAA", "year": 2025, "quarter": 3, "conference_date":
5
+ "2025-02-10T13:00:00.000-05:00", "company_name": "Motorcar Parts of America,
6
+ Inc.", "transcript_ready": true}, {"exchange": "NASDAQ", "symbol": "SPSC", "year":
7
+ 2024, "quarter": 4, "conference_date": "2025-02-10T16:30:00-05:00", "company_name":
8
+ "SPS Commerce, Inc.", "transcript_ready": true}, {"exchange": "NASDAQ", "symbol":
9
+ "RICK", "year": 2025, "quarter": 1, "conference_date": "2025-02-10T16:30:00-05:00",
10
+ "company_name": "RCI Hospitality Holdings, Inc.", "transcript_ready": true},
11
+ {"exchange": "NASDAQ", "symbol": "ALAB", "year": 2024, "quarter": 4, "conference_date":
12
+ "2025-02-10T16:30:00-05:00", "company_name": "Astera Labs, Inc.", "transcript_ready":
13
+ true}, {"exchange": "NASDAQ", "symbol": "PETS", "year": 2025, "quarter": 3,
14
+ "conference_date": "2025-02-10T16:30:00-05:00", "company_name": "PetMed Express,
15
+ Inc.", "transcript_ready": true}, {"exchange": "NASDAQ", "symbol": "XAIR", "year":
16
+ 2025, "quarter": 3, "conference_date": "2025-02-10T16:30:00-05:00", "company_name":
17
+ "Beyond Air, Inc.", "transcript_ready": true}, {"exchange": "NASDAQ", "symbol":
18
+ "BLFS", "year": 2024, "quarter": 4, "conference_date": "2025-02-10T16:30:00.000-05:00",
19
+ "company_name": "BioLife Solutions, Inc.", "transcript_ready": true}, {"exchange":
20
+ "NASDAQ", "symbol": "VRTX", "year": 2024, "quarter": 4, "conference_date": "2025-02-10T16:30:00.000-05:00",
21
+ "company_name": "Vertex Pharmaceuticals Incorporated", "transcript_ready": true},
22
+ {"exchange": "NYSE", "symbol": "TAK", "year": 2025, "quarter": 1, "conference_date":
23
+ "2025-02-10T16:30:00-05:00", "company_name": "Takeda Pharmaceutical Company
24
+ Limited", "transcript_ready": true}, {"exchange": "OTC", "symbol": "PRKA", "year":
25
+ 2025, "quarter": 1, "conference_date": "2025-02-10T16:30:00.000-05:00", "company_name":
26
+ "Parks! America Inc", "transcript_ready": true}, {"exchange": "NASDAQ", "symbol":
27
+ "AMKR", "year": 2024, "quarter": 4, "conference_date": "2025-02-10T17:00:00.000-05:00",
28
+ "company_name": "Amkor Technology, Inc.", "transcript_ready": true}, {"exchange":
29
+ "NASDAQ", "symbol": "MITK", "year": 2025, "quarter": 1, "conference_date": "2025-02-10T17:00:00-05:00",
30
+ "company_name": "Mitek Systems, Inc.", "transcript_ready": true}, {"exchange":
31
+ "NYSE", "symbol": "SLQT", "year": 2025, "quarter": 2, "conference_date": "2025-02-10T17:00:00-05:00",
32
+ "company_name": "SelectQuote, Inc.", "transcript_ready": true}, {"exchange":
33
+ "NYSE", "symbol": "NGL", "year": 2025, "quarter": 3, "conference_date": "2025-02-10T16:00:00-06:00",
34
+ "company_name": "NGL Energy Partners LP", "transcript_ready": true}, {"exchange":
35
+ "NASDAQ", "symbol": "LSCC", "year": 2024, "quarter": 4, "conference_date": "2025-02-10T17:00:00-05:00",
36
+ "company_name": "Lattice Semiconductor Corporation", "transcript_ready": true},
37
+ {"exchange": "TSX", "symbol": "CVO", "year": 2025, "quarter": 3, "conference_date":
38
+ "2025-02-10T17:00:00-05:00", "company_name": "Coveo Solutions Inc.", "transcript_ready":
39
+ true}, {"exchange": "NYSE", "symbol": "INSP", "year": 2024, "quarter": 4, "conference_date":
40
+ "2025-02-10T17:00:00-05:00", "company_name": "Inspire Medical Systems, Inc.",
41
+ "transcript_ready": true}, {"exchange": "NASDAQ", "symbol": "CMCO", "year":
42
+ 2025, "quarter": 3, "conference_date": "2025-02-10T17:00:00-05:00", "company_name":
43
+ "Columbus McKinnon Corporation", "transcript_ready": true}, {"exchange": "NASDAQ",
44
+ "symbol": "HLIT", "year": 2024, "quarter": 4, "conference_date": "2025-02-10T14:00:00-08:00",
45
+ "company_name": "Harmonic Inc.", "transcript_ready": true}, {"exchange": "NYSE",
46
+ "symbol": "SSD", "year": 2024, "quarter": 4, "conference_date": "2025-02-10T17:00:00-05:00",
47
+ "company_name": "Simpson Manufacturing Company, Inc.", "transcript_ready": true}]'
48
+ content_type: text/plain
49
+ headers:
50
+ Cache-Control: public, max-age=500
51
+ ETag: W/"ff349ae06e2284df57d342fd9e3c9d4b"
52
+ Last-Modified: Wed, 12 Feb 2025 15:44:12 GMT
53
+ Transfer-Encoding: chunked
54
+ Vary: accept-encoding
55
+ Via: 1.1 dab9621fb9e60d4beae799f308450f86.cloudfront.net (CloudFront)
56
+ X-Amz-Cf-Id: o-89nbe8YAinJi-F0rXkAr8qIUAcTkW6Mmvk4vQeDH-9ob1MZ7QVfg==
57
+ X-Amz-Cf-Pop: IAH50-C4
58
+ X-Cache: Miss from cloudfront
59
+ x-amz-server-side-encryption: AES256
60
+ method: GET
61
+ status: 200
62
+ url: https://v2.api.earningscall.biz/calendar?apikey=XXXXXXXXXXX&year=2025&month=2&day=10
@@ -14,14 +14,6 @@ from earningscall.symbols import CompanyInfo, clear_symbols
14
14
  from earningscall.utils import data_path
15
15
 
16
16
 
17
- # Uncomment and run following code to generate msft-transcript-response.yaml file
18
- #
19
- # from responses import _recorder
20
- # @_recorder.record(file_path="meta-q3-2024-not-found.yaml")
21
- # def test_save_symbols_v1():
22
- # requests.get("https://v2.api.alpha.earningscall.biz/audio?apikey=brocktest&exchange=NASDAQ&symbol=META&year=2024&quarter=3")
23
-
24
-
25
17
  @pytest.fixture(autouse=True)
26
18
  def run_before_and_after_tests():
27
19
  """Fixture to execute asserts before and after a test is run"""
@@ -0,0 +1,106 @@
1
+ from datetime import datetime, date, timedelta
2
+
3
+ import pytest
4
+ import requests
5
+ import responses
6
+
7
+ import earningscall
8
+ from earningscall import get_calendar
9
+ from earningscall.api import purge_cache
10
+ from earningscall.errors import InsufficientApiAccessError
11
+ from earningscall.utils import data_path
12
+
13
+
14
+ # Uncomment and run following code to generate data/get-calendar-not-found-response.yaml file
15
+ #
16
+
17
+ # import earningscall
18
+ # @_recorder.record(file_path="data/get-calendar-not-found-response.yaml")
19
+ # def test_save_symbols_v1():
20
+ # requests.get("https://v2.api.earningscall.biz/calendar?apikey=demo&year=1980&month=1&day=1")
21
+
22
+
23
+ @pytest.fixture(autouse=True)
24
+ def run_before_and_after_tests():
25
+ """Fixture to execute asserts before and after a test is run"""
26
+ # Setup
27
+ earningscall.api_key = None
28
+ earningscall.retry_strategy = {
29
+ "strategy": "exponential",
30
+ "base_delay": 0.01,
31
+ "max_attempts": 3,
32
+ }
33
+ purge_cache()
34
+ yield # this is where the testing happens
35
+ # Teardown
36
+ earningscall.api_key = None
37
+
38
+
39
+ @responses.activate
40
+ def test_get_calendar_invalid_inputs():
41
+ with pytest.raises(ValueError):
42
+ get_calendar(None)
43
+ with pytest.raises(ValueError):
44
+ get_calendar("2025-02-10")
45
+ with pytest.raises(ValueError):
46
+ get_calendar(123456)
47
+ with pytest.raises(ValueError):
48
+ get_calendar(date(2017, 12, 31))
49
+ with pytest.raises(ValueError):
50
+ get_calendar(datetime.now() + timedelta(days=31))
51
+
52
+
53
+ @responses.activate
54
+ def test_get_non_demo_date():
55
+ with pytest.raises(InsufficientApiAccessError):
56
+ get_calendar(date(2020, 1, 1))
57
+
58
+
59
+ @responses.activate
60
+ def test_get_calendar_success():
61
+ earningscall.api_key = "XXXXXXXXXXX"
62
+ responses._add_from_file(file_path=data_path("get-calendar-successful-response.yaml"))
63
+ calendar = get_calendar(date(2025, 2, 10))
64
+ assert len(calendar) == 20
65
+ assert calendar[0].exchange == "NASDAQ"
66
+ assert calendar[0].symbol == "MPAA"
67
+ assert calendar[0].year == 2025
68
+ assert calendar[0].quarter == 3
69
+ assert calendar[0].conference_date.year == 2025
70
+ assert calendar[0].conference_date.month == 2
71
+ assert calendar[0].conference_date.day == 10
72
+ assert calendar[0].transcript_ready
73
+ assert calendar[0].company_name == "Motorcar Parts of America, Inc."
74
+
75
+
76
+ @responses.activate
77
+ def test_get_calendar_success_with_datetime():
78
+ earningscall.api_key = "XXXXXXXXXXX"
79
+ responses._add_from_file(file_path=data_path("get-calendar-successful-response.yaml"))
80
+ calendar = get_calendar(datetime(2025, 2, 10))
81
+ assert len(calendar) == 20
82
+ assert calendar[0].exchange == "NASDAQ"
83
+ assert calendar[0].symbol == "MPAA"
84
+ assert calendar[0].year == 2025
85
+ assert calendar[0].quarter == 3
86
+ assert calendar[0].conference_date.year == 2025
87
+ assert calendar[0].conference_date.month == 2
88
+ assert calendar[0].conference_date.day == 10
89
+ assert calendar[0].transcript_ready
90
+ assert calendar[0].company_name == "Motorcar Parts of America, Inc."
91
+
92
+
93
+ @responses.activate
94
+ def test_get_calendar_server_error():
95
+ earningscall.api_key = "XXXXXXXXXXX"
96
+ responses._add_from_file(file_path=data_path("get-calendar-500-error.yaml"))
97
+ with pytest.raises(requests.exceptions.HTTPError):
98
+ get_calendar(date(2020, 1, 1))
99
+
100
+
101
+ @responses.activate
102
+ def test_get_calendar_not_found():
103
+ earningscall.api_key = "XXXXXXXXXXX"
104
+ responses._add_from_file(file_path=data_path("get-calendar-not-found-response.yaml"))
105
+ calendar = get_calendar(date(2018, 1, 1))
106
+ assert len(calendar) == 0
@@ -1 +0,0 @@
1
- 3.12.8
@@ -1,45 +0,0 @@
1
- from typing import Optional, Iterator
2
-
3
- from earningscall.api import get_sp500_companies_txt_file
4
- from earningscall.company import Company
5
- from earningscall.symbols import get_symbols
6
-
7
-
8
- def get_company(symbol: str, exchange: Optional[str] = None) -> Optional[Company]:
9
- """
10
- Get a company by symbol and optionally an exchange.
11
-
12
- :param str symbol: The symbol to get the company for.
13
- :param Optional[str] exchange: The exchange to get the company for.
14
-
15
- :return: The company for the given symbol and exchange.
16
- """
17
- company_info = get_symbols().lookup_company(symbol=symbol, exchange=exchange)
18
- if company_info:
19
- return Company(company_info=company_info)
20
- return None
21
-
22
-
23
- def get_all_companies() -> Iterator[Company]:
24
- """
25
- Get all companies.
26
-
27
- :return: An iterator of all companies that is available to the current API plan.
28
- """
29
- for company_info in get_symbols().get_all():
30
- yield Company(company_info=company_info)
31
-
32
-
33
- def get_sp500_companies() -> Iterator[Company]:
34
- """
35
- Get all S&P 500 companies.
36
-
37
- :return: An iterator of all S&P 500 companies that is available to the current API plan.
38
- """
39
- resp = get_sp500_companies_txt_file()
40
- if not resp:
41
- return
42
- for ticker_symbol in resp.split("\n"):
43
- company_info = get_symbols().lookup_company(ticker_symbol)
44
- if company_info:
45
- yield Company(company_info=company_info)
File without changes
File without changes
File without changes
File without changes
File without changes