earningscall 0.0.17__tar.gz → 0.0.19__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 (69) hide show
  1. {earningscall-0.0.17 → earningscall-0.0.19}/.gitignore +1 -0
  2. earningscall-0.0.19/.python-version +1 -0
  3. earningscall-0.0.19/CHANGELOG.md +30 -0
  4. {earningscall-0.0.17 → earningscall-0.0.19}/DEVELOPMENT.md +6 -2
  5. {earningscall-0.0.17 → earningscall-0.0.19}/PKG-INFO +1 -1
  6. {earningscall-0.0.17 → earningscall-0.0.19}/TODO.md +0 -1
  7. earningscall-0.0.19/earningscall/api.py +182 -0
  8. earningscall-0.0.19/earningscall/company.py +159 -0
  9. {earningscall-0.0.17 → earningscall-0.0.19}/earningscall/symbols.py +1 -1
  10. earningscall-0.0.19/earningscall/transcript.py +25 -0
  11. {earningscall-0.0.17 → earningscall-0.0.19}/pyproject.toml +1 -1
  12. earningscall-0.0.19/scripts/download_audio_files.py +45 -0
  13. earningscall-0.0.19/scripts/download_single_audio_file.py +29 -0
  14. earningscall-0.0.19/scripts/download_sp500_audio_files.py +33 -0
  15. earningscall-0.0.19/scripts/get_single_transcript.py +13 -0
  16. {earningscall-0.0.17 → earningscall-0.0.19}/scripts/list_companies.py +0 -1
  17. earningscall-0.0.19/tests/data/aapl-q1-2022-advanced-data-level-2.yaml +627 -0
  18. earningscall-0.0.19/tests/data/aapl-q1-2022-advanced-data-level-3.yaml +1869 -0
  19. earningscall-0.0.19/tests/data/aapl-q1-2022-advanced-data-level-4.yaml +595 -0
  20. earningscall-0.0.19/tests/data/aapl-q1-2030-not-authorized-l2.yaml +16 -0
  21. earningscall-0.0.19/tests/data/aapl-q1-2030-not-authorized.yaml +16 -0
  22. earningscall-0.0.19/tests/data/aapl-q1-2030-not-found.yaml +17 -0
  23. earningscall-0.0.19/tests/data/aapl-q1-2030-server-error.yaml +16 -0
  24. {earningscall-0.0.17 → earningscall-0.0.19}/tests/data/demo-symbols-v2-alpha.yaml +1 -1
  25. earningscall-0.0.19/tests/data/meta-q3-2024-not-authorized.yaml +15 -0
  26. earningscall-0.0.19/tests/data/meta-q3-2024-not-found.yaml +16 -0
  27. earningscall-0.0.19/tests/data/meta-q3-2024-other-error.yaml +15 -0
  28. earningscall-0.0.19/tests/data/msft-q1-2022-audio-file-short-clip.yaml +5510 -0
  29. {earningscall-0.0.17 → earningscall-0.0.19}/tests/data/msft-transcript-response.yaml +1 -1
  30. earningscall-0.0.19/tests/data/sp500-company-list-failed.yaml +1024 -0
  31. earningscall-0.0.19/tests/data/sp500-company-list.yaml +1024 -0
  32. earningscall-0.0.19/tests/test_company.py +34 -0
  33. earningscall-0.0.19/tests/test_download_audio_files.py +122 -0
  34. earningscall-0.0.19/tests/test_exports.py +69 -0
  35. earningscall-0.0.19/tests/test_get_transcript.py +252 -0
  36. {earningscall-0.0.17 → earningscall-0.0.19}/tests/test_symbols.py +12 -3
  37. earningscall-0.0.17/.python-version +0 -1
  38. earningscall-0.0.17/CHANGELOG.md +0 -9
  39. earningscall-0.0.17/earningscall/api.py +0 -104
  40. earningscall-0.0.17/earningscall/company.py +0 -55
  41. earningscall-0.0.17/earningscall/transcript.py +0 -17
  42. earningscall-0.0.17/scripts/get_single_transcript.py +0 -7
  43. earningscall-0.0.17/tests/test_get_transcript.py +0 -86
  44. {earningscall-0.0.17 → earningscall-0.0.19}/.github/workflows/release.yml +0 -0
  45. {earningscall-0.0.17 → earningscall-0.0.19}/.github/workflows/test.yml +0 -0
  46. {earningscall-0.0.17 → earningscall-0.0.19}/LICENSE +0 -0
  47. {earningscall-0.0.17 → earningscall-0.0.19}/README.md +0 -0
  48. {earningscall-0.0.17 → earningscall-0.0.19}/earningscall/__init__.py +0 -0
  49. {earningscall-0.0.17 → earningscall-0.0.19}/earningscall/errors.py +0 -0
  50. {earningscall-0.0.17 → earningscall-0.0.19}/earningscall/event.py +0 -0
  51. {earningscall-0.0.17 → earningscall-0.0.19}/earningscall/exports.py +0 -0
  52. {earningscall-0.0.17 → earningscall-0.0.19}/earningscall/sectors.py +0 -0
  53. {earningscall-0.0.17 → earningscall-0.0.19}/earningscall/utils.py +0 -0
  54. {earningscall-0.0.17 → earningscall-0.0.19}/hatch.toml +0 -0
  55. {earningscall-0.0.17 → earningscall-0.0.19}/scripts/get_all_company_transcripts.py +0 -0
  56. {earningscall-0.0.17 → earningscall-0.0.19}/setup.cfg +0 -0
  57. {earningscall-0.0.17 → earningscall-0.0.19}/tests/data/demo-symbols-v2.yaml +0 -0
  58. {earningscall-0.0.17 → earningscall-0.0.19}/tests/data/msft-company-events.yaml +0 -0
  59. {earningscall-0.0.17 → earningscall-0.0.19}/tests/data/symbols-v2-missing-edge-cases.yaml +0 -0
  60. {earningscall-0.0.17 → earningscall-0.0.19}/tests/data/symbols-v2.yaml +0 -0
  61. {earningscall-0.0.17 → earningscall-0.0.19}/tests/data/symbols.txt +0 -0
  62. {earningscall-0.0.17 → earningscall-0.0.19}/tests/data/symbols.yaml +0 -0
  63. {earningscall-0.0.17 → earningscall-0.0.19}/tests/test_earnings_event.py +0 -0
  64. {earningscall-0.0.17 → earningscall-0.0.19}/tests/test_get_company_events.py +0 -0
  65. {earningscall-0.0.17 → earningscall-0.0.19}/tests/test_get_sp500_companies_api.py +0 -0
  66. {earningscall-0.0.17 → earningscall-0.0.19}/tests/test_helper.py +0 -0
  67. {earningscall-0.0.17 → earningscall-0.0.19}/tests/test_responses_mocking.py +0 -0
  68. {earningscall-0.0.17 → earningscall-0.0.19}/tests/test_sectors.py +0 -0
  69. {earningscall-0.0.17 → earningscall-0.0.19}/tests/test_utils.py +0 -0
