earningscall 0.0.10__tar.gz → 0.0.13__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.10 → earningscall-0.0.13}/.github/workflows/release.yml +43 -38
  2. earningscall-0.0.13/.github/workflows/test.yml +68 -0
  3. {earningscall-0.0.10 → earningscall-0.0.13}/.gitignore +5 -1
  4. {earningscall-0.0.10 → earningscall-0.0.13}/DEVELOPMENT.md +23 -3
  5. {earningscall-0.0.10 → earningscall-0.0.13}/PKG-INFO +3 -5
  6. {earningscall-0.0.10 → earningscall-0.0.13}/README.md +1 -4
  7. {earningscall-0.0.10 → earningscall-0.0.13}/earningscall/__init__.py +2 -0
  8. {earningscall-0.0.10 → earningscall-0.0.13}/earningscall/api.py +8 -6
  9. {earningscall-0.0.10 → earningscall-0.0.13}/earningscall/company.py +16 -16
  10. {earningscall-0.0.10 → earningscall-0.0.13}/earningscall/errors.py +1 -2
  11. {earningscall-0.0.10 → earningscall-0.0.13}/earningscall/event.py +3 -2
  12. {earningscall-0.0.10 → earningscall-0.0.13}/earningscall/exports.py +4 -4
  13. {earningscall-0.0.10 → earningscall-0.0.13}/earningscall/sectors.py +5 -8
  14. {earningscall-0.0.10 → earningscall-0.0.13}/earningscall/symbols.py +32 -19
  15. earningscall-0.0.13/hatch.toml +47 -0
  16. earningscall-0.0.13/pyproject.toml +127 -0
  17. {earningscall-0.0.10 → earningscall-0.0.13}/scripts/get_all_company_transcripts.py +1 -1
  18. {earningscall-0.0.10 → earningscall-0.0.13}/tests/test_earnings_event.py +7 -5
  19. {earningscall-0.0.10 → earningscall-0.0.13}/tests/test_get_transcript.py +6 -4
  20. {earningscall-0.0.10 → earningscall-0.0.13}/tests/test_symbols.py +27 -6
  21. earningscall-0.0.10/pyproject.toml +0 -74
  22. earningscall-0.0.10/requirements-dev.lock +0 -46
  23. earningscall-0.0.10/requirements.lock +0 -35
  24. {earningscall-0.0.10 → earningscall-0.0.13}/.python-version +0 -0
  25. {earningscall-0.0.10 → earningscall-0.0.13}/CHANGELOG.md +0 -0
  26. {earningscall-0.0.10 → earningscall-0.0.13}/LICENSE +0 -0
  27. {earningscall-0.0.10 → earningscall-0.0.13}/TODO.md +0 -0
  28. {earningscall-0.0.10 → earningscall-0.0.13}/earningscall/transcript.py +0 -0
  29. {earningscall-0.0.10 → earningscall-0.0.13}/earningscall/utils.py +0 -0
  30. {earningscall-0.0.10 → earningscall-0.0.13}/scripts/get_single_transcript.py +0 -0
  31. {earningscall-0.0.10 → earningscall-0.0.13}/scripts/list_companies.py +0 -0
  32. {earningscall-0.0.10 → earningscall-0.0.13}/setup.cfg +0 -0
  33. {earningscall-0.0.10 → earningscall-0.0.13}/tests/data/demo-symbols-v2-alpha.yaml +0 -0
  34. {earningscall-0.0.10 → earningscall-0.0.13}/tests/data/demo-symbols-v2.yaml +0 -0
  35. {earningscall-0.0.10 → earningscall-0.0.13}/tests/data/msft-transcript-response.yaml +0 -0
  36. {earningscall-0.0.10 → earningscall-0.0.13}/tests/data/symbols-v2.yaml +0 -0
  37. {earningscall-0.0.10 → earningscall-0.0.13}/tests/data/symbols.txt +0 -0
  38. {earningscall-0.0.10 → earningscall-0.0.13}/tests/data/symbols.yaml +0 -0
  39. {earningscall-0.0.10 → earningscall-0.0.13}/tests/test_helper.py +0 -0
@@ -1,9 +1,18 @@
1
1
  # https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/
2
- name: Build and Release
2
+ name: Build
3
3
 
4
- on: push
4
+ on:
5
+ push:
6
+ tags:
7
+ - v*
8
+ branches:
9
+ - master
10
+ pull_request:
11
+ branches:
12
+ - master
5
13
 
6
14
  jobs:
15
+
7
16
  build:
8
17
  name: Build distribution 📦
9
18
  runs-on: ubuntu-latest
@@ -20,22 +29,16 @@ jobs:
20
29
  pip install
21
30
  build
22
31
  --user
23
- - name: Install Rye
24
- run: |
25
- curl -sSf https://rye.astral.sh/get | RYE_NO_AUTO_INSTALL=1 RYE_INSTALL_OPTION="--yes" bash
26
- # $(HOME)/.rye/shims/rye pin $(PYTHON_VERSION)
27
- # $(HOME)/.rye/shims/rye sync --no-lock
28
- # https://rye.astral.sh/guide/publish/
32
+ - name: Build
33
+ run: python -m build
29
34
  - name: Build a binary wheel and sdist
