aimodelshare 0.3.7__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.
- aimodelshare/README.md +26 -0
- aimodelshare/__init__.py +100 -0
- aimodelshare/aimsonnx.py +2381 -0
- aimodelshare/api.py +836 -0
- aimodelshare/auth.py +163 -0
- aimodelshare/aws.py +511 -0
- aimodelshare/aws_client.py +173 -0
- aimodelshare/base_image.py +154 -0
- aimodelshare/bucketpolicy.py +106 -0
- aimodelshare/color_mappings/color_mapping_keras.csv +121 -0
- aimodelshare/color_mappings/color_mapping_pytorch.csv +117 -0
- aimodelshare/containerisation.py +244 -0
- aimodelshare/containerization.py +712 -0
- aimodelshare/containerization_templates/Dockerfile.txt +8 -0
- aimodelshare/containerization_templates/Dockerfile_PySpark.txt +23 -0
- aimodelshare/containerization_templates/buildspec.txt +14 -0
- aimodelshare/containerization_templates/lambda_function.txt +40 -0
- aimodelshare/custom_approach/__init__.py +1 -0
- aimodelshare/custom_approach/lambda_function.py +17 -0
- aimodelshare/custom_eval_metrics.py +103 -0
- aimodelshare/data_sharing/__init__.py +0 -0
- aimodelshare/data_sharing/data_sharing_templates/Dockerfile.txt +3 -0
- aimodelshare/data_sharing/data_sharing_templates/__init__.py +1 -0
- aimodelshare/data_sharing/data_sharing_templates/buildspec.txt +15 -0
- aimodelshare/data_sharing/data_sharing_templates/codebuild_policies.txt +129 -0
- aimodelshare/data_sharing/data_sharing_templates/codebuild_trust_relationship.txt +12 -0
- aimodelshare/data_sharing/download_data.py +620 -0
- aimodelshare/data_sharing/share_data.py +373 -0
- aimodelshare/data_sharing/utils.py +8 -0
- aimodelshare/deploy_custom_lambda.py +246 -0
- aimodelshare/documentation/Makefile +20 -0
- aimodelshare/documentation/karma_sphinx_theme/__init__.py +28 -0
- aimodelshare/documentation/karma_sphinx_theme/_version.py +2 -0
- aimodelshare/documentation/karma_sphinx_theme/breadcrumbs.html +70 -0
- aimodelshare/documentation/karma_sphinx_theme/layout.html +172 -0
- aimodelshare/documentation/karma_sphinx_theme/search.html +50 -0
- aimodelshare/documentation/karma_sphinx_theme/searchbox.html +14 -0
- aimodelshare/documentation/karma_sphinx_theme/static/css/custom.css +2 -0
- aimodelshare/documentation/karma_sphinx_theme/static/css/custom.css.map +1 -0
- aimodelshare/documentation/karma_sphinx_theme/static/css/theme.css +2751 -0
- aimodelshare/documentation/karma_sphinx_theme/static/css/theme.css.map +1 -0
- aimodelshare/documentation/karma_sphinx_theme/static/css/theme.min.css +2 -0
- aimodelshare/documentation/karma_sphinx_theme/static/css/theme.min.css.map +1 -0
- aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.eot +0 -0
- aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.svg +32 -0
- aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.ttf +0 -0
- aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.woff +0 -0
- aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.woff2 +0 -0
- aimodelshare/documentation/karma_sphinx_theme/static/js/theme.js +68 -0
- aimodelshare/documentation/karma_sphinx_theme/theme.conf +9 -0
- aimodelshare/documentation/make.bat +35 -0
- aimodelshare/documentation/requirements.txt +2 -0
- aimodelshare/documentation/source/about.rst +18 -0
- aimodelshare/documentation/source/advanced_features.rst +137 -0
- aimodelshare/documentation/source/competition.rst +218 -0
- aimodelshare/documentation/source/conf.py +58 -0
- aimodelshare/documentation/source/create_credentials.rst +86 -0
- aimodelshare/documentation/source/example_notebooks.rst +132 -0
- aimodelshare/documentation/source/functions.rst +151 -0
- aimodelshare/documentation/source/gettingstarted.rst +390 -0
- aimodelshare/documentation/source/images/creds1.png +0 -0
- aimodelshare/documentation/source/images/creds2.png +0 -0
- aimodelshare/documentation/source/images/creds3.png +0 -0
- aimodelshare/documentation/source/images/creds4.png +0 -0
- aimodelshare/documentation/source/images/creds5.png +0 -0
- aimodelshare/documentation/source/images/creds_file_example.png +0 -0
- aimodelshare/documentation/source/images/predict_tab.png +0 -0
- aimodelshare/documentation/source/index.rst +110 -0
- aimodelshare/documentation/source/modelplayground.rst +132 -0
- aimodelshare/exceptions.py +11 -0
- aimodelshare/generatemodelapi.py +1270 -0
- aimodelshare/iam/codebuild_policy.txt +129 -0
- aimodelshare/iam/codebuild_trust_relationship.txt +12 -0
- aimodelshare/iam/lambda_policy.txt +15 -0
- aimodelshare/iam/lambda_trust_relationship.txt +12 -0
- aimodelshare/json_templates/__init__.py +1 -0
- aimodelshare/json_templates/api_json.txt +155 -0
- aimodelshare/json_templates/auth/policy.txt +1 -0
- aimodelshare/json_templates/auth/role.txt +1 -0
- aimodelshare/json_templates/eval/policy.txt +1 -0
- aimodelshare/json_templates/eval/role.txt +1 -0
- aimodelshare/json_templates/function/policy.txt +1 -0
- aimodelshare/json_templates/function/role.txt +1 -0
- aimodelshare/json_templates/integration_response.txt +5 -0
- aimodelshare/json_templates/lambda_policy_1.txt +15 -0
- aimodelshare/json_templates/lambda_policy_2.txt +8 -0
- aimodelshare/json_templates/lambda_role_1.txt +12 -0
- aimodelshare/json_templates/lambda_role_2.txt +16 -0
- aimodelshare/leaderboard.py +174 -0
- aimodelshare/main/1.txt +132 -0
- aimodelshare/main/1B.txt +112 -0
- aimodelshare/main/2.txt +153 -0
- aimodelshare/main/3.txt +134 -0
- aimodelshare/main/4.txt +128 -0
- aimodelshare/main/5.txt +109 -0
- aimodelshare/main/6.txt +105 -0
- aimodelshare/main/7.txt +144 -0
- aimodelshare/main/8.txt +142 -0
- aimodelshare/main/__init__.py +1 -0
- aimodelshare/main/authorization.txt +275 -0
- aimodelshare/main/eval_classification.txt +79 -0
- aimodelshare/main/eval_lambda.txt +1709 -0
- aimodelshare/main/eval_regression.txt +80 -0
- aimodelshare/main/lambda_function.txt +8 -0
- aimodelshare/main/nst.txt +149 -0
- aimodelshare/model.py +1543 -0
- aimodelshare/modeluser.py +215 -0
- aimodelshare/moral_compass/README.md +408 -0
- aimodelshare/moral_compass/__init__.py +65 -0
- aimodelshare/moral_compass/_version.py +3 -0
- aimodelshare/moral_compass/api_client.py +601 -0
- aimodelshare/moral_compass/apps/__init__.py +69 -0
- aimodelshare/moral_compass/apps/ai_consequences.py +540 -0
- aimodelshare/moral_compass/apps/bias_detective.py +714 -0
- aimodelshare/moral_compass/apps/ethical_revelation.py +898 -0
- aimodelshare/moral_compass/apps/fairness_fixer.py +889 -0
- aimodelshare/moral_compass/apps/judge.py +888 -0
- aimodelshare/moral_compass/apps/justice_equity_upgrade.py +853 -0
- aimodelshare/moral_compass/apps/mc_integration_helpers.py +820 -0
- aimodelshare/moral_compass/apps/model_building_game.py +1104 -0
- aimodelshare/moral_compass/apps/model_building_game_beginner.py +687 -0
- aimodelshare/moral_compass/apps/moral_compass_challenge.py +858 -0
- aimodelshare/moral_compass/apps/session_auth.py +254 -0
- aimodelshare/moral_compass/apps/shared_activity_styles.css +349 -0
- aimodelshare/moral_compass/apps/tutorial.py +481 -0
- aimodelshare/moral_compass/apps/what_is_ai.py +853 -0
- aimodelshare/moral_compass/challenge.py +365 -0
- aimodelshare/moral_compass/config.py +187 -0
- aimodelshare/placeholders/model.onnx +0 -0
- aimodelshare/placeholders/preprocessor.zip +0 -0
- aimodelshare/playground.py +1968 -0
- aimodelshare/postprocessormodules.py +157 -0
- aimodelshare/preprocessormodules.py +373 -0
- aimodelshare/pyspark/1.txt +195 -0
- aimodelshare/pyspark/1B.txt +181 -0
- aimodelshare/pyspark/2.txt +220 -0
- aimodelshare/pyspark/3.txt +204 -0
- aimodelshare/pyspark/4.txt +187 -0
- aimodelshare/pyspark/5.txt +178 -0
- aimodelshare/pyspark/6.txt +174 -0
- aimodelshare/pyspark/7.txt +211 -0
- aimodelshare/pyspark/8.txt +206 -0
- aimodelshare/pyspark/__init__.py +1 -0
- aimodelshare/pyspark/authorization.txt +258 -0
- aimodelshare/pyspark/eval_classification.txt +79 -0
- aimodelshare/pyspark/eval_lambda.txt +1441 -0
- aimodelshare/pyspark/eval_regression.txt +80 -0
- aimodelshare/pyspark/lambda_function.txt +8 -0
- aimodelshare/pyspark/nst.txt +213 -0
- aimodelshare/python/my_preprocessor.py +58 -0
- aimodelshare/readme.md +26 -0
- aimodelshare/reproducibility.py +181 -0
- aimodelshare/sam/Dockerfile.txt +8 -0
- aimodelshare/sam/Dockerfile_PySpark.txt +24 -0
- aimodelshare/sam/__init__.py +1 -0
- aimodelshare/sam/buildspec.txt +11 -0
- aimodelshare/sam/codebuild_policies.txt +129 -0
- aimodelshare/sam/codebuild_trust_relationship.txt +12 -0
- aimodelshare/sam/codepipeline_policies.txt +173 -0
- aimodelshare/sam/codepipeline_trust_relationship.txt +12 -0
- aimodelshare/sam/spark-class.txt +2 -0
- aimodelshare/sam/template.txt +54 -0
- aimodelshare/tools.py +103 -0
- aimodelshare/utils/__init__.py +78 -0
- aimodelshare/utils/optional_deps.py +38 -0
- aimodelshare/utils.py +57 -0
- aimodelshare-0.3.7.dist-info/METADATA +298 -0
- aimodelshare-0.3.7.dist-info/RECORD +171 -0
- aimodelshare-0.3.7.dist-info/WHEEL +5 -0
- aimodelshare-0.3.7.dist-info/licenses/LICENSE +5 -0
- aimodelshare-0.3.7.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1270 @@
|
|
|
1
|
+
import email
|
|
2
|
+
import boto3
|
|
3
|
+
import botocore
|
|
4
|
+
import os
|
|
5
|
+
import jwt
|
|
6
|
+
from numpy.core.fromnumeric import var
|
|
7
|
+
import requests
|
|
8
|
+
import uuid
|
|
9
|
+
import json
|
|
10
|
+
import math
|
|
11
|
+
import time
|
|
12
|
+
import datetime
|
|
13
|
+
import onnx
|
|
14
|
+
import tempfile
|
|
15
|
+
import types
|
|
16
|
+
import sys
|
|
17
|
+
import base64
|
|
18
|
+
import mimetypes
|
|
19
|
+
import numpy as np
|
|
20
|
+
import pandas as pd
|
|
21
|
+
from aimodelshare.tools import extract_varnames_fromtrainingdata, _get_extension_from_filepath
|
|
22
|
+
from aimodelshare.aws import get_s3_iam_client, run_function_on_lambda, get_token, get_aws_token, get_aws_client
|
|
23
|
+
from aimodelshare.bucketpolicy import _custom_upload_policy
|
|
24
|
+
from aimodelshare.exceptions import AuthorizationError, AWSAccessError, AWSUploadError
|
|
25
|
+
from aimodelshare.api import get_api_json
|
|
26
|
+
from aimodelshare.modeluser import decode_token_unverified
|
|
27
|
+
from aimodelshare.preprocessormodules import upload_preprocessor
|
|
28
|
+
from aimodelshare.model import _get_predictionmodel_key, _extract_model_metadata
|
|
29
|
+
from aimodelshare.data_sharing.share_data import share_data_codebuild
|
|
30
|
+
from aimodelshare.aimsonnx import _get_metadata
|
|
31
|
+
from aimodelshare.utils import HiddenPrints
|
|
32
|
+
|
|
33
|
+
def take_user_info_and_generate_api(model_filepath, model_type, categorical,labels, preprocessor_filepath,
|
|
34
|
+
custom_libraries, requirements, exampledata_json_filepath, repo_name,
|
|
35
|
+
image_tag, reproducibility_env_filepath, memory, timeout, pyspark_support=False):
|
|
36
|
+
"""
|
|
37
|
+
Generates an api using model parameters and user credentials, from the user
|
|
38
|
+
|
|
39
|
+
-----------
|
|
40
|
+
Parameters
|
|
41
|
+
|
|
42
|
+
model_filepath : string ends with '.onnx'
|
|
43
|
+
value - Absolute path to model file [REQUIRED] to be set by the user
|
|
44
|
+
.onnx is the only accepted model file extension
|
|
45
|
+
"example_model.onnx" filename for file in directory.
|
|
46
|
+
"/User/xyz/model/example_model.onnx" absolute path to model file from local directory
|
|
47
|
+
preprocessor_filepath: string
|
|
48
|
+
value - absolute path to preprocessor file
|
|
49
|
+
[REQUIRED] to be set by the user
|
|
50
|
+
"./preprocessor.zip"
|
|
51
|
+
searches for an exported zip preprocessor file in the current directory
|
|
52
|
+
file is generated using export_preprocessor function from the AI Modelshare library
|
|
53
|
+
model_type : string
|
|
54
|
+
values - [ 'text' , 'image' , 'tabular' , 'timeseries' ]
|
|
55
|
+
Type of model data
|
|
56
|
+
categorical: string
|
|
57
|
+
"TRUE" if model is Classification, categorical type
|
|
58
|
+
"FALSE" if model is continuous, Regression type
|
|
59
|
+
labels: list
|
|
60
|
+
value - labels for training data
|
|
61
|
+
can be extracted from columns of y train or can be provided by the user
|
|
62
|
+
custom_libraries: string
|
|
63
|
+
"TRUE" if user wants to load custom Python libraries to their prediction runtime
|
|
64
|
+
"FALSE" if user wishes to use AI Model Share base libraries including latest versions of most common ML libs.
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
-----------
|
|
68
|
+
Returns
|
|
69
|
+
finalresult : list
|
|
70
|
+
length 5
|
|
71
|
+
[ api url, api status code,
|
|
72
|
+
start time for api generation,
|
|
73
|
+
model unique key,
|
|
74
|
+
s3 bucket name
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
"""
|
|
78
|
+
import tempfile
|
|
79
|
+
from zipfile import ZipFile
|
|
80
|
+
import os
|
|
81
|
+
import random
|
|
82
|
+
|
|
83
|
+
# create temporary folder
|
|
84
|
+
temp_dir = tempfile.gettempdir()
|
|
85
|
+
|
|
86
|
+
api_json= get_api_json()
|
|
87
|
+
user_client = boto3.client('apigateway', aws_access_key_id=str(
|
|
88
|
+
os.environ.get("AWS_ACCESS_KEY_ID_AIMS")), aws_secret_access_key=str(os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS")), region_name=str(os.environ.get("AWS_REGION_AIMS")))
|
|
89
|
+
|
|
90
|
+
response2 = user_client.import_rest_api(
|
|
91
|
+
failOnWarnings=True,
|
|
92
|
+
parameters={
|
|
93
|
+
'endpointConfigurationTypes': 'REGIONAL'
|
|
94
|
+
},
|
|
95
|
+
body=api_json
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
api_id = response2['id']
|
|
99
|
+
now = datetime.datetime.now()
|
|
100
|
+
s3, iam, region = get_s3_iam_client(os.environ.get("AWS_ACCESS_KEY_ID_AIMS"), os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS"), os.environ.get("AWS_REGION_AIMS"))
|
|
101
|
+
|
|
102
|
+
def create_bucket(s3_client, bucket_name, region):
|
|
103
|
+
try:
|
|
104
|
+
response=s3_client.head_bucket(Bucket=bucket_name)
|
|
105
|
+
except:
|
|
106
|
+
if(region=="us-east-1"):
|
|
107
|
+
response = s3_client.create_bucket(
|
|
108
|
+
ACL="private",
|
|
109
|
+
Bucket=bucket_name
|
|
110
|
+
)
|
|
111
|
+
else:
|
|
112
|
+
location={'LocationConstraint': region}
|
|
113
|
+
response=s3_client.create_bucket(
|
|
114
|
+
ACL="private",
|
|
115
|
+
Bucket=bucket_name,
|
|
116
|
+
CreateBucketConfiguration=location
|
|
117
|
+
)
|
|
118
|
+
return response
|
|
119
|
+
|
|
120
|
+
create_bucket(s3['client'], os.environ.get("BUCKET_NAME"), region)
|
|
121
|
+
|
|
122
|
+
# model upload
|
|
123
|
+
if isinstance(model_filepath, onnx.ModelProto):
|
|
124
|
+
model = model_filepath
|
|
125
|
+
temp_prep=tempfile.mkdtemp()
|
|
126
|
+
Filepath = temp_prep+"/model.onnx"
|
|
127
|
+
with open(Filepath, "wb") as f:
|
|
128
|
+
f.write(model.SerializeToString())
|
|
129
|
+
else:
|
|
130
|
+
Filepath = model_filepath
|
|
131
|
+
model = onnx.load(model_filepath)
|
|
132
|
+
metadata = _extract_model_metadata(model)
|
|
133
|
+
input_shape = metadata["input_shape"]
|
|
134
|
+
#tab_imports ='./tabular_imports.pkl'
|
|
135
|
+
#img_imports ='./image_imports.pkl'
|
|
136
|
+
file_extension = _get_extension_from_filepath(Filepath)
|
|
137
|
+
unique_model_id = str(api_id)
|
|
138
|
+
file_key, versionfile_key = _get_predictionmodel_key(
|
|
139
|
+
unique_model_id, file_extension)
|
|
140
|
+
try:
|
|
141
|
+
s3["client"].upload_file(exampledata_json_filepath, os.environ.get("BUCKET_NAME"), unique_model_id + "/exampledata.json")
|
|
142
|
+
except:
|
|
143
|
+
pass
|
|
144
|
+
try:
|
|
145
|
+
s3["client"].upload_file(Filepath, os.environ.get("BUCKET_NAME"), file_key)
|
|
146
|
+
s3["client"].upload_file(Filepath, os.environ.get("BUCKET_NAME"), versionfile_key)
|
|
147
|
+
|
|
148
|
+
# preprocessor upload
|
|
149
|
+
#s3["client"].upload_file(tab_imports, os.environ.get("BUCKET_NAME"), 'tabular_imports.pkl')
|
|
150
|
+
#s3["client"].upload_file(img_imports, os.environ.get("BUCKET_NAME"), 'image_imports.pkl')
|
|
151
|
+
# preprocessor upload
|
|
152
|
+
|
|
153
|
+
# ADD model/Preprocessor VERSION
|
|
154
|
+
if isinstance(preprocessor_filepath, types.FunctionType):
|
|
155
|
+
from aimodelshare.preprocessormodules import export_preprocessor
|
|
156
|
+
temp_prep=tempfile.mkdtemp()
|
|
157
|
+
with HiddenPrints():
|
|
158
|
+
export_preprocessor(preprocessor_filepath,temp_prep)
|
|
159
|
+
preprocessor_filepath = temp_prep+"/preprocessor.zip"
|
|
160
|
+
response = upload_preprocessor(
|
|
161
|
+
preprocessor_filepath, s3, os.environ.get("BUCKET_NAME"), unique_model_id, 1)
|
|
162
|
+
preprocessor_file_extension = _get_extension_from_filepath(
|
|
163
|
+
preprocessor_filepath)
|
|
164
|
+
# write runtime JSON
|
|
165
|
+
json_path = os.path.join(temp_dir, "runtime_data.json")
|
|
166
|
+
if(preprocessor_file_extension == '.py'):
|
|
167
|
+
runtime_preprocessor_type = "module"
|
|
168
|
+
elif(preprocessor_file_extension == '.pkl'):
|
|
169
|
+
runtime_preprocessor_type = "pickle object"
|
|
170
|
+
else:
|
|
171
|
+
runtime_preprocessor_type = "others"
|
|
172
|
+
runtime_data = {}
|
|
173
|
+
runtime_data["runtime_model"] = {"name": "runtime_model.onnx"}
|
|
174
|
+
runtime_data["runtime_preprocessor"] = runtime_preprocessor_type
|
|
175
|
+
|
|
176
|
+
#runtime_data = {"runtime_model": {"name": "runtime_model.onnx"},"runtime_preprocessor": runtime_preprocessor_type }
|
|
177
|
+
json_string = json.dumps(runtime_data, sort_keys=False)
|
|
178
|
+
with open(json_path, 'w') as outfile:
|
|
179
|
+
outfile.write(json_string)
|
|
180
|
+
s3["client"].upload_file(
|
|
181
|
+
json_path, os.environ.get("BUCKET_NAME"), unique_model_id + "/runtime_data.json"
|
|
182
|
+
)
|
|
183
|
+
os.remove(json_path)
|
|
184
|
+
|
|
185
|
+
# upload model metadata
|
|
186
|
+
upload_model_metadata(model, s3, os.environ.get("BUCKET_NAME"), unique_model_id)
|
|
187
|
+
|
|
188
|
+
# upload reproducibility env
|
|
189
|
+
if reproducibility_env_filepath:
|
|
190
|
+
upload_reproducibility_env(reproducibility_env_filepath, s3, os.environ.get("BUCKET_NAME"), unique_model_id)
|
|
191
|
+
except Exception as err:
|
|
192
|
+
raise AWSUploadError(
|
|
193
|
+
"There was a problem with model/preprocessor upload. "+str(err))
|
|
194
|
+
|
|
195
|
+
#Delete Legacy exampledata json:
|
|
196
|
+
try:
|
|
197
|
+
os.remove(exampledata_json_filepath)
|
|
198
|
+
except:
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
#headers = {'content-type': 'application/json'}
|
|
202
|
+
|
|
203
|
+
### Progress Update #2/6 {{{
|
|
204
|
+
sys.stdout.write('\r')
|
|
205
|
+
sys.stdout.write("[======== ] Progress: 30% - Building lambda functions and updating permissions...")
|
|
206
|
+
sys.stdout.flush()
|
|
207
|
+
# }}}
|
|
208
|
+
|
|
209
|
+
from aimodelshare.api import create_prediction_api
|
|
210
|
+
apiurl = create_prediction_api(model_filepath, unique_model_id,
|
|
211
|
+
model_type, categorical, labels,api_id,
|
|
212
|
+
custom_libraries, requirements, repo_name,
|
|
213
|
+
image_tag, memory, timeout, pyspark_support=pyspark_support)
|
|
214
|
+
|
|
215
|
+
finalresult = [apiurl["body"], apiurl["statusCode"],
|
|
216
|
+
now, unique_model_id, os.environ.get("BUCKET_NAME"), input_shape]
|
|
217
|
+
return finalresult
|
|
218
|
+
|
|
219
|
+
def upload_reproducibility_env(reproducibility_env_file, s3, bucket, model_id):
|
|
220
|
+
# Check the reproducibility_env {{{
|
|
221
|
+
with open(reproducibility_env_file) as json_file:
|
|
222
|
+
reproducibility_env = json.load(json_file)
|
|
223
|
+
if "global_seed_code" not in reproducibility_env \
|
|
224
|
+
or "local_seed_code" not in reproducibility_env \
|
|
225
|
+
or "gpu_cpu_parallelism_ops" not in reproducibility_env \
|
|
226
|
+
or "session_runtime_info" not in reproducibility_env:
|
|
227
|
+
raise Exception("reproducibility environment is not complete")
|
|
228
|
+
|
|
229
|
+
# Upload the json {{{
|
|
230
|
+
try:
|
|
231
|
+
s3["client"].upload_file(
|
|
232
|
+
reproducibility_env_file, bucket, model_id + "/runtime_reproducibility.json"
|
|
233
|
+
)
|
|
234
|
+
except Exception as err:
|
|
235
|
+
raise err
|
|
236
|
+
# }}}
|
|
237
|
+
|
|
238
|
+
def upload_model_metadata(model, s3, bucket, model_id):
|
|
239
|
+
meta_dict = _get_metadata(model)
|
|
240
|
+
model_metadata = {
|
|
241
|
+
"model_config": meta_dict["model_config"],
|
|
242
|
+
"ml_framework": meta_dict["ml_framework"],
|
|
243
|
+
"model_type": meta_dict["model_type"]
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
temp = tempfile.mkdtemp()
|
|
247
|
+
model_metadata_path = temp + "/" + 'model_metadata.json'
|
|
248
|
+
with open(model_metadata_path, 'w') as outfile:
|
|
249
|
+
json.dump(model_metadata, outfile)
|
|
250
|
+
|
|
251
|
+
# Upload the json {{{
|
|
252
|
+
try:
|
|
253
|
+
s3["client"].upload_file(
|
|
254
|
+
model_metadata_path, bucket, model_id + "/runtime_metadata.json"
|
|
255
|
+
)
|
|
256
|
+
except Exception as err:
|
|
257
|
+
raise err
|
|
258
|
+
|
|
259
|
+
def send_model_data_to_dyndb_and_return_api(api_info, private, categorical, preprocessor_filepath,
|
|
260
|
+
aishare_modelname, aishare_modeldescription, aishare_modelevaluation, model_type,
|
|
261
|
+
aishare_tags, aishare_apicalls, exampledata_json_filepath,
|
|
262
|
+
variablename_and_type_data="default", email_list=[]):
|
|
263
|
+
"""
|
|
264
|
+
Updates dynamodb with model data taken as input from user along with already generated api info
|
|
265
|
+
-----------
|
|
266
|
+
Parameters
|
|
267
|
+
api_info : list
|
|
268
|
+
length 5
|
|
269
|
+
api and s3 bucket information
|
|
270
|
+
returned from take_user_info_and_generate_api function
|
|
271
|
+
private : string, default="FALSE"
|
|
272
|
+
TRUE if model and its corresponding data is not public
|
|
273
|
+
FALSE if model and its corresponding data is public
|
|
274
|
+
categorical: string, default="TRUE"
|
|
275
|
+
TRUE if model is of Classification type with categorical variables
|
|
276
|
+
FALSE if model is Regression type with continuous variables
|
|
277
|
+
preprocessor_filepath: string
|
|
278
|
+
value - Absolute path to preprocessor file [REQUIRED] to be set by the user
|
|
279
|
+
"./preprocessor.zip"
|
|
280
|
+
searches for an exported zip preprocessor file in the current directory
|
|
281
|
+
variablename_and_type_data : list, default='default'
|
|
282
|
+
value- extracted from example_data
|
|
283
|
+
[variable types,variable columns]
|
|
284
|
+
'default' when training data info is not available to extract columns
|
|
285
|
+
email_list: list of strings
|
|
286
|
+
values - list including all emails of users who have access the playground.
|
|
287
|
+
list should contain same emails used to sign up for modelshare.ai account.
|
|
288
|
+
[OPTIONAL] to be set by the playground owner
|
|
289
|
+
-----------
|
|
290
|
+
Results
|
|
291
|
+
print (api_info) : statements with the generated live prediction API information for the user
|
|
292
|
+
|
|
293
|
+
"""
|
|
294
|
+
|
|
295
|
+
# unpack user credentials
|
|
296
|
+
unique_model_id = api_info[3]
|
|
297
|
+
bucket_name = api_info[4]
|
|
298
|
+
input_shape = api_info[5]
|
|
299
|
+
if variablename_and_type_data == "default":
|
|
300
|
+
variablename_and_type_data = ["", ""]
|
|
301
|
+
|
|
302
|
+
# needs to use double backslashes and have full filepath
|
|
303
|
+
|
|
304
|
+
if isinstance(preprocessor_filepath, types.FunctionType):
|
|
305
|
+
from aimodelshare.preprocessormodules import export_preprocessor
|
|
306
|
+
temp_prep=tempfile.mkdtemp()
|
|
307
|
+
with HiddenPrints():
|
|
308
|
+
export_preprocessor(preprocessor_filepath,temp_prep)
|
|
309
|
+
preprocessor_filepath = temp_prep+"/preprocessor.zip"
|
|
310
|
+
preprocessor_file_extension = _get_extension_from_filepath(
|
|
311
|
+
preprocessor_filepath)
|
|
312
|
+
if exampledata_json_filepath!="":
|
|
313
|
+
exampledata_addtodatabase={"exampledata":"TRUE"}
|
|
314
|
+
else:
|
|
315
|
+
exampledata_addtodatabase={"exampledata":"FALSE"}
|
|
316
|
+
bodydata = {
|
|
317
|
+
"id": int(math.log(1/((time.time()*1000000)))*100000000000000),
|
|
318
|
+
"unique_model_id": unique_model_id,
|
|
319
|
+
"apideveloper": os.environ.get("username"), # change this to first and last name
|
|
320
|
+
"apimodeldescription": aishare_modeldescription,
|
|
321
|
+
"apimodelevaluation": aishare_modelevaluation,
|
|
322
|
+
"apimodeltype": model_type,
|
|
323
|
+
# getting rid of extra quotes that screw up dynamodb string search on apiurls
|
|
324
|
+
"apiurl": api_info[0].strip('\"'),
|
|
325
|
+
"bucket_name": bucket_name,
|
|
326
|
+
"version": 1,
|
|
327
|
+
"modelname": aishare_modelname,
|
|
328
|
+
"tags": aishare_tags,
|
|
329
|
+
"Private": private,
|
|
330
|
+
"Categorical": categorical,
|
|
331
|
+
"delete": "FALSE",
|
|
332
|
+
"input_feature_dtypes": variablename_and_type_data[0],
|
|
333
|
+
"input_feature_names": variablename_and_type_data[1],
|
|
334
|
+
"preprocessor": preprocessor_filepath,
|
|
335
|
+
"preprocessor_fileextension": preprocessor_file_extension,
|
|
336
|
+
"input_shape": input_shape,
|
|
337
|
+
"email_list": email_list,
|
|
338
|
+
"useremails": ','.join(email_list),
|
|
339
|
+
}
|
|
340
|
+
bodydata.update(exampledata_addtodatabase)
|
|
341
|
+
# Get the response
|
|
342
|
+
headers_with_authentication = {'Content-Type': 'application/json', 'authorizationToken': os.environ.get("JWT_AUTHORIZATION_TOKEN"), 'Access-Control-Allow-Headers':
|
|
343
|
+
'Content-Type,X-Amz-Date,authorizationToken,Access-Control-Allow-Origin,X-Api-Key,X-Amz-Security-Token,Authorization', 'Access-Control-Allow-Origin': '*'}
|
|
344
|
+
# modeltoapi lambda function invoked through below url to return new prediction api in response
|
|
345
|
+
response = requests.post("https://bhrdesksak.execute-api.us-east-1.amazonaws.com/dev/modeldata",
|
|
346
|
+
json=bodydata, headers=headers_with_authentication)
|
|
347
|
+
response_string = response.text
|
|
348
|
+
response_string = response_string[1:-1]
|
|
349
|
+
import json
|
|
350
|
+
response_stringfinal = json.loads(response_string).get("id","no id")
|
|
351
|
+
# Build output {{{
|
|
352
|
+
final_message = ("\nYou can now use your Model Playground.\n\n"
|
|
353
|
+
"Follow this link to explore your Model Playground's functionality\n"
|
|
354
|
+
"You can make predictions with the Dashboard and access example code from the Programmatic tab.\n")
|
|
355
|
+
web_dashboard_url = ("https://www.modelshare.ai/detail/"+ response_stringfinal)
|
|
356
|
+
|
|
357
|
+
start = api_info[2]
|
|
358
|
+
end = datetime.datetime.now()
|
|
359
|
+
difference = (end - start).total_seconds()
|
|
360
|
+
finalresult2 = "Success! Your Model Playground was created in " + \
|
|
361
|
+
str(int(difference)) + " seconds. \n" + " Playground Url: " + api_info[0]
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
# }}}
|
|
365
|
+
|
|
366
|
+
### Progress Update #6/6 {{{
|
|
367
|
+
sys.stdout.write('\r')
|
|
368
|
+
sys.stdout.write("[=====================================] Progress: 100% - Complete! ")
|
|
369
|
+
sys.stdout.flush()
|
|
370
|
+
# }}}
|
|
371
|
+
|
|
372
|
+
return print("\n\n" + finalresult2 + "\n" + final_message + web_dashboard_url)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def model_to_api(model_filepath, model_type, private, categorical, y_train, preprocessor_filepath,
|
|
376
|
+
custom_libraries="FALSE", example_data=None, image="",
|
|
377
|
+
base_image_api_endpoint="https://vupwujn586.execute-api.us-east-1.amazonaws.com/dev/copybasetouseracct",
|
|
378
|
+
update=False, reproducibility_env_filepath=None, memory=None, timeout=None, email_list=[],pyspark_support=False,
|
|
379
|
+
input_dict=None, print_output=True, playground_id=False):
|
|
380
|
+
"""
|
|
381
|
+
Launches a live prediction REST API for deploying ML models using model parameters and user credentials, provided by the user
|
|
382
|
+
Inputs : 8
|
|
383
|
+
Output : model launched to an API
|
|
384
|
+
detaled API info printed out
|
|
385
|
+
|
|
386
|
+
-----------
|
|
387
|
+
Parameters
|
|
388
|
+
|
|
389
|
+
model_filepath : string ends with '.onnx'
|
|
390
|
+
value - Absolute path to model file
|
|
391
|
+
[REQUIRED] to be set by the user
|
|
392
|
+
.onnx is the only accepted model file extension
|
|
393
|
+
"example_model.onnx" filename for file in directory.
|
|
394
|
+
"/User/xyz/model/example_model.onnx" absolute path to model file from local directory
|
|
395
|
+
preprocessor_filepath: string
|
|
396
|
+
value - absolute path to preprocessor file
|
|
397
|
+
[REQUIRED] to be set by the user
|
|
398
|
+
"./preprocessor.zip"
|
|
399
|
+
searches for an exported zip preprocessor file in the current directory
|
|
400
|
+
file is generated using export_preprocessor function from the AI Modelshare library
|
|
401
|
+
model_type : string
|
|
402
|
+
values - [ 'Text' , 'Image' , 'Tabular' , 'Timeseries' ]
|
|
403
|
+
type of model data
|
|
404
|
+
categorical: bool, default=True
|
|
405
|
+
True [DEFAULT] if model is of Classification type with categorical target variables
|
|
406
|
+
False if model is of Regression type with continuous target variables
|
|
407
|
+
y_train : training labels of size of dataset
|
|
408
|
+
value - y values for model
|
|
409
|
+
[REQUIRED] for classification type models
|
|
410
|
+
expects a one hot encoded y train data format
|
|
411
|
+
private : bool, default = False
|
|
412
|
+
True if model and its corresponding data is not public
|
|
413
|
+
False [DEFAULT] if model and its corresponding data is public
|
|
414
|
+
custom_libraries: string
|
|
415
|
+
"TRUE" if user wants to load custom Python libraries to their prediction runtime
|
|
416
|
+
"FALSE" if user wishes to use AI Model Share base libraries including latest versions of most common ML libs.
|
|
417
|
+
example_data: pandas DataFrame (for tabular & text data) OR filepath as string (image, audio, video data)
|
|
418
|
+
tabular data - pandas DataFrame in same structure expected by preprocessor function
|
|
419
|
+
other data types - absolute path to folder containing example data
|
|
420
|
+
(first five files with relevent file extensions will be accepted)
|
|
421
|
+
[REQUIRED] for tabular data
|
|
422
|
+
reproducibility_env_filepath: string
|
|
423
|
+
value - absolute path to environment environment json file
|
|
424
|
+
[OPTIONAL] to be set by the user
|
|
425
|
+
"./reproducibility.json"
|
|
426
|
+
file is generated using export_reproducibility_env function from the AI Modelshare library
|
|
427
|
+
email_list: list of strings
|
|
428
|
+
values - list including all emails of users who have access the playground.
|
|
429
|
+
list should contain same emails used to sign up for modelshare.ai account.
|
|
430
|
+
[OPTIONAL] to be set by the playground owner
|
|
431
|
+
-----------
|
|
432
|
+
Returns
|
|
433
|
+
print_api_info : prints statements with generated live prediction API details
|
|
434
|
+
also prints steps to update the model submissions by the user/team
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
"""
|
|
438
|
+
# Used 2 python functions :
|
|
439
|
+
# 1. take_user_info_and_generate_api : to upload model/preprocessor and generate an api for model submitted by user
|
|
440
|
+
# 2. send_model_data_to_dyndb_and_return_api : to add new record to database with user data, model and api related information
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
# Get user inputs, pass to other functions {{{
|
|
444
|
+
user_session = boto3.session.Session(aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID_AIMS"),
|
|
445
|
+
aws_secret_access_key = os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS"),
|
|
446
|
+
region_name=os.environ.get("AWS_REGION_AIMS"))
|
|
447
|
+
|
|
448
|
+
if all([isinstance(email_list, list)]):
|
|
449
|
+
idtoken = get_aws_token()
|
|
450
|
+
decoded = decode_token_unverified(idtoken)
|
|
451
|
+
|
|
452
|
+
email=None
|
|
453
|
+
email = decoded['email']
|
|
454
|
+
# Owner has to be the first on the list
|
|
455
|
+
email_list.insert(0, email)
|
|
456
|
+
if any([private==False,private==None]):
|
|
457
|
+
email_list=["publicaimsplayground"]
|
|
458
|
+
else:
|
|
459
|
+
pass
|
|
460
|
+
else:
|
|
461
|
+
return print("email_list argument empty or incorrectly formatted. Please provide a list of emails for authorized playground users formatted as strings.")
|
|
462
|
+
|
|
463
|
+
if(image!=""):
|
|
464
|
+
repo_name, image_tag = image.split(':')
|
|
465
|
+
elif model_type=="tabular":
|
|
466
|
+
repo_name, image_tag = "aimodelshare_base_image", "tabular"
|
|
467
|
+
elif model_type=="text":
|
|
468
|
+
repo_name, image_tag = "aimodelshare_base_image", "texttest"
|
|
469
|
+
elif model_type=="image":
|
|
470
|
+
repo_name, image_tag = "aimodelshare_base_image", "v3"
|
|
471
|
+
elif model_type=="video":
|
|
472
|
+
repo_name, image_tag = "aimodelshare_base_image", "v3"
|
|
473
|
+
else:
|
|
474
|
+
repo_name, image_tag = "aimodelshare_base_image", "v3"
|
|
475
|
+
|
|
476
|
+
# Pyspark mode
|
|
477
|
+
if pyspark_support:
|
|
478
|
+
repo_name, image_tag = "aimodelshare_base_image", "pyspark"
|
|
479
|
+
|
|
480
|
+
from aimodelshare.containerization import clone_base_image
|
|
481
|
+
response = clone_base_image(user_session, repo_name, image_tag, "517169013426", base_image_api_endpoint, update)
|
|
482
|
+
if(response["Status"]==0):
|
|
483
|
+
print(response["Success"])
|
|
484
|
+
return
|
|
485
|
+
|
|
486
|
+
if input_dict == None:
|
|
487
|
+
print("We need some information about your model before we can build your REST API and interactive Model Playground.")
|
|
488
|
+
print(" ")
|
|
489
|
+
|
|
490
|
+
requirements = ""
|
|
491
|
+
if(any([custom_libraries=='TRUE',custom_libraries=='true'])):
|
|
492
|
+
requirements = input("Enter all required Python libraries you need at prediction runtime (separated with commas):")
|
|
493
|
+
#_confirm_libraries_exist(requirements)
|
|
494
|
+
|
|
495
|
+
aishare_modelname = input("Model Name (for AI Model Share Website):")
|
|
496
|
+
aishare_modeldescription = input("Model Description (Explain what your model does and \n why end-users would find your model useful):")
|
|
497
|
+
|
|
498
|
+
aishare_tags = input(
|
|
499
|
+
"Model Key Words (Search categories that describe your model, separated with commas):")
|
|
500
|
+
print(" ")
|
|
501
|
+
# }}}
|
|
502
|
+
else:
|
|
503
|
+
requirements = input_dict.get("requirements","")
|
|
504
|
+
aishare_modelname = input_dict.get("model_name","")
|
|
505
|
+
aishare_modeldescription = input_dict.get("model_description","")
|
|
506
|
+
aishare_tags = input_dict.get("tags","")
|
|
507
|
+
|
|
508
|
+
aishare_modelevaluation = "unverified" # verified metrics added to playground once 1. a model is submitted to a competition leaderboard and 2. playground owner updates runtime
|
|
509
|
+
#...model with update_runtime_model()
|
|
510
|
+
aishare_apicalls = 0
|
|
511
|
+
|
|
512
|
+
# Force user to provide example data for tabular models {{{
|
|
513
|
+
if any([model_type.lower() == "tabular", model_type.lower() == "timeseries"]):
|
|
514
|
+
if not isinstance(example_data, pd.DataFrame):
|
|
515
|
+
return print("Error: Example data is required for tabular models. \n Please provide a pandas DataFrame with a sample of your X data (in the format expected by your preprocessor) and try again.")
|
|
516
|
+
else:
|
|
517
|
+
pass
|
|
518
|
+
#}}}
|
|
519
|
+
|
|
520
|
+
print("Creating your prediction API. (This process may take several minutes.)\n")
|
|
521
|
+
variablename_and_type_data = None
|
|
522
|
+
private = str(private).upper()
|
|
523
|
+
categorical = str(categorical).upper()
|
|
524
|
+
if model_type == "tabular" or "keras_tabular" or 'Tabular':
|
|
525
|
+
variablename_and_type_data = extract_varnames_fromtrainingdata(
|
|
526
|
+
example_data)
|
|
527
|
+
if categorical == "TRUE":
|
|
528
|
+
try:
|
|
529
|
+
labels = y_train.columns.tolist()
|
|
530
|
+
except:
|
|
531
|
+
#labels = list(set(y_train.to_frame()['tags'].tolist()))
|
|
532
|
+
labels = list(set(y_train))
|
|
533
|
+
else:
|
|
534
|
+
labels = "no data"
|
|
535
|
+
temp_dir = tempfile.gettempdir()
|
|
536
|
+
# Create Example Data JSON
|
|
537
|
+
exampledata_json_filepath = ""
|
|
538
|
+
if example_data is not None:
|
|
539
|
+
_create_exampledata_json(model_type, example_data)
|
|
540
|
+
exampledata_json_filepath = temp_dir+ "/exampledata.json"
|
|
541
|
+
|
|
542
|
+
### Progress Update #1/6 {{{
|
|
543
|
+
sys.stdout.write("[=== ] Progress: 5% - Accessing Amazon Web Services, uploading resources...")
|
|
544
|
+
sys.stdout.flush()
|
|
545
|
+
# }}}
|
|
546
|
+
|
|
547
|
+
api_info = take_user_info_and_generate_api(
|
|
548
|
+
model_filepath, model_type, categorical, labels,
|
|
549
|
+
preprocessor_filepath, custom_libraries, requirements,
|
|
550
|
+
exampledata_json_filepath, repo_name, image_tag,
|
|
551
|
+
reproducibility_env_filepath, memory, timeout, pyspark_support=pyspark_support)
|
|
552
|
+
|
|
553
|
+
if input_dict and playground_id:
|
|
554
|
+
aishare_modelname = aishare_modelname+" "+str(api_info[3])
|
|
555
|
+
|
|
556
|
+
### Progress Update #5/6 {{{
|
|
557
|
+
sys.stdout.write('\r')
|
|
558
|
+
sys.stdout.write("[================================= ] Progress: 90% - Finishing web dashboard... ")
|
|
559
|
+
sys.stdout.flush()
|
|
560
|
+
# }}}
|
|
561
|
+
|
|
562
|
+
print_api_info = send_model_data_to_dyndb_and_return_api(
|
|
563
|
+
api_info, private, categorical,preprocessor_filepath, aishare_modelname,
|
|
564
|
+
aishare_modeldescription, aishare_modelevaluation, model_type,
|
|
565
|
+
aishare_tags, aishare_apicalls, exampledata_json_filepath,
|
|
566
|
+
variablename_and_type_data, email_list)
|
|
567
|
+
|
|
568
|
+
return api_info[0]
|
|
569
|
+
|
|
570
|
+
def create_competition(apiurl, data_directory, y_test, eval_metric_filepath=None, email_list=[], public=False, public_private_split=0.5,
|
|
571
|
+
input_dict=None, print_output=True):
|
|
572
|
+
"""
|
|
573
|
+
Creates a model competition for a deployed prediction REST API
|
|
574
|
+
Inputs : 4
|
|
575
|
+
Output : Create ML model competition and allow authorized users to submit models to resulting leaderboard/competition
|
|
576
|
+
|
|
577
|
+
---------
|
|
578
|
+
Parameters
|
|
579
|
+
|
|
580
|
+
apiurl: string
|
|
581
|
+
URL of deployed prediction API
|
|
582
|
+
|
|
583
|
+
y_test : list of y values for test data used to generate metrics from predicted values from X test data submitted via the submit_model() function
|
|
584
|
+
[REQUIRED] to generate eval metrics in competition leaderboard
|
|
585
|
+
|
|
586
|
+
data_directory : folder storing training data and test data (excluding Y test data)
|
|
587
|
+
email_list: [REQUIRED] list of comma separated emails for users who are allowed to submit models to competition. Emails should be strings in a list.
|
|
588
|
+
|
|
589
|
+
---------
|
|
590
|
+
Returns
|
|
591
|
+
finalmessage : Information such as how to submit models to competition
|
|
592
|
+
|
|
593
|
+
"""
|
|
594
|
+
if all([isinstance(email_list, list)]):
|
|
595
|
+
if any([len(email_list)>0, public=="True",public=="TRUE",public==True]):
|
|
596
|
+
idtoken=get_aws_token()
|
|
597
|
+
decoded = decode_token_unverified(idtoken)
|
|
598
|
+
|
|
599
|
+
email=decoded['email']
|
|
600
|
+
email_list.append(email)
|
|
601
|
+
else:
|
|
602
|
+
return print("email_list argument empty or incorrectly formatted. Please provide a list of emails for authorized competition participants formatted as strings.")
|
|
603
|
+
|
|
604
|
+
# create temporary folder
|
|
605
|
+
temp_dir = tempfile.gettempdir()
|
|
606
|
+
|
|
607
|
+
s3, iam, region = get_s3_iam_client(os.environ.get("AWS_ACCESS_KEY_ID_AIMS"), os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS"), os.environ.get("AWS_REGION_AIMS"))
|
|
608
|
+
|
|
609
|
+
# Get bucket and model_id subfolder for user based on apiurl {{{
|
|
610
|
+
response, error = run_function_on_lambda(
|
|
611
|
+
apiurl, **{"delete": "FALSE", "versionupdateget": "TRUE"}
|
|
612
|
+
)
|
|
613
|
+
if error is not None:
|
|
614
|
+
raise error
|
|
615
|
+
|
|
616
|
+
_, api_bucket, model_id = json.loads(response.content.decode("utf-8"))
|
|
617
|
+
# }}}
|
|
618
|
+
|
|
619
|
+
# upload y_test data:
|
|
620
|
+
ytest_path = os.path.join(temp_dir, "ytest.pkl")
|
|
621
|
+
import pickle
|
|
622
|
+
#ytest data to load to s3
|
|
623
|
+
|
|
624
|
+
if y_test is not None:
|
|
625
|
+
if type(y_test) is not list:
|
|
626
|
+
y_test=y_test.tolist()
|
|
627
|
+
else:
|
|
628
|
+
pass
|
|
629
|
+
|
|
630
|
+
if all(isinstance(x, (np.float64)) for x in y_test):
|
|
631
|
+
y_test = [float(i) for i in y_test]
|
|
632
|
+
else:
|
|
633
|
+
pass
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
pickle.dump(y_test,open(ytest_path,"wb"))
|
|
637
|
+
s3["client"].upload_file(ytest_path, os.environ.get("BUCKET_NAME"), model_id + "/competition/ytest.pkl")
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
if eval_metric_filepath is not None:
|
|
641
|
+
|
|
642
|
+
if isinstance(eval_metric_filepath, list):
|
|
643
|
+
|
|
644
|
+
for i in eval_metric_filepath:
|
|
645
|
+
|
|
646
|
+
eval_metric_name = i.split('/')[-1]
|
|
647
|
+
|
|
648
|
+
s3["client"].upload_file(i, os.environ.get("BUCKET_NAME"), model_id + '/competition/metrics_' + eval_metric_name)
|
|
649
|
+
|
|
650
|
+
else:
|
|
651
|
+
|
|
652
|
+
eval_metric_name = eval_metric_filepath.split('/')[-1]
|
|
653
|
+
|
|
654
|
+
print(eval_metric_name)
|
|
655
|
+
|
|
656
|
+
s3["client"].upload_file(eval_metric_filepath, os.environ.get("BUCKET_NAME"), model_id + '/competition/metrics_' + eval_metric_name)
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
# get api_id from apiurl, generate txt file name
|
|
660
|
+
api_url_trim = apiurl.split('https://')[1]
|
|
661
|
+
api_id = api_url_trim.split(".")[0]
|
|
662
|
+
|
|
663
|
+
if input_dict == None:
|
|
664
|
+
print("\n--INPUT COMPETITION DETAILS--\n")
|
|
665
|
+
aishare_competitionname = input("Enter competition name:")
|
|
666
|
+
aishare_competitiondescription = input("Enter competition description:")
|
|
667
|
+
|
|
668
|
+
print("\n--INPUT DATA DETAILS--\n")
|
|
669
|
+
print("Note: (optional) Save an optional LICENSE.txt file in your competition data directory to make users aware of any restrictions on data sharing/usage.\n")
|
|
670
|
+
|
|
671
|
+
aishare_datadescription = input(
|
|
672
|
+
"Enter data description (i.e.- filenames denoting training and test data, file types, and any subfolders where files are stored):")
|
|
673
|
+
|
|
674
|
+
aishare_datalicense = input(
|
|
675
|
+
"Enter optional data license descriptive name (e.g.- 'MIT, Apache 2.0, CC0, Other, etc.'):")
|
|
676
|
+
|
|
677
|
+
else:
|
|
678
|
+
aishare_competitionname = input_dict["competition_name"]
|
|
679
|
+
aishare_competitiondescription = input_dict["competition_description"]
|
|
680
|
+
aishare_datadescription = input_dict["data_description"]
|
|
681
|
+
aishare_datalicense = input_dict["data_license"]
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
user_session = boto3.session.Session(aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID_AIMS"),
|
|
685
|
+
aws_secret_access_key = os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS"),
|
|
686
|
+
region_name=os.environ.get("AWS_REGION_AIMS"))
|
|
687
|
+
|
|
688
|
+
account_number = user_session.client(
|
|
689
|
+
'sts').get_caller_identity().get('Account')
|
|
690
|
+
|
|
691
|
+
datauri=share_data_codebuild(account_number,os.environ.get("AWS_REGION_AIMS"),data_directory)
|
|
692
|
+
|
|
693
|
+
#create and upload json file with list of authorized users who can submit to this competition.
|
|
694
|
+
_create_competitionuserauth_json(apiurl, email_list,public,datauri['ecr_uri'], submission_type="competition")
|
|
695
|
+
_create_public_private_split_json(apiurl, public_private_split, "competition")
|
|
696
|
+
|
|
697
|
+
bodydata = {"unique_model_id": model_id,
|
|
698
|
+
"bucket_name": api_bucket,
|
|
699
|
+
"apideveloper": os.environ.get("username"), # change this to first and last name
|
|
700
|
+
"competitionname":aishare_competitionname,
|
|
701
|
+
"competitiondescription": aishare_competitiondescription,
|
|
702
|
+
# getting rid of extra quotes that screw up dynamodb string search on apiurls
|
|
703
|
+
"apiurl": apiurl,
|
|
704
|
+
"version": 0,
|
|
705
|
+
"Private": "FALSE",
|
|
706
|
+
"delete": "FALSE",
|
|
707
|
+
'datadescription':aishare_datadescription,
|
|
708
|
+
'dataecruri':datauri['ecr_uri'],
|
|
709
|
+
'datalicense': aishare_datalicense}
|
|
710
|
+
|
|
711
|
+
# Get the response
|
|
712
|
+
headers_with_authentication = {'Content-Type': 'application/json', 'authorizationToken': os.environ.get("JWT_AUTHORIZATION_TOKEN"), 'Access-Control-Allow-Headers':
|
|
713
|
+
'Content-Type,X-Amz-Date,authorizationToken,Access-Control-Allow-Origin,X-Api-Key,X-Amz-Security-Token,Authorization', 'Access-Control-Allow-Origin': '*'}
|
|
714
|
+
# modeltoapi lambda function invoked through below url to return new prediction api in response
|
|
715
|
+
requests.post("https://o35jwfakca.execute-api.us-east-1.amazonaws.com/dev/modeldata",
|
|
716
|
+
json=bodydata, headers=headers_with_authentication)
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
final_message = ("\n Success! Model competition created. \n\n"
|
|
720
|
+
"You may now update your prediction API runtime model and verify evaluation metrics with the update_runtime_model() function.\n\n"
|
|
721
|
+
"To upload new models and/or preprocessors to this API, team members should use \n"
|
|
722
|
+
"the following credentials:\n\napiurl='" + apiurl+"'"+"\nfrom aimodelshare.aws import set_credentials\nset_credentials(apiurl=apiurl)\n\n"
|
|
723
|
+
"They can then submit models to your competition by using the following code: \n\ncompetition= ai.Competition(apiurl)\n"
|
|
724
|
+
"download_data('"+datauri['ecr_uri']+"') \n"
|
|
725
|
+
"# Use this data to preprocess data and train model. Write and save preprocessor fxn, save model to onnx file, generate predicted y values\n using X test data, then submit a model below.\n\n"
|
|
726
|
+
"competition.submit_model(model_filepath, preprocessor_filepath, prediction_submission_list)")
|
|
727
|
+
|
|
728
|
+
if print_output:
|
|
729
|
+
return print(final_message)
|
|
730
|
+
else:
|
|
731
|
+
return
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
def create_experiment(apiurl, data_directory, y_test, eval_metric_filepath=None, email_list=[], public=False, public_private_split=0,
|
|
736
|
+
input_dict=None, print_output=True):
|
|
737
|
+
"""
|
|
738
|
+
Creates a model experiment for a deployed prediction REST API
|
|
739
|
+
Inputs : 4
|
|
740
|
+
Output : Create ML model experiment and allow authorized users to submit models to resulting leaderboard/competition
|
|
741
|
+
|
|
742
|
+
---------
|
|
743
|
+
Parameters
|
|
744
|
+
|
|
745
|
+
apiurl: string
|
|
746
|
+
URL of deployed prediction API
|
|
747
|
+
|
|
748
|
+
y_test : list of y values for test data used to generate metrics from predicted values from X test data submitted via the submit_model() function
|
|
749
|
+
[REQUIRED] to generate eval metrics in competition leaderboard
|
|
750
|
+
|
|
751
|
+
data_directory : folder storing training data and test data (excluding Y test data)
|
|
752
|
+
email_list: [REQUIRED] list of comma separated emails for users who are allowed to submit models to experiment. Emails should be strings in a list.
|
|
753
|
+
|
|
754
|
+
---------
|
|
755
|
+
Returns
|
|
756
|
+
finalmessage : Information such as how to submit models to competition
|
|
757
|
+
|
|
758
|
+
"""
|
|
759
|
+
if all([isinstance(email_list, list)]):
|
|
760
|
+
if any([len(email_list)>0, public=="True",public=="TRUE",public==True]):
|
|
761
|
+
idtoken=get_aws_token()
|
|
762
|
+
decoded = decode_token_unverified(idtoken)
|
|
763
|
+
|
|
764
|
+
email=decoded['email']
|
|
765
|
+
email_list.append(email)
|
|
766
|
+
else:
|
|
767
|
+
return print("email_list argument empty or incorrectly formatted. Please provide a list of emails for authorized competition participants formatted as strings.")
|
|
768
|
+
|
|
769
|
+
# create temporary folder
|
|
770
|
+
temp_dir = tempfile.gettempdir()
|
|
771
|
+
|
|
772
|
+
s3, iam, region = get_s3_iam_client(os.environ.get("AWS_ACCESS_KEY_ID_AIMS"), os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS"), os.environ.get("AWS_REGION_AIMS"))
|
|
773
|
+
|
|
774
|
+
# Get bucket and model_id subfolder for user based on apiurl {{{
|
|
775
|
+
response, error = run_function_on_lambda(
|
|
776
|
+
apiurl, **{"delete": "FALSE", "versionupdateget": "TRUE"}
|
|
777
|
+
)
|
|
778
|
+
if error is not None:
|
|
779
|
+
raise error
|
|
780
|
+
|
|
781
|
+
_, api_bucket, model_id = json.loads(response.content.decode("utf-8"))
|
|
782
|
+
# }}}
|
|
783
|
+
|
|
784
|
+
# upload y_test data:
|
|
785
|
+
ytest_path = os.path.join(temp_dir, "ytest.pkl")
|
|
786
|
+
import pickle
|
|
787
|
+
#ytest data to load to s3
|
|
788
|
+
|
|
789
|
+
if y_test is not None:
|
|
790
|
+
if type(y_test) is not list:
|
|
791
|
+
y_test=y_test.tolist()
|
|
792
|
+
else:
|
|
793
|
+
pass
|
|
794
|
+
|
|
795
|
+
if all(isinstance(x, (np.float64)) for x in y_test):
|
|
796
|
+
y_test = [float(i) for i in y_test]
|
|
797
|
+
else:
|
|
798
|
+
pass
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
pickle.dump(y_test,open(ytest_path,"wb"))
|
|
802
|
+
s3["client"].upload_file(ytest_path, os.environ.get("BUCKET_NAME"), model_id + "/experiment/ytest.pkl")
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
if eval_metric_filepath is not None:
|
|
806
|
+
|
|
807
|
+
if isinstance(eval_metric_filepath, list):
|
|
808
|
+
|
|
809
|
+
for i in eval_metric_filepath:
|
|
810
|
+
|
|
811
|
+
eval_metric_name = i.split('/')[-1]
|
|
812
|
+
|
|
813
|
+
s3["client"].upload_file(i, os.environ.get("BUCKET_NAME"), model_id + '/experiment/metrics_' + eval_metric_name)
|
|
814
|
+
|
|
815
|
+
else:
|
|
816
|
+
|
|
817
|
+
eval_metric_name = eval_metric_filepath.split('/')[-1]
|
|
818
|
+
|
|
819
|
+
print(eval_metric_name)
|
|
820
|
+
|
|
821
|
+
s3["client"].upload_file(eval_metric_filepath, os.environ.get("BUCKET_NAME"), model_id + '/experiment/metrics_' + eval_metric_name)
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
# get api_id from apiurl, generate txt file name
|
|
825
|
+
api_url_trim = apiurl.split('https://')[1]
|
|
826
|
+
api_id = api_url_trim.split(".")[0]
|
|
827
|
+
|
|
828
|
+
if input_dict == None:
|
|
829
|
+
print("\n--INPUT EXPERIMENT DETAILS--\n")
|
|
830
|
+
aishare_competitionname = input("Enter experiment name:")
|
|
831
|
+
aishare_competitiondescription = input("Enter experiment description:")
|
|
832
|
+
|
|
833
|
+
print("\n--INPUT DATA DETAILS--\n")
|
|
834
|
+
print("Note: (optional) Save an optional LICENSE.txt file in your experiment data directory to make users aware of any restrictions on data sharing/usage.\n")
|
|
835
|
+
|
|
836
|
+
aishare_datadescription = input(
|
|
837
|
+
"Enter data description (i.e.- filenames denoting training and test data, file types, and any subfolders where files are stored):")
|
|
838
|
+
|
|
839
|
+
aishare_datalicense = input(
|
|
840
|
+
"Enter optional data license descriptive name (e.g.- 'MIT, Apache 2.0, CC0, Other, etc.'):")
|
|
841
|
+
|
|
842
|
+
else:
|
|
843
|
+
aishare_competitionname = input_dict["experiment_name"]
|
|
844
|
+
aishare_competitiondescription = input_dict["experiment_description"]
|
|
845
|
+
aishare_datadescription = input_dict["data_description"]
|
|
846
|
+
aishare_datalicense = input_dict["data_license"]
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
user_session = boto3.session.Session(aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID_AIMS"),
|
|
850
|
+
aws_secret_access_key = os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS"),
|
|
851
|
+
region_name=os.environ.get("AWS_REGION_AIMS"))
|
|
852
|
+
|
|
853
|
+
account_number = user_session.client(
|
|
854
|
+
'sts').get_caller_identity().get('Account')
|
|
855
|
+
|
|
856
|
+
datauri=share_data_codebuild(account_number,os.environ.get("AWS_REGION_AIMS"),data_directory)
|
|
857
|
+
|
|
858
|
+
#create and upload json file with list of authorized users who can submit to this competition.
|
|
859
|
+
_create_competitionuserauth_json(apiurl, email_list,public,datauri['ecr_uri'], submission_type="experiment")
|
|
860
|
+
_create_public_private_split_json(apiurl, public_private_split, "experiment")
|
|
861
|
+
|
|
862
|
+
bodydata = {"unique_model_id": model_id,
|
|
863
|
+
"bucket_name": api_bucket,
|
|
864
|
+
"apideveloper": os.environ.get("username"), # change this to first and last name
|
|
865
|
+
"experiment":"TRUE",
|
|
866
|
+
"competitionname":aishare_competitionname,
|
|
867
|
+
"competitiondescription": aishare_competitiondescription,
|
|
868
|
+
# getting rid of extra quotes that screw up dynamodb string search on apiurls
|
|
869
|
+
"apiurl": apiurl,
|
|
870
|
+
"version": 0,
|
|
871
|
+
"Private": "FALSE",
|
|
872
|
+
"delete": "FALSE",
|
|
873
|
+
'datadescription':aishare_datadescription,
|
|
874
|
+
'dataecruri':datauri['ecr_uri'],
|
|
875
|
+
'datalicense': aishare_datalicense}
|
|
876
|
+
|
|
877
|
+
# Get the response
|
|
878
|
+
headers_with_authentication = {'Content-Type': 'application/json', 'authorizationToken': os.environ.get("JWT_AUTHORIZATION_TOKEN"), 'Access-Control-Allow-Headers':
|
|
879
|
+
'Content-Type,X-Amz-Date,authorizationToken,Access-Control-Allow-Origin,X-Api-Key,X-Amz-Security-Token,Authorization', 'Access-Control-Allow-Origin': '*'}
|
|
880
|
+
# modeltoapi lambda function invoked through below url to return new prediction api in response
|
|
881
|
+
requests.post("https://o35jwfakca.execute-api.us-east-1.amazonaws.com/dev/modeldata",
|
|
882
|
+
json=bodydata, headers=headers_with_authentication)
|
|
883
|
+
|
|
884
|
+
|
|
885
|
+
final_message = ("\n Success! Model experiment created. \n\n"
|
|
886
|
+
"You may now update your prediction API runtime model and verify evaluation metrics with the update_runtime_model() function.\n\n"
|
|
887
|
+
"To upload new models and/or preprocessors to this API, team members should use \n"
|
|
888
|
+
"the following credentials:\n\napiurl='" + apiurl+"'"+"\nfrom aimodelshare.aws import set_credentials\nset_credentials(apiurl=apiurl)\n\n"
|
|
889
|
+
"They can then submit models to your experiment by using the following code: \n\nexperiment= ai.Experiment(apiurl)\n"
|
|
890
|
+
"download_data('"+datauri['ecr_uri']+"') \n"
|
|
891
|
+
"# Use this data to preprocess data and train model. Write and save preprocessor fxn, save model to onnx file, generate predicted y values\n using X test data, then submit a model below.\n\n"
|
|
892
|
+
"experiment.submit_model(model_filepath, preprocessor_filepath, prediction_submission_list)")
|
|
893
|
+
|
|
894
|
+
if print_output:
|
|
895
|
+
return print(final_message)
|
|
896
|
+
else:
|
|
897
|
+
return
|
|
898
|
+
|
|
899
|
+
def _create_public_private_split_json(apiurl, split=0.5, submission_type='competition'):
|
|
900
|
+
import json
|
|
901
|
+
if all(["AWS_ACCESS_KEY_ID_AIMS" in os.environ,
|
|
902
|
+
"AWS_SECRET_ACCESS_KEY_AIMS" in os.environ,
|
|
903
|
+
"AWS_REGION_AIMS" in os.environ,
|
|
904
|
+
"username" in os.environ,
|
|
905
|
+
"password" in os.environ]):
|
|
906
|
+
pass
|
|
907
|
+
else:
|
|
908
|
+
return print("'Set public-private split' unsuccessful. Please provide credentials with set_credentials().")
|
|
909
|
+
|
|
910
|
+
# Create user session
|
|
911
|
+
aws_client=get_aws_client(aws_key=os.environ.get('AWS_ACCESS_KEY_ID_AIMS'),
|
|
912
|
+
aws_secret=os.environ.get('AWS_SECRET_ACCESS_KEY_AIMS'),
|
|
913
|
+
aws_region=os.environ.get('AWS_REGION_AIMS'))
|
|
914
|
+
|
|
915
|
+
user_sess = boto3.session.Session(aws_access_key_id=os.environ.get('AWS_ACCESS_KEY_ID_AIMS'),
|
|
916
|
+
aws_secret_access_key=os.environ.get('AWS_SECRET_ACCESS_KEY_AIMS'),
|
|
917
|
+
region_name=os.environ.get('AWS_REGION_AIMS'))
|
|
918
|
+
|
|
919
|
+
s3 = user_sess.resource('s3')
|
|
920
|
+
|
|
921
|
+
# Get bucket and model_id for user based on apiurl {{{
|
|
922
|
+
response, error = run_function_on_lambda(
|
|
923
|
+
apiurl, **{"delete": "FALSE", "versionupdateget": "TRUE"}
|
|
924
|
+
)
|
|
925
|
+
if error is not None:
|
|
926
|
+
raise error
|
|
927
|
+
|
|
928
|
+
_, api_bucket, model_id = json.loads(response.content.decode("utf-8"))
|
|
929
|
+
# }}}
|
|
930
|
+
|
|
931
|
+
import json
|
|
932
|
+
import tempfile
|
|
933
|
+
tempdir = tempfile.TemporaryDirectory()
|
|
934
|
+
with open(tempdir.name+'/public_private_split.json', 'w', encoding='utf-8') as f:
|
|
935
|
+
json.dump({"public_private_split": str(split)}, f, ensure_ascii=False, indent=4)
|
|
936
|
+
|
|
937
|
+
aws_client['client'].upload_file(
|
|
938
|
+
tempdir.name+"/public_private_split.json", api_bucket, model_id +"/"+submission_type+"/public_private_split.json"
|
|
939
|
+
)
|
|
940
|
+
|
|
941
|
+
return
|
|
942
|
+
|
|
943
|
+
def _create_competitionuserauth_json(apiurl, email_list=[],public=False, datauri=None, submission_type="competition"):
|
|
944
|
+
import json
|
|
945
|
+
if all(["AWS_ACCESS_KEY_ID_AIMS" in os.environ,
|
|
946
|
+
"AWS_SECRET_ACCESS_KEY_AIMS" in os.environ,
|
|
947
|
+
"AWS_REGION_AIMS" in os.environ,
|
|
948
|
+
"username" in os.environ,
|
|
949
|
+
"password" in os.environ]):
|
|
950
|
+
pass
|
|
951
|
+
else:
|
|
952
|
+
return print("'Update Runtime Model' unsuccessful. Please provide credentials with set_credentials().")
|
|
953
|
+
|
|
954
|
+
# Create user session
|
|
955
|
+
aws_client=get_aws_client(aws_key=os.environ.get('AWS_ACCESS_KEY_ID_AIMS'),
|
|
956
|
+
aws_secret=os.environ.get('AWS_SECRET_ACCESS_KEY_AIMS'),
|
|
957
|
+
aws_region=os.environ.get('AWS_REGION_AIMS'))
|
|
958
|
+
|
|
959
|
+
user_sess = boto3.session.Session(aws_access_key_id=os.environ.get('AWS_ACCESS_KEY_ID_AIMS'),
|
|
960
|
+
aws_secret_access_key=os.environ.get('AWS_SECRET_ACCESS_KEY_AIMS'),
|
|
961
|
+
region_name=os.environ.get('AWS_REGION_AIMS'))
|
|
962
|
+
|
|
963
|
+
s3 = user_sess.resource('s3')
|
|
964
|
+
|
|
965
|
+
# Get bucket and model_id for user based on apiurl {{{
|
|
966
|
+
response, error = run_function_on_lambda(
|
|
967
|
+
apiurl, **{"delete": "FALSE", "versionupdateget": "TRUE"}
|
|
968
|
+
)
|
|
969
|
+
if error is not None:
|
|
970
|
+
raise error
|
|
971
|
+
|
|
972
|
+
_, api_bucket, model_id = json.loads(response.content.decode("utf-8"))
|
|
973
|
+
# }}}
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
import json
|
|
977
|
+
import tempfile
|
|
978
|
+
tempdir = tempfile.TemporaryDirectory()
|
|
979
|
+
with open(tempdir.name+'/competitionuserdata.json', 'w', encoding='utf-8') as f:
|
|
980
|
+
json.dump({"emaillist": email_list, "public":str(public).upper(),"datauri":str(datauri)}, f, ensure_ascii=False, indent=4)
|
|
981
|
+
|
|
982
|
+
aws_client['client'].upload_file(
|
|
983
|
+
tempdir.name+"/competitionuserdata.json", api_bucket, model_id +"/"+submission_type+"/competitionuserdata.json"
|
|
984
|
+
)
|
|
985
|
+
|
|
986
|
+
return
|
|
987
|
+
|
|
988
|
+
def update_playground_access_list(apiurl, email_list=[], update_type="Add"):
|
|
989
|
+
"""
|
|
990
|
+
Updates list of authenticated participants who can submit new models to a competition.
|
|
991
|
+
---------------
|
|
992
|
+
Parameters:
|
|
993
|
+
apiurl: string
|
|
994
|
+
URL of deployed prediction API
|
|
995
|
+
|
|
996
|
+
email_list: [REQUIRED] list of comma separated emails for users who are allowed to access model playground. Emails should be strings in a list.
|
|
997
|
+
update_type:[REQUIRED] options, string: 'Add', 'Remove', 'Replace'. Add appends user emails to original list, Remove deletes users from list,
|
|
998
|
+
and 'Replace' overwrites the original list with the new list provided.
|
|
999
|
+
-----------------
|
|
1000
|
+
Returns
|
|
1001
|
+
response: "Success" upon successful request
|
|
1002
|
+
"""
|
|
1003
|
+
if update_type not in ['Add', 'Remove', 'Replace']:
|
|
1004
|
+
return "Error: update_type must be in the form of 'Add', 'Remove', or 'Replace'"
|
|
1005
|
+
|
|
1006
|
+
bodydata = {
|
|
1007
|
+
"modifyaccess": "TRUE",
|
|
1008
|
+
"apideveloper": os.environ.get("username"), # change this to first and last name
|
|
1009
|
+
"apiurl": apiurl,
|
|
1010
|
+
"operation": update_type,
|
|
1011
|
+
"email_list": email_list,
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
# Get the response
|
|
1015
|
+
headers_with_authentication = {'Content-Type': 'application/json', 'authorizationToken': os.environ.get("JWT_AUTHORIZATION_TOKEN"), 'Access-Control-Allow-Headers':
|
|
1016
|
+
'Content-Type,X-Amz-Date,authorizationToken,Access-Control-Allow-Origin,X-Api-Key,X-Amz-Security-Token,Authorization', 'Access-Control-Allow-Origin': '*'}
|
|
1017
|
+
# modeltoapi lambda function invoked through below url to return new prediction api in response
|
|
1018
|
+
response = requests.post("https://bhrdesksak.execute-api.us-east-1.amazonaws.com/dev/modeldata",
|
|
1019
|
+
json=bodydata, headers=headers_with_authentication)
|
|
1020
|
+
response_string = response.text
|
|
1021
|
+
response_string = response_string[1:-1]
|
|
1022
|
+
|
|
1023
|
+
# Build output {{{
|
|
1024
|
+
finalresult = "Success! Your Model Playground email list has been updated. Playground Url: " + apiurl
|
|
1025
|
+
return finalresult
|
|
1026
|
+
|
|
1027
|
+
def get_playground_access_list(apiurl):
|
|
1028
|
+
bodydata = {
|
|
1029
|
+
"getaccess": "TRUE",
|
|
1030
|
+
"apideveloper": os.environ.get("username"), # change this to first and last name
|
|
1031
|
+
"apiurl": apiurl,
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
# Get the response
|
|
1035
|
+
headers_with_authentication = {'Content-Type': 'application/json', 'authorizationToken': os.environ.get("JWT_AUTHORIZATION_TOKEN"), 'Access-Control-Allow-Headers':
|
|
1036
|
+
'Content-Type,X-Amz-Date,authorizationToken,Access-Control-Allow-Origin,X-Api-Key,X-Amz-Security-Token,Authorization', 'Access-Control-Allow-Origin': '*'}
|
|
1037
|
+
# modeltoapi lambda function invoked through below url to return new prediction api in response
|
|
1038
|
+
response = requests.post("https://bhrdesksak.execute-api.us-east-1.amazonaws.com/dev/modeldata",
|
|
1039
|
+
json=bodydata, headers=headers_with_authentication)
|
|
1040
|
+
response_string = response.text
|
|
1041
|
+
response_string = response_string[1:-1]
|
|
1042
|
+
|
|
1043
|
+
return response_string.split(',')
|
|
1044
|
+
|
|
1045
|
+
def update_access_list(apiurl, email_list=[],update_type="Add", submission_type="competition"):
|
|
1046
|
+
"""
|
|
1047
|
+
Updates list of authenticated participants who can submit new models to a competition.
|
|
1048
|
+
---------------
|
|
1049
|
+
Parameters:
|
|
1050
|
+
apiurl: string
|
|
1051
|
+
URL of deployed prediction API
|
|
1052
|
+
|
|
1053
|
+
email_list: [REQUIRED] list of comma separated emails for users who are allowed to submit models to competition. Emails should be strings in a list.
|
|
1054
|
+
update_type:[REQUIRED] options, string: 'Add', 'Remove', 'Replace_list','Get. Add appends user emails to original list, Remove deletes users from list,
|
|
1055
|
+
'Replace_list' overwrites the original list with the new list provided, and Get returns the current list.
|
|
1056
|
+
-----------------
|
|
1057
|
+
Returns
|
|
1058
|
+
response: "Success" upon successful request
|
|
1059
|
+
"""
|
|
1060
|
+
import json
|
|
1061
|
+
import os
|
|
1062
|
+
if all(["AWS_ACCESS_KEY_ID_AIMS" in os.environ,
|
|
1063
|
+
"AWS_SECRET_ACCESS_KEY_AIMS" in os.environ,
|
|
1064
|
+
"AWS_REGION_AIMS" in os.environ,
|
|
1065
|
+
"username" in os.environ,
|
|
1066
|
+
"password" in os.environ]):
|
|
1067
|
+
pass
|
|
1068
|
+
else:
|
|
1069
|
+
return print("'Update unsuccessful. Please provide credentials with set_credentials().")
|
|
1070
|
+
|
|
1071
|
+
if all([isinstance(email_list, list)]):
|
|
1072
|
+
if all([len(email_list)>0]):
|
|
1073
|
+
pass
|
|
1074
|
+
else:
|
|
1075
|
+
return print("email_list argument empty or incorrectly formatted. Please provide a list of emails for authorized competition participants formatted as strings.")
|
|
1076
|
+
|
|
1077
|
+
# Create user session
|
|
1078
|
+
aws_client=get_aws_client(aws_key=os.environ.get('AWS_ACCESS_KEY_ID_AIMS'),
|
|
1079
|
+
aws_secret=os.environ.get('AWS_SECRET_ACCESS_KEY_AIMS'),
|
|
1080
|
+
aws_region=os.environ.get('AWS_REGION_AIMS'))
|
|
1081
|
+
|
|
1082
|
+
user_sess = boto3.session.Session(aws_access_key_id=os.environ.get('AWS_ACCESS_KEY_ID_AIMS'),
|
|
1083
|
+
aws_secret_access_key=os.environ.get('AWS_SECRET_ACCESS_KEY_AIMS'),
|
|
1084
|
+
region_name=os.environ.get('AWS_REGION_AIMS'))
|
|
1085
|
+
|
|
1086
|
+
s3 = user_sess.resource('s3')
|
|
1087
|
+
|
|
1088
|
+
# Get bucket and model_id for user based on apiurl {{{
|
|
1089
|
+
response, error = run_function_on_lambda(
|
|
1090
|
+
apiurl, **{"delete": "FALSE", "versionupdateget": "TRUE"}
|
|
1091
|
+
)
|
|
1092
|
+
if error is not None:
|
|
1093
|
+
raise error
|
|
1094
|
+
|
|
1095
|
+
_, api_bucket, model_id = json.loads(response.content.decode("utf-8"))
|
|
1096
|
+
# }}}
|
|
1097
|
+
|
|
1098
|
+
email_list=json.loads(json.dumps(email_list))
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
if update_type=="Replace_list":
|
|
1102
|
+
import json
|
|
1103
|
+
import tempfile
|
|
1104
|
+
tempdir = tempfile.TemporaryDirectory()
|
|
1105
|
+
content_object = aws_client['resource'].Object(bucket_name=api_bucket, key=model_id +"/"+submission_type+"/competitionuserdata.json")
|
|
1106
|
+
file_content = content_object.get()['Body'].read().decode('utf-8')
|
|
1107
|
+
json_content = json.loads(file_content)
|
|
1108
|
+
json_content['emaillist']=email_list
|
|
1109
|
+
with open(tempdir.name+'/competitionuserdata.json', 'w', encoding='utf-8') as f:
|
|
1110
|
+
json.dump(json_content, f, ensure_ascii=False, indent=4)
|
|
1111
|
+
|
|
1112
|
+
aws_client['client'].upload_file(
|
|
1113
|
+
tempdir.name+"/competitionuserdata.json", api_bucket, model_id +"/"+submission_type+"/competitionuserdata.json"
|
|
1114
|
+
)
|
|
1115
|
+
elif update_type=="Add":
|
|
1116
|
+
import json
|
|
1117
|
+
import tempfile
|
|
1118
|
+
tempdir = tempfile.TemporaryDirectory()
|
|
1119
|
+
content_object = aws_client['resource'].Object(bucket_name=api_bucket, key=model_id +"/"+submission_type+"/competitionuserdata.json")
|
|
1120
|
+
file_content = content_object.get()['Body'].read().decode('utf-8')
|
|
1121
|
+
json_content = json.loads(file_content)
|
|
1122
|
+
|
|
1123
|
+
email_list_old=json_content["emaillist"]
|
|
1124
|
+
email_list_new=email_list_old+email_list
|
|
1125
|
+
print(email_list_new)
|
|
1126
|
+
|
|
1127
|
+
json_content["emaillist"]=email_list_new
|
|
1128
|
+
with open(tempdir.name+'/competitionuserdata.json', 'w', encoding='utf-8') as f:
|
|
1129
|
+
json.dump(json_content, f, ensure_ascii=False, indent=4)
|
|
1130
|
+
|
|
1131
|
+
aws_client['client'].upload_file(
|
|
1132
|
+
tempdir.name+"/competitionuserdata.json", api_bucket, model_id +"/"+submission_type+"/competitionuserdata.json"
|
|
1133
|
+
)
|
|
1134
|
+
|
|
1135
|
+
return "Success: Your competition participant access list is now updated."
|
|
1136
|
+
elif update_type=="Remove":
|
|
1137
|
+
import json
|
|
1138
|
+
import tempfile
|
|
1139
|
+
tempdir = tempfile.TemporaryDirectory()
|
|
1140
|
+
|
|
1141
|
+
aws_client['resource']
|
|
1142
|
+
content_object = aws_client['resource'].Object(bucket_name=api_bucket, key=model_id +"/"+submission_type+"/competitionuserdata.json")
|
|
1143
|
+
file_content = content_object.get()['Body'].read().decode('utf-8')
|
|
1144
|
+
json_content = json.loads(file_content)
|
|
1145
|
+
|
|
1146
|
+
email_list_old=json_content["emaillist"]
|
|
1147
|
+
email_list_new=list(set(list(email_list_old)) - set(email_list))
|
|
1148
|
+
print(email_list_new)
|
|
1149
|
+
|
|
1150
|
+
json_content["emaillist"]=email_list_new
|
|
1151
|
+
with open(tempdir.name+'/competitionuserdata.json', 'w', encoding='utf-8') as f:
|
|
1152
|
+
json.dump(json_content, f, ensure_ascii=False, indent=4)
|
|
1153
|
+
|
|
1154
|
+
aws_client['client'].upload_file(
|
|
1155
|
+
tempdir.name+"/competitionuserdata.json", api_bucket, model_id +"/"+submission_type+"/competitionuserdata.json"
|
|
1156
|
+
)
|
|
1157
|
+
return "Success: Your competition participant access list is now updated."
|
|
1158
|
+
elif update_type=="Get":
|
|
1159
|
+
import json
|
|
1160
|
+
import tempfile
|
|
1161
|
+
tempdir = tempfile.TemporaryDirectory()
|
|
1162
|
+
|
|
1163
|
+
aws_client['resource']
|
|
1164
|
+
content_object = aws_client['resource'].Object(bucket_name=api_bucket, key=model_id +"/"+submission_type+"/competitionuserdata.json")
|
|
1165
|
+
file_content = content_object.get()['Body'].read().decode('utf-8')
|
|
1166
|
+
json_content = json.loads(file_content)
|
|
1167
|
+
return json_content['emaillist']
|
|
1168
|
+
else:
|
|
1169
|
+
return "Error: Check inputs and resubmit."
|
|
1170
|
+
|
|
1171
|
+
|
|
1172
|
+
def _confirm_libraries_exist(requirements):
|
|
1173
|
+
requirements = requirements.split(",")
|
|
1174
|
+
for i in range(len(requirements)):
|
|
1175
|
+
requirements[i] = requirements[i].strip(" ")
|
|
1176
|
+
exists = requests.get("https://pypi.org/project/" + requirements[i])
|
|
1177
|
+
if exists.status_code == 404:
|
|
1178
|
+
try_again_message = ("The entered library '" + requirements[i] + "' was not found. "
|
|
1179
|
+
"Please confirm and re-submit library name: ")
|
|
1180
|
+
requirements[i] = input(try_again_message)
|
|
1181
|
+
exists = requests.get("https://pypi.org/project/" + requirements[i])
|
|
1182
|
+
|
|
1183
|
+
if exists.status_code == 404:
|
|
1184
|
+
error_message = ("ModuleNotFoundError: No module named '" + requirements[i] + "' found in the Python Package Index (PyPI). \n"
|
|
1185
|
+
"Please double-check library name and try again.")
|
|
1186
|
+
return print(error_message)
|
|
1187
|
+
|
|
1188
|
+
return
|
|
1189
|
+
|
|
1190
|
+
|
|
1191
|
+
|
|
1192
|
+
def _create_exampledata_json(model_type, exampledata_folder_filepath):
|
|
1193
|
+
import tempfile
|
|
1194
|
+
# create temporary folder
|
|
1195
|
+
temp_dir = tempfile.gettempdir()
|
|
1196
|
+
|
|
1197
|
+
image_extensions = ['.png', '.jpg', '.jpeg', '.tiff', '.bmp', '.gif', '.avif',
|
|
1198
|
+
'.svg', '.webp', '.tif', '.bmp', '.jpe', '.jif', '.jfif',
|
|
1199
|
+
'.jfi', 'psd', '.raw', '.arw', '.cr2', '.nrw', '.k25', '.eps']
|
|
1200
|
+
video_extensions = ['.avchd', '.avi', '.flv', '.mov', '.mkv', '.mp4', '.wmv']
|
|
1201
|
+
audio_extensions = ['.m4a', '.flac', '.mp3', '.mp4', '.wav', '.wma', '.aac']
|
|
1202
|
+
|
|
1203
|
+
import pandas as pd
|
|
1204
|
+
|
|
1205
|
+
if any([model_type.lower() == "tabular", model_type.lower() == "timeseries", model_type.lower() == "text"]):
|
|
1206
|
+
#confirm data type is data frame, try to convert if not [necessary for front end]
|
|
1207
|
+
if isinstance(exampledata_folder_filepath, pd.DataFrame):
|
|
1208
|
+
pass
|
|
1209
|
+
else:
|
|
1210
|
+
exampledata_folder_filepath = pd.DataFrame(exampledata_folder_filepath)
|
|
1211
|
+
|
|
1212
|
+
tabularjson = exampledata_folder_filepath.to_json(orient='split', index=False)
|
|
1213
|
+
|
|
1214
|
+
|
|
1215
|
+
with open(temp_dir+'/exampledata.json', 'w', encoding='utf-8') as f:
|
|
1216
|
+
json.dump({"exampledata": tabularjson, "totalfiles":1}, f, ensure_ascii=False, indent=4)
|
|
1217
|
+
|
|
1218
|
+
return
|
|
1219
|
+
|
|
1220
|
+
else:
|
|
1221
|
+
|
|
1222
|
+
if isinstance(exampledata_folder_filepath, pd.DataFrame):
|
|
1223
|
+
|
|
1224
|
+
tabularjson = exampledata_folder_filepath.to_json(orient='split', index=False)
|
|
1225
|
+
with open(temp_dir+'/exampledata.json', 'w', encoding='utf-8') as f:
|
|
1226
|
+
json.dump({"exampledata": tabularjson, "totalfiles":1}, f, ensure_ascii=False, indent=4)
|
|
1227
|
+
else:
|
|
1228
|
+
#Check file types & make list to convert
|
|
1229
|
+
data = ""
|
|
1230
|
+
file_list = os.listdir(exampledata_folder_filepath)
|
|
1231
|
+
files_to_convert = []
|
|
1232
|
+
for i in range(len(file_list)):
|
|
1233
|
+
file_list[i] = exampledata_folder_filepath + "/" + file_list[i]
|
|
1234
|
+
root, ext = os.path.splitext(file_list[i])
|
|
1235
|
+
|
|
1236
|
+
if not ext:
|
|
1237
|
+
ext = mimetypes.guess_extension(file_list[i])
|
|
1238
|
+
|
|
1239
|
+
if model_type.lower() == "image" and ext in image_extensions:
|
|
1240
|
+
files_to_convert.append(file_list[i])
|
|
1241
|
+
|
|
1242
|
+
if model_type.lower() == "video" and ext in video_extensions:
|
|
1243
|
+
files_to_convert.append(file_list[i])
|
|
1244
|
+
|
|
1245
|
+
if model_type.lower() == "audio" and ext in audio_extensions:
|
|
1246
|
+
files_to_convert.append(file_list[i])
|
|
1247
|
+
|
|
1248
|
+
i += 1
|
|
1249
|
+
if len(files_to_convert) == 5:
|
|
1250
|
+
break
|
|
1251
|
+
|
|
1252
|
+
#base64 encode confirmed file list
|
|
1253
|
+
for i in range(len(files_to_convert)):
|
|
1254
|
+
with open(files_to_convert[i], "rb") as current_file:
|
|
1255
|
+
encoded_string = base64.b64encode(current_file.read())
|
|
1256
|
+
data = data + encoded_string.decode('utf-8') + ", "
|
|
1257
|
+
i += 1
|
|
1258
|
+
|
|
1259
|
+
#build json
|
|
1260
|
+
with open(temp_dir+'/exampledata.json', 'w', encoding='utf-8') as f:
|
|
1261
|
+
json.dump({"exampledata": data[:-2], "totalfiles": len(files_to_convert)}, f, ensure_ascii=False, indent=4)
|
|
1262
|
+
|
|
1263
|
+
return
|
|
1264
|
+
|
|
1265
|
+
__all__ = [
|
|
1266
|
+
take_user_info_and_generate_api,
|
|
1267
|
+
send_model_data_to_dyndb_and_return_api,
|
|
1268
|
+
model_to_api,
|
|
1269
|
+
create_competition,update_access_list
|
|
1270
|
+
]
|