earningscall 0.0.7__tar.gz → 0.0.9__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 (39) hide show
  1. {earningscall-0.0.7 → earningscall-0.0.9}/DEVELOPMENT.md +4 -4
  2. {earningscall-0.0.7 → earningscall-0.0.9}/PKG-INFO +11 -4
  3. {earningscall-0.0.7 → earningscall-0.0.9}/README.md +10 -3
  4. earningscall-0.0.9/TODO.md +9 -0
  5. {earningscall-0.0.7 → earningscall-0.0.9}/earningscall/api.py +15 -7
  6. {earningscall-0.0.7 → earningscall-0.0.9}/earningscall/errors.py +0 -4
  7. {earningscall-0.0.7 → earningscall-0.0.9}/earningscall/exports.py +9 -5
  8. {earningscall-0.0.7 → earningscall-0.0.9}/earningscall/symbols.py +2 -2
  9. {earningscall-0.0.7 → earningscall-0.0.9}/earningscall/transcript.py +1 -0
  10. earningscall-0.0.9/earningscall/utils.py +33 -0
  11. {earningscall-0.0.7 → earningscall-0.0.9}/pyproject.toml +1 -1
  12. earningscall-0.0.9/scripts/list_companies.py +25 -0
  13. earningscall-0.0.7/TODO.md +0 -5
  14. earningscall-0.0.7/earningscall/utils.py +0 -20
  15. earningscall-0.0.7/scripts/list_all_companies.py +0 -8
  16. earningscall-0.0.7/tests/test_simple.py +0 -27
  17. {earningscall-0.0.7 → earningscall-0.0.9}/.github/workflows/release.yml +0 -0
  18. {earningscall-0.0.7 → earningscall-0.0.9}/.gitignore +0 -0
  19. {earningscall-0.0.7 → earningscall-0.0.9}/.python-version +0 -0
  20. {earningscall-0.0.7 → earningscall-0.0.9}/CHANGELOG.md +0 -0
  21. {earningscall-0.0.7 → earningscall-0.0.9}/LICENSE +0 -0
  22. {earningscall-0.0.7 → earningscall-0.0.9}/earningscall/__init__.py +1 -1
  23. {earningscall-0.0.7 → earningscall-0.0.9}/earningscall/company.py +0 -0
  24. {earningscall-0.0.7 → earningscall-0.0.9}/earningscall/event.py +1 -1
  25. {earningscall-0.0.7 → earningscall-0.0.9}/earningscall/sectors.py +0 -0
  26. {earningscall-0.0.7 → earningscall-0.0.9}/requirements-dev.lock +0 -0
  27. {earningscall-0.0.7 → earningscall-0.0.9}/requirements.lock +0 -0
  28. {earningscall-0.0.7 → earningscall-0.0.9}/scripts/get_all_company_transcripts.py +0 -0
  29. {earningscall-0.0.7 → earningscall-0.0.9}/scripts/get_single_transcript.py +0 -0
  30. {earningscall-0.0.7 → earningscall-0.0.9}/tests/data/demo-symbols-v2-alpha.yaml +0 -0
  31. {earningscall-0.0.7 → earningscall-0.0.9}/tests/data/demo-symbols-v2.yaml +0 -0
  32. {earningscall-0.0.7 → earningscall-0.0.9}/tests/data/msft-transcript-response.yaml +0 -0
  33. {earningscall-0.0.7 → earningscall-0.0.9}/tests/data/symbols-v2.yaml +0 -0
  34. {earningscall-0.0.7 → earningscall-0.0.9}/tests/data/symbols.txt +0 -0
  35. {earningscall-0.0.7 → earningscall-0.0.9}/tests/data/symbols.yaml +0 -0
  36. {earningscall-0.0.7 → earningscall-0.0.9}/tests/test_earnings_event.py +0 -0
  37. {earningscall-0.0.7 → earningscall-0.0.9}/tests/test_get_transcript.py +0 -0
  38. {earningscall-0.0.7 → earningscall-0.0.9}/tests/test_helper.py +0 -0
  39. {earningscall-0.0.7 → earningscall-0.0.9}/tests/test_symbols.py +0 -0
@@ -28,10 +28,10 @@ def test_save_symbols_v1():
28
28
 
29
29
  ### Publishing a new Version to PyPI
30
30
 
31
- Make your changes
31
+ Assuming you want to publish version 0.0.7, first, make your changes, then run the commands:
32
32
 
33
33
  ```sh
34
34
  git commit -a
35
- git tag v0.0.6
36
-
37
- ```
35
+ git tag v0.0.7
36
+ git push --atomic origin master v0.0.7
37
+ ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: earningscall
3
- Version: 0.0.7
3
+ Version: 0.0.9
4
4
  Summary: The EarningsCall Python library.
