Skip to content

Commit ec3c61d

Browse files
committed
add main
0 parents  commit ec3c61d

18 files changed

+1408
-0
lines changed

.dockerignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.env
2+
images/
3+
__pycache__/
4+
.vscode/
5+
video/
6+
rdp_controller/__pycache__/
7+
sample_jsons/
8+
tests/__pycache__/
9+
README.md
10+
.git/
11+
.gitignore

.env.test

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# RDP Core Credentials
2+
RDP_USERNAME=testuser1
3+
RDP_PASSWORD=testpassword1
4+
RDP_CLIENTID=testclientid1
5+
6+
# RDP Core Endpoints
7+
RDP_BASE_URL=https://api.refinitiv.com
8+
RDP_AUTH_URL=/auth/oauth2/v1/token
9+
RDP_ESG_URL=/data/environmental-social-governance/v2/views/scores-full
10+
RDP_SEARCH_EXPLORE_URL=/discovery/search/v1/explore

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
video/
2+
.env
3+
sample_jsons/
4+
tests/__pycache__/
5+
rdp_controller/__pycache__/
6+
__pycache__/
7+
tree.txt

.vscode/settings.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"python.testing.unittestArgs": [
3+
"-v",
4+
"-s",
5+
".",
6+
"-p",
7+
"test*.py"
8+
],
9+
"python.testing.pytestEnabled": false,
10+
"python.testing.unittestEnabled": true
11+
}

Dockerfile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#Build stage, using slim based-image because alpine cannot use Pandas and Matplotlib
2+
FROM python:3.9.13-slim-buster AS builder
3+
4+
#Copy requirements.txt
5+
COPY requirements.txt .
6+
7+
# install dependencies to the local user directory (eg. /root/.local)
8+
RUN pip install --user -r requirements.txt
9+
10+
# Run stage, using slim based-image because alpine cannot use Pandas and Matplotlib
11+
FROM python:3.9.13-slim-buster
12+
WORKDIR /app
13+
14+
# Update PATH environment variable + set Python buffer to make Docker print every message instantly.
15+
ENV PATH=/root/.local:$PATH \
16+
USERNAME=DOCKER_CONTAINER \
17+
PYTHONUNBUFFERED=1
18+
19+
# copy only the dependencies installation from the 1st stage image
20+
COPY --from=builder /root/.local /root/.local
21+
# Copy env.test, module and tests folder.
22+
COPY app.py .env.test ./
23+
ADD rdp_controller /app/rdp_controller
24+
ADD tests /app/tests
25+
WORKDIR /app/tests
26+
27+
#Run Python
28+
ENTRYPOINT [ "python", "-m", "unittest", "discover" ]

LICENSE.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
MIT License
2+
3+
Copyright (C) 2019-2022 Refinitiv. All rights reserved.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining
6+
a copy of this software and associated documentation files (the
7+
"Software"), to deal in the Software without restriction, including
8+
without limitation the rights to use, copy, modify, merge, publish,
9+
distribute, sublicense, and/or sell copies of the Software, and to
10+
permit persons to whom the Software is furnished to do so, subject to
11+
the following conditions:
12+
13+
The above copyright notice and this permission notice shall be
14+
included in all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 348 additions & 0 deletions
Large diffs are not rendered by default.

