{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Module 3. 개인화 추천 모델 학습 및 캠페인 생성\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Library Import \n",
    "\n",
    "파이썬에는 광범위한 라이브러리 모음이 포함되어 있으며, 본 LAB을 위해서 핵심 Data Scientist용 Tool 인 boto3 (AWS SDK) 및 Pandas/Numpy와 같은 라이브러리를 가져와야 합니다. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Imports\n",
    "#from codes import lambda_personalize\n",
    "import boto3\n",
    "import json\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import time\n",
    "import jsonlines\n",
    "import os\n",
    "\n",
    "\n",
    "from datetime import datetime\n",
    "import sagemaker\n",
    "import time\n",
    "import warnings\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib.dates import DateFormatter\n",
    "import matplotlib.dates as mdate\n",
    "from botocore.exceptions import ClientError"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "다음으로 여러분의 환경이 Amazon Personalize와 성공적으로 통신할 수 있는지 확인해야 합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Configure the SDK to Personalize:\n",
    "personalize = boto3.client('personalize')\n",
    "personalize_runtime = boto3.client('personalize-runtime')\n",
    "s3 = boto3.resource('s3')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 아래 코드 셀은 이전 notebook에서 저장했던 변수들을 불러옵니다.\n",
    "%store -r"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "DATA_PREFIX = \"dataset\"\n",
    "KEY_PREFIX = \"user-personalization\"\n",
    "DATA_SET_GROUP_NAME= WORK_DATE+ \"-dataset-group\"\n",
    "SCHEMA_NAME_INTERACTION= WORK_DATE+ \"-schema-interactions\"\n",
    "DATASET_NAME_INTERACTION=\"dataset-interactions\"\n",
    "DATASET_NAME_USERS=\"dataset-users\"\n",
    "DATASET_NAME_ITEMS=\"dataset-items\"\n",
    "\n",
    "#생성할 오브젝트의 끝에 임의의 숫자를 부여하기 위해 suffix 정의\n",
    "suffix = str(np.random.uniform())[4:9]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%store suffix \n",
    "%store DATA_PREFIX \n",
    "%store KEY_PREFIX \n",
    "%store DATA_SET_GROUP_NAME\n",
    "%store SCHEMA_NAME_INTERACTION\n",
    "%store DATASET_NAME_INTERACTION\n",
    "%store DATASET_NAME_USERS\n",
    "%store DATASET_NAME_ITEMS"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "inter_train=pd.read_csv(interaction_train_file)\n",
    "user_train=pd.read_csv(user_train_file)\n",
    "item_train=pd.read_csv(item_train_file)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "inter_train.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "user_train.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "item_train.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 퍼스널라이즈 워크 플로우\n",
    "\n",
    "Personalize는 S3에 업로드 된 데이터를 고객이 정의한 스키마에 맞춰 학습 노드로 임포트 하게 됩니다. 데이터 임포트 후에는 퍼스널라이즈에서 추가적인 데이터 분석 및 EDA 작업을 한뒤 학습을 하게 됩니다. 학습 완료 후 모델의 성능을 확인해 보고 캠페인 생성을 통해 서비스를 위해 배포하게 됩니다. 상세한 내용은 [여기](https://docs.aws.amazon.com/ko_kr/personalize/latest/dg/what-is-personalize.html) 링크를 통해 확인해 봅니다.\n",
    "\n",
    "\n",
    "![image.png](images/image2.png)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## S3로 데이터 업로드 하기\n",
    "\n",
    "정제된 파일을 S3에 업로드합니다.\n",
    "업로드에는 몇분이 걸릴 수 있습니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "target=KEY_PREFIX+\"/\"+DATA_PREFIX+\"/\"+interaction_train_file\n",
    "boto3.Session().resource('s3').Bucket(BUCKET_NAME).Object(target).upload_file(interaction_train_file)\n",
    "\n",
    "target=KEY_PREFIX+\"/\"+DATA_PREFIX+\"/\"+user_train_file\n",
    "boto3.Session().resource('s3').Bucket(BUCKET_NAME).Object(target).upload_file(user_train_file)\n",
    "\n",
    "\n",
    "target=KEY_PREFIX+\"/\"+DATA_PREFIX+\"/\"+item_train_file\n",
    "boto3.Session().resource('s3').Bucket(BUCKET_NAME).Object(target).upload_file(item_train_file)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 스키마 생성\n",
    "\n",
    "Personalize가 데이터를 이해하는 방법의 핵심 구성 요소는 아래 정의 된 스키마(schema)에서 비롯됩니다. 이 설정은 CSV 파일을 통해 제공된 데이터를 요약하는 방법을 Personalize 서비스에 알려줍니다. 스카마의 이름(name)과 유형(type)은 이전에 생성한 학습 파일의 컬럼의 순서와 유형하고 일치합니다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interaction 스키마 생성"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "interaction_schema_name=\"Interaction-schema-\"+WORK_DATE+\"-\"+suffix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "interaction_schema = {\n",
    "    \"type\": \"record\",\n",
    "    \"name\": \"Interactions\",\n",
    "    \"namespace\": \"com.amazonaws.personalize.schema\",\n",
    "    \"fields\": [\n",
    "        {\n",
    "            \"name\": \"USER_ID\",\n",
    "            \"type\": \"string\"\n",
    "        },\n",
    "        {\n",
    "            \"name\": \"ITEM_ID\",\n",
    "            \"type\": \"string\"\n",
    "        },\n",
    "        { \n",
    "            \"name\": \"EVENT_VALUE\",\n",
    "            \"type\": \"float\"\n",
    "        },\n",
    "        {\n",
    "            \"name\": \"TIMESTAMP\",\n",
    "            \"type\": \"long\"\n",
    "        },\n",
    "        {\n",
    "            \"name\": \"EVENT_TYPE\",\n",
    "            \"type\": \"string\"\n",
    "        }\n",
    "    ],\n",
    "    \"version\": \"1.0\"\n",
    "}\n",
    "\n",
    "\n",
    "create_schema_response = personalize.create_schema(\n",
    "    name = interaction_schema_name,\n",
    "    schema = json.dumps(interaction_schema)\n",
    ")\n",
    "\n",
    "interaction_schema_arn = create_schema_response['schemaArn']\n",
    "print(json.dumps(create_schema_response, indent=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### User 스키마 생성"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "user_schema_name=\"User-schema-\"+WORK_DATE+\"-\"+suffix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "user_schema = {\n",
    "    \"type\": \"record\",\n",
    "    \"name\": \"Users\",\n",
    "    \"namespace\": \"com.amazonaws.personalize.schema\",\n",
    "    \"fields\": [\n",
    "        {\n",
    "            \"name\": \"USER_ID\",\n",
    "            \"type\": \"string\"\n",
    "        },\n",
    "      {\n",
    "          \"name\": \"GENDER\",\n",
    "          \"type\": [\"string\",\"null\"],\n",
    "          \"categorical\": True\n",
    "      },\n",
    "        {\n",
    "          \"name\": \"AGE\",\n",
    "          \"type\": [\"string\",\"null\"],\n",
    "          \"categorical\": True\n",
    "      },\n",
    "        {\n",
    "          \"name\": \"OCCUPATION\",\n",
    "          \"type\": [\"string\",\"null\"],\n",
    "          \"categorical\": True\n",
    "      }\n",
    "     \n",
    "    ],\n",
    "    \"version\": \"1.0\"\n",
    "}\n",
    "\n",
    "\n",
    "create_schema_response = personalize.create_schema(\n",
    "    name = user_schema_name,\n",
    "    schema = json.dumps(user_schema)\n",
    ")\n",
    "\n",
    "user_schema_arn = create_schema_response['schemaArn']\n",
    "print(json.dumps(create_schema_response, indent=2))\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ITEM 스키마 생성"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "item_schema_name=\"Item-schema-\"+WORK_DATE+\"-\"+suffix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "item_schema = {\n",
    "    \"type\": \"record\",\n",
    "    \"name\": \"Items\",\n",
    "    \"namespace\": \"com.amazonaws.personalize.schema\",\n",
    "    \"fields\": [\n",
    "    {\n",
    "        \"name\": \"ITEM_ID\",\n",
    "        \"type\": \"string\"\n",
    "    },\n",
    "   \n",
    "    \n",
    "    {\n",
    "        \"name\": \"GENRE\",\n",
    "        \"type\": [\"string\",\"null\"],\n",
    "        \"categorical\": True\n",
    "    }\n",
    "      \n",
    "        \n",
    "    ],\n",
    "    \"version\": \"1.0\"\n",
    "}\n",
    "\n",
    "create_metadata_schema_response = personalize.create_schema(\n",
    "    name = item_schema_name,\n",
    "    schema = json.dumps(item_schema)\n",
    ")\n",
    "\n",
    "item_schema_arn = create_metadata_schema_response['schemaArn']\n",
    "print(json.dumps(create_metadata_schema_response, indent=2))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 데이터 세트 그룹 생성 및 대기\n",
    "\n",
    "Personalize에서 가장 큰 단위는 **데이터 세트 그룹(Dataset Group)** 이며, 이렇게 하면 데이터, 이벤트 추적기(event tracker), 솔루션(solution) 및 캠페인(campaign)이 분리됩니다. 공통의 데이터 수집을 공유하는 것들을 그룹화합니다. 원하는 경우 아래 그룹명을 자유롭게 변경해 주세요.\n",
    "\n",
    "![image.png](images/image3.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 데이터 세트 그룹 생성"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "create_dataset_group_response = personalize.create_dataset_group(\n",
    "    name = DATA_SET_GROUP_NAME\n",
    ")\n",
    "\n",
    "dataset_group_arn = create_dataset_group_response['datasetGroupArn']\n",
    "print(json.dumps(create_dataset_group_response, indent=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 데이터 세트 그룹이 활성화 상태가 될 때까지 대기\n",
    "\n",
    "아래의 모든 항목에서 Dataset Group을 사용하려면 활성화(active)가 되어야 합니다. 아래 셀을 실행하고 DatasetGroup: ACTIVE로 변경될 때까지 기다려 주세요."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "max_time = time.time() + 3*60*60 # 3 hours\n",
    "while time.time() < max_time:\n",
    "    describe_dataset_group_response = personalize.describe_dataset_group(\n",
    "        datasetGroupArn = dataset_group_arn\n",
    "    )\n",
    "    status = describe_dataset_group_response[\"datasetGroup\"][\"status\"]\n",
    "    print(\"DatasetGroup: {}\".format(status))\n",
    "    \n",
    "    if status == \"ACTIVE\" or status == \"CREATE FAILED\":\n",
    "        break\n",
    "        \n",
    "    time.sleep(15)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 데이터 세트 생성\n",
    "\n",
    "그룹 다음으로 생성할 것은 실제 데이터 세트입니다. 아래의 코드 셀을 실행하여 데이터 세트을 생성해 주세요."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interaction 데이터 세트 생성"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dataset_type = \"INTERACTIONS\"\n",
    "create_dataset_response = personalize.create_dataset(\n",
    "    name = DATASET_NAME_INTERACTION,\n",
    "    datasetType = dataset_type,\n",
    "    datasetGroupArn = dataset_group_arn,\n",
    "    schemaArn = interaction_schema_arn\n",
    ")\n",
    "\n",
    "interaction_dataset_arn = create_dataset_response['datasetArn']\n",
    "print(json.dumps(create_dataset_response, indent=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  USER 데이터 세트 생성 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dataset_type = \"USERS\"\n",
    "\n",
    "create_dataset_response = personalize.create_dataset(\n",
    "    name = DATASET_NAME_USERS,\n",
    "    datasetType = dataset_type,\n",
    "    datasetGroupArn = dataset_group_arn,\n",
    "    schemaArn = user_schema_arn\n",
    ")\n",
    "\n",
    "user_dataset_arn = create_dataset_response['datasetArn']\n",
    "print(json.dumps(create_dataset_response, indent=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ITEM 데이터 세트 생성"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dataset_type = \"ITEMS\"\n",
    "\n",
    "create_dataset_response = personalize.create_dataset(\n",
    "    name = DATASET_NAME_ITEMS,\n",
    "    datasetType = dataset_type,\n",
    "    datasetGroupArn = dataset_group_arn,\n",
    "    schemaArn = item_schema_arn\n",
    ")\n",
    "\n",
    "item_dataset_arn = create_dataset_response['datasetArn']\n",
    "print(json.dumps(create_dataset_response, indent=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### S3 버킷에 정책 부여\n",
    "\n",
    "Amazon Personalize는 앞서 생성한 S3 버킷의 내용을 읽을 수 있어야 합니다. 아래 코드 셀로 S3 버킷 접근 정책(policy)을 부여합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "s3 = boto3.client(\"s3\")\n",
    "\n",
    "policy = {\n",
    "    \"Version\": \"2012-10-17\",\n",
    "    \"Id\": \"PersonalizeS3BucketAccessPolicy\",\n",
    "    \"Statement\": [\n",
    "        {\n",
    "            \"Sid\": \"PersonalizeS3BucketAccessPolicy\",\n",
    "            \"Effect\": \"Allow\",\n",
    "            \"Principal\": {\n",
    "                \"Service\": \"personalize.amazonaws.com\"\n",
    "            },\n",
    "            \"Action\": [\n",
    "                \"s3:*Object\",\n",
    "                \"s3:ListBucket\",\n",
    "            ],\n",
    "            \"Resource\": [\n",
    "                \"arn:aws:s3:::{}\".format(BUCKET_NAME),\n",
    "                \"arn:aws:s3:::{}/*\".format(BUCKET_NAME)\n",
    "            ]\n",
    "        }\n",
    "    ]\n",
    "}\n",
    "\n",
    "s3.put_bucket_policy(Bucket=BUCKET_NAME, Policy=json.dumps(policy))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Personalize IAM Role 생성\n",
    "\n",
    "또한, Amazon Personalize는 특정 작업들을 실행할 권한을 갖기 위해, AWS에서 역할을 맡을 수 있는 기능이 필요합니다. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "iam = boto3.client(\"iam\")\n",
    "role_name = \"PersonalizeRoleDemo\" + suffix\n",
    "assume_role_policy_document = {\n",
    "    \"Version\": \"2012-10-17\",\n",
    "    \"Statement\": [\n",
    "        {\n",
    "          \"Effect\": \"Allow\",\n",
    "          \"Principal\": {\n",
    "            \"Service\": \"personalize.amazonaws.com\"\n",
    "          },\n",
    "          \"Action\": \"sts:AssumeRole\"\n",
    "        }\n",
    "    ]\n",
    "}\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "try:\n",
    "    create_role_response = iam.create_role(\n",
    "        RoleName = role_name,\n",
    "        AssumeRolePolicyDocument = json.dumps(assume_role_policy_document)\n",
    "    );\n",
    "\n",
    "    iam.attach_role_policy(\n",
    "        RoleName = role_name,\n",
    "        PolicyArn = \"arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess\"\n",
    "    );\n",
    "\n",
    "    role_arn = create_role_response[\"Role\"][\"Arn\"]\n",
    "except ClientError as e:\n",
    "    if e.response['Error']['Code'] == 'EntityAlreadyExists':\n",
    "        role_arn = iam.get_role(RoleName=role_name)['Role']['Arn']\n",
    "    else:\n",
    "        raise\n",
    "        \n",
    "\n",
    "# AmazonPersonalizeFullAccess provides access to any S3 bucket with a name that includes \"personalize\" or \"Personalize\" \n",
    "# if you would like to use a bucket with a different name, please consider creating and attaching a new policy\n",
    "# that provides read access to your bucket or attaching the AmazonS3ReadOnlyAccess policy to the role\n",
    "policy_arn = \"arn:aws:iam::aws:policy/service-role/AmazonPersonalizeFullAccess\"\n",
    "iam.attach_role_policy(\n",
    "    RoleName = role_name,\n",
    "    PolicyArn = policy_arn\n",
    ")\n",
    "\n",
    "# Now add S3 support\n",
    "iam.attach_role_policy(\n",
    "    RoleName=role_name,    \n",
    "    PolicyArn='arn:aws:iam::aws:policy/AmazonS3FullAccess'\n",
    ")\n",
    "time.sleep(60) # wait for a minute to allow IAM role policy attachment to propagate\n",
    "\n",
    "role_arn = create_role_response[\"Role\"][\"Arn\"]\n",
    "print(role_arn)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%store role_arn"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 데이터 세트 Import\n",
    "\n",
    "이전에는 정보를 저장하기 위해 데이터 세트 그룹 및 데이터 세트를 생성했으므로, \n",
    "이제는 모델 구축을 위해 S3에서 Amazon Personalize로 데이터를 로드하는 import job을 실행합니다.\n",
    "\n",
    "#### Interaction 데이터 세트 Import Job 생성"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(BUCKET_NAME, KEY_PREFIX+\"/\"+DATA_PREFIX+\"/\"+interaction_train_file)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "create_dataset_import_job_response = personalize.create_dataset_import_job(\n",
    "    jobName = \"interaction-dataset-import-\" + WORK_DATE,\n",
    "    datasetArn = interaction_dataset_arn,\n",
    "    dataSource = {\n",
    "        \"dataLocation\": \"s3://{}/{}\".format(BUCKET_NAME, KEY_PREFIX+\"/\"+DATA_PREFIX+\"/\"+interaction_train_file)\n",
    "    },\n",
    "    roleArn = role_arn\n",
    ")\n",
    "\n",
    "interation_dataset_import_job_arn = create_dataset_import_job_response['datasetImportJobArn']\n",
    "print(json.dumps(create_dataset_import_job_response, indent=2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(BUCKET_NAME, KEY_PREFIX+\"/\"+DATA_PREFIX+\"/\"+user_train_file)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "create_dataset_import_job_response = personalize.create_dataset_import_job(\n",
    "    jobName = \"users-dataset-import-\" + WORK_DATE,\n",
    "    datasetArn = user_dataset_arn,\n",
    "    dataSource = {\n",
    "        \"dataLocation\": \"s3://{}/{}\".format(BUCKET_NAME, KEY_PREFIX+\"/\"+DATA_PREFIX+\"/\"+user_train_file)\n",
    "    },\n",
    "    roleArn = role_arn\n",
    ")\n",
    "\n",
    "user_dataset_import_job_arn = create_dataset_import_job_response['datasetImportJobArn']\n",
    "print(json.dumps(create_dataset_import_job_response, indent=2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(BUCKET_NAME, KEY_PREFIX+\"/\"+DATA_PREFIX+\"/\"+user_train_file)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "create_dataset_import_job_response = personalize.create_dataset_import_job(\n",
    "    jobName = \"item-dataset-import-\" + WORK_DATE,\n",
    "    datasetArn = item_dataset_arn,\n",
    "    dataSource = {\n",
    "        \"dataLocation\": \"s3://{}/{}\".format(BUCKET_NAME, KEY_PREFIX+\"/\"+DATA_PREFIX+\"/\"+item_train_file)\n",
    "    },\n",
    "    roleArn = role_arn\n",
    ")\n",
    "\n",
    "item_dataset_import_job_arn = create_dataset_import_job_response['datasetImportJobArn']\n",
    "print(json.dumps(create_dataset_import_job_response, indent=2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%store dataset_group_arn\n",
    "%store interaction_dataset_arn\n",
    "%store user_dataset_arn\n",
    "%store item_dataset_arn\n",
    "%store interation_dataset_import_job_arn\n",
    "%store user_dataset_import_job_arn\n",
    "%store item_dataset_import_job_arn\n",
    "%store role_arn\n",
    "%store role_name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 아이템 데이터 세트 Import Job 생성"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 데이터 세트 Import job이 활성화 상태가 될 때까지 대기\n",
    "\n",
    "Import job이 완료되기까지 시간이 걸립니다. 아래 코드 셀의 출력 결과가 DatasetImportJob: ACTIVE가 될 때까지 기다려 주세요."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "\n",
    "status = None\n",
    "max_time = time.time() + 3*60*60 # 3 hours\n",
    "while time.time() < max_time:\n",
    "    describe_dataset_import_job_response = personalize.describe_dataset_import_job(\n",
    "        datasetImportJobArn = interation_dataset_import_job_arn\n",
    "    )\n",
    "    \n",
    "    dataset_import_job = describe_dataset_import_job_response[\"datasetImportJob\"]\n",
    "    if \"latestDatasetImportJobRun\" not in dataset_import_job:\n",
    "        status = dataset_import_job[\"status\"]\n",
    "        print(\"DatasetImportJob: {}\".format(status))\n",
    "    else:\n",
    "        status = dataset_import_job[\"latestDatasetImportJobRun\"][\"status\"]\n",
    "        print(\"LatestDatasetImportJobRun: {}\".format(status))\n",
    "    \n",
    "    if status == \"ACTIVE\" or status == \"CREATE FAILED\":\n",
    "        break\n",
    "        \n",
    "    time.sleep(60)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "status = None\n",
    "max_time = time.time() + 3*60*60 # 3 hours\n",
    "while time.time() < max_time:\n",
    "    describe_dataset_import_job_response = personalize.describe_dataset_import_job(\n",
    "        datasetImportJobArn = user_dataset_import_job_arn\n",
    "    )\n",
    "    \n",
    "    dataset_import_job = describe_dataset_import_job_response[\"datasetImportJob\"]\n",
    "    if \"latestDatasetImportJobRun\" not in dataset_import_job:\n",
    "        status = dataset_import_job[\"status\"]\n",
    "        print(\"DatasetImportJob: {}\".format(status))\n",
    "    else:\n",
    "        status = dataset_import_job[\"latestDatasetImportJobRun\"][\"status\"]\n",
    "        print(\"LatestDatasetImportJobRun: {}\".format(status))\n",
    "    \n",
    "    if status == \"ACTIVE\" or status == \"CREATE FAILED\":\n",
    "        break\n",
    "        \n",
    "    time.sleep(60)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "status = None\n",
    "max_time = time.time() + 3*60*60 # 3 hours\n",
    "while time.time() < max_time:\n",
    "    describe_dataset_import_job_response = personalize.describe_dataset_import_job(\n",
    "        datasetImportJobArn = item_dataset_import_job_arn\n",
    "    )\n",
    "    \n",
    "    dataset_import_job = describe_dataset_import_job_response[\"datasetImportJob\"]\n",
    "    if \"latestDatasetImportJobRun\" not in dataset_import_job:\n",
    "        status = dataset_import_job[\"status\"]\n",
    "        print(\"DatasetImportJob: {}\".format(status))\n",
    "    else:\n",
    "        status = dataset_import_job[\"latestDatasetImportJobRun\"][\"status\"]\n",
    "        print(\"LatestDatasetImportJobRun: {}\".format(status))\n",
    "    \n",
    "    if status == \"ACTIVE\" or status == \"CREATE FAILED\":\n",
    "        break\n",
    "        \n",
    "    time.sleep(60)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 솔루션 생성 및 솔루션 버전 생성\n",
    "Amazon Personalize에서 훈련된 모델을 솔루션이라고 하며, 각 솔루션에는 모델이 훈련되었을 때 주어진 데이터량과 관련된 많은 특정 버전들이 있을 수 있습니다.\n",
    "\n",
    "우선, Amazon Personalize에서 지원되는 모든 레시피(알고리즘)을 나열합니다. 리스트업된 레시피들 중 User-personalize를 하나를 선택하고 이를 사용하여 모델을 빌드해 봅니다.\n",
    "\n",
    "\n",
    "이 프로세스의 완료는 실제로 40분 이상 소요됩니다. 작업이 완료될 때까지(즉, 활성화 상태가 될 때까지) while 루프를 수행하는 방법도 있지만, 이렇게 하면 다른 셀의 실행을 차단하게 됩니다. 따라서, 많은 모델을 만들어 신속하게 배포하려면 while 루프를 사용하는 대신, 필요한 솔루션 버전들을 생성 후, SageMaker 및 Cloudwatch에서 업데이트를 확인하세요.\n",
    "\n",
    "###  레시피 리스트 확인"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "recipe_list = personalize.list_recipes()\n",
    "for recipe in recipe_list['recipes']:\n",
    "    print(recipe['recipeArn'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recipe 선택 및 솔루션 생성 \n",
    "여기에서는 알고리즘 및 학습 파라미터 선택을 하여 모델 생성을 해 봅니다.\n",
    "Personalize는 훈련된 모델을 최적화 하기위한 하이퍼 파라미터 튜닝 작업을 진행할수 있습니다. 레시피에 따라 사용 가능한 하퍼파라미터는 [여기](https://docs.aws.amazon.com/ko_kr/personalize/latest/dg/working-with-predefined-recipes.html)에서 확인해 봅니다. \n",
    "아래는 하이퍼파라미터 세팅 예제입니다. 여기에서는 시간 관계상 다른 실험에서 찾은 최적화한 결과 값을 적용하도록 합니다. \n",
    "또한 이전 단계에서 확인 했던대로 min/max_user_history_length_percentile을 조정하여 봅니다.\n",
    "\n",
    "하이퍼 파라미터에 관련하여 개발자 가이드 [하이퍼파라미터 및 HPO단원](https://docs.aws.amazon.com/ko_kr/personalize/latest/dg/customizing-solution-config-hpo.html) 참고합니다.\n",
    "\n",
    "\n",
    "```\n",
    "{\n",
    "    \"performAutoML\": false,\n",
    "    \"recipeArn\": \"arn:aws:personalize:::recipe/aws-user-personalization\",\n",
    "    \"performHPO\": true,\n",
    "    \"solutionConfig\": {\n",
    "        \"algorithmHyperParameters\": {\n",
    "            \"hidden_dimension\": \"55\"\n",
    "        },\n",
    "        \"hpoConfig\": {\n",
    "            \"algorithmHyperParameterRanges\": {\n",
    "                \"categoricalHyperParameterRanges\": [\n",
    "                    {\n",
    "                        \"name\": \"recency_mask\",\n",
    "                        \"values\": [ \"true\", \"false\" ]\n",
    "                    }\n",
    "                ],\n",
    "                \"integerHyperParameterRanges\": [\n",
    "                    {\n",
    "                        \"name\": \"bptt\",\n",
    "                        \"minValue\": 20,\n",
    "                        \"maxValue\": 40\n",
    "                    }\n",
    "                ]\n",
    "            },\n",
    "            \"hpoResourceConfig\": {\n",
    "                \"maxNumberOfTrainingJobs\": \"4\",\n",
    "                \"maxParallelTrainingJobs\": \"2\"\n",
    "            }\n",
    "        }\n",
    "    }\n",
    "}\n",
    "```\n",
    "\n",
    "},\n",
    "                ]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "user_personalization_recipe_arn=\"arn:aws:personalize:::recipe/aws-user-personalization\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution 생성 \n",
    "create_solution_response = personalize.create_solution(\n",
    "    name = \"user-personalization-\" + WORK_DATE,\n",
    "    datasetGroupArn = dataset_group_arn,\n",
    "    recipeArn = user_personalization_recipe_arn,\n",
    "    performHPO=False,\n",
    "    solutionConfig={  \n",
    "        \"featureTransformationParameters\": {\n",
    "            \"max_user_history_length_percentile\": \"0.99\",\n",
    "            \"min_user_history_length_percentile\": \"0.05\"\n",
    "        },\n",
    "\n",
    "        \"algorithmHyperParameters\": {\n",
    "            \"bptt\": \"31\",\n",
    "            \"hidden_dimension\": \"211\",\n",
    "            \"recency_mask\": \"true\"\n",
    "        },\n",
    "   }\n",
    "    \n",
    " )\n",
    "user_personalization_solution_arn = create_solution_response['solutionArn']\n",
    "print(json.dumps(create_solution_response, indent=2))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 솔루션 버전 생성\n",
    "create_solution_version_response = personalize.create_solution_version(\n",
    "    solutionArn = user_personalization_solution_arn \n",
    ")\n",
    "\n",
    "user_personalization_solution_version_arn = create_solution_version_response['solutionVersionArn']\n",
    "print(json.dumps(create_solution_version_response, indent=2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%store user_personalization_solution_version_arn\n",
    "%store user_personalization_solution_arn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "\n",
    "max_time = time.time() + 8*60*60 # 8 hours\n",
    "while time.time() < max_time:\n",
    "      \n",
    "    #hrnn status\n",
    "    describe_solution_version_response = personalize.describe_solution_version(\n",
    "        solutionVersionArn = user_personalization_solution_version_arn\n",
    "    )  \n",
    "    status= describe_solution_version_response[\"solutionVersion\"][\"status\"]\n",
    "    print(\"User-Personalization SolutionVersion: {}\".format(status))\n",
    "       \n",
    "    if  (status== \"ACTIVE\" or status == \"CREATE FAILED\"):\n",
    "        break\n",
    " \n",
    "    time.sleep(300)\n",
    "\n",
    "print(\"All solution creation completed\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 솔루션 평가하기 \n",
    "\n",
    "\n",
    "이번 파트에서는 Amazon Personalize에서 기본으로 제공하는 솔루션에 대한 평가 지표를 확인해 봅니다. \n",
    "Amazon Personalize에서는 평가 지표를 생성하기 위해 약 랜덤으로 10% 사용자의 interaction data를 테스트 용으로 활용합니다. \n",
    "\n",
    "아래 이미지는 Amazon Personalize가 데이터를 분리하는 방법을 보여줍니다. 사용자가 10 명이고 각각 10 개의 상호 작용이있는 경우 (여기에서 원은 Interaction data를 나타냄) 타임 스탬프를 기준으로 가장 오래된 것부터 최신 것까지 나열된 것입니다. Amazon Personalize는 사용자의 90 % (파란색 원)의 모든 Interaction 데이터를 사용하여 솔루션 버전을 훈련시키고 나머지 10 %는 평가를 위해 사용합니다. 나머지 10 %의 각 사용자에 대해 Interaction data (녹색 원)의 90 %가 훈련 된 모델의 입력값으로 사용됩니다. 데이터의 나머지 10 % (주황색 원)는 모델에서 생성 된 추천 결과물과 비교되고 평가 지표를 계산하는 데 사용됩니다.\n",
    "\n",
    "\n",
    "\n",
    "![personalize metrics](images/image4.png)\n",
    "\n",
    "[솔류션 평가 지표 정의](https://docs.aws.amazon.com/personalize/latest/dg/working-with-training-metrics.html)\n",
    "는 개발자 문서의 링크 참조 바랍니다. 또한 이 링크 [솔류션 평가 정의 예제](http://francescopochetti.com/recommend-expedia-hotels-with-amazon-personalize-the-magic-of-hierarchical-rnns/) 의 페이지 맨 아래 쪽을 보시면 조금 더 직관적인 그림을 보실 수 있습니다.\n",
    " <br>\n",
    "또한 reciprocal_rank_at_5, normalized_discounted_cumulative_gain_at_5,precision_at_5 의 예제는 아래와 같습니다. \n",
    "* Exmaple\n",
    "    * 5 개의 추천리스트를 제공했고, 이 중에 2번째와 5번째가 실제 데이타와 일치 했다고 하면, 쉽게 이렇게 [0,1,0,0,1] 표시 할 수 있습니다.\n",
    "        * reciprocal_rank\n",
    "            * 1/2 (0.5) # 가장 빠른 순서의 하나만을 선택 합니다\n",
    "        * normalized_discounted_cumulative_gain_at_5\n",
    "            * (1/log(1+2) + 1/log(1+5)) / (1/log(1+1) + 1/log(1+2)) = 0.6241\n",
    "        * precision_at_5\n",
    "            * 2/5 (0.4)\n",
    "\n",
    "\n",
    "\n",
    "#### 조금더 상세하고 Custum 평가 지표를 얻기 위해서 이전에 분리해둔 테스트 데이터를 가지고 캠페인 생성 후 별도 테스트를 진행하도록 합니다.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "get_solution_metrics_response = personalize.get_solution_metrics(\n",
    "    solutionVersionArn = user_personalization_solution_version_arn\n",
    ")\n",
    "\n",
    "print(json.dumps(get_solution_metrics_response, indent=2))\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 캠페인 생성하기 \n",
    "\n",
    "\n",
    "\n",
    "작동하는 솔루션 버전을 보유하고 있으므로, 이제 애플리케이션과 함께 사용할 캠페인을 생성합니다. 인프라가 프로비저닝되기까지의 시간이 소요됩니다.\n",
    "\n",
    "\n",
    "User-personalization 솔루션은 캠페인을 만들 때 itemExplorationConfig를 설정하여 콜드 아이템 탐색 가중치와 탐색 연령 제한을 구성 할 수 있습니다. 지금은 더 높은 explorationWeight를 0로 설정하고 콜드 아이템이 추천 되지 않도록 세팅합니다. explorationItemAgeCutOff를 7로 설정합니다.\n",
    "이경우 생성한 지 7 일 미만의 모든 항목 생성 시간은 콜드 항목으로 간주됩니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "create_campaign_response = personalize.create_campaign(\n",
    "    name = \"user-personalization-campaign-\" + WORK_DATE,\n",
    "    solutionVersionArn = user_personalization_solution_version_arn,\n",
    "    campaignConfig = {\"itemExplorationConfig\": {\"explorationWeight\": \"0\", \"explorationItemAgeCutOff\": \"7\"}},\n",
    "    minProvisionedTPS = 1\n",
    ")\n",
    "\n",
    "user_personalization_campaign_arn = create_campaign_response['campaignArn']\n",
    "print(json.dumps(create_campaign_response, indent=2))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%store user_personalization_campaign_arn "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "status = None\n",
    "max_time = time.time() + 3*60*60 # 3 hours\n",
    "while time.time() < max_time:\n",
    "    describe_campaign_response = personalize.describe_campaign(\n",
    "        campaignArn = user_personalization_campaign_arn\n",
    "    )\n",
    "    status = describe_campaign_response[\"campaign\"][\"status\"]\n",
    "    print(\"Campaign: {}\".format(status))\n",
    "    \n",
    "    if status == \"ACTIVE\" or status == \"CREATE FAILED\":\n",
    "        break\n",
    "        \n",
    "    time.sleep(60)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "describe_campaign_response = personalize.describe_campaign(campaignArn = user_personalization_campaign_arn)\n",
    "campaign_summary = describe_campaign_response[\"campaign\"]\n",
    "campaign_summary"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "items=pd.read_csv(item_file)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_movie_title(movie_id):\n",
    "    \"\"\"\n",
    "    Takes in an ID, returns a title\n",
    "    \"\"\"\n",
    "    movie_id = int(movie_id)\n",
    "    movie_title=items[items['ITEM_ID']==movie_id]['TITLE']\n",
    "    return (movie_title.tolist())\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "user_id= 100\n",
    "get_recommendations_response = personalize_runtime.get_recommendations(\n",
    "    campaignArn = user_personalization_campaign_arn,\n",
    "    userId = str(user_id),\n",
    ")\n",
    "# Update DF rendering\n",
    "pd.set_option('display.max_rows', 30)\n",
    "\n",
    "print(\"Recommendations for user: \", user_id)\n",
    "\n",
    "item_list = get_recommendations_response['itemList']\n",
    "\n",
    "recommendation_title_list = []\n",
    "recommendation_id_list=[]\n",
    "for item in item_list:\n",
    "    title = get_movie_title(item['itemId'])\n",
    "    score=item['score']\n",
    "    recommendation_title_list.append([title,score])\n",
    "    recommendation_id_list.append(item['itemId'])\n",
    "recommendations_df = pd.DataFrame(recommendation_title_list ,columns = ['OriginalRecs','score'])\n",
    "recommendations_df"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "conda_python3",
   "language": "python",
   "name": "conda_python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}