Created
January 24, 2023 22:58
-
-
Save ojmccall/c1e1f2d2f96a9b2cfe31274bcc502462 to your computer and use it in GitHub Desktop.
deployment script for slack app to deploy IS datasets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pandas as pd | |
import requests | |
from requests.auth import HTTPBasicAuth | |
from google.cloud import storage | |
import calendar | |
import datetime | |
from datetime import timedelta | |
import sys | |
import ast | |
from os import environ | |
def hello_gcs(event, context): | |
slackURL = "" #unique slack url here | |
file = format(event["name"]) | |
bucket = format(event["bucket"]) | |
token = environ["SlackBot_Token"] | |
token = token.replace("\n","") | |
storage_client = storage.Client() | |
bucket = storage_client.get_bucket(bucket) | |
blob = bucket.blob(file) | |
blob =blob.download_as_string() | |
body = ast.literal_eval(blob.decode('utf-8')) | |
print(body) | |
if "text" in str(body): | |
slackData = body["event"]["user"] | |
text = body["event"]["text"] | |
file_id = body["event"]["files"][0]["id"] | |
url = f"https://slack.com/api/files.info?file={file_id}" | |
r = requests.get(url,headers={"Authorization": "Bearer %s" % token}) | |
r.raise_for_status | |
response = r.json() | |
assert response["ok"] | |
filename = response["file"]["name"] | |
file_url = response["file"]["url_private"] | |
file_count = 1 | |
elif "text" not in str(body) and "file_id" in str(body): | |
slackData = body["event"]["user_id"] | |
text = "deploy IS " | |
file_id = body["event"]["file_id"] | |
url = f"https://slack.com/api/files.info?file={file_id}" | |
r = requests.get(url,headers={"Authorization": "Bearer %s" % token}) | |
r.raise_for_status | |
response = r.json() | |
assert response["ok"] | |
filename = response["file"]["name"] | |
file_url = response["file"]["url_private"] | |
file_count = 1 | |
if "deploy IS" not in text: | |
sys.exit() | |
if "deploy IS" in text and file_count > 0: | |
messagePart = [] | |
messageBody = { | |
"blocks": [ | |
{ | |
"type": "header", | |
"text": { | |
"type": "plain_text", | |
"text": f"IS Deployment &/OR Event Data Request" | |
} | |
}, | |
{ | |
"type": "divider" | |
}, | |
{ | |
"type": "divider" | |
}, | |
{ | |
"type": "section", | |
"text": { | |
"type": "mrkdwn", | |
"text": f"\n*Thanks for your submission to build an IS Dataset or just send some events to an existing dataset* <@{slackData}>" | |
} | |
}, | |
{ | |
"type": "section", | |
"text": | |
{ | |
"type": "mrkdwn", | |
"text": "\n I'll get back to you in a minute or 2 after I've processed your file :eyes:" | |
} | |
}, | |
{ | |
"type": "divider" | |
} | |
] | |
} | |
url = slackURL | |
header = {"Content-Type":"application/json"} | |
post = requests.post(url, json = messageBody ,headers = header) | |
body = post.content | |
status = post.status_code | |
print(body) | |
clean = filename.replace(".xls","") | |
split = clean.split("|") | |
account = split[0] | |
region = split[1] | |
sessionId = split[2] | |
exportbucket = "bhd_is_config" | |
cookies = { | |
"JSESSIONID": sessionId | |
} | |
headers = { | |
"authority": f"{account}.{region}.evergage.com", | |
"accept": "application/json, text/plain, */*", | |
"accept-language": "en-GB,en-US;q=0.9,en;q=0.8", | |
"content-type": "application/json;charset=UTF-8", | |
"origin": f"https://{account}.{region}.evergage.com", | |
"referer": f"https://{account}.{region}.evergage.com/ui/", | |
"sec-ch-ua-mobile": "?0", | |
"sec-ch-ua-platform": "macOS", | |
"sec-fetch-dest": "empty", | |
"sec-fetch-mode": "cors", | |
"sec-fetch-site": "same-origin", | |
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36", | |
"x-requested-with": "XMLHttpRequest" | |
} | |
r = requests.get(file_url, headers={"Authorization": "Bearer %s" % token}) | |
file_data = r.content # get binary content | |
fullpath= "/tmp/file.csv" | |
# save file to disk | |
with open(fullpath , "w+b") as f: | |
f.write(bytearray(file_data)) | |
#get domain name and generic values | |
genericData = pd.read_excel(fullpath, sheet_name="Generic") | |
genericData = genericData.fillna("") | |
genericList = genericData.to_dict(orient="records") | |
datasetIdObject = genericList[0] | |
datasetId = datasetIdObject.get("value") | |
datasetNameObject = genericList[1] | |
datasetName = datasetNameObject.get("value") | |
domainNameObject = genericList[2] | |
domainName = domainNameObject.get("value") | |
localeObject = genericList[3] | |
locale = localeObject.get("value") | |
currencyObject = genericList[4] | |
currency = currencyObject.get("value") | |
localizationObject = genericList[5] | |
localization = localizationObject.get("value") | |
webAttributeObject = genericList[6] | |
webAttribute = webAttributeObject.get("value") | |
mobileAttributeObject = genericList[7] | |
mobileAttribute = mobileAttributeObject.get("value") | |
serverAttributeObject = genericList[8] | |
serverAttribute = serverAttributeObject.get("value") | |
oteAttributeObject = genericList[9] | |
oteAttribute = oteAttributeObject.get("value") | |
testDataAttributeObject = genericList[10] | |
testData = testDataAttributeObject.get("value") | |
testDataEmailAttributeObject = genericList[11] | |
testDataEmail = testDataEmailAttributeObject.get("value") | |
testDataOnlyAttributeObject = genericList[12] | |
testDataOnly = testDataOnlyAttributeObject.get("value") | |
introMessage = {} | |
negative = ":x:" | |
positive = ":white_check_mark:" | |
if len(slackData) >0: | |
introMessageBody = { | |
"type": "section", | |
"text": { | |
"type": "mrkdwn", | |
"text": f":tada: <@{slackData}> *Your job ran successfully, here are your results:*" | |
} | |
} | |
else: | |
introMessageBody = { | |
"type": "section", | |
"text": { | |
"type": "mrkdwn", | |
"text": f" :ghost: *I don't know who created this job, anyway, here are your results*" | |
} | |
} | |
introMessage.update(introMessageBody) | |
#static values | |
userAPI = "attributeDefinitions/user" | |
catalogAPI = "catalogConfig" | |
integrationAPI = "integrationSetup" | |
dataset = datasetId | |
userURL = f"https://{account}.{region}.evergage.com/internal/dataset/{dataset}/{userAPI}" | |
catalogURL = f"https://{account}.{region}.evergage.com/internal/dataset/{dataset}/{catalogAPI}" | |
datasetURL = f"https://{account}.{region}.evergage.com/internal/manageDatasets/" | |
domainURL = f"https://{account}.{region}.evergage.com/internal/dataset/{dataset}/allowedDomains" | |
ftpURL = f"https://{account}.{region}.evergage.com/internal/sftp/principals" | |
apiURL = f"https://{account}.{region}.evergage.com/internal/apiTokens/create" | |
integrationURL = f"https://{account}.{region}.evergage.com/internal/dataset/{dataset}/{integrationAPI}" | |
identityURL = f"https://{account}.{region}.evergage.com/internal/identityTypes" | |
sendEventURL = f"https://{account}.{region}.evergage.com/api2/authevent/{dataset}" | |
#configure slack notification | |
messagePart = [] | |
messageBody ={ | |
"blocks": [ | |
{ | |
"type": "header", | |
"text": { | |
"type": "plain_text", | |
"text": f"IS Deployment &/OR Event Data for {datasetName}" | |
} | |
}, | |
{ | |
"type": "divider" | |
}, | |
introMessage, | |
{ | |
"type": "divider" | |
}, | |
{ | |
"type": "section", | |
"fields": messagePart | |
}, | |
{ | |
"type": "divider" | |
} | |
] | |
} | |
errorMessage= { | |
"type": "mrkdwn", | |
"text": "*ERROR!!!*\nUnfortunately your file was no beuno :poop:, perhaps learn excel and come back to see me later?\n\n *possible causes:*\n:rotating_light: - expired JSESSION\n :rotating_light: - bad xls filename\n :rotating_light: - dataset already exists" | |
} | |
if testDataOnly != "Yes": | |
# start creating IS data | |
#create new dataset | |
newDataset = { | |
"datasetId": datasetId, | |
"datasetLabel": datasetName, | |
"trackAccounts": "False", | |
"datasetType": "Development" | |
} | |
url = datasetURL | |
response = requests.put(url, cookies=cookies, headers=headers, params= newDataset) | |
body = response.content | |
status = response.status_code | |
#print("dataset creation post: "+str(status)) | |
if status < 400: | |
datasetCreated = "True" | |
else: | |
#send slack notification | |
messagePart.append(errorMessage) | |
url = slackURL | |
header = {"Content-Type":"application/json"} | |
post = requests.post(url, json = messageBody ,headers = header) | |
body = post.content | |
status = post.status_code | |
sys.exit("goodnight cruel world") | |
#add domain | |
json_data = [ | |
{ | |
"domainName": domainName, | |
"includeSubdomain": True | |
} | |
] | |
url = domainURL | |
response = requests.put(url, cookies=cookies, headers=headers, json=json_data) | |
body = response.content | |
status = response.status_code | |
#print("domain config post: "+str(status)) | |
if status == 200: | |
domainCreated = "True" | |
#add ftp account and get info | |
if testDataOnly != "Yes": | |
params = { | |
"datasetName": dataset, | |
"description": "SFTP user" | |
} | |
json_data = {} | |
response = requests.post(ftpURL, params=params, cookies=cookies, headers=headers, json=json_data) | |
body = response.content | |
bodyJson = ast.literal_eval(body.decode('utf-8')) | |
ftpUserId = bodyJson["principalId"] | |
ftpPassword = bodyJson["generatedPlaintextPassword"] | |
#add api keys | |
json_data = { | |
"enabled": True, | |
"roles": [ | |
{ | |
"id": "role.api.access", | |
"dataset": [ | |
dataset | |
], | |
}, | |
{ | |
"id": "role.api.sendEvents", | |
"dataset": [ | |
dataset | |
], | |
}, | |
], | |
"type": "SYSTEM", | |
"grantedAuthorities": [], | |
"ipWhitelist": [] | |
} | |
response = requests.post(apiURL, cookies=cookies, headers=headers, json=json_data) | |
body = response.content | |
bodyJson = ast.literal_eval(body.decode('utf-8')) | |
apiId = bodyJson["principalId"] | |
apiKey = bodyJson["generatedPlaintextPassword"] | |
if testDataOnly != "Yes": | |
exportJson = { "API":{ | |
"Id": apiId, | |
"Key": apiKey | |
}, | |
"SFTP":{ | |
"Id": ftpUserId, | |
"Key": ftpPassword | |
} | |
} | |
#upload from keys to GCP | |
storage_client = storage.Client() | |
exportBucket = storage_client.get_bucket(exportbucket) | |
exportFileName = f"{dataset}_keys.json" | |
blob = exportBucket.blob(exportFileName) | |
blob.upload_from_string(str(exportJson),content_type="application/json") | |
#identity endpoint data | |
identityAttributesCreated = 0 | |
url = identityURL | |
userData = pd.read_excel(fullpath, sheet_name="Identity") | |
userData = userData.fillna("") | |
userList = userData.to_dict(orient="records") | |
identityAttributeCount = len(userList) | |
if len(userList) > 0: | |
i=0 | |
while i < len(userList): | |
string = (userList[i]) | |
# find items in each dict | |
id = string.get("id") | |
label = string.get("label") | |
caseSensitive = string.get("caseSensitive") | |
if caseSensitive == "Yes": | |
caseBooleanSensitive = True | |
else: | |
caseBooleanSensitive = False | |
identityAttribute = { | |
"id": id, | |
"label": label, | |
"caseSensitive": caseBooleanSensitive, | |
"uniqueDiscriminatorType": "NoDiscriminator" | |
} | |
response = requests.post(url, cookies=cookies, headers=headers, json=identityAttribute) | |
body = response.content | |
status = response.status_code | |
#print("identity attribute post: "+str(status)) | |
if status < 400: | |
identityAttributesCreated +=1 | |
i+=1 | |
#User endpoint data | |
userAttributesCreated = 0 | |
url = userURL | |
userData = pd.read_excel(fullpath, sheet_name="User") | |
userData = userData.fillna("") | |
userList = userData.to_dict(orient="records") | |
userAttributeCount = len(userList) | |
if len(userList) > 0: | |
i=0 | |
while i < len(userList): | |
string = (userList[i]) | |
# find items in each dict | |
name = string.get("name") | |
label = string.get("label") | |
type = string.get("type") | |
overrideClassification = string.get("overrideClassification") | |
identityAttribute = string.get("identityAttributeId") | |
userAttribute = { | |
"name": name, | |
"label": label, | |
"type": type, | |
"overrideClassification": overrideClassification, | |
"format":"" | |
} | |
#if IdentityAttribute is defined add to payload otherwise omit | |
if len(str(identityAttribute)) >0: | |
identityObject = { | |
"IdentityAttribute": identityAttribute | |
} | |
userAttribute.update(identityObject) | |
response = requests.post(url, cookies=cookies, headers=headers, json=userAttribute) | |
body = response.content | |
status = response.status_code | |
#print("user attribute post: "+str(status)) | |
if status < 400: | |
userAttributesCreated +=1 | |
i+=1 | |
#Catalog endpoint data | |
catalogCreated = 0 | |
sheetnames = ["Product","Category","Article","Blog","Promotion","CatalogDimension"] | |
catalogDimensions=[] | |
catalogRelationships = [] | |
enabledItemTypeConfigs = {} | |
catalogItems = [] | |
categoryData = "" | |
promotionData = "" | |
for sheet in sheetnames: | |
dfs = pd.read_excel(fullpath, sheet_name=sheet) | |
sheetlist = dfs.to_dict(orient="records") | |
if len(sheetlist) >0 and sheet=="CatalogDimension": | |
dimensionArray = [] | |
#loop through dimension rows | |
i=0 | |
while i < len(sheetlist): | |
string = (sheetlist[i]) | |
# find items in each dict | |
name = string.get("name") | |
label = string.get("label") | |
description = string.get("description") | |
cardinality = string.get("cardinality") | |
catalogItem= { | |
"@class": "com.apptegic.domain.catalog.metadata.DimensionTypeConfig", | |
"name": name, | |
"label": label, | |
"description": description, | |
"fontIconClass": None, | |
"baseUrl": None, | |
"enabled": True, | |
"attributeDefinitions": [], | |
"catalogRelationships": [], | |
"accumulable": False, | |
"cardinality": cardinality | |
} | |
catalogDimensions.append(catalogItem) | |
dimensionList = { | |
"catalogObjectType": name, | |
"cardinality": cardinality | |
} | |
catalogRelationships.append(dimensionList) | |
dimensionArray.append(name) | |
enabledtype = {name: { | |
"@class": "com.apptegic.domain.catalog.metadata.DimensionTypeConfig", | |
"name": name, | |
"label": label, | |
"description": description, | |
"fontIconClass": None, | |
"baseUrl": None, | |
"enabled": True, | |
"attributeDefinitions": [], | |
"catalogRelationships": [], | |
"accumulable": False, | |
"cardinality": cardinality | |
} | |
} | |
#append dimensions to updated objects in payload | |
enabledItemTypeConfigs.update(enabledtype) | |
i+=1 | |
elif len(sheetlist) >0 and sheet != "CatalogDimension" : | |
if sheet == "Category": | |
catalogOption = "Category" | |
elif sheet == "Promotion": | |
catalogOption = "Promotion" | |
else: | |
catalogOption = "CatalogItem" | |
itemAttributes = [] | |
catRecipeAttributes =[] | |
#loop through dimension rows | |
i=0 | |
while i < len(sheetlist) and sheet != "CatalogDimension": | |
string = (sheetlist[i]) | |
# find items in each dict | |
name = string.get("name") | |
label = string.get("label") | |
type = string.get("type") | |
attributeData = { | |
"name": name, | |
"label": label, | |
"type": type, | |
"dirtyFields": { | |
"name": True, | |
"label": True | |
}, | |
} | |
#create attributes for category recipe configu | |
if sheet == "Category": | |
catAttribute = { | |
"name": name, | |
"label": label, | |
"type": type, | |
"systemManaged": False, | |
"identityArchived": False, | |
"identityAttribute": False, | |
} | |
catRecipeAttributes.append(catAttribute) | |
itemAttributes.append(attributeData) | |
i+=1 | |
catalogItemAdditions = { | |
"@class": f"com.apptegic.domain.catalog.metadata.{catalogOption}TypeConfig", | |
"name": sheet, | |
"label": sheet, | |
"description": None, | |
"fontIconClass": "far fa-file-alt", | |
"baseUrl": None, | |
"enabled": True, | |
"attributeDefinitions": itemAttributes, | |
"catalogRelationships": catalogRelationships, | |
"builtin": True | |
} | |
configuredItems = {sheet:catalogItemAdditions} | |
enabledItemTypeConfigs.update(configuredItems) | |
if catalogOption == "CatalogItem": | |
catalogItems.append(catalogItemAdditions) | |
elif catalogOption == "Category": | |
categoryData = catalogItemAdditions | |
elif sheet == "Promotion": | |
promotionData = { | |
"@class": f"com.apptegic.domain.catalog.metadata.{catalogOption}TypeConfig", | |
"name": sheet, | |
"label": sheet, | |
"description": None, | |
"fontIconClass": "far fa-file-alt", | |
"baseUrl": None, | |
"enabled": True, | |
"attributeDefinitions": itemAttributes, | |
"catalogRelationships": catalogRelationships, | |
"builtin": True | |
} | |
#set product info to use in recipe | |
if sheet == "Product": | |
productInfo = catalogItemAdditions | |
productInfo.update({"fullName": "Product", | |
"itemType": "Product", | |
"stateId": "products", | |
"stateName": "products"}) | |
#compile Catalog | |
catalog_data = { | |
"locale": locale, | |
"currency": currency, | |
"cacheNameToProductId": False, | |
"promoteProductsOnly": False, | |
"migrateSkusToProductsInUserHistory": False, | |
"localizationEnabled": localization, | |
"strictCatalogSecurityEnabled": False, | |
"purchaseDateUpsertSlopMillis": 0, | |
"browseAbandonmentDaysViewedWithoutAddCart": 2, | |
"outlierFiltering": { | |
"enabled": False, | |
"perOrderValueThreshold": 0, | |
"perOrderUnitCountThreshold": 0 | |
}, | |
"cartAbandonment": { | |
"minimumInactivityMillis": 1800000, | |
"maximumInactivityMillis": 259200000, | |
}, | |
"triggeredItemFrequencyExclusionEnabled": True, | |
"triggeredItemExclusionMaxOccurrence": 1, | |
"triggeredItemExclusionLookbackTimeDays": 30, | |
"ui": { | |
"revenueTrackingEnabled": True, | |
"filterCampaignStatsByPurchasedProduct": False | |
}, | |
"staticRelationships": [], | |
"catalogItemTypes": catalogItems, | |
"dimensionTypes": catalogDimensions, | |
"categoryType": categoryData, | |
"promotionType": promotionData, | |
"dimensionTypesInitialized": True, | |
"useCategoryIdPathsForSegmentRules": False, | |
"backInStockTriggerCurrentStateAge": 7200000, | |
"backInStockTriggerPreviousStateAge": 604800000, | |
"priceReductionTriggerCurrentPriceAge": 7200000, | |
"priceReductionTriggerPreviousPriceAge": 604800000, | |
"allowUnescapedHtmlInOpenTimeTemplates": False, | |
"itemMetadataUpdateViaEventsDisabled": False, | |
"dimensionsToSupportStaticEmailInclusion": [], | |
"supportNewOpenTimeEmailApiCalls": False, | |
"enabledItemTypeConfigs": enabledItemTypeConfigs | |
} | |
#print(catalog_data) | |
url = catalogURL | |
response = requests.post(url, cookies=cookies, headers=headers, json=catalog_data) | |
body = response.content | |
status = response.status_code | |
#print("body catlog post: "+str(status)) | |
if status < 400: | |
catalogCreated +=1 | |
#generic integration settings | |
integrationsCreated = 0 | |
integration_data = { | |
"label": datasetName, | |
"trackAccounts": True, | |
"trackSubdomainAsAccount": False, | |
"trackAnonymousVisitors": True, | |
"mergeAnonymousUsers": True, | |
"enableMessages": True, | |
"enableSmartHistoryOnly": False, | |
"enableDefaultCustomerOverviewScreenType": False, | |
"enableMessageSecurity": False, | |
"enableEncryptedFields": False, | |
"enableDynamicMessageContent": True, | |
"treatHashAsNewPage": False, | |
"spaRouteChangeTimeout": 500, | |
"allowUsersToSwitchAccounts": False, | |
"autoPopulateUserAccount": False, | |
"autoPopulateUserAccountFromEmailAddress": False, | |
"siteConfigExecBeforePageReady": False, | |
"hideContentSections": False, | |
"hideContentSectionsMillis": 2500, | |
"hidePagesForRedirect": False, | |
"hidePagesForRedirectMillis": 1000, | |
"overrideEmailUserIdTag": False, | |
"emailUserIdTagValue": "EmailAddress", | |
"emailClickUserIdQueryParamEnabled": False, | |
"emailClickAnonIdQueryParamEnabled": True, | |
"multipleIdentitiesUserResolutionEnabled": True, | |
"emailIdentityDetectionEnabled": False, | |
"processMobilePromoteData": False, | |
"processAllMobilePromoteData": False, | |
"disablePrioritizingDataCampaignsByTargetName": False, | |
"ignoreSomeMobileViewControllers": False, | |
"enableItemCounters": False, | |
"syncAnonymousUsersToC360a": False, | |
"trackPagesByTitle": False, | |
"renameUsers": False, | |
"preventSensitiveDataCapture": False, | |
"limitDailyBucketsRetention": False, | |
"dailyBucketsRetentionDays": 90, | |
"limitAttributionWindow": False, | |
"attributionWindowDays": 7, | |
"enableMaxPromotedItemsPerMessage": False, | |
"maxPromotedItemsPerMessage": 12, | |
"enableNumberOfElasticsearchShards": True, | |
"numberOfElasticsearchShardsOverride": 1, | |
"sendErrorEvents": True, | |
"enableRememberMeUserIds": False, | |
"rememberMeUserIdsValueTyped": 2, | |
"disableQtipWindowScroll": False, | |
"campaignPrioritizationDisabled": False, | |
"enableMessageRotation": False, | |
"cacheNameToProductId": False, | |
"promoteProductsOnly": False, | |
"trackContextualRelatedItems": False, | |
"doNotTrackPingRequestsForActions": False, | |
"doNotStoreCookiesRequireProvidedAnonId": False, | |
"changeConfidenceThreshold": False, | |
"confidenceThreshold": 85, | |
"enableGuardian": False, | |
"guardianDisableRevenuePredictions": True, | |
"enableCorsRestrictedOrigins": False, | |
"corsAllowedOriginsString": "*", | |
"customActionRateLimitsEnabled": False, | |
"allowSimulatedEmailNotificationEvents": False, | |
"allowBotTraffic": False, | |
"contextLogForEachImpression": False, | |
"restrictStagingJobLookupToUniqueIdentityTypes": True, | |
"enforceAuthenticatedRequestsOnly": False, | |
"precedenceBulkEmailHeaderEnabled": True, | |
"smartBatchThrottled": False, | |
"returnPathEnabled": False, | |
"enableEmailAddressYank": False, | |
"findUserByEmailAddress": False, | |
"openTimeParamFindsByEmailAddress": False, | |
"datasetType": "Development", | |
"mobileViewControllersToIgnore": [], | |
"defaultCustomerOverviewScreenType": None, | |
"webUserIdIdentityTypeId": webAttribute, | |
"mobileUserIdIdentityTypeId": mobileAttribute, | |
"serverSideUserIdIdentityTypeId": serverAttribute, | |
"defaultOpenTimeEmailIdentityTypeId": oteAttribute, | |
"defaultEmailAddressIdentityTypeId": None, | |
"rememberMeUserIdsMillis": 63072000000, | |
"corsAllowedOrigins": [ | |
"*" | |
], | |
"validatedDomains": [], | |
"actionRateLimiterConfig": None, | |
"hideTrackAccounts": False, | |
"c360aIntegrationEnabled": False | |
} | |
url = integrationURL | |
response = requests.post(url, cookies=cookies, headers=headers, json=integration_data) | |
body = response.content | |
status = response.status_code | |
#print("integration config post: "+str(status)) | |
if status < 400: | |
integrationsCreated += 1 | |
#get recipe tab info and create draft recipes | |
recipesCreated = 0 | |
genericData = pd.read_excel(fullpath, sheet_name="Recipes") | |
genericData = genericData.fillna("") | |
genericList = genericData.to_dict(orient="records") | |
recipeCount = len(genericList) | |
pdpRecipeObject = genericList[0] | |
pdpRecipe = pdpRecipeObject.get("required") | |
categoryRecipeObject = genericList[1] | |
categoryRecipe = categoryRecipeObject.get("required") | |
inCartRecipeObject = genericList[2] | |
incartRecipe = inCartRecipeObject.get("required") | |
if pdpRecipe == "Yes": | |
recipeIndex = "1ABCD" | |
params = { | |
"forceRetrain": "False", | |
"name": "PDP Co-Buy Recommendation" | |
} | |
json_data = { | |
"id": recipeIndex, | |
"fallbackEnabled": True, | |
"fallbackPromotedContent": { | |
"@class": "com.apptegic.domain.campaign.promote.DynamicPromotedContent", | |
"minimumPromotedItemCount": 1, | |
"maximumPromotedItemCount": 120, | |
"skipPromotionOfPreviouslyViewedItems": False, | |
"skipPromotionOfPreviouslyPurchasedProducts": False, | |
"skipPromotionOfItemsInCart": True, | |
"promoteItemEvenIfBeingViewed": False, | |
"allowZeroPromotedItems": False, | |
"trendingFallbackDisabled": False, | |
"campaignTemplateLayout": None, | |
"primaryItemType": { | |
"itemType": "Product" | |
}, | |
"primaryItemStatType": "View", | |
"primaryStatConditionType": "GreatestValue", | |
"primaryItemLookback": { | |
"condition": "LESS_THAN", | |
"lookbackDays1": 14, | |
"lookbackDays2": 0 | |
}, | |
"relatedItemType": None, | |
"relatedItemStatType": None, | |
"relatedStatConditionType": None, | |
"relatedItemLookback": { | |
"condition": "LESS_THAN", | |
"lookbackDays1": 7, | |
"lookbackDays2": 0 | |
}, | |
"promoteItemOnlyIfPromotionStateIsPrioritized": False, | |
"useOnlyDepartmentsAsPrimaryCategories": False | |
}, | |
"created": 1657685396551, | |
"updated": 1657685396551, | |
"itemTypeToRecommend": productInfo | |
, | |
"timeSensitiveTraining": False, | |
"shouldRecordTrainingData": False, | |
"storeRecsOnUser": False, | |
"ingredients": [ | |
{ | |
"@class": "com.apptegic.domain.recommendation.metadata.ingredient.CoBuyIngredientConfig", | |
"ingredient": { | |
"differentiator": None, | |
"type": "CoBuy" | |
}, | |
"minSupportTarget": 5, | |
"useInCartAnchorItem": False, | |
"anchorItemType": "OnPage", | |
"newLogic": True, | |
"requireSameBasket": False, | |
"lookbackDays": 30, | |
"weight": 5, | |
"lookbackForRecentPurchasesAnchor": 30, | |
"transientOptions": { | |
"id": "pVMFX", | |
"ingredientKey": "CoBuy", | |
"editMode": True | |
}, | |
"itemTypeToRecommend":productInfo | |
, | |
}, | |
], | |
"exclusions": [ | |
{ | |
"@class": "com.apptegic.domain.recommendation.metadata.exclusion.ContextExclusionConfig", | |
"itemType": productInfo | |
, | |
"id": "1", | |
"negated": False, | |
"contextType": "InCart" | |
}, | |
{ | |
"@class": "com.apptegic.domain.recommendation.metadata.exclusion.ContextExclusionConfig", | |
"itemType": productInfo | |
, | |
"id": "2", | |
"contextType": "LastPurchased", | |
"negated": False | |
}, | |
], | |
"boosters": [], | |
"variations": [], | |
"recipeNormalizationCoefficients": { | |
"nameCoefficient": 1, | |
"descriptionCoefficient": 1, | |
"tagCoefficient": 1, | |
"categoryCoefficient": 1 | |
}, | |
"allowMissingAnchorItemForMultiIngredient": True, | |
"name": None, | |
"needsRetrain": True, | |
"inTraining": False, | |
"state": "Active" | |
} | |
recipeURL = f"https://{account}.{region}.evergage.com/internal/dataset/{dataset}/recipe/{recipeIndex}/unpublished" | |
response = requests.put(recipeURL, params=params, cookies=cookies, headers=headers, json=json_data) | |
body = response.content | |
status = response.status_code | |
#print("recipe config post: "+str(status)) | |
if status < 400: | |
recipesCreated +=1 | |
if categoryRecipe == "Yes": | |
recipeIndex = "gRPCd" | |
params = { | |
"forceRetrain": "False", | |
"name": "Anonymous Users Category Recommendations" | |
} | |
json_data = { | |
"id": "gRPCd", | |
"fallbackEnabled": True, | |
"fallbackPromotedContent": { | |
"@class": "com.apptegic.domain.campaign.promote.DynamicPromotedContent", | |
"minimumPromotedItemCount": 1, | |
"maximumPromotedItemCount": 120, | |
"skipPromotionOfPreviouslyViewedItems": False, | |
"skipPromotionOfPreviouslyPurchasedProducts": False, | |
"skipPromotionOfItemsInCart": True, | |
"promoteItemEvenIfBeingViewed": False, | |
"allowZeroPromotedItems": False, | |
"trendingFallbackDisabled": False, | |
"campaignTemplateLayout": None, | |
"primaryItemType": { | |
"@class": "com.apptegic.domain.catalog.metadata.CategoryTypeConfig", | |
"name": "Category", | |
"label": "Category", | |
"description": None, | |
"fontIconClass": None, | |
"baseUrl": None, | |
"enabled": True, | |
"attributeDefinitions": catRecipeAttributes, | |
"catalogRelationships": [], | |
"accumulable": False, | |
"cardinality": "ManyPerItem", | |
"fullName": "Category", | |
"itemType": "Category", | |
"stateId": "categories", | |
"stateName": "categories" | |
}, | |
"primaryItemStatType": "View", | |
"primaryStatConditionType": "GreatestValue", | |
"primaryItemLookback": { | |
"condition": "LESS_THAN", | |
"lookbackDays1": 14, | |
"lookbackDays2": 0 | |
}, | |
"relatedItemType": None, | |
"relatedItemStatType": None, | |
"relatedStatConditionType": None, | |
"relatedItemLookback": { | |
"condition": "LESS_THAN", | |
"lookbackDays1": 7, | |
"lookbackDays2": 0 | |
}, | |
"promoteItemOnlyIfPromotionStateIsPrioritized": False, | |
"useOnlyDepartmentsAsPrimaryCategories": False | |
}, | |
"created": 1657754477038, | |
"updated": 1657754477038, | |
"itemTypeToRecommend": { | |
"@class": "com.apptegic.domain.catalog.metadata.CategoryTypeConfig", | |
"name": "Category", | |
"label": "Category", | |
"description": None, | |
"fontIconClass": None, | |
"baseUrl": None, | |
"enabled": True, | |
"attributeDefinitions": catRecipeAttributes, | |
"catalogRelationships": [], | |
"accumulable": False, | |
"cardinality": "ManyPerItem", | |
"fullName": "Category", | |
"itemType": "Category", | |
"stateId": "categories", | |
"stateName": "categories" | |
}, | |
"timeSensitiveTraining": False, | |
"shouldRecordTrainingData": False, | |
"storeRecsOnUser": False, | |
"ingredients": [ | |
{ | |
"@class": "com.apptegic.domain.recommendation.metadata.ingredient.TrendingIngredientConfig", | |
"ingredient": { | |
"type": "Trending" | |
}, | |
"lookbackDays": 0, | |
"statType": "ViewTime", | |
"weight": 5, | |
"lookbackForRecentPurchasesAnchor": 30, | |
"transientOptions": { | |
"id": "KDjWa", | |
"ingredientKey": "Trending", | |
"editMode": True | |
}, | |
"itemTypeToRecommend": { | |
"@class": "com.apptegic.domain.catalog.metadata.CategoryTypeConfig", | |
"name": "Category", | |
"label": "Category", | |
"description": None, | |
"fontIconClass": None, | |
"baseUrl": None, | |
"enabled": True, | |
"attributeDefinitions": catRecipeAttributes, | |
"catalogRelationships": [], | |
"accumulable": False, | |
"cardinality": "ManyPerItem", | |
"fullName": "Category", | |
"itemType": "Category", | |
"stateId": "categories", | |
"stateName": "categories" | |
}, | |
}, | |
], | |
"exclusions": [], | |
"boosters": [], | |
"variations": [], | |
"recipeNormalizationCoefficients": { | |
"nameCoefficient": 1, | |
"descriptionCoefficient": 1, | |
"tagCoefficient": 1, | |
"categoryCoefficient": 1 | |
}, | |
"allowMissingAnchorItemForMultiIngredient": True, | |
"name": None, | |
"needsRetrain": True, | |
"inTraining": False, | |
"state": "Active" | |
} | |
recipeURL = f"https://{account}.{region}.evergage.com/internal/dataset/{dataset}/recipe/{recipeIndex}/unpublished" | |
response = requests.put(recipeURL, params=params, cookies=cookies, headers=headers, json=json_data) | |
body = response.content | |
status = response.status_code | |
#print("recipe config post: "+str(status)) | |
if status < 400: | |
recipesCreated +=1 | |
if incartRecipe == "Yes": | |
recipeIndex = "IN0OC" | |
params = { | |
"forceRetrain": "False", | |
"name": "In Cart Recommendation" | |
} | |
json_data = { | |
"id": "IN0OC", | |
"fallbackEnabled": True, | |
"fallbackPromotedContent": { | |
"@class": "com.apptegic.domain.campaign.promote.DynamicPromotedContent", | |
"minimumPromotedItemCount": 1, | |
"maximumPromotedItemCount": 120, | |
"skipPromotionOfPreviouslyViewedItems": False, | |
"skipPromotionOfPreviouslyPurchasedProducts": False, | |
"skipPromotionOfItemsInCart": True, | |
"promoteItemEvenIfBeingViewed": False, | |
"allowZeroPromotedItems": False, | |
"trendingFallbackDisabled": False, | |
"campaignTemplateLayout": None, | |
"primaryItemType": { | |
"itemType": "Product" | |
}, | |
"primaryItemStatType": "View", | |
"primaryStatConditionType": "GreatestValue", | |
"primaryItemLookback": { | |
"condition": "LESS_THAN", | |
"lookbackDays1": 14, | |
"lookbackDays2": 0 | |
}, | |
"relatedItemType": None, | |
"relatedItemStatType": None, | |
"relatedStatConditionType": None, | |
"relatedItemLookback": { | |
"condition": "LESS_THAN", | |
"lookbackDays1": 7, | |
"lookbackDays2": 0 | |
}, | |
"promoteItemOnlyIfPromotionStateIsPrioritized": False, | |
"useOnlyDepartmentsAsPrimaryCategories": False | |
}, | |
"created": 1657756617792, | |
"updated": 1657756617792, | |
"itemTypeToRecommend": productInfo , | |
"timeSensitiveTraining": False, | |
"shouldRecordTrainingData": False, | |
"storeRecsOnUser": False, | |
"ingredients": [ | |
{ | |
"@class": "com.apptegic.domain.recommendation.metadata.ingredient.CoBuyIngredientConfig", | |
"ingredient": { | |
"differentiator": None, | |
"type": "CoBuy" | |
}, | |
"minSupportTarget": 5, | |
"useInCartAnchorItem": False, | |
"anchorItemType": "InCart", | |
"newLogic": True, | |
"requireSameBasket": True, | |
"lookbackDays": 30, | |
"weight": 3, | |
"lookbackForRecentPurchasesAnchor": 30, | |
"transientOptions": { | |
"id": "wJQuF", | |
"ingredientKey": "CoBuy", | |
"editMode": True | |
}, | |
"itemTypeToRecommend": productInfo, | |
}, | |
], | |
"exclusions": [ | |
{ | |
"@class": "com.apptegic.domain.recommendation.metadata.exclusion.ContextExclusionConfig", | |
"itemType": productInfo | |
, | |
"id": "4uow3", | |
"negated": False, | |
"contextType": "InCart", | |
}, | |
{ | |
"@class": "com.apptegic.domain.recommendation.metadata.exclusion.PurchasedExclusionConfig", | |
"lookbackDays": 30, | |
"minStatCount": 1, | |
"itemTypeAsString": "Product", | |
"id": "AdxrI", | |
"itemType": productInfo, | |
}, | |
], | |
"boosters": [], | |
"variations": [], | |
"recipeNormalizationCoefficients": { | |
"nameCoefficient": 1, | |
"descriptionCoefficient": 1, | |
"tagCoefficient": 1, | |
"categoryCoefficient": 1 | |
}, | |
"allowMissingAnchorItemForMultiIngredient": True, | |
"name": None, | |
"needsRetrain": True, | |
"inTraining": False, | |
"state": "Active" | |
} | |
recipeURL = f"https://{account}.{region}.evergage.com/internal/dataset/{dataset}/recipe/{recipeIndex}/unpublished" | |
response = requests.put(recipeURL, params=params, cookies=cookies, headers=headers, json=json_data) | |
body = response.content | |
status = response.status_code | |
#print("recipe config post: "+str(status)) | |
if status < 400: | |
recipesCreated +=1 | |
if datasetCreated == "True": | |
datasetCreated = str(datasetCreated) | |
domainCreated = str(domainCreated) | |
identityAttributesCreated = str(identityAttributesCreated) | |
userAttributesCreated = str(userAttributesCreated) | |
catalogCreated = str(catalogCreated) | |
integrationsCreated = str(integrationsCreated) | |
recipesCreated = str(recipesCreated) | |
if datasetCreated == "True": | |
iconData1 = positive | |
else: | |
iconData1 = negative | |
if domainCreated == "True": | |
iconData2 = positive | |
else: | |
iconData2 = negative | |
if identityAttributeCount > int(identityAttributesCreated): | |
iconData3 = negative | |
else: | |
iconData3 = positive | |
if int(userAttributeCount) > int(userAttributesCreated): | |
iconData4 = negative | |
else: | |
iconData4 = positive | |
if catalogCreated == "1": | |
iconData5 = positive | |
else: | |
iconData5 = negative | |
if integrationsCreated == "1": | |
iconData6 = positive | |
else: | |
iconData6 = negative | |
if recipeCount > int(recipesCreated): | |
iconData7 = negative | |
else: | |
iconData7 = positive | |
userAttributeCount = str(userAttributeCount) | |
identityAttributeCount = str(identityAttributeCount) | |
datasetMessage ={ | |
"type": "mrkdwn", | |
"text": f"*Dataset Creation Report*\n\n {iconData1} Dataset Created? {datasetCreated}\n {iconData2} Domain Added? {domainCreated}\n {iconData3} Count of Identity Attributes added: {identityAttributesCreated}/{identityAttributeCount}\n {iconData4} Count of User Attributes added: {userAttributesCreated}/{userAttributeCount}\n {iconData5} Catalog created? {catalogCreated}\n {iconData6} Integrations created? {integrationsCreated}\n {positive} Count of recipes added: {recipesCreated}\n " | |
} | |
messagePart.append(datasetMessage) | |
print() | |
#ProductViews | |
if testData == "Yes": | |
#Get additional user attributes | |
userAttributes = {"emailAddress": testDataEmail | |
} | |
genericData = pd.read_excel(fullpath, sheet_name="UserData") | |
obj = genericData.fillna("") | |
obj = genericData.to_dict(orient="records") | |
if len(obj) > 0: | |
additionalUserAttributes = {} | |
i=0 | |
while i < len(obj): | |
string = (obj[i]) | |
for key, value in string.items(): | |
key = key | |
value = value | |
dim = {key:value} | |
additionalUserAttributes.update(dim) | |
i+=1 | |
userAttributes.update(additionalUserAttributes) | |
#create Product views for file, skip sheets with no rows | |
itemViews = 0 | |
itemsInSheets = 0 | |
itemTypes = ["ArticleViews","ProductViews","BlogViews",] | |
for item in itemTypes: | |
type = item.replace("Views","") | |
genericData = pd.read_excel(fullpath, sheet_name=item) | |
obj = genericData.fillna("") | |
obj = genericData.to_dict(orient="records") | |
if len(obj) > 0: | |
i=0 | |
while i < len(obj): | |
itemsInSheets += 1 | |
string = (obj[i]) | |
_id = string.get("_id") | |
offset = string.get("offset") | |
if offset == "" or offset is None: | |
offset = 0 | |
date = datetime.datetime.utcnow() + timedelta(minutes=offset) | |
utc_time = calendar.timegm(date.utctimetuple()) | |
time = utc_time*1000 | |
if type == "Product": | |
categories = string.get("categories") | |
productinfo = {} | |
dimensions = {} | |
for key, value in string.items(): | |
key = key | |
value = value | |
if key.find("dimension") >-1: | |
if len(str(value)) > 1: | |
value =str(value) | |
list = value.split(",") | |
name = key.replace("dimension-","") | |
dim = {name:list} | |
dimensions.update(dim) | |
elif key.find("_id")<0 and key.find("dimension") < 0 and key.find("categories") and key.find("offset")<0 : | |
prod = {key:value} | |
productinfo.update(prod) | |
dimensionObject ={"relatedCatalogObjects":dimensions} | |
product = { | |
"_id":_id, | |
} | |
product.update(productinfo) | |
if type == "Product": | |
catDetail = { | |
"categories": | |
[ | |
{"type":"c", | |
"_id": categories | |
} | |
] | |
} | |
product.update(catDetail) | |
if len(dimensions) >0: | |
product.update(dimensionObject) | |
json = { | |
type : product | |
} | |
payload = { | |
"action": f"{type} View", | |
"itemAction": "View Item", | |
"catalog":json, | |
"user": | |
{"id": testDataEmail, | |
"attributes": userAttributes | |
}, | |
"source": | |
{"channel": "Web", | |
"application": "CMS", | |
"time": time | |
} | |
} | |
# Post to endpoint | |
header = {"Content-Type":"application/json"} | |
url = sendEventURL | |
post = requests.post(url, json = payload ,headers = header, auth = HTTPBasicAuth(apiId,apiKey)) | |
# Catch Response | |
body = post.content | |
status = post.status_code | |
#print("Item View post status code:"+str(status)) | |
if status < 400: | |
itemViews +=1 | |
i+=1 | |
#get data from Puchase sheet | |
orderIds = [] | |
purchasesCreated = 0 | |
purchasesInSheet = 0 | |
genericPurchaseData = pd.read_excel(fullpath, sheet_name="Purchase") | |
obj = genericPurchaseData.fillna("") | |
purchaseObj = obj.to_dict(orient="records") | |
if len(purchaseObj) > 0: | |
purchasesInSheet += 1 | |
i = 0 | |
while i < len(purchaseObj): | |
string = (purchaseObj[i]) | |
orderId = string.get("orderId") | |
orderIds.append(orderId) | |
i+=1 | |
#add to cart items | |
itemsInCartSheet = 0 | |
cartCreated = 0 | |
genericData = pd.read_excel(fullpath, sheet_name="AddToCart") | |
obj = genericData.fillna("") | |
addtoCartObj = obj.to_dict(orient="records") | |
for arrayId in orderIds: | |
lineItems = [] | |
i=0 | |
while i < len(addtoCartObj): | |
#Add Items to cart | |
string = (addtoCartObj[i]) | |
cartId = string.get("cartId") | |
if arrayId == cartId: | |
price = string.get("price") | |
_id = string.get("_id") | |
quantity = string.get("quantity") | |
offset = string.get("offset") | |
if offset == "": | |
offset = 0 | |
date = datetime.datetime.utcnow() + timedelta(minutes=offset) | |
utc_time = calendar.timegm(date.utctimetuple()) | |
time = utc_time*1000 | |
lineItem = { | |
"_id": _id, | |
"price": price, | |
"quantity": quantity | |
} | |
lineItems.append(lineItem) | |
payload = { | |
"action":"AddToCart", | |
"itemAction":"Add To Cart", | |
"cart":{ | |
"singleLine":{ | |
"Product":{ | |
"_id":_id, | |
"price":price, | |
"quantity":quantity | |
} | |
} | |
} | |
, | |
"user": | |
{"id": testDataEmail, | |
"attributes": userAttributes | |
} | |
, | |
"source":{ | |
"channel":"Web", | |
"application":"CMS", | |
"time": time | |
} | |
} | |
header = {"Content-Type":"application/json"} | |
url = sendEventURL | |
post = requests.post(url, json = payload ,headers = header, auth = HTTPBasicAuth(apiId,apiKey)) | |
# Catch Response | |
body = post.content | |
status = post.status_code | |
if status < 400: | |
cartCreated += 1 | |
i+=1 | |
#Purchase events | |
if len(purchaseObj) > 0: | |
x = 0 | |
while x < len(purchaseObj): | |
string = (purchaseObj[x]) | |
orderId = string.get("orderId") | |
if orderId == arrayId: | |
total = string.get("total") | |
offset = string.get("offset") | |
currency = string.get("currency") | |
if offset == "": | |
offset = 0 | |
date = datetime.datetime.utcnow() + timedelta(minutes=offset) | |
utc_time = calendar.timegm(date.utctimetuple()) | |
time = utc_time*1000 | |
payload = { | |
"action": "Order Success", | |
"itemAction": "Purchase", | |
"order": { | |
"Product": { | |
"orderId": orderId, | |
"totalValue": total, | |
"currency": currency, | |
"lineItems": lineItems | |
} | |
} | |
, | |
"user": | |
{"id": testDataEmail, | |
"attributes": userAttributes | |
}, | |
"source":{ | |
"channel":"Web", | |
"application":"CMS", | |
"time": time | |
} | |
} | |
header = {"Content-Type":"application/json"} | |
url = sendEventURL | |
post = requests.post(url, json = payload ,headers = header, auth = HTTPBasicAuth(apiId,apiKey)) | |
# Catch Response | |
body = post.content | |
status = post.status_code | |
if status < 400: | |
purchasesCreated+=1 | |
x+=1 | |
#create data for slack report post | |
purchasesInSheet = len(purchaseObj) | |
itemsInCartSheet = len(addtoCartObj) | |
itemsInSheets = str(itemsInSheets) | |
itemViews = str(itemViews) | |
itemsInCartSheet = str(itemsInCartSheet) | |
cartCreated = str(cartCreated) | |
purchasesInSheet = str(purchasesInSheet) | |
purchasesCreated = str(purchasesCreated) | |
if int(itemsInSheets) > int(itemViews): | |
icon1 = negative | |
else: | |
icon1 = positive | |
if int(itemsInCartSheet) > int(cartCreated): | |
icon2 = negative | |
else: | |
icon2 = positive | |
if int(purchasesCreated) < int(purchasesInSheet): | |
icon3 = negative | |
else: | |
icon3 = positive | |
if int(itemViews) > 0: | |
testDataMessage = { | |
"type": "mrkdwn", | |
"text": f"\n*Test Event Data Report*\n\n {icon1} Item Views: {itemViews}/{itemsInSheets}\n {icon2} Add to Cart Actions: {cartCreated}/{itemsInCartSheet}\n {icon3} Sucessful Purchases: {purchasesCreated}/{purchasesInSheet}" | |
} | |
messagePart.append(testDataMessage) | |
else: | |
messagePart.append(errorMessage) | |
elif "deploy IS" in text: | |
errorMessage= { | |
"type": "mrkdwn", | |
"text": "*ERROR!!!*\n File Missing, where is your IS xls file?" | |
} | |
messagePart.append(errorMessage) | |
url = slackURL | |
header = {"Content-Type":"application/json"} | |
post = requests.post(url, json = messageBody ,headers = header) | |
body = post.content | |
status = post.status_code | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment