Skip to content

Instantly share code, notes, and snippets.

@ojmccall
Created January 24, 2023 22:58
Show Gist options
  • Save ojmccall/c1e1f2d2f96a9b2cfe31274bcc502462 to your computer and use it in GitHub Desktop.
Save ojmccall/c1e1f2d2f96a9b2cfe31274bcc502462 to your computer and use it in GitHub Desktop.
deployment script for slack app to deploy IS datasets
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