# AUTOGENERATED! DO NOT EDIT! File to edit: database.ipynb (unless otherwise specified).

__all__ = ['DATABASE_TABLE_NAME', 'INVENTORY_BUCKET_NAME', 'INPUT_BUCKET_NAME', 'REGION', 'ACCESS_KEY_ID',
           'SECRET_ACCESS_KEY', 'LINEKEY', 'DBHASHLOCATION', 'DBCACHELOCATION', 'INTCOLS', 'ProductDatabase', 'cacheDb',
           'lambdaDumpToS3', 'Product', 'ValueUpdate', 'chunks', 'lambdaUpdateProduct', 'updateS3Input',
           'lambdaUpdateS3', 'ProductsFromList', 'lambdaProductsFromList', 'lambdaSingleQuery', 'lambdaAllQuery',
           'lambdaAllQueryFeather']

# Cell
import pandas as pd
from datetime import datetime
from pynamodb.models import Model
from pynamodb.attributes import UnicodeAttribute, NumberAttribute, JSONAttribute, BooleanAttribute, BinaryAttribute
from pynamodb.indexes import GlobalSecondaryIndex, AllProjection
from botocore.config import Config
from s3bz.s3bz import S3
from pprint import pprint
from nicHelper.wrappers import add_method, add_class_method, add_static_method
from nicHelper.dictUtil import stripDict, printDict
from nicHelper.exception import errorString
from nicHelper import pdUtils
from awsSchema.apigateway import Response, Event
from dataclasses_json import dataclass_json, Undefined, CatchAll
from dataclasses import dataclass
from typing import List
from .query import Querier
from .helpers import Helpers
from .s3 import S3Cache
from .schema import KeySchema, createIndex
from .update import Updater
from requests import post

import pickle, json, boto3, bz2, requests, validators, os, logging, traceback, zlib

# Cell
import os

DATABASE_TABLE_NAME = os.environ.get('DATABASE_TABLE_NAME')
INVENTORY_BUCKET_NAME = os.environ.get('INVENTORY_BUCKET_NAME')
INPUT_BUCKET_NAME = os.environ.get('INPUT_BUCKET_NAME')
REGION = os.environ.get('REGION') or 'ap-southeast-1'
ACCESS_KEY_ID = os.environ.get('USER') or None
SECRET_ACCESS_KEY = os.environ.get('PW') or None
LINEKEY= os.environ.get('LINEKEY')
DBHASHLOCATION = '/mnt/efs/database.hash'
DBCACHELOCATION = '/mnt/efs/database.cache'
INTCOLS = json.loads(os.environ.get('INTCOLS') or '{}')

try:
  DAX_ENDPOINT = os.environ['DAX_ENDPOINT']
except KeyError as e:
  DAX_ENDPOINT = None
  print(f'dax endpoint missing {e}')

print(DAX_ENDPOINT)

# Cell
# dont forget to import dependent classes from the relevant notebooks
class ProductDatabase( Querier, Helpers, KeySchema, S3Cache, Updater):
  class Meta:
    aws_access_key_id = ACCESS_KEY_ID
    aws_secret_access_key = SECRET_ACCESS_KEY
    table_name = DATABASE_TABLE_NAME
    region = REGION
    billing_mode='PAY_PER_REQUEST'
    dax_read_endpoints = [DAX_ENDPOINT] if DAX_ENDPOINT else None
    dax_write_endpoints = [DAX_ENDPOINT] if DAX_ENDPOINT else None


  TRUE = 'Y'
  FALSE = 'N'

  # indexes
  needsUpdateIndex = createIndex('needsUpdate','sellingPrice')
  cprcodeIndex = createIndex('cprcode', 'sellingPrice')
  oprcodeIndex = createIndex('oprcode', 'sellingPrice')
  pr_dpcodeIndex = createIndex('pr_dpcode', 'sellingPrice')
  pr_barcodeIndex = createIndex('pr_barcode', 'sellingPrice')
  pr_barcode2Index = createIndex('pr_barcode2', 'sellingPrice')
  pr_suref3Index = createIndex('pr_suref3', 'sellingPrice')
  pr_sa_methodIndex = createIndex('pr_sa_method', 'sellingPrice')



  def __repr__(self):
    return self.returnKW(self.data)
  def syncIndex(self):
    keys = {k:v for k,v in self.data if k in []}


  @staticmethod
  def returnKW(inputDict):
    outputStr = 'ProductDatabase Object\n'
    for k,v in inputDict.items():
      outputStr += f'{k} {v}\n'
    return outputStr
  @staticmethod
  def convertIntCols(products:List[dict])->List[dict]:
    convertedProducts = [{k:int(float(v)) if k in INTCOLS else v for k,v in product.items()}for product in products]
    return convertedProducts