30
- run: |
31
- source ~/.rye/env
32
- rye sync
33
- rye build
35
+ run: python -m build
34
36
  - name: Store the distribution packages
35
- uses: actions/upload-artifact@v3
37
+ uses: actions/upload-artifact@v4
36
38
  with:
37
39
  name: python-package-distributions
38
40
  path: dist/
41
+ if-no-files-found: error
39
42
 
40
43
  publish-to-pypi:
41
44
  name: >-
@@ -52,7 +55,7 @@ jobs:
52
55
 
53
56
  steps:
54
57
  - name: Download all the dists
55
- uses: actions/download-artifact@v3
58
+ uses: actions/download-artifact@v4
56
59
  with:
57
60
  name: python-package-distributions
58
61
  path: dist/
@@ -73,7 +76,7 @@ jobs:
73
76
 
74
77
  steps:
75
78
  - name: Download all the dists
76
- uses: actions/download-artifact@v3
79
+ uses: actions/download-artifact@v4
77
80
  with:
78
81
  name: python-package-distributions
79
82
  path: dist/
@@ -102,26 +105,28 @@ jobs:
102
105
  '${{ github.ref_name }}' dist/**
103
106
  --repo '${{ github.repository }}'
104
107
 
105
- publish-to-testpypi:
106
- name: Publish Python 🐍 distribution 📦 to TestPyPI
107
- needs:
108
- - build
109
- runs-on: ubuntu-latest
110
-
111
- environment:
112
- name: testpypi
113
- url: https://test.pypi.org/p/earningscall
114
-
115
- permissions:
116
- id-token: write # IMPORTANT: mandatory for trusted publishing
117
-
118
- steps:
119
- - name: Download all the dists
120
- uses: actions/download-artifact@v3
121
- with:
122
- name: python-package-distributions
123
- path: dist/
124
- - name: Publish distribution 📦 to TestPyPI
125
- uses: pypa/gh-action-pypi-publish@release/v1
126
- with:
127
- repository-url: https://test.pypi.org/legacy/
108
+ # For now, don't publish to TestPyPI (as it needs a unique version each time).
109
+ # Is there a solution for this?
110
+ # publish-to-testpypi:
111
+ # name: Publish Python 🐍 distribution 📦 to TestPyPI
112
+ # needs:
113
+ # - build
114
+ # runs-on: ubuntu-latest
115
+ #
116
+ # environment:
117
+ # name: testpypi
118
+ # url: https://test.pypi.org/p/earningscall
119
+ #
120
+ # permissions:
121
+ # id-token: write # IMPORTANT: mandatory for trusted publishing
122
+ #
123
+ # steps:
124
+ # - name: Download all the dists
125
+ # uses: actions/download-artifact@v4
126
+ # with:
127
+ # name: python-package-distributions
128
+ # path: dist/
129
+ # - name: Publish distribution 📦 to TestPyPI
130
+ # uses: pypa/gh-action-pypi-publish@release/v1
131
+ # with:
132
+ # repository-url: https://test.pypi.org/legacy/
@@ -0,0 +1,68 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+ branches:
9
+ - master
10
+
11
+ concurrency:
12
+ group: test-${{ github.head_ref }}
13
+ cancel-in-progress: true
14
+
15
+ env:
16
+ PYTHONUNBUFFERED: "1"
17
+ FORCE_COLOR: "1"
18
+
19
+ jobs:
20
+ run:
21
+ name: Python ${{ matrix.python-version }} on ${{ startsWith(matrix.os, 'macos-') && 'macOS' || startsWith(matrix.os, 'windows-') && 'Windows' || 'Linux' }}
22
+ runs-on: ${{ matrix.os }}
23
+ strategy:
24
+ fail-fast: false
25
+ matrix:
26
+ os: [ubuntu-latest]
27
+ python-version: ['3.9']
28
+
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+
32
+ - name: Set up Python ${{ matrix.python-version }}
33
+ uses: actions/setup-python@v5
34
+ with:
35
+ python-version: ${{ matrix.python-version }}
36
+
37
+ - name: Install Hatch
38
+ run: pip install --upgrade hatch
39
+
40
+ - if: matrix.python-version == '3.9' && runner.os == 'Linux'
41
+ name: Lint
42
+ run: hatch run lint:all
43
+
44
+ - name: Run tests and track code coverage
45
+ run: hatch run cov
46
+ - if: matrix.python-version == '3.9' && runner.os == 'Linux'
47
+ name: Publish Coverage to Coveralls
48
+ run: hatch run coveralls
49
+ env:
50
+ COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
51
+
52
+ run-container-matrix:
53
+ name: Container matrix on Linux
54
+ runs-on: ubuntu-latest
55
+
56
+ steps:
57
+ - uses: actions/checkout@v4
58
+
59
+ - name: Set up Python 3.10
60
+ uses: actions/setup-python@v5
61
+ with:
62
+ python-version: '3.10'
63
+
64
+ - name: Install Hatch
65
+ run: pip install --upgrade hatch hatch-containers
66
+
67
+ - name: Run tests in container matrix
68
+ run: hatch run all:test
@@ -31,4 +31,8 @@ tasks.xml
31
31
  /.idea
32
32
  /build
33
33
  /sc.egg-info
34
- /packages
34
+ /packages
35
+
36
+ # Root files
37
+ /.coverage*
38
+ /.mypy_cache/
@@ -1,8 +1,6 @@
1
1
  # Development
2
2
 
3
- TODO: Add rye installation instructions.
4
-
5
- This project uses Rye: https://rye.astral.sh/
3
+ TODO: Add hatch installation instructions.
6
4
 
7
5
 
8
6
  ### Saving Server-Side Responses for a Mocked Unit test
@@ -35,3 +33,25 @@ git commit -a
35
33
  git tag v0.0.7
36
34
  git push --atomic origin master v0.0.7
37
35
  ```
36
+
37
+
38
+
39
+ ### Manually Running Scripts
40
+
41
+ Use the library to get a single transcript from the API:
42
+
43
+ ```shell
44
+ python -m scripts.get_single_transcript
45
+ ```
46
+
47
+ Get all transcripts for a company:
48
+
49
+ ```shell
50
+ python -m scripts.get_all_company_transcripts
51
+ ```
52
+
53
+ List all companies:
54
+
55
+ ```shell
56
+ python -m scripts.list_companies
57
+ ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: earningscall
3
- Version: 0.0.10
3
+ Version: 0.0.13
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
@@ -34,6 +34,7 @@ License-File: LICENSE
34
34
  Requires-Python: >=3.8
35
35
  Requires-Dist: dataclasses-json>=0.6.4
36
36
  Requires-Dist: dataclasses>=0.6
37
+ Requires-Dist: requests-cache>=1.2.0
37
38
  Requires-Dist: requests>=2.30.0
38
39
  Description-Content-Type: text/markdown
39
40
 
@@ -58,7 +59,7 @@ pip install --upgrade earningscall
58
59
 
59
60
  # Requirements
60
61
 
61
- * Python 3.8+ (PyPI supported)
62
+ * Python 3.8+
62
63
 
63
64
  ## Get Transcript for a Single Quarter
64
65
 
@@ -129,10 +130,8 @@ To gain access to 5,000+ companies please [signup here](https://earningscall.biz
129
130
  Once you have access to your API key, you can set the API Key like this:
130
131
 
131
132
  ```python
132
-
133
133
  import earningscall
134
134
 
135
-
136
135
  earningscall.api_key = "YOUR SECRET API KEY GOES HERE"
137
136
  ```
138
137
 
@@ -151,4 +150,3 @@ from earningscall import get_sp500_companies
151
150
  for company in get_sp500_companies():
152
151
  print(f"{company.company_info} -- {company.company_info.sector} -- {company.company_info.industry}")
153
152
  ```
154
-
@@ -19,7 +19,7 @@ pip install --upgrade earningscall
19
19
 
20
20
  # Requirements
21
21
 
22
- * Python 3.8+ (PyPI supported)
22
+ * Python 3.8+
23
23
 
24
24
  ## Get Transcript for a Single Quarter
25
25
 
@@ -90,10 +90,8 @@ To gain access to 5,000+ companies please [signup here](https://earningscall.biz
90
90
  Once you have access to your API key, you can set the API Key like this:
91
91
 
92
92
  ```python
93
-
94
93
  import earningscall
95
94
 
96
-
97
95
  earningscall.api_key = "YOUR SECRET API KEY GOES HERE"
98
96
  ```
99
97
 
@@ -112,4 +110,3 @@ from earningscall import get_sp500_companies
112
110
  for company in get_sp500_companies():
113
111
  print(f"{company.company_info} -- {company.company_info.sector} -- {company.company_info.industry}")
114
112
  ```
115
-
@@ -4,3 +4,5 @@ from earningscall.exports import get_company, get_all_companies, get_sp500_compa
4
4
  from earningscall.symbols import Symbols, load_symbols
5
5
 
6
6
  api_key: Optional[str] = None
7
+
8
+ __all__ = ["get_company", "get_all_companies", "get_sp500_companies", "Symbols", "load_symbols"]
@@ -27,8 +27,7 @@ def is_demo_account():
27
27
  return get_api_key() == "demo"
28
28
 
29
29
 
30
- def get_events(exchange: str,
31
- symbol: str):
30
+ def get_events(exchange: str, symbol: str):
32
31
 
33
32
  log.debug(f"get_events exchange: {exchange} symbol: {symbol}")
34
33
  params = {
@@ -42,10 +41,7 @@ def get_events(exchange: str,
42
41
  return response.json()
43
42
 
44
43
 
45
- def get_transcript(exchange: str,
46
- symbol: str,
47
- year: int,
48
- quarter: int) -> Optional[str]:
44
+ def get_transcript(exchange: str, symbol: str, year: int, quarter: int) -> Optional[str]:
49
45
 
50
46
  log.debug(f"get_transcript year: {year} quarter: {quarter}")
51
47
  params = {
@@ -80,3 +76,9 @@ def get_sp500_companies_txt_file():
80
76
  if response.status_code != 200:
81
77
  return None
82
78
  return response.text
79
+
80
+
81
+ # def do_something():
82
+ # session = CachedSession('demo_cache', cache_control=True)
83
+ #
84
+ # # CachedSession()
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import Optional
2
+ from typing import Optional, List
3
3
 
4
4
  from earningscall import api
5
5
  from earningscall.event import EarningsEvent
@@ -12,8 +12,8 @@ log = logging.getLogger(__file__)
12
12
  class Company:
13
13
 
14
14
  company_info: CompanyInfo
15
- name: str
16
- _events: [EarningsEvent]
15
+ name: Optional[str]
16
+ _events: Optional[List[EarningsEvent]]
17
17
 
18
18
  def __init__(self, company_info: CompanyInfo):
19
19
  if not company_info:
@@ -25,31 +25,31 @@ class Company:
25
25
  def __str__(self):
26
26
  return str(self.name)
27
27
 
28
- def name(self) -> str:
29
- return self.company_info.name
30
-
31
- def _get_events(self):
28
+ def _get_events(self) -> List[EarningsEvent]:
29
+ if not self.company_info.exchange or not self.company_info.symbol:
30
+ return []
32
31
  raw_response = api.get_events(self.company_info.exchange, self.company_info.symbol)
33
32
  if not raw_response:
34
33
  return []
35
- return [EarningsEvent.from_dict(event) for event in raw_response["events"]]
34
+ return [EarningsEvent.from_dict(event) for event in raw_response["events"]] # type: ignore
36
35
 
37
- def events(self) -> [EarningsEvent]:
36
+ def events(self) -> List[EarningsEvent]:
38
37
  if not self._events:
39
38
  self._events = self._get_events()
40
39
  return self._events
41
40
 
42
- def get_transcript(self,
43
- year: Optional[int] = None,
44
- quarter: Optional[int] = None,
45
- event: Optional[EarningsEvent] = None) -> Optional[Transcript]:
41
+ def get_transcript(
42
+ self, year: Optional[int] = None, quarter: Optional[int] = None, event: Optional[EarningsEvent] = None
43
+ ) -> Optional[Transcript]:
46
44
 
45
+ if not self.company_info.exchange or not self.company_info.symbol:
46
+ return None
47
47
  if (not year or not quarter) and event:
48
48
  year = event.year
49
49
  quarter = event.quarter
50
- elif (not year or not quarter) and not event:
50
+ if (not year or not quarter) and not event:
51
51
  raise ValueError("Must specify either event or year and quarter")
52
- resp = api.get_transcript(self.company_info.exchange, self.company_info.symbol, year, quarter)
52
+ resp = api.get_transcript(self.company_info.exchange, self.company_info.symbol, year, quarter) # type: ignore
53
53
  if not resp:
54
54
  return None
55
- return Transcript.from_dict(resp)
55
+ return Transcript.from_dict(resp) # type: ignore
@@ -1,5 +1,3 @@
1
-
2
-
3
1
  class BaseError(RuntimeError):
4
2
  """
5
3
  Base error
@@ -18,6 +16,7 @@ class ClientError(BaseError):
18
16
  """
19
17
  Used to return 4XX errors.
20
18
  """
19
+
21
20
  status: int = 400 # https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400
22
21
 
23
22
 
@@ -16,6 +16,7 @@ class EarningsEvent:
16
16
  """
17
17
  EarningsEvent
18
18
  """
19
+
19
20
  year: int
20
21
  quarter: int
21
22
  conference_date: Optional[datetime] = field(
@@ -23,6 +24,6 @@ class EarningsEvent:
23
24
  metadata=config(
24
25
  encoder=lambda date: date.isoformat() if date else None,
25
26
  decoder=lambda date: datetime.fromisoformat(date) if date else None,
26
- mm_field=fields.DateTime(format="iso")
27
- )
27
+ mm_field=fields.DateTime(format="iso"),
28
+ ),
28
29
  )
@@ -1,4 +1,4 @@
1
- from typing import Optional
1
+ from typing import Optional, Iterator
2
2
 
3
3
  from earningscall.api import get_sp500_companies_txt_file
4
4
  from earningscall.company import Company
@@ -12,15 +12,15 @@ def get_company(symbol: str) -> Optional[Company]:
12
12
  return None
13
13
 
14
14
 
15
- def get_all_companies() -> [Company]:
15
+ def get_all_companies() -> Iterator[Company]:
16
16
  for company_info in get_symbols().get_all():
17
17
  yield Company(company_info=company_info)
18
18
 
19
19
 
20
- def get_sp500_companies() -> [Company]:
20
+ def get_sp500_companies() -> Iterator[Company]:
21
21
  resp = get_sp500_companies_txt_file()
22
22
  if not resp:
23
- return []
23
+ return
24
24
  for ticker_symbol in resp.split("\n"):
25
25
  company_info = get_symbols().lookup_company(ticker_symbol)
26
26
  if company_info:
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  import logging
3
-
3
+ from typing import Optional
4
4
 
5
5
  log = logging.getLogger(__file__)
6
6
  sectors_file_name = "sectors.json"
@@ -17,7 +17,7 @@ SECTORS_IN_ORDER = [
17
17
  'Industrials',
18
18
  'Real Estate',
19
19
  'Technology',
20
- 'Utilities'
20
+ 'Utilities',
21
21
  ]
22
22
 
23
23
 
@@ -166,7 +166,7 @@ INDUSTRIES_IN_ORDER = [
166
166
  'Utilities - Regulated Gas',
167
167
  'Utilities - Regulated Water',
168
168
  'Utilities - Renewable',
169
- 'Waste Management'
169
+ 'Waste Management',
170
170
  ]
171
171
 
172
172
 
@@ -198,9 +198,7 @@ def industry_to_index(_industry: str) -> int:
198
198
 
199
199
  class Sectors:
200
200
 
201
- def __init__(self,
202
- sectors: set = None,
203
- industries: set = None):
201
+ def __init__(self, sectors: Optional[set] = None, industries: Optional[set] = None):
204
202
  if sectors:
205
203
  self.sectors = sectors
206
204
  else:
@@ -218,7 +216,7 @@ class Sectors:
218
216
  if industry is not None:
219
217
  self.industries.add(industry)
220
218
 
221
- def to_dicts(self) -> {}:
219
+ def to_dicts(self) -> dict:
222
220
  return {
223
221
  "sectors": list(self.sectors),
224
222
  "industries": list(self.industries),
@@ -231,4 +229,3 @@ class Sectors:
231
229
  def from_json(json_str):
232
230
  data = json.loads(json_str)
233
231
  return Sectors(set(data["sectors"]), set(data["industries"]))
234
-
@@ -2,7 +2,7 @@ import json
2
2
  import logging
3
3
  import re
4
4
  from collections import defaultdict
5
- from typing import Optional
5
+ from typing import Optional, Iterator, List
6
6
 
7
7
  from earningscall.api import get_symbols_v2, is_demo_account
8
8
  from earningscall.errors import InsufficientApiAccessError
@@ -14,7 +14,9 @@ EXCHANGES_IN_ORDER = ["NYSE", "NASDAQ", "AMEX", "TSX", "TSXV", "OTC"]
14
14
  log = logging.getLogger(__file__)
15
15
 
16
16
 
17
- def exchange_to_index(_exchange: str) -> int:
17
+ def exchange_to_index(_exchange: Optional[str]) -> int:
18
+ if not _exchange:
19
+ return -1
18
20
  try:
19
21
  return EXCHANGES_IN_ORDER.index(_exchange)
20
22
  except ValueError:
@@ -110,7 +112,7 @@ class Symbols:
110
112
  if len(self.by_exchange_and_sym) == size_before:
111
113
  log.debug(f"Duplicate: {_sym}")
112
114
 
113
- def get_all(self) -> [CompanyInfo]:
115
+ def get_all(self) -> Iterator[CompanyInfo]:
114
116
  for _exchange_symbol, _symbol in self.by_exchange_and_sym.items():
115
117
  yield _symbol
116
118
 
@@ -129,8 +131,10 @@ class Symbols:
129
131
  except KeyError:
130
132
  pass
131
133
  if is_demo_account():
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
+ raise InsufficientApiAccessError(
135
+ f"\"{symbol}\" requires an API Key for access. To get your API Key,"
136
+ f" see: https://earningscall.biz/api-pricing"
137
+ )
134
138
  return None
135
139
 
136
140
  def remove_exchange_symbol(self, exchange_symbol: str):
@@ -142,11 +146,13 @@ class Symbols:
142
146
  def remove_keys(symbol_as_dict: dict, keys_to_remove: set):
143
147
  return {key: value for key, value in symbol_as_dict.items() if key not in keys_to_remove}
144
148
 
145
- def without_security_names(self) -> [dict]:
146
- return [self.remove_keys(symbol_as_dict, {"security_name", "sector", "industry"})
147
- for symbol_as_dict in self.to_dicts()]
149
+ def without_security_names(self) -> List[dict]:
150
+ return [
151
+ self.remove_keys(symbol_as_dict, {"security_name", "sector", "industry"})
152
+ for symbol_as_dict in self.to_dicts()
153
+ ]
148
154
 
149
- def to_dicts(self) -> [dict]:
155
+ def to_dicts(self) -> List[dict]:
150
156
  return [__symbol.__dict__ for __symbol in self.get_all()]
151
157
 
152
158
  def to_json(self, remove_security_names: bool = False) -> str:
@@ -154,9 +160,14 @@ class Symbols:
154
160
  return json.dumps(self.without_security_names())
155
161
  return json.dumps(self.to_dicts())
156
162
 
157
- def to_json_v2(self) -> str:
158
- return json.dumps([[exchange_to_index(__symbol.exchange), __symbol.company_info, __symbol.name]
159
- for __symbol in self.get_all()])
163
+ # TODO: Test this
164
+ # def to_json_v2(self) -> str:
165
+ # return json.dumps(
166
+ # [
167
+ # [exchange_to_index(__symbol.exchange), __symbol.company_info, __symbol.name]
168
+ # for __symbol in self.get_all()
169
+ # ]
170
+ # )
160
171
 
161
172
  def to_txt(self) -> str:
162
173
  exchange_symbol_names = [__symbol.to_txt_row() for __symbol in self.get_all()]
@@ -195,13 +206,15 @@ class Symbols:
195
206
  __symbols = Symbols()
196
207
  for line in txt_str.split("\n"):
197
208
  _exchange_index, _symbol, _name, _sector_index, _industry_index = line.split("\t")
198
- __symbols.add(CompanyInfo(
199
- exchange=index_to_exchange(int(_exchange_index)),
200
- symbol=_symbol,
201
- name=_name,
202
- sector=index_to_sector(int(_sector_index)),
203
- industry=index_to_industry(int(_industry_index)),
204
- ))
209
+ __symbols.add(
210
+ CompanyInfo(
211
+ exchange=index_to_exchange(int(_exchange_index)),
212
+ symbol=_symbol,
213
+ name=_name,
214
+ sector=index_to_sector(int(_sector_index)),
215
+ industry=index_to_industry(int(_industry_index)),
216
+ )
217
+ )
205
218
  return __symbols
206
219
 
207
220
  @staticmethod
@@ -0,0 +1,47 @@
1
+ [envs.default]
2
+ dependencies = [
3
+ "coverage[toml]>=6.5",
4
+ "pytest",
5
+ "responses",
6
+ "coveralls",
7
+ ]
8
+ [envs.default.scripts]
9
+ test = "pytest {args:tests}"
10
+ test-cov = "coverage run -m pytest {args:tests}"
11
+ cov-report = [
12
+ "- coverage combine",
13
+ "coverage report --show-missing",
14
+ ]
15
+ cov = [
16
+ "test-cov",
17
+ "cov-report",
18
+ ]
19
+
20
+ [envs.all]
21
+ type = "container"
22
+
23
+ [[envs.all.matrix]]
24
+ python = ["3.8", "3.9", "3.10", "3.11", "3.12"]
25
+
26
+ [envs.lint]
27
+ detached = true
28
+ dependencies = [
29
+ "black>=22.10.0",
30
+ "mypy>=0.991",
31
+ "ruff>=0.0.166",
32
+ ]
33
+ [envs.lint.scripts]
34
+ typing = "mypy --install-types --non-interactive {args:earningscall tests}"
35
+ style = [
36
+ "ruff {args:.}",
37
+ "black --check --diff {args:.}",
38
+ ]
39
+ fmt = [
40
+ "black {args:.}",
41
+ "ruff --fix {args:.}",
42
+ "style",
43
+ ]
44
+ all = [
45
+ "style",
46
+ "typing",
47
+ ]
@@ -0,0 +1,127 @@
1
+ [project]
2
+ name = "earningscall"
3
+ version = "0.0.13"
4
+ description = "The EarningsCall Python library."
5
+ readme = "README.md"
6
+ authors = [
7
+ {name = "EarningsCall", email = "dev@earningscall.biz"},
8
+ ]
9
+ requires-python = ">= 3.8"
10
+ dependencies = [
11
+ "dataclasses>=0.6",
12
+ "dataclasses-json>=0.6.4",
13
+ "requests>=2.30.0",
14
+ "requests-cache>=1.2.0",
15
+ ]
16
+ license = { file = "LICENSE" }
17
+
18
+ [project.urls]
19
+ Homepage = "https://earningscall.biz"
20
+ Documentation = "https://github.com/EarningsCall/earningscall-python"
21
+ Repository = "https://github.com/EarningsCall/earningscall-python"
22
+ Issues = "https://github.com/EarningsCall/earningscall-python/issues"
23
+ Source = "https://github.com/EarningsCall/earningscall-python"
24
+ Changelog = "https://github.com/EarningsCall/earningscall-python/blob/master/CHANGELOG.md"
25
+
26
+ [build-system]
27
+ requires = ["hatchling"]
28
+ build-backend = "hatchling.build"
29
+
30
+
31
+ [tool.hatch.metadata]
32
+ allow-direct-references = true
33
+
34
+
35
+ [tool.hatch.build.targets.wheel]
36
+ packages = ["earningscall"]
37
+
38
+ [tool.hatch.version]
39
+ path = "hatch_init/__about__.py"
40
+
41
+ [tool.hatch.envs.default]
42
+ dependencies = [
43
+ "pytest",
44
+ "pytest-cov",
45
+ ]
46
+ [tool.hatch.envs.default.scripts]
47
+ cov = "pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=hatch_init --cov=tests"
48
+ no-cov = "cov --no-cov"
49
+
50
+ [[tool.hatch.envs.test.matrix]]
51
+ python = ["3.8", "3.9", "3.10", "3.11", "3.12"]
52
+
53
+
54
+ [tool.hatch.build.targets.wheel.hooks.mypyc]
55
+ enable-by-default = false
56
+ dependencies = ["hatch-mypyc>=0.14.1"]
57
+ require-runtime-dependencies = true
58
+ mypy-args = [
59
+ "--no-warn-unused-ignores",
60
+ ]
61
+
62
+ [tool.mypy]
63
+ disallow_untyped_defs = false
64
+ follow_imports = "normal"
65
+ ignore_missing_imports = true
66
+ pretty = true
67
+ show_column_numbers = true
68
+ warn_no_return = false
69
+ warn_unused_ignores = true
70
+
71
+
72
+ [tool.black]
73
+ target-version = ["py37"]
74
+ line-length = 120
75
+ skip-string-normalization = true
76
+
77
+ [tool.ruff]
78
+ target-version = "py37"
79
+
80
+ [tool.ruff.lint]
81
+ ignore = [
82
+ # Allow non-abstract empty methods in abstract base classes
83
+ "B027",
84
+ # Allow boolean positional values in function calls, like `dict.get(... True)`
85
+ "FBT003",
86
+ # Ignore checks for possible passwords
87
+ # Ignore complexity
88
+ "C901", "PLR0911", "PLR0912", "PLR0913", "PLR0915",
89
+ "PLC1901", # empty string comparisons
90
+ "PLW2901", # `for` loop variable overwritten
91
+ "SIM114", # Combine `if` branches using logical `or` operator
92
+ ]
93
+ unfixable = [
94
+ # Don't touch unused imports
95
+ "F401",
96
+ ]
97
+
98
+ [tool.ruff.lint.flake8-quotes]
99
+ inline-quotes = "single"
100
+
101
+ [tool.ruff.lint.isort]
102
+ known-first-party = ["earningscall"]
103
+
104
+
105
+ [tool.ruff.lint.flake8-tidy-imports]
106
+ ban-relative-imports = "all"
107
+
108
+ [tool.ruff.lint.per-file-ignores]
109
+ # Tests can use relative imports and assertions
110
+ "tests/**/*" = ["TID252", "S101"]
111
+
112
+ [tool.coverage.run]
113
+ source_pkgs = ["earningscall", "tests"]
114
+ branch = true
115
+ parallel = true
116
+ omit = []
117
+
118
+ [tool.coverage.paths]
119
+ earningscall = ["earningscall"]
120
+ tests = ["tests"]
121
+
122
+ [tool.coverage.report]
123
+ exclude_lines = [
124
+ "no cov",
125
+ "if __name__ == .__main__.:",
126
+ "if TYPE_CHECKING:",
127
+ ]
@@ -11,4 +11,4 @@ for event in company.events():
11
11
  if transcript:
