Using Personas to Simulate Hallux Data
Contents
Using Personas to Simulate Hallux Data#
This example has two goals#
To assign personas to the different entities in the Hallux database either randomly or based on existing data.
To use the assigned personas to predict an entity’s behavoior in order to create new data.
The example starts by importing the required libraries and creating dictionaries#
# import libraries
from os import getenv
import pandas as pd
import random
from datetime import *; from dateutil.relativedelta import *
import pymssql
from sqlalchemy import create_engine
# Personas dictionary (defined in Hallux tables Persona_Type and Persona)
Personas = {
"Album" : ("Bomb","Non-Certified","Gold","Platinum","Multi Platinum","Diamond"),
"Band" : ("Bar Band","Cover","Tribute","1 hit wonder","Popular","Cult Following","Megaband"),
"Customer" : ("Occasioanl","Discount","Loyal"),
"Employee" : ("Bad","Good","Overachiever"),
"Experience" : ("Bad","Good","Wonderful"),
"Fan" : ("Casual","Fan","Fanatic"),
"Music" : ("Popular", "Regional","Seasonal","Specialty","Nostalgic"),
"Musician" : ("Amateur","Studio","Backup","Lead","Superstar"),
"Performance" : ("Bad","Good","Epic"),
"Person" : ("Student","Young Adult","Mature"),
"Song" : ("Bad","Good","Popular","Hit"),
"Video" : ("Bad","Good","Viral")
}
# Hallux entity types dictionary defined in the Entity_Type table
# Added an additional value list (table, id column, persona type, assignment method)
# and put in the order needed for processing if not using random selection
# For example, a band's persona will be determined by the personas of its band members.
Entities_List_Columns = ("Table Name","Artificial Key","Persona Type","Assignment Method","Persona Status","Simulation Status")
Entities = {
"Genre" : ("Genre","Genre_Id","Music","Market Share","Under Construction","Under Construction"),
"Member" : ("Band_Member","Member_Id","Musician","Random","Completed","Under Construction"),
"Band" : ("Band","Band_Id","Band","Members","Under Construction","Under Construction"),
"Agent" : ("Agent","Agent_Id","Employee","Commissions","Under Construction","Under Construction"),
"Album" : ("Album","Album_Id","Album","Sales","Under Construction","Under Construction"),
"Song" : ("Song","Song_Id","Song","Sales","Under Construction","Under Construction"),
"Performance" : ("Performance","Performance_Id","Performance","Revenue","Under Construction","Under Construction"),
"Producer" : ("Producer","Producer_Id","Employee","Revenue","Under Construction","Under Construction"),
"Video" : ("Video","Video_Id","Video","Streams","Under Construction","Under Construction"),
"Profile" : ("Customer_Profile","Profile_Id","Person","Demographics","Under Construction","Under Construction"),
"Customer" : ("Customer","Customer_Id","Customer","Purchases","Under Construction","Under Construction"),
"Follower" : ("Band_Follower","Follower_Id","Fan","Engagement","Under Construction","Under Construction"),
"Playlist" : ("Playlist","Playlist_Id","Experience","Streams","Under Construction","Under Construction"),
"Order" : ("Order","Order_Id","Experience","Probability","Under Construction","Under Construction"),
"Stream" : ("Stream","Stream_Id","Experience","Probability","Under Construction","Under Construction")
}
Let’s assign Personas#
The dictionaries defined above reflect the Hallux tables designed to support personas#