@@ -32,6 +32,7 @@ tasks.xml
32
32
  /build
33
33
  /sc.egg-info
34
34
  /packages
35
+ /audio_files
35
36
 
36
37
  # Root files
37
38
  /.coverage*
@@ -0,0 +1 @@
1
+ 3.12.6
@@ -0,0 +1,30 @@
1
+ ## Release `0.0.19` - 2024-10-07
2
+
3
+ * Add Advanced Transcript Data structures (Beta: and subject to change in the future).
4
+ * Add additional client-side verification and parameter checking.
5
+ * Don't throw an exception when audio file is missing, just return `None`.
6
+ * Throw useful error in the case of not authorized to download Audio Files.
7
+ * Add additional unit test coverage.
8
+
9
+ ## Release `0.0.18` - 2024-10-01
10
+
11
+ * Add Download Audio File Feature: add `download_audio_file` function to the `Company` class.
12
+ * Add "LSE" to the list of exchanges.
13
+
14
+ ## Release `0.0.17` - 2024-08-04
15
+
16
+ * Remove apikey from the ignored parameters list.
17
+
18
+ When `apikey=demo`, the API will return different results. If we change
19
+ apikey to some other value, then the old result from when `apikey=demo`
20
+ would be returned. This is incorrect.
21
+
22
+ ## Release `0.0.16` - 2024-06-12
23
+ * Bump version number.
24
+
25
+ ## Release `0.0.15` - 2024-06-12
26
+ * Remove `dataclasses` as a project dependency. It is built directly into Python.
27
+
28
+ ## Release `0.0.14` - 2024-06-08
29
+ * Add caching to improve client performance.
30
+ * Add `company_info` attribute in `Company` class.
@@ -1,7 +1,12 @@
1
1
  # Development