12
12
  print(f" Transcript Text: \"{transcript.text[:100]}...\"")
13
13
  else:
14
- print(f" No transcript found.")
14
+ print(" No transcript found.")
@@ -26,11 +26,13 @@ def test_basic_without_conference_date():
26
26
 
27
27
  def test_date_field_deserialization():
28
28
  #
29
- earnings_event = EarningsEvent.from_dict({
30
- "year": 2024,
31
- "quarter": 1,
32
- "conference_date": "2024-04-28T15:00:00.000-05:00",
33
- })
29
+ earnings_event = EarningsEvent.from_dict(
30
+ {
31
+ "year": 2024,
32
+ "quarter": 1,
33
+ "conference_date": "2024-04-28T15:00:00.000-05:00",
34
+ }
35
+ )
34
36
  #
35
37
  assert earnings_event.year == 2024
36
38
  assert earnings_event.quarter == 1
@@ -25,8 +25,9 @@ def test_get_demo_company():
25
25
  company = get_company("msft")
26
26
  ##
27
27
  transcript = company.get_transcript(year=2023, quarter=1)
28
- assert transcript.text[:100] == ('Greetings, and welcome to the Microsoft Fiscal Year 2023 First Quarter Earnings '
29
- 'Conference Call. At ')
28
+ assert transcript.text[:100] == (
29
+ 'Greetings, and welcome to the Microsoft Fiscal Year 2023 First Quarter Earnings ' 'Conference Call. At '
30
+ )
30
31
 
