earningscall 1.2.1__tar.gz → 1.3.1__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.1 → earningscall-1.3.1}/CHANGELOG.md +8 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/PKG-INFO +14 -2
- {earningscall-1.2.1 → earningscall-1.3.1}/README.md +13 -1
- {earningscall-1.2.1 → earningscall-1.3.1}/earningscall/api.py +36 -4
- {earningscall-1.2.1 → earningscall-1.3.1}/earningscall/company.py +48 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/pyproject.toml +1 -1
- earningscall-1.3.1/scripts/download_slides.py +34 -0
- earningscall-1.3.1/tests/data/meta-q3-2024-slides-not-authorized.yaml +15 -0
- earningscall-1.3.1/tests/data/meta-q3-2024-slides-not-found.yaml +16 -0
- earningscall-1.3.1/tests/data/meta-q3-2024-slides-other-error.yaml +15 -0
- earningscall-1.3.1/tests/data/msft-q1-2022-slide-deck-short-clip.yaml +62 -0
- earningscall-1.3.1/tests/test_download_slide_decks.py +147 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/.github/workflows/release.yml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/.github/workflows/test.yml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/.gitignore +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/.python-version +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/DEVELOPMENT.md +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/LICENSE +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/TODO.md +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/earningscall/__init__.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/earningscall/calendar.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/earningscall/errors.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/earningscall/event.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/earningscall/exports.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/earningscall/sectors.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/earningscall/symbols.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/earningscall/transcript.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/earningscall/utils.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/hatch.toml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/scripts/download_audio_files.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/scripts/download_single_audio_file.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/scripts/download_sp500_audio_files.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/scripts/get_all_company_transcripts.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/scripts/get_all_sp500_transcript_texts.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/scripts/get_calendar.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/scripts/get_single_transcript.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/scripts/list_companies.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/setup.cfg +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/aapl-q1-2022-advanced-data-level-2.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/aapl-q1-2022-advanced-data-level-3.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/aapl-q1-2022-advanced-data-level-4.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/aapl-q1-2022-speaker-name-map-v2.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/aapl-q1-2030-not-authorized-l2.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/aapl-q1-2030-not-authorized.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/aapl-q1-2030-not-found.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/aapl-q1-2030-server-error.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/demo-symbols-v2-alpha.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/demo-symbols-v2.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/get-calendar-500-error.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/get-calendar-not-found-response.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/get-calendar-successful-response.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/meta-q3-2024-not-authorized.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/meta-q3-2024-not-found.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/meta-q3-2024-other-error.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/msft-company-events.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/msft-q1-2022-audio-file-short-clip.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/msft-transcript-response.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/nvda-q2-2025-level-4-data-missing.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/sp500-company-list-failed.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/sp500-company-list.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/symbols-v2-missing-edge-cases.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/symbols-v2.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/symbols.txt +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/data/symbols.yaml +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/test_api.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/test_company.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/test_download_audio_files.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/test_earnings_event.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/test_errors.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/test_exports.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/test_get_calendar.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/test_get_company_events.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/test_get_sp500_companies_api.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/test_get_transcript.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/test_helper.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/test_responses_mocking.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/test_sectors.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/test_symbols.py +0 -0
- {earningscall-1.2.1 → earningscall-1.3.1}/tests/test_utils.py +0 -0
@@ -1,5 +1,13 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## Release `1.3.1` - 2025-07-18
|
4
|
+
|
5
|
+
* Update README.md with new slide deck download example.
|
6
|
+
|
7
|
+
## Release `1.3.0` - 2025-07-17
|
8
|
+
|
9
|
+
* Add `download_slide_deck` function to the `Company` class.
|
10
|
+
|
3
11
|
## Release `1.2.1` - 2025-04-04
|
4
12
|
|
5
13
|
* Bugfix: handle case where Q&A section is missing from the transcript for level 4 transcription.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: earningscall
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.3.1
|
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
|
@@ -257,7 +257,19 @@ from earningscall import get_company
|
|
257
257
|
company = get_company("aapl") # Lookup Apple, Inc by its ticker symbol, "AAPL"
|
258
258
|
|
259
259
|
print("Downloading audio file for Apple Inc. Q3 2021...")
|
260
|
-
audio_file = company.download_audio_file(year=2021, quarter=3, file_name="Apple
|
260
|
+
audio_file = company.download_audio_file(year=2021, quarter=3, file_name="Apple-Q3-2021.mp3")
|
261
|
+
print(f"Downloaded audio file to: {audio_file}")
|
262
|
+
```
|
263
|
+
|
264
|
+
## Download Slide Deck
|
265
|
+
|
266
|
+
```python
|
267
|
+
from earningscall import get_company
|
268
|
+
|
269
|
+
company = get_company("MSFT") # Lookup Microsoft by its ticker symbol, "MSFT"
|
270
|
+
|
271
|
+
slide_deck_filename = company.download_slide_deck(year=2025, quarter=1, file_name="Microsoft-Q1-2025-Slides.pdf")
|
272
|
+
print(f"Downloaded slide deck to: {slide_deck_filename}")
|
261
273
|
```
|
262
274
|
|
263
275
|
## Get Earnings Event Calendar
|
@@ -205,7 +205,19 @@ from earningscall import get_company
|
|
205
205
|
company = get_company("aapl") # Lookup Apple, Inc by its ticker symbol, "AAPL"
|
206
206
|
|
207
207
|
print("Downloading audio file for Apple Inc. Q3 2021...")
|
208
|
-
audio_file = company.download_audio_file(year=2021, quarter=3, file_name="Apple
|
208
|
+
audio_file = company.download_audio_file(year=2021, quarter=3, file_name="Apple-Q3-2021.mp3")
|
209
|
+
print(f"Downloaded audio file to: {audio_file}")
|
210
|
+
```
|
211
|
+
|
212
|
+
## Download Slide Deck
|
213
|
+
|
214
|
+
```python
|
215
|
+
from earningscall import get_company
|
216
|
+
|
217
|
+
company = get_company("MSFT") # Lookup Microsoft by its ticker symbol, "MSFT"
|
218
|
+
|
219
|
+
slide_deck_filename = company.download_slide_deck(year=2025, quarter=1, file_name="Microsoft-Q1-2025-Slides.pdf")
|
220
|
+
print(f"Downloaded slide deck to: {slide_deck_filename}")
|
209
221
|
```
|
210
222
|
|
211
223
|
## Get Earnings Event Calendar
|
@@ -1,10 +1,9 @@
|
|
1
|
-
import importlib
|
1
|
+
import importlib.metadata
|
2
2
|
import logging
|
3
3
|
import os
|
4
4
|
import platform
|
5
5
|
import time
|
6
6
|
import urllib.parse
|
7
|
-
from importlib.metadata import PackageNotFoundError
|
8
7
|
from typing import Dict, Optional, Union
|
9
8
|
|
10
9
|
import requests
|
@@ -65,7 +64,7 @@ def purge_cache():
|
|
65
64
|
def get_earnings_call_version():
|
66
65
|
try:
|
67
66
|
return importlib.metadata.version("earningscall")
|
68
|
-
except PackageNotFoundError:
|
67
|
+
except importlib.metadata.PackageNotFoundError:
|
69
68
|
return None
|
70
69
|
|
71
70
|
|
@@ -164,7 +163,7 @@ def do_get(
|
|
164
163
|
log.warning(
|
165
164
|
f"Rate limited (429). Retrying in {wait_time} seconds... (Attempt {attempt + 1}/{max_attempts})"
|
166
165
|
)
|
167
|
-
time.sleep(wait_time)
|
166
|
+
time.sleep(float(wait_time))
|
168
167
|
|
169
168
|
return response # Return the last response if all retries failed
|
170
169
|
|
@@ -266,3 +265,36 @@ def download_audio_file(
|
|
266
265
|
for chunk in response.iter_content(chunk_size=8192):
|
267
266
|
f.write(chunk)
|
268
267
|
return local_filename
|
268
|
+
|
269
|
+
|
270
|
+
def download_slide_deck(
|
271
|
+
exchange: str,
|
272
|
+
symbol: str,
|
273
|
+
year: int,
|
274
|
+
quarter: int,
|
275
|
+
file_name: Optional[str] = None,
|
276
|
+
) -> Optional[str]:
|
277
|
+
"""
|
278
|
+
Get the slide deck for a given exchange, symbol, year, and quarter.
|
279
|
+
|
280
|
+
:param str exchange: The exchange to get the slide deck for.
|
281
|
+
:param str symbol: The symbol to get the slide deck for.
|
282
|
+
:param int year: The 4-digit year to get the slide deck for.
|
283
|
+
:param int quarter: The quarter to get the slide deck for (1, 2, 3, or 4).
|
284
|
+
:param file_name: Optionally specify the filename to save the slide deck to.
|
285
|
+
:return: The filename of the downloaded slide deck file.
|
286
|
+
:rtype Optional[str]: The filename of the downloaded slide deck file.
|
287
|
+
"""
|
288
|
+
params = {
|
289
|
+
"exchange": exchange,
|
290
|
+
"symbol": symbol,
|
291
|
+
"year": str(year),
|
292
|
+
"quarter": str(quarter),
|
293
|
+
}
|
294
|
+
local_filename = file_name or f"{exchange}_{symbol}_{year}_{quarter}_slides.pdf"
|
295
|
+
with do_get("slides", params=params, stream=True) as response:
|
296
|
+
response.raise_for_status()
|
297
|
+
with open(local_filename, "wb") as f:
|
298
|
+
for chunk in response.iter_content(chunk_size=8192):
|
299
|
+
f.write(chunk)
|
300
|
+
return local_filename
|
@@ -164,3 +164,51 @@ class Company:
|
|
164
164
|
log.error(error_message)
|
165
165
|
raise InsufficientApiAccessError(error_message)
|
166
166
|
raise error
|
167
|
+
|
168
|
+
def download_slide_deck(
|
169
|
+
self,
|
170
|
+
year: Optional[int] = None,
|
171
|
+
quarter: Optional[int] = None,
|
172
|
+
event: Optional[EarningsEvent] = None,
|
173
|
+
file_name: Optional[str] = None,
|
174
|
+
) -> Optional[str]:
|
175
|
+
"""
|
176
|
+
Download the slide deck for a given year and quarter.
|
177
|
+
|
178
|
+
:param Optional[int] year: The year to get the slide deck for.
|
179
|
+
:param Optional[int] quarter: The quarter to get the slide deck for.
|
180
|
+
:param Optional[EarningsEvent] event: The event to get the slide deck for.
|
181
|
+
:param Optional[str] file_name: The file name to save the slide deck to.
|
182
|
+
|
183
|
+
:return: The filename of the downloaded slide deck file.
|
184
|
+
"""
|
185
|
+
if not self.company_info.exchange or not self.company_info.symbol:
|
186
|
+
return None
|
187
|
+
if (not year or not quarter) and event:
|
188
|
+
year = event.year
|
189
|
+
quarter = event.quarter
|
190
|
+
if not year or not quarter:
|
191
|
+
raise ValueError("Must specify either event or year and quarter")
|
192
|
+
if quarter < 1 or quarter > 4:
|
193
|
+
raise ValueError("Invalid quarter. Must be one of: {1,2,3,4}")
|
194
|
+
try:
|
195
|
+
resp = api.download_slide_deck(
|
196
|
+
exchange=self.company_info.exchange,
|
197
|
+
symbol=self.company_info.symbol,
|
198
|
+
year=year,
|
199
|
+
quarter=quarter,
|
200
|
+
file_name=file_name,
|
201
|
+
)
|
202
|
+
return resp
|
203
|
+
except requests.exceptions.HTTPError as error:
|
204
|
+
if error.response.status_code == 404:
|
205
|
+
return None
|
206
|
+
if error.response.status_code == 403:
|
207
|
+
plan_name = error.response.headers["X-Plan-Name"]
|
208
|
+
error_message = (
|
209
|
+
f"Your plan ({plan_name}) does not include Slide Decks. "
|
210
|
+
"Upgrade your plan here: https://earningscall.biz/api-pricing"
|
211
|
+
)
|
212
|
+
log.error(error_message)
|
213
|
+
raise InsufficientApiAccessError(error_message)
|
214
|
+
raise error
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "earningscall"
|
3
|
-
version = "1.
|
3
|
+
version = "1.3.1"
|
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,34 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Script to download slides for a given company and quarter
|
4
|
+
"""
|
5
|
+
|
6
|
+
import earningscall
|
7
|
+
|
8
|
+
|
9
|
+
def test_slides_api():
|
10
|
+
"""Test the new slides API functionality"""
|
11
|
+
|
12
|
+
# Get a company
|
13
|
+
company = earningscall.get_company("MSFT")
|
14
|
+
if not company:
|
15
|
+
print("Could not find MSFT company")
|
16
|
+
return
|
17
|
+
|
18
|
+
print(f"Testing slides API for {company.company_info.name}")
|
19
|
+
|
20
|
+
try:
|
21
|
+
# Try to download slides for Q1 2025 (using demo data)
|
22
|
+
slide_file = company.download_slide_deck(year=2025, quarter=1)
|
23
|
+
if slide_file:
|
24
|
+
print(f"Successfully downloaded slides: {slide_file}")
|
25
|
+
else:
|
26
|
+
print("No slides available for the requested quarter")
|
27
|
+
|
28
|
+
except Exception as e:
|
29
|
+
print(f"Error downloading slides: {e}")
|
30
|
+
# This is expected for demo account or if slides aren't available
|
31
|
+
|
32
|
+
|
33
|
+
if __name__ == "__main__":
|
34
|
+
test_slides_api()
|
@@ -0,0 +1,15 @@
|
|
1
|
+
responses:
|
2
|
+
- response:
|
3
|
+
auto_calculate_content_length: false
|
4
|
+
body: ''
|
5
|
+
content_type: text/plain
|
6
|
+
headers:
|
7
|
+
Transfer-Encoding: chunked
|
8
|
+
Via: 1.1 dda997f5acee0f79dcf70db4a17d82f6.cloudfront.net (CloudFront)
|
9
|
+
X-Amz-Cf-Id: hHQoX1-pbWB6rw1QN1G5grABhjXtX_cdjfpnR4lkIU4ypiZRBRk4kQ==
|
10
|
+
X-Amz-Cf-Pop: IAH50-C4
|
11
|
+
X-Cache: Error from cloudfront
|
12
|
+
X-Plan-Name: basic
|
13
|
+
method: GET
|
14
|
+
status: 403
|
15
|
+
url: https://v2.api.earningscall.biz/slides?apikey=foobar&exchange=NASDAQ&symbol=META&year=2024&quarter=3
|
@@ -0,0 +1,16 @@
|
|
1
|
+
responses:
|
2
|
+
- response:
|
3
|
+
auto_calculate_content_length: false
|
4
|
+
body: '<?xml version="1.0" encoding="UTF-8"?>
|
5
|
+
|
6
|
+
<Error><Code>NoSuchKey</Code><Message>The specified key does not exist.</Message><Key>404.html</Key><RequestId>C1SFD1W2WXC0JHHC</RequestId><HostId>Fp+yajCTsxImKV1EaTFJ7C9i+jpeNUy1t4FcY8Iq0B3Z8js6baJPx4qhYaTECecl8jiD85Odqoc=</HostId></Error>'
|
7
|
+
content_type: text/plain
|
8
|
+
headers:
|
9
|
+
Transfer-Encoding: chunked
|
10
|
+
Via: 1.1 dda997f5acee0f79dcf70db4a17d82f6.cloudfront.net (CloudFront)
|
11
|
+
X-Amz-Cf-Id: hHQoX1-pbWB6rw1QN1G5grABhjXtX_cdjfpnR4lkIU4ypiZRBRk4kQ==
|
12
|
+
X-Amz-Cf-Pop: IAH50-C4
|
13
|
+
X-Cache: Error from cloudfront
|
14
|
+
method: GET
|
15
|
+
status: 404
|
16
|
+
url: https://v2.api.earningscall.biz/slides?apikey=foobar&exchange=NASDAQ&symbol=META&year=2024&quarter=3
|
@@ -0,0 +1,15 @@
|
|
1
|
+
responses:
|
2
|
+
- response:
|
3
|
+
auto_calculate_content_length: false
|
4
|
+
body: ''
|
5
|
+
content_type: text/plain
|
6
|
+
headers:
|
7
|
+
Transfer-Encoding: chunked
|
8
|
+
Via: 1.1 dda997f5acee0f79dcf70db4a17d82f6.cloudfront.net (CloudFront)
|
9
|
+
X-Amz-Cf-Id: hHQoX1-pbWB6rw1QN1G5grABhjXtX_cdjfpnR4lkIU4ypiZRBRk4kQ==
|
10
|
+
X-Amz-Cf-Pop: IAH50-C4
|
11
|
+
X-Cache: Error from cloudfront
|
12
|
+
X-Plan-Name: basic
|
13
|
+
method: GET
|
14
|
+
status: 500
|
15
|
+
url: https://v2.api.earningscall.biz/slides?apikey=foobar&exchange=NASDAQ&symbol=META&year=2024&quarter=3
|
@@ -0,0 +1,62 @@
|
|
1
|
+
responses:
|
2
|
+
- response:
|
3
|
+
auto_calculate_content_length: false
|
4
|
+
body: '%PDF-1.4
|
5
|
+
1 0 obj
|
6
|
+
<<
|
7
|
+
/Type /Catalog
|
8
|
+
/Pages 2 0 R
|
9
|
+
>>
|
10
|
+
endobj
|
11
|
+
2 0 obj
|
12
|
+
<<
|
13
|
+
/Type /Pages
|
14
|
+
/Kids [3 0 R]
|
15
|
+
/Count 1
|
16
|
+
>>
|
17
|
+
endobj
|
18
|
+
3 0 obj
|
19
|
+
<<
|
20
|
+
/Type /Page
|
21
|
+
/Parent 2 0 R
|
22
|
+
/MediaBox [0 0 612 792]
|
23
|
+
/Contents 4 0 R
|
24
|
+
>>
|
25
|
+
endobj
|
26
|
+
4 0 obj
|
27
|
+
<<
|
28
|
+
/Length 44
|
29
|
+
>>
|
30
|
+
stream
|
31
|
+
BT
|
32
|
+
/F1 12 Tf
|
33
|
+
100 700 Td
|
34
|
+
(Test Slide Deck) Tj
|
35
|
+
ET
|
36
|
+
endstream
|
37
|
+
endobj
|
38
|
+
xref
|
39
|
+
0 5
|
40
|
+
0000000000 65535 f
|
41
|
+
0000000009 00000 n
|
42
|
+
0000000058 00000 n
|
43
|
+
0000000115 00000 n
|
44
|
+
0000000208 00000 n
|
45
|
+
trailer
|
46
|
+
<<
|
47
|
+
/Size 5
|
48
|
+
/Root 1 0 R
|
49
|
+
>>
|
50
|
+
startxref
|
51
|
+
299
|
52
|
+
%%EOF'
|
53
|
+
content_type: application/pdf
|
54
|
+
headers:
|
55
|
+
Transfer-Encoding: chunked
|
56
|
+
Via: 1.1 dda997f5acee0f79dcf70db4a17d82f6.cloudfront.net (CloudFront)
|
57
|
+
X-Amz-Cf-Id: hHQoX1-pbWB6rw1QN1G5grABhjXtX_cdjfpnR4lkIU4ypiZRBRk4kQ==
|
58
|
+
X-Amz-Cf-Pop: IAH50-C4
|
59
|
+
X-Cache: Hit from cloudfront
|
60
|
+
method: GET
|
61
|
+
status: 200
|
62
|
+
url: https://v2.api.earningscall.biz/slides?apikey=demo&exchange=NASDAQ&symbol=MSFT&year=2022&quarter=1
|
@@ -0,0 +1,147 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
import pytest
|
4
|
+
import responses
|
5
|
+
from requests import HTTPError
|
6
|
+
|
7
|
+
import earningscall
|
8
|
+
from earningscall import get_company
|
9
|
+
from earningscall.api import purge_cache
|
10
|
+
from earningscall.company import Company
|
11
|
+
from earningscall.errors import InsufficientApiAccessError
|
12
|
+
from earningscall.event import EarningsEvent
|
13
|
+
from earningscall.symbols import CompanyInfo, clear_symbols
|
14
|
+
from earningscall.utils import data_path
|
15
|
+
|
16
|
+
|
17
|
+
@pytest.fixture(autouse=True)
|
18
|
+
def run_before_and_after_tests():
|
19
|
+
"""Fixture to execute asserts before and after a test is run"""
|
20
|
+
# Setup: fill with any logic you want
|
21
|
+
earningscall.api_key = None
|
22
|
+
earningscall.retry_strategy = {
|
23
|
+
"strategy": "exponential",
|
24
|
+
"base_delay": 0.001,
|
25
|
+
"max_attempts": 3,
|
26
|
+
}
|
27
|
+
purge_cache()
|
28
|
+
clear_symbols()
|
29
|
+
yield # this is where the testing happens
|
30
|
+
# Teardown : fill with any logic you want
|
31
|
+
earningscall.api_key = None
|
32
|
+
|
33
|
+
|
34
|
+
@responses.activate
|
35
|
+
def test_download_slide_deck():
|
36
|
+
##
|
37
|
+
responses._add_from_file(file_path=data_path("symbols-v2.yaml"))
|
38
|
+
responses._add_from_file(file_path=data_path("msft-q1-2022-slide-deck-short-clip.yaml"))
|
39
|
+
responses._add_from_file(file_path=data_path("msft-company-events.yaml"))
|
40
|
+
##
|
41
|
+
company = get_company("msft")
|
42
|
+
file_name = company.download_slide_deck(year=2022, quarter=1)
|
43
|
+
#
|
44
|
+
assert os.path.exists(file_name)
|
45
|
+
assert os.path.getsize(file_name) > 0
|
46
|
+
# Check that it's a PDF file (starts with %PDF)
|
47
|
+
with open(file_name, 'rb') as f:
|
48
|
+
assert f.read(4) == b'%PDF'
|
49
|
+
os.unlink(file_name)
|
50
|
+
|
51
|
+
|
52
|
+
@responses.activate
|
53
|
+
def test_download_slide_deck_event():
|
54
|
+
##
|
55
|
+
responses._add_from_file(file_path=data_path("symbols-v2.yaml"))
|
56
|
+
responses._add_from_file(file_path=data_path("msft-q1-2022-slide-deck-short-clip.yaml"))
|
57
|
+
responses._add_from_file(file_path=data_path("msft-company-events.yaml"))
|
58
|
+
##
|
59
|
+
company = get_company("msft")
|
60
|
+
file_name = company.download_slide_deck(event=EarningsEvent(year=2022, quarter=1))
|
61
|
+
#
|
62
|
+
assert os.path.exists(file_name)
|
63
|
+
assert os.path.getsize(file_name) > 0
|
64
|
+
# Check that it's a PDF file (starts with %PDF)
|
65
|
+
with open(file_name, 'rb') as f:
|
66
|
+
assert f.read(4) == b'%PDF'
|
67
|
+
os.unlink(file_name)
|
68
|
+
|
69
|
+
|
70
|
+
@responses.activate
|
71
|
+
def test_download_slide_deck_missing_params_raises_value_error():
|
72
|
+
##
|
73
|
+
responses._add_from_file(file_path=data_path("symbols-v2.yaml"))
|
74
|
+
##
|
75
|
+
company = get_company("msft")
|
76
|
+
with pytest.raises(ValueError):
|
77
|
+
company.download_slide_deck()
|
78
|
+
with pytest.raises(ValueError):
|
79
|
+
company.download_slide_deck(year=2023)
|
80
|
+
with pytest.raises(ValueError):
|
81
|
+
company.download_slide_deck(quarter=1)
|
82
|
+
with pytest.raises(ValueError):
|
83
|
+
company.download_slide_deck(quarter=0)
|
84
|
+
with pytest.raises(ValueError):
|
85
|
+
company.download_slide_deck(year=2023, quarter=5)
|
86
|
+
##
|
87
|
+
invalid_company = Company(company_info=CompanyInfo())
|
88
|
+
slide_deck = invalid_company.download_slide_deck(year=2023, quarter=1)
|
89
|
+
##
|
90
|
+
assert slide_deck is None
|
91
|
+
|
92
|
+
|
93
|
+
@responses.activate
|
94
|
+
def test_download_slide_deck_missing_from_server():
|
95
|
+
##
|
96
|
+
earningscall.api_key = "foobar" # Set to a bogus API Key.
|
97
|
+
responses._add_from_file(file_path=data_path("symbols-v2.yaml"))
|
98
|
+
responses._add_from_file(file_path=data_path("meta-q3-2024-slides-not-found.yaml"))
|
99
|
+
##
|
100
|
+
company = get_company("meta")
|
101
|
+
output_file = company.download_slide_deck(year=2024, quarter=3)
|
102
|
+
##
|
103
|
+
assert output_file is None
|
104
|
+
|
105
|
+
|
106
|
+
@responses.activate
|
107
|
+
def test_download_slide_deck_not_authorized():
|
108
|
+
##
|
109
|
+
earningscall.api_key = "foobar" # Set to a bogus API Key.
|
110
|
+
responses._add_from_file(file_path=data_path("symbols-v2.yaml"))
|
111
|
+
responses._add_from_file(file_path=data_path("meta-q3-2024-slides-not-authorized.yaml"))
|
112
|
+
##
|
113
|
+
company = get_company("meta")
|
114
|
+
with pytest.raises(InsufficientApiAccessError):
|
115
|
+
company.download_slide_deck(year=2024, quarter=3)
|
116
|
+
|
117
|
+
|
118
|
+
@responses.activate
|
119
|
+
def test_download_slide_deck_500_error():
|
120
|
+
##
|
121
|
+
earningscall.api_key = "foobar" # Set to a bogus API Key.
|
122
|
+
responses._add_from_file(file_path=data_path("symbols-v2.yaml"))
|
123
|
+
responses._add_from_file(file_path=data_path("meta-q3-2024-slides-other-error.yaml"))
|
124
|
+
##
|
125
|
+
company = get_company("meta")
|
126
|
+
with pytest.raises(HTTPError):
|
127
|
+
company.download_slide_deck(year=2024, quarter=3)
|
128
|
+
|
129
|
+
|
130
|
+
@responses.activate
|
131
|
+
def test_download_slide_deck_custom_filename():
|
132
|
+
##
|
133
|
+
responses._add_from_file(file_path=data_path("symbols-v2.yaml"))
|
134
|
+
responses._add_from_file(file_path=data_path("msft-q1-2022-slide-deck-short-clip.yaml"))
|
135
|
+
responses._add_from_file(file_path=data_path("msft-company-events.yaml"))
|
136
|
+
##
|
137
|
+
company = get_company("msft")
|
138
|
+
custom_filename = "test_custom_slides.pdf"
|
139
|
+
file_name = company.download_slide_deck(year=2022, quarter=1, file_name=custom_filename)
|
140
|
+
#
|
141
|
+
assert file_name == custom_filename
|
142
|
+
assert os.path.exists(file_name)
|
143
|
+
assert os.path.getsize(file_name) > 0
|
144
|
+
# Check that it's a PDF file (starts with %PDF)
|
145
|
+
with open(file_name, 'rb') as f:
|
146
|
+
assert f.read(4) == b'%PDF'
|
147
|
+
os.unlink(file_name)
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{earningscall-1.2.1 → earningscall-1.3.1}/tests/data/aapl-q1-2022-advanced-data-level-2.yaml
RENAMED
File without changes
|
{earningscall-1.2.1 → earningscall-1.3.1}/tests/data/aapl-q1-2022-advanced-data-level-3.yaml
RENAMED
File without changes
|
{earningscall-1.2.1 → earningscall-1.3.1}/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
|
File without changes
|
File without changes
|
File without changes
|
{earningscall-1.2.1 → earningscall-1.3.1}/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
|
File without changes
|
File without changes
|
File without changes
|