2
2
 
3
- First, install Hatch. See the Hatch [installation instructions](https://hatch.pypa.io/latest/install/).
3
+ First, install Hatch plus other build dependencies. See the Hatch [installation instructions](https://hatch.pypa.io/latest/install/).
4
4
 
5
+ My preferred way to install it is to use `pip`:
6
+
7
+ ```shell
8
+ pip install hatch coverage black
9
+ ```
5
10
 
6
11
  ## Run Build
7
12
 
@@ -9,7 +14,6 @@ First, install Hatch. See the Hatch [installation instructions](https://hatch.p
9
14
  hatch build
10
15
  ```
11
16
 
12
-
13
17
  ### Running Unit Tests
14
18
 
15
19
  ```shell
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: earningscall
3
- Version: 0.0.17
3
+ Version: 0.0.19
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
@@ -3,7 +3,6 @@
3
3
 
4
4
  * Add ability to retrieve advanced transcript data
5
5
  * Raise helpful error if the user tries to make a call that the current plan doesn't support
6
- * Add documentation on how to disable caching
7
6
 
8
7
 
9
8
  # Considerations
@@ -0,0 +1,182 @@
1
+ import importlib
2
+ import logging
3
+ import os
4
+ from typing import Optional
5
+
6
+ import requests
7
+ from requests_cache import CachedSession
8
+
9
+ import earningscall
10
+
11
+ log = logging.getLogger(__file__)
12
+
13
+ DOMAIN = os.environ.get("ECALL_DOMAIN", "earningscall.biz")
14
+ API_BASE = f"https://v2.api.{DOMAIN}"
15
+ EARNINGS_CALL_VERSION = importlib.metadata.version("earningscall")
16
+
17
+
18
+ def get_api_key():
19
+ if earningscall.api_key:
20
+ return earningscall.api_key
21
+ return os.environ.get("ECALL_API_KEY", "demo")
22
+
23
+
24
+ def api_key_param():
25
+ return {"apikey": get_api_key()}
26
+
27
+
28
+ def is_demo_account():
29
+ return get_api_key() == "demo"
30
+
31
+
32
+ def cache_session() -> CachedSession:
33
+ return CachedSession(
34
+ ".earningscall_cache",
35
+ backend="sqlite",
36
+ cache_control=True,
37
+ use_temp=True,
38
+ ignored_parameters=[],
39
+ )
40
+
41
+
42
+ def cached_urls():
43
+ return cache_session().cache.urls()
44
+
45
+
46
+ def purge_cache():
47
+ return cache_session().cache.clear()
48
+
49
+
50
+ def get_headers():
51
+ return {
52
+ "User-Agent": f"EarningsCall Python/{EARNINGS_CALL_VERSION}",
53
+ "X-EarningsCall-Version": EARNINGS_CALL_VERSION,
54
+ }
55
+
56
+
57
+ def do_get(
58
+ path: str,
59
+ use_cache: bool = False,
60
+ **kwargs,
61
+ ) -> requests.Response:
62
+ """
63
+ Do a GET request to the API.
64
+
65
+ Args:
66
+ path (str): The path to request.
67
+ use_cache (bool): Whether to use the cache.
68
+ **kwargs: Additional arguments to pass to the request.
69
+
70
+ Returns:
71
+ requests.Response: The response from the API.
72
+ """
73
+ params = {
74
+ **api_key_param(),
75
+ **kwargs.get("params", {}),
76
+ }
77
+ url = f"{API_BASE}/{path}"
78
+ log.debug(f"do_get url: {url} params: {params}")
79
+ if use_cache and earningscall.enable_requests_cache:
80
+ return cache_session().get(url, params=params)
81
+ else:
82
+ return requests.get(
83
+ url,
84
+ params=params,
85
+ headers=get_headers(),
86
+ stream=kwargs.get("stream"),
87
+ )
88
+
89
+
90
+ def get_events(exchange: str, symbol: str):
91
+ log.debug(f"get_events exchange: {exchange} symbol: {symbol}")
92
+ params = {
93
+ **api_key_param(),
94
+ "exchange": exchange,
95
+ "symbol": symbol,
96
+ }
97
+ response = do_get("events", params=params)
98
+ if response.status_code != 200:
99
+ return None
100
+ return response.json()
101
+
102
+
103
+ def get_transcript(
104
+ exchange: str,
105
+ symbol: str,
106
+ year: int,
107
+ quarter: int,
108
+ level: Optional[int] = None,
109
+ ) -> Optional[dict]:
110
+ """
111
+ Get the transcript for a given exchange, symbol, year, and quarter.
112
+
113
+ :param str exchange: The exchange to get the transcript for.
114
+ :param str symbol: The symbol to get the transcript for.
115
+ :param int year: The year to get the transcript for.
116
+ :param int quarter: The quarter to get the transcript for.
117
+ :param Optional[int] level: The level to get the transcript for.
118
+
119
+ :return: The transcript for the given exchange, symbol, year, and quarter.
120
+ """
121
+ log.debug(f"get_transcript exchange: {exchange} symbol: {symbol} year: {year} quarter: {quarter} level: {level}")
122
+ params = {
123
+ **api_key_param(),
124
+ "exchange": exchange,
125
+ "symbol": symbol,
126
+ "year": str(year),
127
+ "quarter": str(quarter),
128
+ "level": str(level or 1),
129
+ }
130
+ response = do_get("transcript", params=params)
131
+ response.raise_for_status()
132
+ return response.json()
133
+
134
+
135
+ def get_symbols_v2():
136
+ log.debug("get_symbols_v2")
137
+ response = do_get("symbols-v2.txt", use_cache=True)
138
+ if response.status_code != 200:
139
+ return None
140
+ return response.text
141
+
142
+
143
+ def get_sp500_companies_txt_file():
144
+ log.debug("get_sp500_companies_txt_file")
145
+ response = do_get("symbols/sp500.txt", use_cache=True)
146
+ if response.status_code != 200:
147
+ return None
148
+ return response.text
149
+
150
+
151
+ def download_audio_file(
152
+ exchange: str,
153
+ symbol: str,
154
+ year: int,
155
+ quarter: int,
156
+ file_name: Optional[str] = None,
157
+ ) -> Optional[str]:
158
+ """
159
+ Get the audio for a given exchange, symbol, year, and quarter.
160
+
161
+ :param str exchange: The exchange to get the audio for.
162
+ :param str symbol: The symbol to get the audio for.
163
+ :param int year: The 4-digit year to get the audio for.
164
+ :param int quarter: The quarter to get the audio for (1, 2, 3, or 4).
165
+ :param file_name: Optionally specify the filename to save the audio to.
166
+ :return: The filename of the downloaded audio file.
167
+ :rtype Optional[str]: The filename of the downloaded audio file.
168
+ """
169
+ params = {
170
+ **api_key_param(),
171
+ "exchange": exchange,
172
+ "symbol": symbol,
173
+ "year": str(year),
174
+ "quarter": str(quarter),
175
+ }
176
+ local_filename = file_name or f"{exchange}_{symbol}_{year}_{quarter}.mp3"
177
+ with do_get("audio", params=params, stream=True) as response:
178
+ response.raise_for_status()
179
+ with open(local_filename, "wb") as f:
180
+ for chunk in response.iter_content(chunk_size=8192):
181
+ f.write(chunk)
182
+ return local_filename
@@ -0,0 +1,159 @@
1
+ import logging
2
+ from typing import Optional, List
3
+
4
+ import requests
5
+
6
+ from earningscall import api
7
+ from earningscall.errors import InsufficientApiAccessError
8
+ from earningscall.event import EarningsEvent
9
+ from earningscall.symbols import CompanyInfo
10
+ from earningscall.transcript import Transcript
11
+
12
+ log = logging.getLogger(__file__)
13
+
14
+
15
+ class Company:
16
+ """
17
+ A class representing a company.
18
+ """
19
+
20
+ company_info: CompanyInfo
21
+ name: Optional[str]
22
+ _events: Optional[List[EarningsEvent]]
23
+
24
+ def __init__(self, company_info: CompanyInfo):
25
+ if not company_info:
26
+ raise ValueError("company_info must be present.")
27
+ self.company_info = company_info
28
+ self.name = company_info.name
29
+ self._events = None
30
+
31
+ def __str__(self):
32
+ return str(self.name)
33
+
34
+ def _get_events(self) -> List[EarningsEvent]:
35
+ if not self.company_info.exchange or not self.company_info.symbol:
36
+ return []
37
+ raw_response = api.get_events(self.company_info.exchange, self.company_info.symbol)
38
+ if not raw_response:
39
+ return []
40
+ return [EarningsEvent.from_dict(event) for event in raw_response["events"]] # type: ignore
41
+
42
+ def events(self) -> List[EarningsEvent]:
43
+ if not self._events:
44
+ self._events = self._get_events()
45
+ return self._events
46
+
47
+ def get_transcript(
48
+ self,
49
+ year: Optional[int] = None,
50
+ quarter: Optional[int] = None,
51
+ event: Optional[EarningsEvent] = None,
52
+ level: Optional[int] = None,
53
+ ) -> Optional[Transcript]:
54
+ """
55
+ Get the transcript for a given year and quarter.
56
+
57
+ :param Optional[int] year: The year to get the transcript for.
58
+ :param Optional[int] quarter: The quarter to get the transcript for.
59
+ :param Optional[EarningsEvent] event: The event to get the transcript for.
60
+ :param Optional[int] level: The transcript level to retrieve. Default: 1
61
+
62
+ :return: The transcript for the given year and quarter.
63
+ """
64
+ if not self.company_info.exchange or not self.company_info.symbol:
65
+ return None
66
+ if (not year or not quarter) and event:
67
+ year = event.year
68
+ quarter = event.quarter
69
+ if not year or not quarter:
70
+ raise ValueError("Must specify either event or year and quarter")
71
+ if quarter < 1 or quarter > 4:
72
+ raise ValueError("Invalid level. Must be one of: {1,2,3,4}")
73
+ if level is None:
74
+ level = 1
75
+ if type(level) is not int or level <= 0 or level > 4:
76
+ raise ValueError(f"Invalid level: {level}. Must be one of: 1, 2, 3, or 4.")
77
+ try:
78
+ response_payload = api.get_transcript(
79
+ self.company_info.exchange,
80
+ self.company_info.symbol,
81
+ year,
82
+ quarter,
83
+ level=level,
84
+ )
85
+ # TODO: Investigate alternatives to @dataclass for level 3 transcripts, as this is
86
+ # extremely slow.
87
+ transcript = Transcript.from_dict(response_payload) # type: ignore
88
+ if level == 3:
89
+ for speaker in transcript.speakers:
90
+ speaker.text = " ".join(speaker.words)
91
+ if 2 <= level <= 3:
92
+ transcript.text = " ".join(map(lambda spk: spk.text, transcript.speakers))
93
+ elif level == 4:
94
+ transcript.text = " ".join([transcript.prepared_remarks, transcript.questions_and_answers])
95
+ return transcript
96
+ except requests.exceptions.HTTPError as error:
97
+ if error.response.status_code == 404:
98
+ return None
99
+ if error.response.status_code == 403:
100
+ plan_name = error.response.headers.get("X-Plan-Name", "unknown")
101
+ if 2 <= level <= 4:
102
+ error_message = (
103
+ f"Your plan ({plan_name}) does not include Advanced Transcript Data. "
104
+ "Upgrade your plan here: https://earningscall.biz/api-pricing"
105
+ )
106
+ else:
107
+ error_message = f"Unexpected error code was returned from the server. Your plan is: {plan_name}"
108
+ log.error(error_message)
109
+ raise InsufficientApiAccessError(error_message)
110
+ raise error
111
+
112
+ def download_audio_file(
113
+ self,
114
+ year: Optional[int] = None,
115
+ quarter: Optional[int] = None,
116
+ event: Optional[EarningsEvent] = None,
117
+ file_name: Optional[str] = None,
118
+ ) -> Optional[str]:
119
+ """
120
+ Download the audio file for a given year and quarter.
121
+
122
+ :param Optional[int] year: The year to get the audio for.
123
+ :param Optional[int] quarter: The quarter to get the audio for.
124
+ :param Optional[EarningsEvent] event: The event to get the audio for.
125
+ :param Optional[str] file_name: The file name to save the audio to.
126
+
127
+ :return: The audio for the given year and quarter.
128
+ """
129
+ log.info(f"Downloading audio file for {self.company_info.symbol} {event}")
130
+ if not self.company_info.exchange or not self.company_info.symbol:
131
+ return None
132
+ if (not year or not quarter) and event:
133
+ year = event.year
134
+ quarter = event.quarter
135
+ if not year or not quarter:
136
+ raise ValueError("Must specify either event or year and quarter")
137
+ if quarter < 1 or quarter > 4:
138
+ raise ValueError("Invalid level. Must be one of: {1,2,3,4}")
139
+ try:
140
+ resp = api.download_audio_file(
141
+ exchange=self.company_info.exchange,
142
+ symbol=self.company_info.symbol,
143
+ year=year,
144
+ quarter=quarter,
145
+ file_name=file_name,
146
+ )
147
+ return resp
148
+ except requests.exceptions.HTTPError as error:
149
+ if error.response.status_code == 404:
150
+ return None
151
+ if error.response.status_code == 403:
152
+ plan_name = error.response.headers["X-Plan-Name"]
153
+ error_message = (
154
+ f"Your plan ({plan_name}) does not include Audio Files. "
155
+ "Upgrade your plan here: https://earningscall.biz/api-pricing"
156
+ )
157
+ log.error(error_message)
158
+ raise InsufficientApiAccessError(error_message)
159
+ raise error
@@ -8,7 +8,7 @@ from earningscall.errors import InsufficientApiAccessError
8
8
  from earningscall.sectors import sector_to_index, industry_to_index, index_to_sector, index_to_industry
9
9
 
10
10
  # WARNING: Add new indexes to the *END* of this list
11
- EXCHANGES_IN_ORDER = ["NYSE", "NASDAQ", "AMEX", "TSX", "TSXV", "OTC"]
11
+ EXCHANGES_IN_ORDER = ["NYSE", "NASDAQ", "AMEX", "TSX", "TSXV", "OTC", "LSE"]
12
12
 
13
13
  log = logging.getLogger(__file__)
14
14
 
@@ -0,0 +1,25 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import List, Optional
3
+
4
+ from dataclasses_json import dataclass_json
5
+
6
+ from earningscall.event import EarningsEvent
7
+
8
+
9
+ @dataclass_json
10
+ @dataclass
11
+ class Speaker:
12
+ speaker: str
13
+ text: Optional[str] = field(default=None)
14
+ words: Optional[List[str]] = field(default=None)
15
+ start_times: Optional[List[float]] = field(default=None)
16
+
17
+
18
+ @dataclass_json
19
+ @dataclass
20
+ class Transcript:
21
+ text: Optional[str] = field(default=None)
22
+ event: Optional[EarningsEvent] = field(default=None)
23
+ speakers: Optional[List[Speaker]] = field(default=None)
24
+ prepared_remarks: Optional[str] = field(default=None)
25
+ questions_and_answers: Optional[str] = field(default=None)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "earningscall"
3
- version = "0.0.17"
3
+ version = "0.0.19"
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 = [
@@ -0,0 +1,45 @@
1
+ import argparse
2
+ import os
3
+
4
+ import earningscall # noqa: F401
5
+ from earningscall import get_company
6
+ from earningscall.company import Company
7
+ from earningscall.utils import configure_sane_logging
8
+
9
+
10
+ # TODO: Set your API key here:
11
+ # earningscall.api_key = "YOUR SECRET API KEY GOES HERE"
12
+
13
+
14
+ parser = argparse.ArgumentParser(description='')
15
+ parser.add_argument('--debug', action='store_true', help='Enable debug logs')
16
+ parser.add_argument('--sp-500', action='store_true', help='Show S&P500 Companies')
17
+
18
+ args = parser.parse_args()
19
+ configure_sane_logging()
20
+
21
+
22
+ directory = "audio_files"
23
+ os.makedirs(directory, exist_ok=True)
24
+
25
+
26
+ def download_audio_files(company: Company):
27
+ print(f"Downloading all audio files for: {company}..")
28
+ for event in company.events():
29
+ file_name = os.path.join(
30
+ directory,
31
+ f"{company.company_info.exchange}_{company.company_info.symbol}_{event.year}_Q{event.quarter}.mp3",
32
+ )
33
+ if os.path.exists(file_name):
34
+ print(f"* {company.company_info.symbol} Q{event.quarter} {event.year} -- already downloaded")
35
+ else:
36
+ print(f"* Downloading audio file for {company.company_info.symbol} Q{event.quarter} {event.year}...")
37
+ audio_file = company.download_audio_file(event=event, file_name=file_name)
38
+ if audio_file:
39
+ print(f" Downloaded audio file: \"{audio_file}\"")
40
+ else:
41
+ print(f" No audio file found for {company.company_info.symbol} Q{event.quarter} {event.year}")
42
+
43
+
44
+ company = get_company("aapl") # Lookup Apple, Inc by its ticker symbol, "AAPL"
45
+ download_audio_files(company)
@@ -0,0 +1,29 @@
1
+ import argparse
2
+ import os
3
+
4
+ import earningscall # noqa: F401
5
+ from earningscall import get_company
6
+ from earningscall.utils import configure_sane_logging
7
+
8
+ # TODO: Set your API key here:
9
+ # earningscall.api_key = "YOUR SECRET API KEY GOES HERE"
10
+
11
+
12
+ parser = argparse.ArgumentParser(description='')
13
+ parser.add_argument('--debug', action='store_true', help='Enable debug logs')
14
+ parser.add_argument('--sp-500', action='store_true', help='Show S&P500 Companies')
15
+
16
+ args = parser.parse_args()
17
+ configure_sane_logging()
18
+
19
+
20
+ directory = "audio_files"
21
+ os.makedirs(directory, exist_ok=True)
22
+
23
+
24
+ file_name = os.path.join(
25
+ directory,
26
+ "NASDAQ_META_2024_Q3.mp3",
27
+ )
28
+ company = get_company("meta") # Lookup Meta, Inc by its ticker symbol, "META"
29
+ company.download_audio_file(year=2024, quarter=3, file_name=file_name)
@@ -0,0 +1,33 @@
1
+ import os
2
+
3
+ import earningscall # noqa: F401
4
+ from earningscall import get_sp500_companies
5
+ from earningscall.company import Company
6
+
7
+ # TODO: Set your API key here:
8
+ # earningscall.api_key = "YOUR SECRET API KEY GOES HERE"
9
+
10
+ directory = "audio_files"
11
+ os.makedirs(directory, exist_ok=True)
12
+
13
+
14
+ def download_audio_files(company: Company):
15
+ print(f"Downloading all audio files for: {company}..")
16
+ for event in company.events():
17
+ file_name = os.path.join(
18
+ directory,
19
+ f"{company.company_info.exchange}_{company.company_info.symbol}_{event.year}_Q{event.quarter}.mp3",
20
+ )
21
+ if os.path.exists(file_name):
22
+ print(f"* {company.company_info.symbol} Q{event.quarter} {event.year} -- already downloaded")
23
+ else:
24
+ print(f"* Downloading audio file for {company.company_info.symbol} Q{event.quarter} {event.year}...")
25
+ audio_file = company.download_audio_file(event=event, file_name=file_name)
26
+ if audio_file:
27
+ print(f" Downloaded audio file: \"{audio_file}\"")
28
+ else:
29
+ print(f" No audio file found for {company.company_info.symbol} Q{event.quarter} {event.year}")
30
+
31
+
32
+ for company in get_sp500_companies():
33
+ download_audio_files(company)
@@ -0,0 +1,13 @@
1
+ import earningscall # noqa: F401
2
+
3
+ from earningscall import get_company
4
+
5
+
6
+ # TODO: Set your API key here:
7
+ # earningscall.api_key = "YOUR SECRET API KEY GOES HERE"
8
+
9
+
10
+ company = get_company("aapl")
11
+
12
+ transcript = company.get_transcript(year=2021, quarter=3, level=4)
13
+ print(f"{company} Q3 2021 Transcript Text: \"{transcript.text[:100]}...\"")
@@ -4,7 +4,6 @@ import logging
4
4
  from earningscall import get_all_companies, get_sp500_companies
5
5
  from earningscall.utils import configure_sane_logging
6
6
 
7
-
8
7
  parser = argparse.ArgumentParser(description='')
9
8
  parser.add_argument('--debug', action='store_true', help='Enable debug logs')
10
9
  parser.add_argument('--sp-500', action='store_true', help='Show S&P500 Companies')