Set an individual entity’s persona#
The set_persona function will update the database with an entities persona
Note: This function uses a stored procedure to update data (sql provided below). This allows for greater security and users do not need update, insert or delete permission on specific tables, only execute permission for this stored procedure. Also, the stored procedure will determine if the information needs to be inserted or updated.
def set_persona ( a_entity_type, a_entity_id, a_persona_type, a_persona ):
ret_val = ''
sp_exec_sql = "prc_set_Persona"
parm_list = (str(a_entity_type), int(a_entity_id), str(a_persona_type), str(a_persona))
cursor.callproc(sp_exec_sql,parm_list)
return ret_val
IF OBJECT_ID('dbo.prc_set_Persona') IS NOT NULL
BEGIN
DROP PROCEDURE dbo.prc_set_Persona
IF OBJECT_ID('dbo.prc_set_Persona') IS NOT NULL
PRINT '<<< FAILED DROPPING PROCEDURE dbo.prc_set_Persona >>>'
ELSE
PRINT '<<< DROPPED PROCEDURE dbo.prc_set_Persona >>>'
END
go
Create Proc dbo.prc_set_Persona ( @a_Entity_Type varchar(30), @a_Entity_Id int, @a_Persona_Type varchar(30) , @a_Persona varchar(30) )
As
Begin
/*-----------------------------------------------------------------------------
Procedure Name: prc_set_Persona
Function: Populate the persona table
Parameters: a_Entity_Type, a_Entity_Id, a_Persona_Type, a_Persona
Modifications:
11/18/2022 JDS Initial Version
-----------------------------------------------------------------------------*/
DECLARE @errorMsg varchar(255)
, @errorNum int
, @RowCount int
, @Entity_Type_Id int
, @Persona_Type_Id int
, @Persona_Id int
-- Initialization
SELECT @errorNum = 0
select @Entity_Type_Id = Entity_Type_Id from Entity_Type where Entity_Type = @a_Entity_Type
if @Entity_Type_Id is null
BEGIN
SELECT @errorMsg = 'Unknown Entity Type.'
GOTO err_rtn
END
select @Persona_Type_Id = Persona_Type_Id from Persona_Type where Persona_Type = @a_Persona_Type
if @Persona_Type_Id is null
BEGIN
SELECT @errorMsg = 'Unknown Persona Type.'
GOTO err_rtn
END
select @Persona_Id = Persona_Id from Persona where Persona_Name = @a_Persona and Persona_Type_Id = @Persona_Type_Id
if @Persona_Id is null
BEGIN
SELECT @errorMsg = 'Unknown Persona.'
GOTO err_rtn
END
if not exists ( select 1 from Persona_Entity where Entity_Id = @a_Entity_Id and Entity_Type_Id = @Entity_Type_Id)
begin
insert into Persona_Entity ( Persona_Id, Entity_Id, Entity_Type_Id ) values (@Persona_Id, @a_Entity_Id, @Entity_Type_Id )
SELECT @errorNum = @@error , @Rowcount = @@Rowcount
IF @errorNum != 0 or @Rowcount = 0
BEGIN
SELECT @errorMsg = 'Failed inserting row into Persona_Entity.'
GOTO err_rtn
END
end
else
begin
update Persona_Entity set Persona_Id = @Persona_Id
where Entity_Id = @a_Entity_Id
and Entity_Type_Id = @Entity_Type_Id
SELECT @errorNum = @@error , @Rowcount = @@Rowcount
IF @errorNum != 0 or @Rowcount = 0
BEGIN
SELECT @errorMsg = 'Update of Persona_Entity failed.'
GOTO err_rtn
END
end
good_rtn:
Return 0
err_rtn:
select @errorMsg = 'SQL Exception (' + object_name(@@procid) + ') - ' + @errorMsg + case when IsNull(@errornum,0) <> 0 then ' (SQL Errno: ' + convert(varchar(30),@errorNum) + ')' else null end
select @errmsg
Return -1
End /* prc_set_Persona */
go
IF OBJECT_ID('dbo.prc_set_Persona') IS NOT NULL
PRINT '<<< CREATED PROCEDURE dbo.prc_set_Persona >>>'
ELSE
PRINT '<<< FAILED CREATING PROCEDURE dbo.prc_set_Persona >>>'
go
Get an individual entity’s persona#
The get_persona function will return an entity’s persona
def get_persona ( a_entity_type, a_entity_id, a_persona_type):
my_persona = ''
return my_persona
What is my Persona?#
The my_persona function will determine the persona for an entity
Will start by using a random selection
def whats_my_persona ( a_entity_type, a_entity_id, a_persona_type, a_assignment_method ):
my_persona = ''
if a_assignment_method == 'Random' :
if a_persona_type in Personas:
persona_list = Personas[a_persona_type]
ndx = random.randint(1, len(persona_list)) - 1
my_persona = persona_list[ndx]
return my_persona
Assign Personas#
The assign_personas function will loop thru all the members of a specific Hallux entity and assign them personas
This is where the hard work is done!
def assign_personas ( a_entity_type, a_persona_type, a_assignment_method, a_status ):
ret_val = a_status
if a_status not in ('Completed','Under Construction') :
# get all the entity ids
if a_entity_type in Entities:
entity_list = Entities[a_entity_type]
sql_select = 'SELECT ' + entity_list[1] + ' FROM ' + entity_list[0] + ' order by ' + entity_list[1]
df_entity = pd.read_sql(sql_select, eng)
df_entity.set_index(entity_list[1])
ret_val = str(len(df_entity))
# assign and save persona
for i in range(len(df_entity)):
curr_entity_id = df_entity[entity_list[1]][i]
curr_persona = whats_my_persona ( a_entity_type, curr_entity_id, a_persona_type, a_assignment_method )
if curr_persona != '' :
set_persona ( a_entity_type, curr_entity_id, a_persona_type, curr_persona )
conn.commit()
ret_val = str(len(df_entity))
return ret_val
Now that the Personas have been assigned we can start Simulating data…#
Simulate Follower Data#
This fuction will determine which bands the specified customer profile will follow, if any
def sim_follower_data (a_start_date, a_end_date):
ret_val = 'Under Construction'
return ret_val
Simulate Playlist Data#
This fuction will determine which playlists the specified customer profile will create, if any
def sim_playlist_data (a_start_date, a_end_date):
ret_val = 'Under Construction'
return ret_val
Simuate Streaming Data#
This fuction will determine which songs the specified customer profile will stream, if any
def sim_streaming_data (a_start_date, a_end_date):
ret_val = 'Under Construction'
return ret_val
Simuate Data#
This fuction calls the appropriate simulation function based on the entitity type and status
def sim_data ( a_entity_type, a_start_date, a_end_date, a_status ):
ret_val = a_status
if a_status not in ('Completed','Under Construction') :
if a_entity_type == 'Follower':
ret_val = sim_follower_data (a_start_date, a_end_date )
if a_entity_type == 'Playlist':
ret_val = sim_playlist_data (a_start_date, a_end_date )
if a_entity_type == 'Stream':
ret_val = sim_streaming_data (a_start_date, a_end_date )
return ret_val
Here we go…#
Initalization, database engine
Assign personas to Hallux entities
Generate the data for the specified date range!
This section of code loops thru the different entities and calls the appropriate functions to assign personas and/or simulated data. The persona and simulation status columns in the Entities dictionary are used to determine whether or not an action should be performed. Currently, there is only a random persona assignment logic and no data simulation logic.
hallux_svr = getenv("halluxsvr")
hallux_usr = getenv("halluxusr")
hallux_psd = getenv("halluxpsd")
hallux_db = getenv("halluxdb")
# for use in creating a dataframe
conn_string = f"mssql+pyodbc://{hallux_usr}:{hallux_psd}@{hallux_svr}/{hallux_db}?driver=SQL+Server+Native+Client+11.0&TrustServerCertificate=yes"
eng = create_engine(conn_string, fast_executemany=True)
# used for executing dynamic sql
conn = pymssql.connect(hallux_svr, hallux_usr, hallux_psd, hallux_db)
cursor = conn.cursor(as_dict=True)
# loop thru the entities and assign the appropriate persona
start_date = date(2023, 1, 1)
end_date = date(2023, 12, 31)
for curr_entity, curr_value in Entities.items():
print(curr_entity)
ret_per = assign_personas (curr_entity, curr_value[2], curr_value[3], curr_value[4])
ret_sim = sim_data (curr_entity, start_date, end_date, curr_value[5])
print(' Personas: ',ret_per)
print(' Simulation: ', ret_sim )
conn.close()
eng.dispose()
Genre
Personas: Under Construction
Simulation: Under Construction
Member
Personas: 1415
Simulation: Under Construction
Band
Personas: Under Construction
Simulation: Under Construction
Agent
Personas: Under Construction
Simulation: Under Construction
Album
Personas: Under Construction
Simulation: Under Construction
Song
Personas: Under Construction
Simulation: Under Construction
Performance
Personas: Under Construction
Simulation: Under Construction
Producer
Personas: Under Construction
Simulation: Under Construction
Video
Personas: Under Construction
Simulation: Under Construction
Profile
Personas: Under Construction
Simulation: Under Construction
Customer
Personas: Under Construction
Simulation: Under Construction
Follower
Personas: Under Construction
Simulation: Under Construction
Playlist
Personas: Under Construction
Simulation: Under Construction
Order
Personas: Under Construction
Simulation: Under Construction
Stream
Personas: Under Construction
Simulation: Under Construction