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

__all__ = ['DBHASHLOCATION', 'DBCACHELOCATION', 'DATABASE_TABLE_NAME', 'INVENTORY_BUCKET_NAME', 'INPUT_BUCKET_NAME',
           'REGION', 'ACCESS_KEY_ID', 'SECRET_ACCESS_KEY', 'LINEKEY', 'Updater', 'updateWithDict', 'Product',
           'ValueUpdate', 'chunks', 'valueUpdate2']

# Cell
from s3bz.s3bz import S3
from nicHelper.wrappers import add_method, add_class_method, add_static_method
from nicHelper.dictUtil import stripDict, printDict, hashDict, saveStringToFile, loadStringFromFile, saveDictToFile, loadDictFromFile
from nicHelper.exception import errorString
from dict_hash import dict_hash, sha256
from base64 import b64encode, b64decode
from dataclasses_json import dataclass_json, Undefined, CatchAll
from dataclasses import dataclass
from typing import List
from datetime import datetime
import pandas as pd
import os, logging

# Cell
import os
DBHASHLOCATION = '/tmp/database.hash'
DBCACHELOCATION = '/tmp/database.cache'
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')

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

# Cell
class Updater:
  pass

# Cell
@add_class_method(Updater)
def updateWithDict(cls, originalObject:Updater, inputDict:dict ):
  data = originalObject.data
  data.update(inputDict)
  return cls.fromDict(data)

# Cell
@dataclass_json(undefined=Undefined.INCLUDE)
@dataclass
class Product:
  iprcode: str
  cprcode: str
  data: CatchAll
@dataclass_json
@dataclass
class ValueUpdate:
  items: List[Product]
#export
def chunks(l, n): return [l[x: x+n] for x in range(0, len(l), n)]
@add_class_method(Updater)
def valueUpdate2(cls, inputs:List[dict]):
    '''
      check for difference and batch update the changes in product data
    '''
    t0 = datetime.now()
    ### validate input
    try:
      validInputs = ValueUpdate.from_dict(inputs).to_dict().get('items')
    except Exception as e:
      raise KeyError(f'input failed validation {e}')
      return

    itemsUpdated = {'success':0, 'failure': 0, 'skipped': 0 ,'failureMessage':[], 'timetaken(ms)': 0}
    t0 = datetime.now()

    logging.info(f'there are {len(validInputs)} products to update')

    print(f'input validated {(datetime.now()-t0).total_seconds()*1000} ms')
    ##### dividing input into batch of 500
    inputBatches = chunks(validInputs, 500)
    print(f'divided into chunks {(datetime.now()-t0).total_seconds()*1000} ms')
    db = cls.loadFromCache().fillna('none')
    print(f'get all from s3 {(datetime.now()-t0).total_seconds()*1000} ms')

    for inputBatch in inputBatches:
      with cls.batch_write() as batch:
        # loop through each product
        for input_ in inputBatch:
          iprcode = input_['iprcode']
          cprcode = input_['cprcode']

          ##### check if product is in the database, if not, create an empty class with the product code
          if 'cprcode' in db.columns:
            incumbentDb = db[db['cprcode']==cprcode]
            if incumbentDb.empty:
              incumbentSeries = pd.Series()
            else:
              incumbentSeries = incumbentDb.iloc[0]
            if incumbentSeries.any:
              print(f'incumbentSeries is type {type(incumbentSeries)}')
              incumbentItem = cls.fromSeries(incumbentSeries)
            else: incumbentItem = cls.fromDict({'iprcode': iprcode, 'cprcode': cprcode})
          else: incumbentItem = cls.fromDict({'iprcode': iprcode, 'cprcode': cprcode})

          ##### make a copy of original data
          originalData = incumbentItem.data.copy()
          ###### update data
          updatedData = cls.updateWithDict(incumbentItem, input_)

          logging.info(f'incumbentBr is {incumbentItem.iprcode}\n, prcode is {iprcode}')

          # check for difference
          try:
            if updatedData.data != originalData:
              logging.info(f'product {iprcode} has changed from \n{originalData} \n{updatedData.data}')
              batch.save(updatedData)
              itemsUpdated['success'] += 1
            else:
              logging.info(f'no change for {iprcode}')
              itemsUpdated['skipped'] += 1
          except Exception as e:
            itemsUpdated['failure'] += 1
            itemsUpdated['failureMessage'].append(e)

        # log time taken
        itemsUpdated['timetaken(ms)'] = (datetime.now()- t0).total_seconds()*1000
    return itemsUpdated