# Cell
@add_class_method(ProductDatabase)
def cacheDb(cls, bucketName = INVENTORY_BUCKET_NAME, key = 'allData', limit=100, **kwargs):
  '''cache db to s3 and local efs'''
  db:pd.DataFrame = cls.loadFromCache(key=key, localCache=DBCACHELOCATION,
                                      localHash=DBHASHLOCATION, bucket=bucketName)
  logging.debug(f'origin item is shape{db.shape}')
  #### get change
  print('quering for changes')
  changes = list(cls.needsUpdateIndex.query(cls.TRUE, limit=limit))
  print('convert to df')
  changesDf = cls.toDf(changes)
  changesDf.set_index('cprcode', inplace=True)
  print(changesDf.shape)
#   db.set_index('cprcode', inplace=True)
  updatedDb = db.append(changesDf).drop_duplicates('cprcode', keep='last')
#   updatedDb.reset_index(inplace=True)
  cls.saveRemoteCache(updatedDb)
  print('saving to remote cache')
  with cls.batch_write() as batch:
    for item in changes:
      item.setNoUpdate(batch=batch)
  cls.notify(f'db shape is {db.shape}')
  return updatedDb

# Cell
def lambdaDumpToS3(event, _):
  result = ProductDatabase.cacheDb(limit = 500)
  lastItem = result.iloc[0].to_json()
  return Response.returnSuccess({'result': json.loads(lastItem)})
#   print(result.fillna(''))
#   return Response.getReturn(body = {'result': result.fillna('').iloc[0].to_dict()})

# Cell
@dataclass_json(undefined=Undefined.INCLUDE)
@dataclass
class Product:
  iprcode: int
  cprcode: int
  data: CatchAll
@dataclass_json
@dataclass
class ValueUpdate:
  items: List[Product]

# Cell
def chunks(l, n): return [l[x: x+n] for x in range(0, len(l), n)]

# Cell
def lambdaUpdateProduct (event, _):
  products = Event.parseBody(event)['products']
  convertedProducts = ProductDatabase.convertIntCols(products)
  result = ProductDatabase.valueUpdate2({'items':convertedProducts})
  return Response.getReturn(body = result)

# Cell
@add_class_method(ProductDatabase)
def updateS3Input(cls, inputBucketName:str = INPUT_BUCKET_NAME, key:str = '', **kwargs):
  products = S3.load(key=key, bucket = inputBucketName,  **kwargs)
  updateResult = cls.valueUpdate2({'items':cls.convertIntCols(products)})
  return updateResult


# Cell
def lambdaUpdateS3(event, _):
  inputKeyName = Event.from_dict(event).key()
  try:
    updateResult = ProductDatabase.updateS3Input(
      inputBucketName=INPUT_BUCKET_NAME, key= inputKeyName)
  except:
    ProductDatabase.notify(f'error updating with s3 {errorString()}')
    return Response.returnError(errorString())



  ProductDatabase.notify(f'success update {updateResult}')
  return Response.getReturn(body = updateResult)

# Cell
@dataclass_json
@dataclass
class ProductsFromList:
  iprcodes: List[str]

# Cell
def lambdaProductsFromList(event, *args):
  productsFromList = Event.parseDataClass(ProductsFromList,event)
  result:pd.DataFrame = ProductDatabase.productsFromList(productsFromList.iprcodes)
  results:List[ProductDatabase] = ProductDatabase.fromDf(result)
  resultDicts:List[dict] = ProductDatabase.toListDict(results)
  return Response.returnSuccess(resultDicts)

# Cell
def lambdaSingleQuery(event, _):
  key, value = Event.from_dict(event).firstKey()
  try:
    result = ProductDatabase.singleProductQuery({key:value}).to_dict()
  except Exception as e:
    return Response.returnError(f'{e}')
  return Response.returnSuccess(body = result)

# Cell
def lambdaAllQuery(event, *args):
  url = ProductDatabase.allQuery(bucket = INVENTORY_BUCKET_NAME, key='allData-json.zl')
  return Response.getReturn(body = {'url': url})

# Cell
def lambdaAllQueryFeather(event, *args):
  key = 'allData'
  bucket = INVENTORY_BUCKET_NAME
  url = ProductDatabase.allQuery(bucket = INVENTORY_BUCKET_NAME, key=key)
  hashCode = pdUtils.loadRemoteHash(key=key, bucket=bucket, useUrl = True)
  return Response.getReturn(body = {'url': url, 'hash': hashCode})