31
32
 
32
33
  @responses.activate
@@ -42,8 +43,9 @@ def test_get_demo_company_with_event_populated():
42
43
  assert transcript.event.year == 2022
43
44
  assert transcript.event.quarter == 1
44
45
  assert transcript.event.conference_date.isoformat() == "2022-01-19T00:00:00-08:00"
45
- assert transcript.text[:100] == ("Good day and welcome to the Apple Q1 Fiscal Year 2021 earnings conference "
46
- "call. Today's call is bein")
46
+ assert transcript.text[:100] == (
47
+ "Good day and welcome to the Apple Q1 Fiscal Year 2021 earnings conference " "call. Today's call is bein"
48
+ )
47
49
 
48
50
 
49
51
  # Uncomment and run following code to generate demo-symbols-v2.yaml file
@@ -26,12 +26,33 @@ def test_load_symbols_txt_v2():
26
26
  def test_symbols_serialization_to_text_v2():
27
27
  ##
28
28
  _symbols = Symbols()
29
- _symbols.add(CompanyInfo(exchange="TSX", symbol="TLRY", name="Tilray, Inc", sector="Energy",
30
- industry="Electronic Gaming & Multimedia"))
31
- _symbols.add(CompanyInfo(exchange="TSX", symbol="ACB", name="Aurora Cannabis Inc.", sector="Technology",
32
- industry="Electronic Gaming & Multimedia"))
33
- _symbols.add(CompanyInfo(exchange="NASDAQ", symbol="HITI", name="High Tide Inc.", sector="Consumer Cyclical",
34
- industry="Electronic Gaming & Multimedia"))
29
+ _symbols.add(
30
+ CompanyInfo(
31
+ exchange="TSX",
32
+ symbol="TLRY",
33
+ name="Tilray, Inc",
34
+ sector="Energy",
35
+ industry="Electronic Gaming & Multimedia",
36
+ )
37
+ )
38
+ _symbols.add(
39
+ CompanyInfo(
40
+ exchange="TSX",
41
+ symbol="ACB",
42
+ name="Aurora Cannabis Inc.",
43
+ sector="Technology",
44
+ industry="Electronic Gaming & Multimedia",
45
+ )
46
+ )
47
+ _symbols.add(
48
+ CompanyInfo(
49
+ exchange="NASDAQ",
50
+ symbol="HITI",
51
+ name="High Tide Inc.",
52
+ sector="Consumer Cyclical",
53
+ industry="Electronic Gaming & Multimedia",
54
+ )
55
+ )
35
56
  ##
