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.
- earningscall-1.2.0/.python-version +1 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/CHANGELOG.md +4 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/PKG-INFO +15 -2
- {earningscall-1.1.1 → earningscall-1.2.0}/README.md +14 -1
- {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/__init__.py +9 -2
- {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/api.py +14 -3
- earningscall-1.2.0/earningscall/calendar.py +30 -0
- earningscall-1.2.0/earningscall/exports.py +86 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/pyproject.toml +1 -1
- earningscall-1.2.0/scripts/get_calendar.py +14 -0
- earningscall-1.2.0/tests/data/get-calendar-500-error.yaml +14 -0
- earningscall-1.2.0/tests/data/get-calendar-not-found-response.yaml +14 -0
- earningscall-1.2.0/tests/data/get-calendar-successful-response.yaml +62 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_download_audio_files.py +0 -8
- earningscall-1.2.0/tests/test_get_calendar.py +106 -0
- earningscall-1.1.1/.python-version +0 -1
- earningscall-1.1.1/earningscall/exports.py +0 -45
- {earningscall-1.1.1 → earningscall-1.2.0}/.github/workflows/release.yml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/.github/workflows/test.yml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/.gitignore +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/DEVELOPMENT.md +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/LICENSE +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/TODO.md +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/company.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/errors.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/event.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/sectors.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/symbols.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/transcript.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/earningscall/utils.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/hatch.toml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/scripts/download_audio_files.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/scripts/download_single_audio_file.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/scripts/download_sp500_audio_files.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/scripts/get_all_company_transcripts.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/scripts/get_all_sp500_transcript_texts.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/scripts/get_single_transcript.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/scripts/list_companies.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/setup.cfg +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2022-advanced-data-level-2.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2022-advanced-data-level-3.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2022-advanced-data-level-4.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2022-speaker-name-map-v2.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2030-not-authorized-l2.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2030-not-authorized.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2030-not-found.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2030-server-error.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/demo-symbols-v2-alpha.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/demo-symbols-v2.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/meta-q3-2024-not-authorized.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/meta-q3-2024-not-found.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/meta-q3-2024-other-error.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/msft-company-events.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/msft-q1-2022-audio-file-short-clip.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/msft-transcript-response.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/sp500-company-list-failed.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/sp500-company-list.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/symbols-v2-missing-edge-cases.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/symbols-v2.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/symbols.txt +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/data/symbols.yaml +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_api.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_company.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_earnings_event.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_errors.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_exports.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_get_company_events.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_get_sp500_companies_api.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_get_transcript.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_helper.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_responses_mocking.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_sectors.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_symbols.py +0 -0
- {earningscall-1.1.1 → earningscall-1.2.0}/tests/test_utils.py +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
3.12.9
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: earningscall
|
3
|
-
Version: 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__ = [
|
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
|
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.
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2022-advanced-data-level-2.yaml
RENAMED
File without changes
|
{earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2022-advanced-data-level-3.yaml
RENAMED
File without changes
|
{earningscall-1.1.1 → earningscall-1.2.0}/tests/data/aapl-q1-2022-advanced-data-level-4.yaml
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{earningscall-1.1.1 → earningscall-1.2.0}/tests/data/msft-q1-2022-audio-file-short-clip.yaml
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|