5
5
  Project-URL: Homepage, https://earningscall.biz
6
6
  Project-URL: Documentation, https://github.com/EarningsCall/earningscall-python
@@ -44,7 +44,6 @@ pip install --upgrade earningscall
44
44
  ```python
45
45
  from earningscall import get_company
46
46
 
47
-
48
47
  company = get_company("aapl") # Lookup Apple, Inc by its ticker symbol, "AAPL"
49
48
 
50
49
  transcript = company.get_transcript(year=2021, quarter=3)
@@ -64,13 +63,12 @@ Apple Inc. Q3 2021 Transcript Text: "Good day, and welcome to the Apple Q3 FY 20
64
63
  ```python
65
64
  from earningscall import get_company
66
65
 
67
-
68
66
  company = get_company("aapl") # Lookup Apple, Inc by its ticker symbol, "AAPL"
69
67
 
70
68
  print(f"Getting all transcripts for: {company}..")
71
69
  # Retrieve all earnings conference call events for a company, and iterate through each one
72
70
  for event in company.events():
73
- transcript = company.get_transcript(event) # Fetch the earnings call transcript for this event
71
+ transcript = company.get_transcript(event=event) # Fetch the earnings call transcript for this event
74
72
  print(f"* Q{event.quarter} {event.year}")
75
73
  if transcript:
76
74
  print(f" Transcript Text: \"{transcript.text[:100]}...\"")
@@ -105,6 +103,15 @@ for company in get_all_companies():
105
103
  print(f"{company.company_info} -- {company.company_info.sector} -- {company.company_info.industry}")
106
104
  ```
107
105
 
106
+ ## List S&P 500 Companies
107
+
108
+ ```python
109
+ from earningscall import get_all_companies
110
+
111
+ for company in get_all_companies():
112
+ print(f"{company.company_info} -- {company.company_info.sector} -- {company.company_info.industry}")
113
+ ```
114
+
108
115
  By default, this library grants you access to only two companies, Apple Inc. and Microsoft, Inc.
109
116
 
110
117
  To gain access to 5,000+ companies please [signup here](https://earningscall.biz/api-pricing) to get your API key.
@@ -26,7 +26,6 @@ pip install --upgrade earningscall
26
26
  ```python
27
27
  from earningscall import get_company
28
28
 
29
-
30
29
  company = get_company("aapl") # Lookup Apple, Inc by its ticker symbol, "AAPL"
31
30
 
32
31
  transcript = company.get_transcript(year=2021, quarter=3)
@@ -46,13 +45,12 @@ Apple Inc. Q3 2021 Transcript Text: "Good day, and welcome to the Apple Q3 FY 20
46
45
  ```python
47
46
  from earningscall import get_company
48
47
 
49
-
50
48
  company = get_company("aapl") # Lookup Apple, Inc by its ticker symbol, "AAPL"
51
49
 
52
50
  print(f"Getting all transcripts for: {company}..")
53
51
  # Retrieve all earnings conference call events for a company, and iterate through each one
54
52
  for event in company.events():
55
- transcript = company.get_transcript(event) # Fetch the earnings call transcript for this event
53
+ transcript = company.get_transcript(event=event) # Fetch the earnings call transcript for this event
56
54
  print(f"* Q{event.quarter} {event.year}")
57
55
  if transcript:
58
56
  print(f" Transcript Text: \"{transcript.text[:100]}...\"")
@@ -87,6 +85,15 @@ for company in get_all_companies():
87
85
  print(f"{company.company_info} -- {company.company_info.sector} -- {company.company_info.industry}")
88
86
  ```
89
87
 
88
+ ## List S&P 500 Companies
89
+
90
+ ```python
91
+ from earningscall import get_all_companies
92
+
93
+ for company in get_all_companies():
94
+ print(f"{company.company_info} -- {company.company_info.sector} -- {company.company_info.industry}")
95
+ ```
96
+
90
97
  By default, this library grants you access to only two companies, Apple Inc. and Microsoft, Inc.
91
98
 
92
99
  To gain access to 5,000+ companies please [signup here](https://earningscall.biz/api-pricing) to get your API key.
@@ -0,0 +1,9 @@
1
+ # A list of things left to do for this project
2
+
3
+ * Cache symbols on disk, to avoid redundant network calls
4
+
5
+
6
+
7
+ # Considerations
8
+
9
+ * Consider implementing @dataclass and @dataclass_json from scratch to avoid third-party dependency
@@ -2,9 +2,9 @@ import logging
2
2
  import os
3
3
  from typing import Optional
4
4
 
5
- import earningscall
6
5
  import requests
7
6
 
7
+ import earningscall
8
8
 
9
9
  log = logging.getLogger(__file__)
10
10
 
@@ -19,6 +19,10 @@ def get_api_key():
19
19
  return api_key
20
20
 
21
21
 
22
+ def api_key_param():
23
+ return {"apikey": get_api_key()}
24
+
25
+
22
26
  def is_demo_account():
23
27
  return get_api_key() == "demo"
24
28
 
@@ -28,7 +32,7 @@ def get_events(exchange: str,
28
32
 
29
33
  log.debug(f"get_events exchange: {exchange} symbol: {symbol}")
30
34
  params = {
31
- "apikey": get_api_key(),
35
+ **api_key_param(),
32
36
  "exchange": exchange,
33
37
  "symbol": symbol,
34
38
  }
@@ -45,7 +49,7 @@ def get_transcript(exchange: str,
45
49
 
46
50
  log.debug(f"get_transcript year: {year} quarter: {quarter}")
47
51
  params = {
48
- "apikey": get_api_key(),
52
+ **api_key_param(),
49
53
  "exchange": exchange,
50
54
  "symbol": symbol,
51
55
  "year": str(year),
@@ -65,10 +69,14 @@ def get_symbols_v1():
65
69
 
66
70
 
67
71
  def get_symbols_v2():
68
- params = {
69
- "apikey": get_api_key(),
70
- }
71
- response = requests.get(f"{API_BASE}/symbols-v2.txt", params=params)
72
+ response = requests.get(f"{API_BASE}/symbols-v2.txt", params=api_key_param())
73
+ if response.status_code != 200:
74
+ return None
75
+ return response.text
76
+
77
+
78
+ def get_sp500_companies_txt_file():
79
+ response = requests.get(f"{API_BASE}/symbols/sp500.txt", params=api_key_param())
72
80
  if response.status_code != 200:
73
81
  return None
74
82
  return response.text
@@ -23,7 +23,3 @@ class ClientError(BaseError):
23
23
 
24
24
  class InsufficientApiAccessError(ClientError):
25
25
  pass
26
-
27
-
28
- class CompanyNotFound(ClientError):
29
- pass
@@ -1,8 +1,8 @@
1
1
  from typing import Optional
2
2
 
3
- from earningscall.symbols import get_symbols
4
-
3
+ from earningscall.api import get_sp500_companies_txt_file
5
4
  from earningscall.company import Company
5
+ from earningscall.symbols import get_symbols
6
6
 
7
7
 
8
8
  def get_company(symbol: str) -> Optional[Company]:
@@ -18,6 +18,10 @@ def get_all_companies() -> [Company]:
18
18
 
19
19
 
20
20
  def get_sp500_companies() -> [Company]:
21
- ## TODO: Actually only return SP500 companies.
22
- for company_info in get_symbols().get_all():
23
- yield Company(company_info=company_info)
21
+ resp = get_sp500_companies_txt_file()
22
+ if not resp:
23
+ return []
24
+ for ticker_symbol in resp.split("\n"):
25
+ company_info = get_symbols().lookup_company(ticker_symbol)
26
+ if company_info:
27
+ yield Company(company_info=company_info)
@@ -129,8 +129,8 @@ class Symbols:
129
129
  except KeyError:
130
130
  pass
131
131
  if is_demo_account():
132
- raise InsufficientApiAccessError(f"For full access, please get an API Key. See: "
133
- f"https://earningscall.biz/api-pricing")
132
+ raise InsufficientApiAccessError(f"\"{symbol}\" requires an API Key for access. To get your API Key,"
133
+ f" see: https://earningscall.biz/api-pricing")
134
134
  return None
135
135
 
136
136
  def remove_exchange_symbol(self, exchange_symbol: str):
@@ -3,6 +3,7 @@ from typing import Optional
3
3
 
4
4
  from dataclasses import dataclass, field
5
5
  from dataclasses_json import dataclass_json
6
+
6
7
  from earningscall.event import EarningsEvent
7
8
 
8
9
  log = logging.getLogger(__file__)
@@ -0,0 +1,33 @@
1
+ import logging
2
+ import os
3
+ import pathlib
4
+ import sys
5
+
6
+ import earningscall
7
+
8
+
9
+ def project_file_path(base_dir, file_name):
10
+ result = os.path.join(pathlib.Path(earningscall.__file__).resolve().parent.parent, base_dir)
11
+ if file_name is None:
12
+ return result
13
+ return os.path.join(result, file_name)
14
+
15
+
16
+ def data_path(file_name=None):
17
+ return project_file_path("tests/data", file_name)
18
+
19
+
20
+ def enable_debug_logs():
21
+ logging.basicConfig(level=logging.DEBUG)
22
+
23
+
24
+ LOG_FORMAT = "[%(asctime)s] [%(levelname)8s] --- %(message)s (%(filename)s:%(lineno)s)"
25
+
26
+
27
+ def configure_sane_logging(level=logging.DEBUG):
28
+ # Manually set the following loggers to "INFO", since they tend to be VERY noisy, and when we are
29
+ # debugging our own application, we only care about DEBUG logs from OUR application, not these libs.
30
+ for name in ['urllib3']:
31
+ logging.getLogger(name).setLevel(logging.INFO)
32
+ logging.getLogger("filelock").setLevel(logging.WARNING)
33
+ logging.basicConfig(level=level, stream=sys.stdout, format=LOG_FORMAT)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "earningscall"
3
- version = "0.0.7"
3
+ version = "0.0.9"
4
4
  description = "The EarningsCall Python library."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -0,0 +1,25 @@
1
+ import argparse
2
+ import logging
3
+
4
+ from earningscall import get_all_companies
5
+ from earningscall.exports import get_sp500_companies
6
+ from earningscall.utils import configure_sane_logging
7
+
8
+
9
+ parser = argparse.ArgumentParser(description='')
10
+ parser.add_argument('--debug', action='store_true', help='Enable debug logs')
11
+ parser.add_argument('--sp-500', action='store_true', help='Show S&P500 Companies')
12
+
13
+ args = parser.parse_args()
14
+ level = logging.DEBUG
15
+ if args.debug:
16
+ level = logging.DEBUG
17
+ configure_sane_logging(level=level)
18
+
19
+ if args.sp_500:
20
+ get_func = get_sp500_companies
21
+ else:
22
+ get_func = get_all_companies
23
+
24
+ for company in get_func():
25
+ print(f"{company.company_info} -- {company.company_info.sector} -- {company.company_info.industry}")
@@ -1,5 +0,0 @@
1
- # A list of things left to do for this project
2
-
3
- * Allow user to set API Key
4
- * If user is using "demo" API key, and user tries to get non-demo symbol, throw a nice error message
5
-
@@ -1,20 +0,0 @@
1
- import logging
2
- import os
3
- import pathlib
4
-
5
- import earningscall
6
-
7
-
8
- def project_file_path(base_dir, file_name):
9
- result = os.path.join(pathlib.Path(earningscall.__file__).resolve().parent.parent, base_dir)
10
- if file_name is None:
11
- return result
12
- return os.path.join(result, file_name)
13
-
14
-
15
- def data_path(file_name=None):
16
- return project_file_path("tests/data", file_name)
17
-
18
-
19
- def enable_debug_logs():
20
- logging.basicConfig(level=logging.DEBUG)
@@ -1,8 +0,0 @@
1
- from earningscall import get_all_companies
2
- from earningscall.utils import enable_debug_logs
3
-
4
-
5
- enable_debug_logs()
6
-
7
- for company in get_all_companies():
8
- print(f"{company.company_info} -- {company.company_info.sector} -- {company.company_info.industry}")
@@ -1,27 +0,0 @@
1
- import responses
2
- import requests
3
- # from responses import _recorder
4
-
5
-
6
- @responses.activate
7
- def test_simple():
8
- responses.add(responses.GET, 'http://twitter.com/api/1/foobar',
9
- json={'error': 'not found'}, status=404)
10
-
11
- resp = requests.get('http://twitter.com/api/1/foobar')
12
-
13
- assert resp.json() == {"error": "not found"}
14
-
15
- assert len(responses.calls) == 1
16
- assert responses.calls[0].request.url == 'http://twitter.com/api/1/foobar'
17
- assert responses.calls[0].response.text == '{"error": "not found"}'
18
-
19
-
20
- # @_recorder.record(file_path="symbols.yaml")
21
- # def test_save_symbols_v1():
22
- # requests.get("https://earningscall.biz/symbols.txt")
23
- #
24
- #
25
- # @_recorder.record(file_path="symbols-v2.yaml")
26
- # def test_save_symbols_v1():
27
- # requests.get("https://earningscall.biz/symbols-v2.txt")
File without changes
File without changes
File without changes
@@ -1,6 +1,6 @@
1
1
  from typing import Optional
2
2
 
3
- from earningscall.symbols import Symbols, load_symbols
4
3
  from earningscall.exports import get_company, get_all_companies
4
+ from earningscall.symbols import Symbols, load_symbols
5
5
 
6
6
  api_key: Optional[str] = None
@@ -1,8 +1,8 @@
1
1
  import logging
2
- from dataclasses import dataclass, field
3
2
  from datetime import datetime
4
3
  from typing import Optional
5
4
 
5
+ from dataclasses import dataclass, field
6
6
  from dataclasses_json import config
7
7
  from dataclasses_json import dataclass_json
8
8
  from marshmallow import fields