36
57
  result = _symbols.to_txt_v2()
37
58
  ##
@@ -1,74 +0,0 @@
1
- [project]
2
- name = "earningscall"
3
- version = "0.0.10"
4
- description = "The EarningsCall Python library."
5
- readme = "README.md"
6
- authors = [
7
- {name = "EarningsCall", email = "dev@earningscall.biz"},
8
- ]
9
- requires-python = ">= 3.8"
10
- dependencies = [
11
- "dataclasses>=0.6",
12
- "dataclasses-json>=0.6.4",
13
- "requests>=2.30.0",
14
- ]
15
- license = { file = "LICENSE" }
16
-
17
- [project.urls]
18
- Homepage = "https://earningscall.biz"
19
- Documentation = "https://github.com/EarningsCall/earningscall-python"
20
- Repository = "https://github.com/EarningsCall/earningscall-python"
21
- Issues = "https://github.com/EarningsCall/earningscall-python/issues"
22
- Source = "https://github.com/EarningsCall/earningscall-python"
23
- Changelog = "https://github.com/EarningsCall/earningscall-python/blob/master/CHANGELOG.md"
24
-
25
- [build-system]
26
- requires = ["hatchling"]
27
- build-backend = "hatchling.build"
28
-
29
- [tool.rye]
30
- managed = true
31
- dev-dependencies = [
32
- "pytest>=8.2.1",
33
- "responses>=0.25.0",
34
- ]
35
-
36
- [tool.hatch.metadata]
37
- allow-direct-references = true
38
-
39
-
40
- [tool.hatch.build.targets.wheel]
41
- packages = ["earningscall"]
42
-
43
- #[tool.hatch.envs.test]
44
- #dependencies = [
45
- # "pytest",
46
- # "pytest-cov",
47
- # "responses"
48
- #]
49
-
50
- [[tool.hatch.envs.test.matrix]]
51
- python = ["3.10", "3.11"]
52
-
53
- [tool.hatch.envs.types]
54
- extra-dependencies = [
55
- "mypy>=1.0.0",
56
- "responses"
57
- ]
58
-
59
- [tool.coverage.run]
60
- source_pkgs = ["earningscall", "tests"]
61
- branch = true
62
- parallel = true
63
- omit = []
64
-
65
- [tool.coverage.paths]
66
- earningscall = ["earningscall"]
67
- tests = ["tests"]
68
-
69
- [tool.coverage.report]
70
- exclude_lines = [
71
- "no cov",
72
- "if __name__ == .__main__.:",
73
- "if TYPE_CHECKING:",
74
- ]
@@ -1,46 +0,0 @@
1
- # generated by rye
2
- # use `rye lock` or `rye sync` to update this lockfile
3
- #
4
- # last locked with the following flags:
5
- # pre: false
6
- # features: []
7
- # all-features: false
8
- # with-sources: false
9
- # generate-hashes: false
10
-
11
- -e file:.
12
- certifi==2024.2.2
13
- # via requests
14
- charset-normalizer==3.3.2
15
- # via requests
16
- dataclasses==0.6
17
- # via earningscall
18
- dataclasses-json==0.6.6
19
- # via earningscall
20
- idna==3.7
21
- # via requests
22
- iniconfig==2.0.0
23
- # via pytest
24
- marshmallow==3.21.2
25
- # via dataclasses-json
26
- mypy-extensions==1.0.0
27
- # via typing-inspect
28
- packaging==24.0
29
- # via marshmallow
30
- # via pytest
31
- pluggy==1.5.0
32
- # via pytest
33
- pytest==8.2.1
34
- pyyaml==6.0.1
35
- # via responses
36
- requests==2.32.2
37
- # via earningscall
38
- # via responses
39
- responses==0.25.0
40
- typing-extensions==4.12.0
41
- # via typing-inspect
42
- typing-inspect==0.9.0
43
- # via dataclasses-json
44
- urllib3==2.2.1
45
- # via requests
46
- # via responses
@@ -1,35 +0,0 @@
1
- # generated by rye
2
- # use `rye lock` or `rye sync` to update this lockfile
3
- #
4
- # last locked with the following flags:
5
- # pre: false
6
- # features: []
7
- # all-features: false
8
- # with-sources: false
9
- # generate-hashes: false
10
-
11
- -e file:.
12
- certifi==2024.2.2
13
- # via requests
14
- charset-normalizer==3.3.2
15
- # via requests
16
- dataclasses==0.6
17
- # via earningscall
18
- dataclasses-json==0.6.6
19
- # via earningscall
20
- idna==3.7
21
- # via requests
22
- marshmallow==3.21.2
23
- # via dataclasses-json
24
- mypy-extensions==1.0.0
25
- # via typing-inspect
26
- packaging==24.0
27
- # via marshmallow
28
- requests==2.32.2
29
- # via earningscall
30
- typing-extensions==4.12.0
31
- # via typing-inspect
32
- typing-inspect==0.9.0
33
- # via dataclasses-json
34
- urllib3==2.2.1
35
- # via requests
File without changes
File without changes
File without changes