bw-essentials-core 0.1.2__tar.gz → 0.1.4__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.
Potentially problematic release.
This version of bw-essentials-core might be problematic. Click here for more details.
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/PKG-INFO +1 -1
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/constants/services.py +2 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/services/notification.py +70 -4
- bw_essentials_core-0.1.4/bw_essentials/services/portfolio_catalogue.py +104 -0
- bw_essentials_core-0.1.4/bw_essentials/services/portfolio_content.py +69 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials_core.egg-info/PKG-INFO +1 -1
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials_core.egg-info/SOURCES.txt +2 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/setup.py +1 -1
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/README.md +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/__init__.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/constants/__init__.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/data_loch/__init__.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/data_loch/data_loch.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/email_client/__init__.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/email_client/email_client.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/notifications/__init__.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/notifications/teams_notification_schemas.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/notifications/teams_notifications.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/s3_utils/__init__.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/s3_utils/s3_utils.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/services/__init__.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/services/api_client.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/services/broker.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/services/market_pricer.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/services/master_data.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/services/model_portfolio_reporting.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/services/trade_placement.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/services/user_app.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/services/user_portfolio.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/services/user_portfolio_reporting.py +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials_core.egg-info/dependency_links.txt +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials_core.egg-info/requires.txt +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials_core.egg-info/top_level.txt +0 -0
- {bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/setup.cfg +0 -0
{bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/services/notification.py
RENAMED
|
@@ -28,10 +28,11 @@ class NotificationService(ApiClient):
|
|
|
28
28
|
self.name = Services.NOTIFICATION.value
|
|
29
29
|
self.base_url = self.get_base_url(self.name)
|
|
30
30
|
self.urls = {
|
|
31
|
-
"whatsapp": "whatsapp"
|
|
31
|
+
"whatsapp": "whatsapp",
|
|
32
|
+
"email": "email"
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
def _whatsapp(self, title, template, platform, params, to, user_id):
|
|
35
|
+
def _whatsapp(self, title, template, platform, params, to, user_id) -> None:
|
|
35
36
|
"""
|
|
36
37
|
Sends a WhatsApp notification.
|
|
37
38
|
|
|
@@ -66,12 +67,77 @@ class NotificationService(ApiClient):
|
|
|
66
67
|
resp_data = self._post(url=self.base_url, endpoint=self.urls.get('whatsapp'), data=payload)
|
|
67
68
|
logger.info(f"Whatsapp response {resp_data =}")
|
|
68
69
|
|
|
69
|
-
def send_whatsapp(self, template, title, params, to, user_id):
|
|
70
|
-
|
|
70
|
+
def send_whatsapp(self, template, title, params, to, user_id) -> None:
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
template (str): Template for the WhatsApp message.
|
|
75
|
+
title (str): Title of the notification.
|
|
76
|
+
params: Parameters for the notification message.
|
|
77
|
+
to (str): Recipient's whatsapp number.
|
|
78
|
+
user_id (str): The ID of the user receiving the notification.
|
|
71
79
|
|
|
80
|
+
Returns:
|
|
81
|
+
None
|
|
82
|
+
"""
|
|
83
|
+
logger.info(f"In - send_whatsapp_notification {user_id =} {title = } {params = } {to = }")
|
|
72
84
|
self._whatsapp(title=title,
|
|
73
85
|
template=template,
|
|
74
86
|
platform=self._get_env_var(NotificationService.PLATFORM),
|
|
75
87
|
params=params,
|
|
76
88
|
to=to,
|
|
77
89
|
user_id=user_id)
|
|
90
|
+
|
|
91
|
+
def _email(self, title: str, content: str, platform: str, to: str, user_id: str) -> None:
|
|
92
|
+
"""
|
|
93
|
+
Sends an email notification using the internal notification service.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
title (str): The subject or title of the email.
|
|
97
|
+
content (str): The HTML or plain text body of the email.
|
|
98
|
+
platform (str): The platform identifier from which the email is sent (e.g., 'prometheus').
|
|
99
|
+
to (str): Recipient's email address.
|
|
100
|
+
user_id (str): The ID of the user for tracking or logging purposes.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
None
|
|
104
|
+
|
|
105
|
+
"""
|
|
106
|
+
logger.info(f"In - email {user_id =}, {to =}, {title =}, {platform =}, {content =}")
|
|
107
|
+
payload = {
|
|
108
|
+
"to": to,
|
|
109
|
+
"userId": user_id,
|
|
110
|
+
"platform": platform,
|
|
111
|
+
"title": title,
|
|
112
|
+
"content": content
|
|
113
|
+
}
|
|
114
|
+
logger.info(f"email {payload =}")
|
|
115
|
+
headers = {
|
|
116
|
+
'api-key': self._get_env_var(NotificationService.NOTIFICATION_API_KEY),
|
|
117
|
+
'Content-Type': 'application/json'
|
|
118
|
+
}
|
|
119
|
+
self.set_headers(headers)
|
|
120
|
+
resp_data = self._post(url=self.base_url, endpoint=self.urls.get('email'), json=payload)
|
|
121
|
+
logger.info(f"Email response {resp_data =}")
|
|
122
|
+
|
|
123
|
+
def send_email(self, title: str, content: str, to: str, user_id: str) -> None:
|
|
124
|
+
"""
|
|
125
|
+
Sends an email notification to the specified recipient.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
title (str): The subject or title of the email.
|
|
129
|
+
content (str): The HTML or plain text content of the email.
|
|
130
|
+
to (str): The recipient's email address.
|
|
131
|
+
user_id (str): The ID of the user associated with the notification.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
None
|
|
135
|
+
"""
|
|
136
|
+
logger.info(f"In - send_email {user_id =}, {title =}, {to =}")
|
|
137
|
+
self._email(
|
|
138
|
+
title=title,
|
|
139
|
+
content=content,
|
|
140
|
+
platform=self._get_env_var(NotificationService.PLATFORM),
|
|
141
|
+
to=to,
|
|
142
|
+
user_id=user_id
|
|
143
|
+
)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""portfolio_catalogue.py
|
|
2
|
+
|
|
3
|
+
Client wrapper for interacting with the Portfolio-Catalogue Service API.
|
|
4
|
+
|
|
5
|
+
This module exposes `PortfolioCatalogue`, a thin abstraction over the
|
|
6
|
+
shared :class:`bw_essentials.services.api_client.ApiClient` that collates
|
|
7
|
+
all URL templates and common logic required to communicate with the
|
|
8
|
+
Portfolio-Catalogue micro-service responsible for portfolio construction
|
|
9
|
+
and rebalancing operations.
|
|
10
|
+
|
|
11
|
+
Features
|
|
12
|
+
--------
|
|
13
|
+
- Centralised mapping of service endpoints.
|
|
14
|
+
- Uniform request/response logging using the shared API client helper.
|
|
15
|
+
- Simple, type-hinted public interface that hides low-level HTTP calls.
|
|
16
|
+
|
|
17
|
+
Example
|
|
18
|
+
-------
|
|
19
|
+
>>> from bw_essentials.services.portfolio_catalogue import PortfolioCatalogue
|
|
20
|
+
>>> client = PortfolioCatalogue(service_user="system")
|
|
21
|
+
>>> data = {
|
|
22
|
+
"portfolioId": "BASKE_e3f7fc",
|
|
23
|
+
"startDate": "2025-07-01",
|
|
24
|
+
"endDate": "2025-07-10",
|
|
25
|
+
"status": "active",
|
|
26
|
+
"createdBy": "KP",
|
|
27
|
+
"constituents": [
|
|
28
|
+
{
|
|
29
|
+
"symbol": "EROSMEDIA",
|
|
30
|
+
"weight": 0.5,
|
|
31
|
+
"isin": "INE416L01017",
|
|
32
|
+
"status": "active",
|
|
33
|
+
"rationale": "Hello"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"symbol": "IDEA",
|
|
37
|
+
"weight": 0.5,
|
|
38
|
+
"isin": "INE669E01016",
|
|
39
|
+
"status": "active",
|
|
40
|
+
"rationale": "Hello"
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
>>> response = client.create_rebalance(json=data)
|
|
45
|
+
>>> print(response)
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
import logging
|
|
49
|
+
from typing import Optional, Dict, Any
|
|
50
|
+
|
|
51
|
+
from bw_essentials.constants.services import Services
|
|
52
|
+
from bw_essentials.services.api_client import ApiClient
|
|
53
|
+
|
|
54
|
+
logger = logging.getLogger(__name__)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class PortfolioCatalogue(ApiClient):
|
|
58
|
+
"""High-level client for the Portfolio-Catalogue Service.
|
|
59
|
+
|
|
60
|
+
This class bundles together the logic for generating fully-qualified
|
|
61
|
+
endpoint URLs and executing authenticated HTTP requests for
|
|
62
|
+
portfolio rebalancing workflows.
|
|
63
|
+
|
|
64
|
+
Attributes
|
|
65
|
+
----------
|
|
66
|
+
base_url : str
|
|
67
|
+
Resolved base URL for the Portfolio-Catalogue service obtained
|
|
68
|
+
from configuration or environment variables.
|
|
69
|
+
name : str
|
|
70
|
+
Canonical service identifier used for logging and telemetry.
|
|
71
|
+
urls : dict[str, str]
|
|
72
|
+
Mapping of human-readable keys to relative endpoint paths.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def __init__(self, service_user: str):
|
|
76
|
+
super().__init__(user=service_user)
|
|
77
|
+
self.base_url = self.get_base_url(Services.PORTFOLIO_CATALOGUE.value)
|
|
78
|
+
self.name = Services.PORTFOLIO_CATALOGUE.value
|
|
79
|
+
self.urls = {
|
|
80
|
+
"rebalance": "rebalance",
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
def create_rebalance(self, json: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
84
|
+
"""Submit a rebalance request.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
json : dict
|
|
89
|
+
Request payload describing the rebalance parameters and
|
|
90
|
+
portfolio metadata.
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
dict
|
|
95
|
+
Parsed JSON response returned by the service.
|
|
96
|
+
"""
|
|
97
|
+
logger.info("In - create_rebalance %s", json)
|
|
98
|
+
data = self._post(
|
|
99
|
+
url=self.base_url,
|
|
100
|
+
endpoint=self.urls["rebalance"],
|
|
101
|
+
json=json,
|
|
102
|
+
)
|
|
103
|
+
logger.info("%s", data)
|
|
104
|
+
return data
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""portfolio_content.py
|
|
2
|
+
|
|
3
|
+
Client wrapper for the Portfolio-Content Service API.
|
|
4
|
+
|
|
5
|
+
This module exposes :class:`PortfolioContent`, a light-weight abstraction
|
|
6
|
+
around :class:`bw_essentials.services.api_client.ApiClient` that
|
|
7
|
+
centralises endpoint definitions and adds structured logging when
|
|
8
|
+
querying portfolio content (e.g. constituent list, metadata).
|
|
9
|
+
|
|
10
|
+
Example
|
|
11
|
+
-------
|
|
12
|
+
>>> from bw_essentials.services.portfolio_content import PortfolioContent
|
|
13
|
+
>>> client = PortfolioContent(service_user="system")
|
|
14
|
+
>>> details = client.get_portfolio_details("BASKE_e3f7fc")
|
|
15
|
+
>>> print(details)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
from typing import Optional, Dict, Any
|
|
20
|
+
|
|
21
|
+
from bw_essentials.constants.services import Services
|
|
22
|
+
from bw_essentials.services.api_client import ApiClient
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class PortfolioContent(ApiClient):
|
|
28
|
+
"""High-level client for the Portfolio-Content Service.
|
|
29
|
+
|
|
30
|
+
Attributes
|
|
31
|
+
----------
|
|
32
|
+
base_url : str
|
|
33
|
+
Resolved base URL of the Portfolio-Content micro-service.
|
|
34
|
+
name : str
|
|
35
|
+
Canonical service identifier for logging/telemetry.
|
|
36
|
+
urls : dict[str, str]
|
|
37
|
+
Mapping of friendly names to relative endpoint paths.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self, service_user: str):
|
|
41
|
+
super().__init__(user=service_user)
|
|
42
|
+
self.base_url = self.get_base_url(Services.PORTFOLIO_CONTENT.value)
|
|
43
|
+
self.name = Services.PORTFOLIO_CONTENT.value
|
|
44
|
+
self.urls = {
|
|
45
|
+
"portfolio": "portfolio",
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
def get_portfolio_details(self, portfolio_id: str) -> Optional[Dict[str, Any]]:
|
|
49
|
+
"""Retrieve full details of a portfolio.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
portfolio_id : str
|
|
54
|
+
Unique identifier of the portfolio whose details should be
|
|
55
|
+
fetched.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
Optional[dict]
|
|
60
|
+
Parsed JSON response if the request succeeds, otherwise
|
|
61
|
+
``None``.
|
|
62
|
+
"""
|
|
63
|
+
logger.info("In - get_portfolio_details %s", portfolio_id)
|
|
64
|
+
data = self._get(
|
|
65
|
+
url=self.base_url,
|
|
66
|
+
endpoint=f"{self.urls['portfolio']}/{portfolio_id}"
|
|
67
|
+
)
|
|
68
|
+
logger.info("%s", data)
|
|
69
|
+
return data
|
{bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials_core.egg-info/SOURCES.txt
RENAMED
|
@@ -19,6 +19,8 @@ bw_essentials/services/market_pricer.py
|
|
|
19
19
|
bw_essentials/services/master_data.py
|
|
20
20
|
bw_essentials/services/model_portfolio_reporting.py
|
|
21
21
|
bw_essentials/services/notification.py
|
|
22
|
+
bw_essentials/services/portfolio_catalogue.py
|
|
23
|
+
bw_essentials/services/portfolio_content.py
|
|
22
24
|
bw_essentials/services/trade_placement.py
|
|
23
25
|
bw_essentials/services/user_app.py
|
|
24
26
|
bw_essentials/services/user_portfolio.py
|
|
@@ -10,7 +10,7 @@ from setuptools import setup, find_packages
|
|
|
10
10
|
|
|
11
11
|
setup(
|
|
12
12
|
name="bw-essentials-core",
|
|
13
|
-
version="0.1.
|
|
13
|
+
version="0.1.4",
|
|
14
14
|
author="InvestorAI",
|
|
15
15
|
author_email="support+tech@investorai.in",
|
|
16
16
|
description="Reusable utilities for S3, email, Data Loch, Microsoft Teams Notifications and more.",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/email_client/__init__.py
RENAMED
|
File without changes
|
{bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/email_client/email_client.py
RENAMED
|
File without changes
|
{bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/notifications/__init__.py
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
|
{bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/services/market_pricer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/services/trade_placement.py
RENAMED
|
File without changes
|
|
File without changes
|
{bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials/services/user_portfolio.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials_core.egg-info/requires.txt
RENAMED
|
File without changes
|
{bw_essentials_core-0.1.2 → bw_essentials_core-0.1.4}/bw_essentials_core.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|