cit-pydata 0.0.1.dev0__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.
- cit-pydata-0.0.1.dev0/LICENSE +19 -0
- cit-pydata-0.0.1.dev0/PKG-INFO +33 -0
- cit-pydata-0.0.1.dev0/README.md +6 -0
- cit-pydata-0.0.1.dev0/pyproject.toml +36 -0
- cit-pydata-0.0.1.dev0/setup.cfg +4 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/__init__.py +9 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/aws/__init__.py +1 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/aws/api.py +204 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/box/__init__.py +1 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/box/api.py +107 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/joblog/__init__.py +1 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/joblog/api.py +93 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/marketo/__init__.py +1 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/marketo/api.py +587 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/salesforce/__init__.py +1 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/salesforce/api.py +604 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/sfsync/__init__.py +1 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/sfsync/api.py +39 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/sftp/__init__.py +1 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/sftp/api.py +379 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/sql/__init__.py +1 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/sql/api.py +939 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/util/__init__.py +1 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata/util/api.py +418 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata.egg-info/PKG-INFO +33 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata.egg-info/SOURCES.txt +27 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata.egg-info/dependency_links.txt +1 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata.egg-info/requires.txt +71 -0
- cit-pydata-0.0.1.dev0/src/cit_pydata.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2024 The Python Packaging Authority
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: cit-pydata
|
|
3
|
+
Version: 0.0.1.dev0
|
|
4
|
+
Summary: Python clients for commonly used api services including Salesforce, SQL, Marketo, Box, SFSync*, and JobLog*
|
|
5
|
+
Author-email: Dionis Wang <dwang@eab.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/EAB-IT-DEV/cit-pydata
|
|
7
|
+
Project-URL: Issues, https://github.com/EAB-IT-DEV/cit-pydata/issues
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
11
|
+
Requires-Python: >=3.7
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Provides-Extra: aws
|
|
14
|
+
Provides-Extra: box
|
|
15
|
+
Provides-Extra: joblog
|
|
16
|
+
Provides-Extra: salesforce
|
|
17
|
+
Provides-Extra: sftp
|
|
18
|
+
Provides-Extra: sql
|
|
19
|
+
Provides-Extra: sql-pyodbc
|
|
20
|
+
Provides-Extra: sql-pymssql
|
|
21
|
+
Provides-Extra: sql-psycopg2
|
|
22
|
+
Provides-Extra: sql-mssql
|
|
23
|
+
Provides-Extra: sql-postgres
|
|
24
|
+
Provides-Extra: local
|
|
25
|
+
Provides-Extra: all
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
|
|
28
|
+
# cit-pydata
|
|
29
|
+
|
|
30
|
+
This repo provides Python clients for commonly used api services including Salesforce, SQL, Marketo, Box, SFSync*, and JobLog*.
|
|
31
|
+
|
|
32
|
+
*SFSync = custom Salesforce <> MSQ SQL integration
|
|
33
|
+
*JobLog = custom centralized logging for data integration jobs
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cit-pydata"
|
|
7
|
+
version = "0.0.1.dev0"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Dionis Wang", email="dwang@eab.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "Python clients for commonly used api services including Salesforce, SQL, Marketo, Box, SFSync*, and JobLog*"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.7"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: POSIX :: Linux",
|
|
18
|
+
]
|
|
19
|
+
[project.urls]
|
|
20
|
+
Homepage = "https://github.com/EAB-IT-DEV/cit-pydata"
|
|
21
|
+
Issues = "https://github.com/EAB-IT-DEV/cit-pydata/issues"
|
|
22
|
+
|
|
23
|
+
[project.optional-dependencies]
|
|
24
|
+
aws = ["aws-lambda-powertools"]
|
|
25
|
+
box = ["boxsdk"]
|
|
26
|
+
joblog = ["marketorestpython"]
|
|
27
|
+
salesforce = ["simple_salesforce","pyforce"]
|
|
28
|
+
sftp = ["paramiko"]
|
|
29
|
+
sql = ["SQLAlchemy==1.4.25", "numpy", "pandas"]
|
|
30
|
+
sql-pyodbc = ["SQLAlchemy==1.4.25", "pyodbc", "numpy", "pandas"]
|
|
31
|
+
sql-pymssql = ["SQLAlchemy==1.4.25", "pymssql", "numpy", "pandas"]
|
|
32
|
+
sql-psycopg2 = ["SQLAlchemy==1.4.25", "psycopg2-binary==2.9.6", "numpy", "pandas"]
|
|
33
|
+
sql-mssql = ["SQLAlchemy==1.4.25", "pymssql", "numpy", "pandas"]
|
|
34
|
+
sql-postgres = ["SQLAlchemy==1.4.25", "psycopg2-binary==2.9.6", "numpy", "pandas"]
|
|
35
|
+
local = ["python-dotenv", "boto3"]
|
|
36
|
+
all = ["aws-lambda-powertools","pandas","boxsdk","marketorestpython","simple_salesforce","pyforce","paramiko","SQLAlchemy==1.4.25", "pyodbc","pymssql", "numpy", "pandas","psycopg2-binary==2.9.6","python-dotenv", "boto3"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .api import S3Client, SSMClient
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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
|