cit-pydata 0.0.1.dev0__py3-none-any.whl
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.
- cit_pydata/__init__.py +9 -0
- cit_pydata/aws/__init__.py +1 -0
- cit_pydata/aws/api.py +204 -0
- cit_pydata/box/__init__.py +1 -0
- cit_pydata/box/api.py +107 -0
- cit_pydata/joblog/__init__.py +1 -0
- cit_pydata/joblog/api.py +93 -0
- cit_pydata/marketo/__init__.py +1 -0
- cit_pydata/marketo/api.py +587 -0
- cit_pydata/salesforce/__init__.py +1 -0
- cit_pydata/salesforce/api.py +604 -0
- cit_pydata/sfsync/__init__.py +1 -0
- cit_pydata/sfsync/api.py +39 -0
- cit_pydata/sftp/__init__.py +1 -0
- cit_pydata/sftp/api.py +379 -0
- cit_pydata/sql/__init__.py +1 -0
- cit_pydata/sql/api.py +939 -0
- cit_pydata/util/__init__.py +1 -0
- cit_pydata/util/api.py +418 -0
- cit_pydata-0.0.1.dev0.dist-info/LICENSE +19 -0
- cit_pydata-0.0.1.dev0.dist-info/METADATA +78 -0
- cit_pydata-0.0.1.dev0.dist-info/RECORD +24 -0
- cit_pydata-0.0.1.dev0.dist-info/WHEEL +5 -0
- cit_pydata-0.0.1.dev0.dist-info/top_level.txt +1 -0
cit_pydata/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .api import S3Client, SSMClient
|
cit_pydata/aws/api.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
from cit_pydata.util import api as util_api
|
|
4
|
+
from cit_pydata.util import ON_AWS
|
|
5
|
+
|
|
6
|
+
from typing import Literal
|
|
7
|
+
import boto3
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _get_boto_session(
|
|
11
|
+
service: str = None,
|
|
12
|
+
environment: str = None,
|
|
13
|
+
iam_user: str = None,
|
|
14
|
+
path_to_env_file: str = None,
|
|
15
|
+
region: str = None,
|
|
16
|
+
logger=util_api.get_logger(__name__, "info"),
|
|
17
|
+
):
|
|
18
|
+
if ON_AWS:
|
|
19
|
+
return boto3.Session()
|
|
20
|
+
|
|
21
|
+
_environment_variable = environment + "_" + iam_user + "_" + "aws_access_key_id"
|
|
22
|
+
aws_access_key_id = util_api.get_environment_variable(
|
|
23
|
+
logger=logger,
|
|
24
|
+
path_to_env_file=path_to_env_file,
|
|
25
|
+
variable_name=_environment_variable,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
if aws_access_key_id is None:
|
|
29
|
+
logger.error(f"Access Key Id does not exist for environment {environment}")
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
_environment_variable = environment + "_" + iam_user + "_" + "aws_secret_access_key"
|
|
33
|
+
aws_secret_access_key = util_api.get_environment_variable(
|
|
34
|
+
logger=logger,
|
|
35
|
+
path_to_env_file=path_to_env_file,
|
|
36
|
+
variable_name=_environment_variable,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
if aws_secret_access_key is None:
|
|
40
|
+
logger.error(f"Secret Key not specified for environment {environment}")
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
_aws_region = region
|
|
44
|
+
if _aws_region is None:
|
|
45
|
+
_environment_variable = environment + "_" + iam_user + "_" + "aws_region"
|
|
46
|
+
_aws_region = util_api.get_environment_variable(
|
|
47
|
+
logger=logger,
|
|
48
|
+
path_to_env_file=path_to_env_file,
|
|
49
|
+
variable_name=_environment_variable,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if _aws_region is None:
|
|
53
|
+
logger.error(f"AWS Region not specified for environment {environment}")
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
_session = boto3.Session(
|
|
57
|
+
aws_access_key_id=aws_access_key_id,
|
|
58
|
+
aws_secret_access_key=aws_secret_access_key,
|
|
59
|
+
region_name=_aws_region,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return _session
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class S3Client:
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
environment: str = None,
|
|
69
|
+
iam_user: str = None,
|
|
70
|
+
path_to_env_file: str = None,
|
|
71
|
+
log_level: Literal["info", "debug"] = "info",
|
|
72
|
+
):
|
|
73
|
+
self.logger = util_api.get_logger(__name__, log_level)
|
|
74
|
+
environment = environment.lower()
|
|
75
|
+
|
|
76
|
+
self.session = _get_boto_session(
|
|
77
|
+
service="s3",
|
|
78
|
+
environment=environment,
|
|
79
|
+
iam_user=iam_user,
|
|
80
|
+
path_to_env_file=path_to_env_file,
|
|
81
|
+
logger=self.logger,
|
|
82
|
+
)
|
|
83
|
+
self.client = self.session.client("s3")
|
|
84
|
+
|
|
85
|
+
def get_object_metadata(self, **kwargs):
|
|
86
|
+
"""
|
|
87
|
+
Returns pandas dataframe with S3 Objects
|
|
88
|
+
|
|
89
|
+
kwargs for method client.list_objects_v2
|
|
90
|
+
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.list_objects_v2
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
# self.logger.info(f'Getting S3 objects: {**kwargs}')
|
|
94
|
+
object_list = []
|
|
95
|
+
next_page_token = "init"
|
|
96
|
+
|
|
97
|
+
# Object metadata is retrieved in batches of 1000
|
|
98
|
+
while next_page_token:
|
|
99
|
+
response: dict = None
|
|
100
|
+
if next_page_token == "init":
|
|
101
|
+
response = self.client.list_objects_v2(**kwargs)
|
|
102
|
+
else:
|
|
103
|
+
response = self.client.list_objects_v2(
|
|
104
|
+
ContinuationToken=next_page_token, **kwargs
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
next_page_token = response.get("NextContinuationToken", None)
|
|
108
|
+
# self.logger.debug(next_page_token)
|
|
109
|
+
for object_item in response["Contents"]:
|
|
110
|
+
object_list.append(object_item)
|
|
111
|
+
response.pop("Contents")
|
|
112
|
+
|
|
113
|
+
if not pandas in sys.modules:
|
|
114
|
+
import pandas
|
|
115
|
+
df_s3_objects = pandas.DataFrame(object_list)
|
|
116
|
+
# self.logger.debug(response)
|
|
117
|
+
# self.logger.debug(df_s3_objects.info())
|
|
118
|
+
|
|
119
|
+
return response, df_s3_objects
|
|
120
|
+
|
|
121
|
+
def metadata_to_sql(self, sql_client, table_name: str, dataframe):
|
|
122
|
+
if "sqlalchemy" not in sys.modules:
|
|
123
|
+
import sqlalchemy
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
sql_client.insert_df(
|
|
127
|
+
table_name=table_name,
|
|
128
|
+
df=dataframe,
|
|
129
|
+
index=False,
|
|
130
|
+
dtype={"LastModified": sqlalchemy.DateTime},
|
|
131
|
+
)
|
|
132
|
+
# dataframe.to_sql(name = table_name, con = sql_client.sql_engine, if_exists='replace', index=False, dtype={'LastModified':sqlalchemy.DateTime})
|
|
133
|
+
except Exception as e:
|
|
134
|
+
self.logger.error(f"Failed to send S3 Object metadata to SQL")
|
|
135
|
+
self.logger.exception(str(e))
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class SSMClient:
|
|
139
|
+
def __init__(
|
|
140
|
+
self,
|
|
141
|
+
environment: str = None,
|
|
142
|
+
iam_user: str = None,
|
|
143
|
+
path_to_env_file: str = None,
|
|
144
|
+
log_level: Literal["info", "debug"] = "info",
|
|
145
|
+
logger=None,
|
|
146
|
+
):
|
|
147
|
+
self.logger = util_api.get_logger(__name__, log_level) if not logger else logger
|
|
148
|
+
self.session = _get_boto_session(
|
|
149
|
+
service="ssm",
|
|
150
|
+
environment=environment,
|
|
151
|
+
iam_user=iam_user,
|
|
152
|
+
path_to_env_file=path_to_env_file,
|
|
153
|
+
logger=self.logger,
|
|
154
|
+
)
|
|
155
|
+
self.client = self.session.client("ssm")
|
|
156
|
+
|
|
157
|
+
def get_parameter(
|
|
158
|
+
self, name: str, with_decryption: bool = False, is_verbose: bool = False
|
|
159
|
+
):
|
|
160
|
+
value = None
|
|
161
|
+
try:
|
|
162
|
+
value = self.client.get_parameter(Name=name, WithDecryption=with_decryption)
|
|
163
|
+
except Exception as ce:
|
|
164
|
+
self.logger.error(ce)
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
if value is None:
|
|
168
|
+
self.logger.error(f'SSM Parameter not found "{name}"')
|
|
169
|
+
return
|
|
170
|
+
|
|
171
|
+
if is_verbose:
|
|
172
|
+
return value
|
|
173
|
+
else:
|
|
174
|
+
return value["Parameter"]["Value"]
|
|
175
|
+
|
|
176
|
+
# for parameter in response['Parameters']:
|
|
177
|
+
# return parameter['Value']
|
|
178
|
+
|
|
179
|
+
def describe_parameters(
|
|
180
|
+
self, filter_parameters: list[dict], is_verbose: bool = False
|
|
181
|
+
):
|
|
182
|
+
"""
|
|
183
|
+
filter_parameters=[
|
|
184
|
+
{
|
|
185
|
+
'Key': 'string',
|
|
186
|
+
'Option': 'string',
|
|
187
|
+
'Values': [
|
|
188
|
+
'string',
|
|
189
|
+
]
|
|
190
|
+
},
|
|
191
|
+
"""
|
|
192
|
+
max_results = 50
|
|
193
|
+
describe_param_dict: dict = self.client.describe_parameters(
|
|
194
|
+
ParameterFilters=filter_parameters, MaxResults=max_results
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
param_list = describe_param_dict.get("Parameters", None)
|
|
198
|
+
# return_list = []
|
|
199
|
+
# for parameter in param_list
|
|
200
|
+
# param_keys_to_return_list = ['Name']
|
|
201
|
+
# param_dict = parameter.get()
|
|
202
|
+
# return_list.append()
|
|
203
|
+
|
|
204
|
+
return param_list
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .api import BoxClient
|
cit_pydata/box/api.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from cit_pydata.util import api as util_api
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BoxClient:
|
|
8
|
+
def __init__(self, box_jwt_auth_file_path, logger=None):
|
|
9
|
+
self.logger = util_api.get_logger(__name__, "INFO") if not logger else logger
|
|
10
|
+
|
|
11
|
+
self.box_jwt_auth_file_path = box_jwt_auth_file_path
|
|
12
|
+
|
|
13
|
+
# client is the boxsdk Client object
|
|
14
|
+
self.client = None
|
|
15
|
+
|
|
16
|
+
def _get_client(self):
|
|
17
|
+
if "boxsdk" not in sys.modules:
|
|
18
|
+
from boxsdk import Client, JWTAuth, OAuth2
|
|
19
|
+
# JWT Auth
|
|
20
|
+
auth = JWTAuth.from_settings_file(self.box_jwt_auth_file_path)
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
self.client = Client(auth)
|
|
24
|
+
except Client.BoxAPIException as err:
|
|
25
|
+
self.logger.error("Failed to Authenticate to Box API:", err)
|
|
26
|
+
|
|
27
|
+
def get_folder(self, folder_id: str):
|
|
28
|
+
if not self.client:
|
|
29
|
+
self._get_client()
|
|
30
|
+
|
|
31
|
+
return self.client.folder(folder_id=folder_id)
|
|
32
|
+
|
|
33
|
+
def get_file(self):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
def get_folder_items(self, folder_id: str, filter_type: str = None):
|
|
37
|
+
if not self.client:
|
|
38
|
+
self._get_client()
|
|
39
|
+
|
|
40
|
+
item_id_dict = {}
|
|
41
|
+
items = self.client.folder(folder_id=folder_id).get_items()
|
|
42
|
+
|
|
43
|
+
assert filter_type in ["file", "folder", None]
|
|
44
|
+
if filter_type == "file":
|
|
45
|
+
for item in items:
|
|
46
|
+
if item.type == filter_type:
|
|
47
|
+
item_id_dict[item.id] = item
|
|
48
|
+
elif filter_type == "folder":
|
|
49
|
+
for item in items:
|
|
50
|
+
if item.type == filter_type:
|
|
51
|
+
item_id_dict[item.id] = item
|
|
52
|
+
else:
|
|
53
|
+
for item in items:
|
|
54
|
+
item_id_dict[item.id] = item
|
|
55
|
+
|
|
56
|
+
for item in items:
|
|
57
|
+
if item.type == filter_type:
|
|
58
|
+
item_id_dict[item.id] = {"name": item.name, "type": item.type}
|
|
59
|
+
else:
|
|
60
|
+
item_id_dict[item.id] = {"name": item.name, "type": item.type}
|
|
61
|
+
|
|
62
|
+
return item_id_dict
|
|
63
|
+
|
|
64
|
+
def get_shared_link(self, folder_id):
|
|
65
|
+
if not self.client:
|
|
66
|
+
self._get_client()
|
|
67
|
+
|
|
68
|
+
folder = self.get_folder(folder_id)
|
|
69
|
+
shared_link = folder.get().shared_link
|
|
70
|
+
return shared_link
|
|
71
|
+
|
|
72
|
+
def copy_file(self, source_file_id, target_folder_id):
|
|
73
|
+
"""
|
|
74
|
+
Copies Box fild to another folder
|
|
75
|
+
source_file_id - Box file id of the file to copy
|
|
76
|
+
target_folder_id - Box folder id of where to copy the file too
|
|
77
|
+
"""
|
|
78
|
+
if not self.client:
|
|
79
|
+
self._get_client()
|
|
80
|
+
|
|
81
|
+
file_to_copy = self.client.file(source_file_id)
|
|
82
|
+
target_folder = self.client.folder(target_folder_id)
|
|
83
|
+
try:
|
|
84
|
+
file_copy = file_to_copy.copy(target_folder)
|
|
85
|
+
self.logger.info(
|
|
86
|
+
'File "{0}" has been copied into folder "{1}"'.format(
|
|
87
|
+
file_copy.name, file_copy.parent.name
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
except Exception as e:
|
|
91
|
+
self.logger.error(f"Failed to copy Box item {e}")
|
|
92
|
+
|
|
93
|
+
def upload_file(self, file_path, box_folder_id):
|
|
94
|
+
if not self.client:
|
|
95
|
+
self._get_client()
|
|
96
|
+
|
|
97
|
+
if os.path.exists(file_path):
|
|
98
|
+
new_file = self.client.folder(box_folder_id).upload(file_path)
|
|
99
|
+
self.logger.info(
|
|
100
|
+
'File "{0}" uploaded to Box with file ID {1}'.format(
|
|
101
|
+
new_file.name, new_file.id
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
return True
|
|
105
|
+
else:
|
|
106
|
+
self.logger.error(f"File does not exist {file_path}")
|
|
107
|
+
return False
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .api import JobLogClient
|
cit_pydata/joblog/api.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
from cit_pydata.util import api as util_api
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class JobLogClient:
|
|
7
|
+
def __init__(self, sql_client, job_name, logger=None):
|
|
8
|
+
self.logger = util_api.get_logger(__name__, "INFO") if not logger else logger
|
|
9
|
+
|
|
10
|
+
self.sql_client = sql_client
|
|
11
|
+
|
|
12
|
+
self.job_name = job_name
|
|
13
|
+
self.log_guid = None
|
|
14
|
+
|
|
15
|
+
def start(self):
|
|
16
|
+
if "uuid" not in sys.modules:
|
|
17
|
+
import uuid
|
|
18
|
+
if not self.log_guid:
|
|
19
|
+
self.log_guid = str(uuid.uuid4())
|
|
20
|
+
param_list = [self.job_name, "initializing", self.log_guid]
|
|
21
|
+
self.sql_client.execute_stored_procedure("usp_Start_JobLog", param_list)
|
|
22
|
+
self.logger.info(
|
|
23
|
+
f"JOBLOG - Started Job {self.job_name} with GUID {self.log_guid}"
|
|
24
|
+
)
|
|
25
|
+
return self.log_guid
|
|
26
|
+
else:
|
|
27
|
+
self.logger.error(f"JOBLOG - Failed to start. Logging already started")
|
|
28
|
+
|
|
29
|
+
def finish(self, is_success: bool = True, message: str = None):
|
|
30
|
+
if is_success:
|
|
31
|
+
result = "Success"
|
|
32
|
+
self.logger.info(
|
|
33
|
+
f'JOBLOG - SUCCESS - "{self.job_name}" with GUID {self.log_guid}'
|
|
34
|
+
)
|
|
35
|
+
else:
|
|
36
|
+
result = "Failure"
|
|
37
|
+
self.logger.info(
|
|
38
|
+
f'JOBLOG - FAILED - "{self.job_name}" with GUID {self.log_guid}'
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
param_list = [self.log_guid, message, result]
|
|
42
|
+
|
|
43
|
+
# Update JobLog with result
|
|
44
|
+
self.sql_client.execute_stored_procedure("usp_Update_JobLog", param_list)
|
|
45
|
+
|
|
46
|
+
def update_status(self, message):
|
|
47
|
+
param_list = [self.log_guid, message, None]
|
|
48
|
+
self.sql_client.execute_stored_procedure("usp_Update_JobLog", param_list)
|
|
49
|
+
|
|
50
|
+
def update_joblog(sql_client, method, guid, status, logger=None):
|
|
51
|
+
# If no sql_client supplied quietly ignore
|
|
52
|
+
if sql_client:
|
|
53
|
+
assert method in ["Success", "Failure", "Status"]
|
|
54
|
+
|
|
55
|
+
if method in ["Success", "Failure"]:
|
|
56
|
+
result = method
|
|
57
|
+
param_list = [guid, status, result]
|
|
58
|
+
elif method == "Status":
|
|
59
|
+
param_list = [guid, status]
|
|
60
|
+
|
|
61
|
+
stored_procedure_name = "usp_Update_JobLog"
|
|
62
|
+
sql_client.execute_stored_procedure(stored_procedure_name, param_list)
|
|
63
|
+
|
|
64
|
+
def insert_message(
|
|
65
|
+
self,
|
|
66
|
+
message_name,
|
|
67
|
+
message_type,
|
|
68
|
+
value_string: str = None,
|
|
69
|
+
value_bit: bool = None,
|
|
70
|
+
value_int: int = None,
|
|
71
|
+
value_date=None,
|
|
72
|
+
):
|
|
73
|
+
"""
|
|
74
|
+
INSERT INTO tbl_JobLogAttribute
|
|
75
|
+
(JobLogGUID,AttributeName,AttributeType,AttributeLogType,ValueType,ValueString,ValueBit,ValueInteger,ValueDate)
|
|
76
|
+
|
|
77
|
+
sample usage:
|
|
78
|
+
job_log.insert_message('Job Parameter', 'Input Parameter', 'getActiveMembershipMainContact')
|
|
79
|
+
"""
|
|
80
|
+
param_list = [
|
|
81
|
+
self.log_guid,
|
|
82
|
+
message_name,
|
|
83
|
+
message_type,
|
|
84
|
+
None,
|
|
85
|
+
None,
|
|
86
|
+
value_string,
|
|
87
|
+
value_bit,
|
|
88
|
+
value_int,
|
|
89
|
+
value_date,
|
|
90
|
+
]
|
|
91
|
+
self.logger.debug(f"JOBLOG - insert attribute param_list: {param_list}")
|
|
92
|
+
# Update JobLog with result
|
|
93
|
+
self.sql_client.execute_stored_procedure("usp_LogJobAttribute", param_list)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .api import MarketoClient
|