app.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#|-----------------------------------------------------------------------------
2+
#| This source code is provided under the MIT license --
3+
#| and is provided AS IS with no warranty or guarantee of fit for purpose. --
4+
#| See the project's LICENSE.md for details. --
5+
#| Copyright Refinitiv 2022. All rights reserved. --
6+
#|-----------------------------------------------------------------------------
7+
8+
"""
9+
Example Code Disclaimer:
10+
ALL EXAMPLE CODE IS PROVIDED ON AN “AS IS” AND “AS AVAILABLE” BASIS FOR ILLUSTRATIVE PURPOSES ONLY. REFINITIV MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, AS TO THE OPERATION OF THE EXAMPLE CODE, OR THE INFORMATION, CONTENT, OR MATERIALS USED IN CONNECTION WITH THE EXAMPLE CODE. YOU EXPRESSLY AGREE THAT YOUR USE OF THE EXAMPLE CODE IS AT YOUR SOLE RISK.
11+
"""
12+
13+
import sys
14+
import os
15+
import pandas as pd
16+
import numpy as np
17+
from dotenv import load_dotenv
18+
19+
load_dotenv() # take environment variables from .env.
20+
21+
from rdp_controller import rdp_http_controller
22+
23+
def convert_pandas(json_data):
24+
if not json_data:
25+
raise TypeError('Received invalid (None or Empty) JSON data')
26+
27+
try:
28+
headers=json_data['headers']
29+
#Get column headers/titles using lambda
30+
titles=map(lambda header:header['title'], headers)
31+
32+
dataArray=np.array(json_data['data'])
33+
df = pd.DataFrame(data=dataArray,columns=titles)
34+
35+
return df
36+
except Exception as exp:
37+
print(f'Error converting JSON to Dataframe exception: {str(exp)}')
38+
raise TypeError('Error converting JSON to Dataframe')
39+
40+
41+
if __name__ == '__main__':
42+
username = os.getenv('RDP_USERNAME')
43+
password = os.getenv('RDP_PASSWORD')
44+
client_id = os.getenv('RDP_CLIENTID')
45+
46+
rdp_controller = rdp_http_controller.RDPHTTPController()
47+
48+
base_URL = os.getenv('RDP_BASE_URL')
49+
auth_endpoint = base_URL + os.getenv('RDP_AUTH_URL')
50+
esg_endpoint = base_URL + os.getenv('RDP_ESG_URL')
51+
search_endpoint = base_URL + os.getenv('RDP_SEARCH_EXPLORE_URL')
52+
53+
access_token = None
54+
refresh_token = None
55+
expires_in = 0
56+
universe = 'LSEG.L'
57+
58+
try:
59+
access_token, refresh_token, expires_in = rdp_controller.rdp_authentication(auth_endpoint, username, password, client_id)
60+
if not access_token:
61+
print('Cannot login to RDP, exiting application')
62+
sys.exit(1)
63+
64+
esg_data = None
65+
esg_data = rdp_controller.rdp_getESG(esg_endpoint, access_token, universe)
66+
if not esg_data:
67+
print(f'No ESG data for {universe}, exiting application')
68+
69+
esg_df = convert_pandas(esg_data)
70+
esg_df = pd.DataFrame(esg_df,columns=['Instrument','Period End Date','ESG Score','ESG Combined Score','ESG Controversies Score'])
71+
print(esg_df.head())
72+
73+
company_data = None
74+
search_payload = {
75+
'View': 'Entities',
76+
'Filter': f'RIC eq \'{universe}\'',
77+
'Select': 'IssuerCommonName,DocumentTitle,RCSExchangeCountryLeaf,IssueISIN,ExchangeName,ExchangeCode,SearchAllCategoryv3,RCSTRBC2012Leaf'
78+
}
79+
company_data = rdp_controller.rdp_getSearchExplore(search_endpoint, access_token, search_payload)
80+
if not company_data:
81+
print(f'No Meta data for {universe}, exiting application')
82+
print(f'RIC: {universe} Metadata:')
83+
print('\tIssuerCommonName: {}'.format(company_data['Hits'][0]['IssuerCommonName']))
84+
print('\tRCSExchangeCountryLeaf: {}'.format(company_data['Hits'][0]['RCSExchangeCountryLeaf']))
85+
print('\tISIN: {}'.format(company_data['Hits'][0]['IssueISIN']))
86+
print('\tExchange Name: {}'.format(company_data['Hits'][0]['ExchangeName']))
87+
print('\tRCSTRBC2012Leaf: {}'.format(company_data['Hits'][0]['RCSTRBC2012Leaf']))
88+
except Exception as exp:
89+
print(f'Caught exception: {str(exp)}')
90+

rdp_controller/__init__.py

Whitespace-only changes.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import requests
2+
import json
3+
4+
class RDPHTTPController():
5+
6+
# Constructor Method
7+
def __init__(self):
8+
self.scope = 'trapi'
9+
self.client_secret = ''
10+
pass
11+
12+
# Send HTTP Post request to get Access Token (Password Grant and Refresh Grant) from RDP Auth Service
13+
def rdp_authentication(self, auth_url, username, password, client_id, old_refresh_token = None):
14+
"""
15+
Send Authentication to RDP Auth service
16+
"""
17+
18+
if not auth_url or not username or not password or not client_id:
19+
raise TypeError('Received invalid (None or Empty) arguments')
20+
21+
access_token = None
22+
refresh_token = None
23+
expires_in = 0
24+
if old_refresh_token is None: # For the Password Grant scenario
25+
payload=f'username={username}&password={password}&grant_type=password&scope={self.scope}&takeExclusiveSignOnControl=true&client_id={client_id}'
26+
else: # For the Refresh Token scenario
27+
payload=f'username={username}&refresh_token={old_refresh_token}&grant_type=refresh_token&client_id={client_id}'
28+
29+
# Send HTTP Request
30+
try:
31+
response = requests.post(auth_url,
32+
headers = {'Content-Type':'application/x-www-form-urlencoded'},
33+
data = payload,
34+
auth = (client_id, self.client_secret)
35+
)
36+
except Exception as exp:
37+
print(f'Caught exception: {exp}')
38+
39+
40+
if response.status_code == 200: # HTTP Status 'OK'
41+
print('Authentication success')
42+
access_token = response.json()['access_token']
43+
refresh_token = response.json()['refresh_token']
44+
expires_in = int(response.json()['expires_in'])
45+
if response.status_code != 200:
46+
print(f'RDP authentication failure: {response.status_code} {response.reason}')
47+
print(f'Text: {response.text}')
48+
raise requests.exceptions.HTTPError(f'RDP authentication failure: {response.status_code} - {response.text} ', response = response )
49+
50+
return access_token, refresh_token, expires_in
51+
52+
def rdp_getESG(self, esg_url, access_token, universe):
53+
54+
if not esg_url or not access_token or not universe:
55+
raise TypeError('Received invalid (None or Empty) arguments')
56+
57+
payload = {'universe': universe}
58+
# Request data for ESG Score Full Service
59+
try:
60+
response = requests.get(esg_url, headers={'Authorization': f'Bearer {access_token}'}, params = payload)
61+
except Exception as exp:
62+
print(f'Caught exception: {exp}')
63+
64+
if response.status_code == 200: # HTTP Status 'OK'
65+
print('Receive ESG Data from RDP APIs')
66+
#print(response.json())
67+
else:
68+
print(f'RDP APIs: ESG data request failure: {response.status_code} {response.reason}')
69+
print(f'Text: {response.text}')
70+
raise requests.exceptions.HTTPError(f'ESG data request failure: {response.status_code} - {response.text} ', response = response )
71+
72+
return response.json()
73+
74+
def rdp_getSearchExplore(self, search_url, access_token, payload):
75+
76+
if not search_url or not access_token or not payload:
77+
raise TypeError('Received invalid (None or Empty) arguments')
78+
79+
headers = {
80+
'Accept': 'application/json',
81+
'Authorization': f'Bearer {access_token}'
82+
}
83+
84+
try:
85+
response = requests.post(search_url, headers = headers, data = json.dumps(payload))
86+
except Exception as exp:
87+
print(f'Caught exception: {exp}')
88+
89+
if response.status_code == 200: # HTTP Status 'OK'
90+
print('Receive Search Explore Data from RDP APIs')
91+
#print(response.json())
92+
else:
93+
print(f'RDP APIs: Search Explore request failure: {response.status_code} {response.reason}')
94+
print(f'Text: {response.text}')
95+
raise requests.exceptions.HTTPError(f'Search Explore request failure: {response.status_code} - {response.text} ', response = response )
96+
97+
return response.json()
98+

0 commit comments

Comments
 (0)